mirror of
https://github.com/rdolbeau/NuBusFPGA.git
synced 2024-06-01 11:42:13 +00:00
Compare commits
3 Commits
cb5d09df4d
...
e82802de4a
Author | SHA1 | Date | |
---|---|---|---|
|
e82802de4a | ||
|
d461da9d8b | ||
|
c6e33c5caf |
|
@ -10,7 +10,7 @@ This project was 'spun off' the [SBusFPGA](https://github.com/rdolbeau/SBusFPGA)
|
||||||
|
|
||||||
## Current status
|
## Current status
|
||||||
|
|
||||||
First prototype is working in a Quadra 650, running MacOS 8.1. It implements a single-screen-resolution, windowboxed multi-resolution, depth-switchable (1/2/4/8/16/32 bits) framebuffer over DVI-in-HDMI-connector (will work with any HDMI-compliant monitor). The framebuffer can be used as secondary/primary/only framebuffer in the machine running OS8.1. Qemu tests indicate this should work with 7.1 & 7.5/7.6 as well. An alternate HDMI PHY also supports audio, enabled as a 8/16 bits, mono/stereo, 44.1 Hz output compoenent in MacOS.
|
First prototype is working in a Quadra 650, running MacOS 8.1. It implements a single-screen-resolution, windowboxed multi-resolution, depth-switchable (1/2/4/8/16/32 bits) framebuffer over DVI-in-HDMI-connector (will work with any HDMI-compliant monitor). The framebuffer can be used as secondary/primary/only framebuffer in the machine running OS8.1. Qemu tests indicate this should work with 7.1 & 7.5/7.6 as well. An alternate HDMI PHY also supports audio, enabled as a 8/16 bits, mono/stereo, 44.1 kHz output component in MacOS.
|
||||||
|
|
||||||
Some basic acceleration now exists for 8/16/32 bits, doing rectangle screen-to-screen blits and pattern rectangle fills. 1/2/4 bits also has some acceleration, but only for byte-aligned cases.
|
Some basic acceleration now exists for 8/16/32 bits, doing rectangle screen-to-screen blits and pattern rectangle fills. 1/2/4 bits also has some acceleration, but only for byte-aligned cases.
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,10 @@ APPLEINCS=${NS816DECLROMDIR}/atrap.inc ${NS816DECLROMDIR}/declrom.inc ${NS816DEC
|
||||||
|
|
||||||
HRES=1920
|
HRES=1920
|
||||||
VRES=1080
|
VRES=1080
|
||||||
|
QEMU=no
|
||||||
|
ifeq ($(QEMU),yes)
|
||||||
|
CFLAGS+=-DQEMU
|
||||||
|
endif
|
||||||
|
|
||||||
CSRC_VIDEO=NuBusFPGADrvr_OpenClose.c NuBusFPGADrvr_Ctrl.c NuBusFPGADrvr_Status.c NuBusFPGAPrimaryInit_Primary.c NuBusFPGAPrimaryInit_RamInit.c NuBusFPGASecondaryInit_Secondary.c
|
CSRC_VIDEO=NuBusFPGADrvr_OpenClose.c NuBusFPGADrvr_Ctrl.c NuBusFPGADrvr_Status.c NuBusFPGAPrimaryInit_Primary.c NuBusFPGAPrimaryInit_RamInit.c NuBusFPGASecondaryInit_Secondary.c
|
||||||
CSRC_RAMDSK=NuBusFPGARAMDskDrvr_OpenClose.c NuBusFPGARAMDskDrvr_Ctrl.c NuBusFPGARAMDskDrvr_Prime.c NuBusFPGARAMDskDrvr_Status.c myrle.c
|
CSRC_RAMDSK=NuBusFPGARAMDskDrvr_OpenClose.c NuBusFPGARAMDskDrvr_Ctrl.c NuBusFPGARAMDskDrvr_Prime.c NuBusFPGARAMDskDrvr_Status.c myrle.c
|
||||||
|
|
|
@ -34,8 +34,10 @@ UInt32 Primary(SEBlock* seblock) {
|
||||||
vres = __builtin_bswap32((UInt32)PRIM_READREG(GOBOFB_VRES)); // fixme: endianness
|
vres = __builtin_bswap32((UInt32)PRIM_READREG(GOBOFB_VRES)); // fixme: endianness
|
||||||
|
|
||||||
/* initialize DRAM controller */
|
/* initialize DRAM controller */
|
||||||
|
#ifndef QEMU
|
||||||
sdram_init(a32);
|
sdram_init(a32);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* grey the screen */
|
/* grey the screen */
|
||||||
/* should switch to HW ? */
|
/* should switch to HW ? */
|
||||||
a32_l0 = a32;
|
a32_l0 = a32;
|
||||||
|
|
|
@ -269,7 +269,7 @@ int main(int argc, char **argv) {
|
||||||
fprintf(fd, "\tOSLstEntry\tsRsrc_GoboFB_R%hux%hu,_sRsrc_GoboFB_R%hux%hu/* video sRsrc List */\n", hres, vres, hres, vres);
|
fprintf(fd, "\tOSLstEntry\tsRsrc_GoboFB_R%hux%hu,_sRsrc_GoboFB_R%hux%hu/* video sRsrc List */\n", hres, vres, hres, vres);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//fprintf(fd, "\tOSLstEntry\tsRsrc_RAMDsk,_sRsrc_RAMDsk\n");
|
fprintf(fd, "\tOSLstEntry\tsRsrc_RAMDsk,_sRsrc_RAMDsk\n");
|
||||||
fprintf(fd, "\tOSLstEntry\tsRsrc_HDMIAudio,_sRsrc_HDMIAudio\n");
|
fprintf(fd, "\tOSLstEntry\tsRsrc_HDMIAudio,_sRsrc_HDMIAudio\n");
|
||||||
fprintf(fd, "\tDatLstEntry endOfList, 0\n");
|
fprintf(fd, "\tDatLstEntry endOfList, 0\n");
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit f93c95ba1eda007ffce0cf5f9c5a3421afbcfdc6
|
Subproject commit 9e350c5962f1dc8f43091a3f44a7c1f8071d2bff
|
|
@ -2,7 +2,7 @@
|
||||||
source /opt/Xilinx/Vivado/2020.1/settings64.sh
|
source /opt/Xilinx/Vivado/2020.1/settings64.sh
|
||||||
export LD_LIBRARY_PATH=/opt/Xilinx/Vivado/2020.1/lib/lnx64.o/SuSE
|
export LD_LIBRARY_PATH=/opt/Xilinx/Vivado/2020.1/lib/lnx64.o/SuSE
|
||||||
|
|
||||||
python3 nubus_to_fpga_soc.py --build --csr-csv csr.csv --csr-json csr.json --variant=ztex2.13a --version=V1.2 --sys-clk-freq 100e6 --goblin --goblin-res 1920x1080@60Hz --hdmi
|
python3 nubus_to_fpga_soc.py --build --csr-csv csr.csv --csr-json csr.json --variant=ztex2.13a --version=V1.2 --sys-clk-freq 100e6 --goblin --goblin-res 1920x1080@60Hz --hdmi # --ethernet
|
||||||
|
|
||||||
#python3 nubus_to_fpga_soc.py --csr-csv csr.csv --csr-json csr.json --variant=ztex2.13a --version=V1.2 --sys-clk-freq 100e6
|
#python3 nubus_to_fpga_soc.py --csr-csv csr.csv --csr-json csr.json --variant=ztex2.13a --version=V1.2 --sys-clk-freq 100e6
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,13 @@ class NuBus(Module):
|
||||||
#led1 = platform.request("user_led", 1)
|
#led1 = platform.request("user_led", 1)
|
||||||
|
|
||||||
if (usesampling):
|
if (usesampling):
|
||||||
# when using 'sampling', the NuBus signals are sampled at sys_clk frequency instead of synchronously using nubus_clk
|
# when using 'sampling', the NuBus clock is sampled at sys_clk frequency instead of used directly
|
||||||
|
# the other signals are still sampled synchronously, and the buffer used from sysclk afterwards
|
||||||
# I'm not completely sure about timings, but in practice it seems to work...
|
# I'm not completely sure about timings, but in practice it seems to work...
|
||||||
# The major benefit is that when a read is detected, it is sent to the Wishbone synchronously as the signals are already in sys_clk
|
# The major benefit is that when a read is detected, it is sent to the Wishbone synchronously as the signals are already in sys_clk
|
||||||
# And when Wishbone answers, we just wait for the next detected NuBus edge to answer
|
# And when Wishbone answers, we just wait for the next detected NuBus edge to answer
|
||||||
# It significantly improves read latency
|
# It significantly improves read latency
|
||||||
# Writes don't see the same improvement, are they always are fire-and-forget in a FIFO anyway
|
# Writes don't see the same improvement, as they always are fire-and-forget in a FIFO anyway
|
||||||
nub_clk = ClockSignal(cd_nubus)
|
nub_clk = ClockSignal(cd_nubus)
|
||||||
nub_resetn = ~ResetSignal(cd_nubus)
|
nub_resetn = ~ResetSignal(cd_nubus)
|
||||||
nub_clk_prev_bits = 4 # how many cycles after posedge do we still dare set some signals (i.e. still before setup time before negedge)
|
nub_clk_prev_bits = 4 # how many cycles after posedge do we still dare set some signals (i.e. still before setup time before negedge)
|
||||||
|
@ -39,10 +40,16 @@ class NuBus(Module):
|
||||||
self.sync += [
|
self.sync += [
|
||||||
nub_clk_prev[i].eq(nub_clk_prev[i-1]) for i in range(1, nub_clk_prev_bits)
|
nub_clk_prev[i].eq(nub_clk_prev[i-1]) for i in range(1, nub_clk_prev_bits)
|
||||||
]
|
]
|
||||||
self.sync += [
|
#self.sync += [
|
||||||
nub_clk_negedge.eq(~nub_clk & nub_clk_prev[0]),
|
# nub_clk_negedge.eq(~nub_clk & nub_clk_prev[0]),
|
||||||
nub_clk_posedge.eq( nub_clk & ~nub_clk_prev[0]),
|
# nub_clk_posedge.eq( nub_clk & ~nub_clk_prev[0]),
|
||||||
nub_clk_insetup.eq( nub_clk & (nub_clk_prev != ((2**nub_clk_prev_bits)-1))), # if one of the previous X cycles is zero, we're early enough to set up signals
|
# nub_clk_insetup.eq( nub_clk & (nub_clk_prev != ((2**nub_clk_prev_bits)-1))), # if one of the previous X cycles is zero, we're early enough to set up signals
|
||||||
|
#]
|
||||||
|
# this should use double sampling; however using [1] and [2] break, perhaps the detection is too late?
|
||||||
|
self.comb += [
|
||||||
|
nub_clk_negedge.eq(~nub_clk_prev[0] & nub_clk_prev[1]),
|
||||||
|
nub_clk_posedge.eq( nub_clk_prev[0] & ~nub_clk_prev[1]),
|
||||||
|
nub_clk_insetup.eq( nub_clk_prev[0] & (nub_clk_prev[1:nub_clk_prev_bits] != ((2**(nub_clk_prev_bits-1))-1))), # if one of the previous X cycles is zero, we're early enough to set up signals
|
||||||
]
|
]
|
||||||
|
|
||||||
# Signals for tri-stated nubus access
|
# Signals for tri-stated nubus access
|
||||||
|
@ -148,66 +155,34 @@ class NuBus(Module):
|
||||||
write_fifo_din = Record(write_fifo_layout)
|
write_fifo_din = Record(write_fifo_layout)
|
||||||
self.comb += write_fifo.din.eq(write_fifo_din.raw_bits())
|
self.comb += write_fifo.din.eq(write_fifo_din.raw_bits())
|
||||||
|
|
||||||
if (usesampling):
|
# nubus-synchronous sampling (in Verilog for negedge)
|
||||||
# sys_clk sampling of the nubus signals
|
self.specials += Instance("nubus_sampling",
|
||||||
self.sync += [
|
i_nub_clkn = ClockSignal(cd_nubus),
|
||||||
#If((~nub_clk & nub_clk_prev[0]), # simultaneous with setting negedge
|
i_nub_resetn = ~ResetSignal(cd_nubus),
|
||||||
If(nub_clk_negedge,
|
i_nub_tm0n = tm0_i_n,
|
||||||
sampled_tm0.eq(~tm0_i_n),
|
i_nub_tm1n = tm1_i_n,
|
||||||
sampled_tm1.eq(~tm1_i_n),
|
i_nub_startn = start_i_n,
|
||||||
sampled_start.eq(~start_i_n),
|
i_nub_rqstn = rqst_i_n,
|
||||||
sampled_rqst.eq(~rqst_i_n),
|
i_nub_ackn = ack_i_n,
|
||||||
sampled_ack.eq(~ack_i_n),
|
i_nub_adn = ad_i_n,
|
||||||
sampled_ad.eq(~ad_i_n),
|
|
||||||
)
|
o_tm0 = sampled_tm0,
|
||||||
]
|
o_tm1 = sampled_tm1,
|
||||||
self.comb += [
|
o_start = sampled_start,
|
||||||
decoded_block.eq(sampled_ad[1] & ~sampled_ad[0] & ~sampled_tm0), # 1x block write or 1x block read
|
o_rqst = sampled_rqst,
|
||||||
decoded_sel[3].eq(sampled_tm1 & sampled_ad[1] & sampled_ad[0] & sampled_tm0 # Byte 3
|
o_ack = sampled_ack,
|
||||||
| sampled_tm1 & sampled_ad[1] & sampled_ad[0] & ~sampled_tm0 # Half 1
|
o_ad = sampled_ad,
|
||||||
| sampled_tm1 & ~sampled_ad[1] & ~sampled_ad[0] & ~sampled_tm0 # Word
|
|
||||||
),
|
o_sel = decoded_sel,
|
||||||
decoded_sel[2].eq(sampled_tm1 & sampled_ad[1] & ~sampled_ad[0] & sampled_tm0 # Byte 2
|
o_block = decoded_block,
|
||||||
| sampled_tm1 & sampled_ad[1] & sampled_ad[0] & ~sampled_tm0 # Half 1
|
o_busy = decoded_busy,
|
||||||
| sampled_tm1 & ~sampled_ad[1] & ~sampled_ad[0] & ~sampled_tm0 # Word
|
)
|
||||||
),
|
|
||||||
decoded_sel[1].eq(sampled_tm1 & ~sampled_ad[1] & sampled_ad[0] & sampled_tm0 # Byte 1
|
|
||||||
| sampled_tm1 & ~sampled_ad[1] & sampled_ad[0] & ~sampled_tm0 # Half 0
|
|
||||||
| sampled_tm1 & ~sampled_ad[1] & ~sampled_ad[0] & ~sampled_tm0 # Word
|
|
||||||
),
|
|
||||||
decoded_sel[0].eq(sampled_tm1 & ~sampled_ad[1] & ~sampled_ad[0] & sampled_tm0 # Byte 0
|
|
||||||
| sampled_tm1 & ~sampled_ad[1] & sampled_ad[0] & ~sampled_tm0 # Half 0
|
|
||||||
| sampled_tm1 & ~sampled_ad[1] & ~sampled_ad[0] & ~sampled_tm0 # Word
|
|
||||||
),
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
# nubus-synchronous sampling (in Verilog for negedge)
|
|
||||||
self.specials += Instance("nubus_sampling",
|
|
||||||
i_nub_clkn = ClockSignal(cd_nubus),
|
|
||||||
i_nub_resetn = ~ResetSignal(cd_nubus),
|
|
||||||
i_nub_tm0n = tm0_i_n,
|
|
||||||
i_nub_tm1n = tm1_i_n,
|
|
||||||
i_nub_startn = start_i_n,
|
|
||||||
i_nub_rqstn = rqst_i_n,
|
|
||||||
i_nub_ackn = ack_i_n,
|
|
||||||
i_nub_adn = ad_i_n,
|
|
||||||
|
|
||||||
o_tm0 = sampled_tm0,
|
|
||||||
o_tm1 = sampled_tm1,
|
|
||||||
o_start = sampled_start,
|
|
||||||
o_rqst = sampled_rqst,
|
|
||||||
o_ack = sampled_ack,
|
|
||||||
o_ad = sampled_ad,
|
|
||||||
|
|
||||||
o_sel = decoded_sel,
|
|
||||||
o_block = decoded_block,
|
|
||||||
o_busy = decoded_busy,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.read_ctr = read_ctr = Signal(32)
|
self.read_ctr = read_ctr = Signal(32)
|
||||||
self.writ_ctr = writ_ctr = Signal(32)
|
self.writ_ctr = writ_ctr = Signal(32)
|
||||||
|
|
||||||
if (usesampling):
|
if (usesampling):
|
||||||
|
# ############# usesampling FSM
|
||||||
self.submodules.slave_fsm = slave_fsm = FSM(reset_state="Reset")
|
self.submodules.slave_fsm = slave_fsm = FSM(reset_state="Reset")
|
||||||
slave_fsm.act("Reset",
|
slave_fsm.act("Reset",
|
||||||
NextState("Idle")
|
NextState("Idle")
|
||||||
|
@ -324,7 +299,9 @@ class NuBus(Module):
|
||||||
ack_o_n.eq(0),
|
ack_o_n.eq(0),
|
||||||
NextState("Idle"),
|
NextState("Idle"),
|
||||||
)
|
)
|
||||||
|
# ############# end of usesampling FSM
|
||||||
else:
|
else:
|
||||||
|
# ############# non-usesampling FSM
|
||||||
self.submodules.slave_fsm = slave_fsm = ClockDomainsRenamer(cd_nubus)(FSM(reset_state="Reset"))
|
self.submodules.slave_fsm = slave_fsm = ClockDomainsRenamer(cd_nubus)(FSM(reset_state="Reset"))
|
||||||
slave_fsm.act("Reset",
|
slave_fsm.act("Reset",
|
||||||
NextState("Idle")
|
NextState("Idle")
|
||||||
|
@ -381,6 +358,7 @@ class NuBus(Module):
|
||||||
NextState("Idle"),
|
NextState("Idle"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
# ############# end of non-usesampling FSM
|
||||||
|
|
||||||
# connect the write FIFO inputs
|
# connect the write FIFO inputs
|
||||||
self.comb += [ write_fifo_din.adr.eq(current_adr), # recorded
|
self.comb += [ write_fifo_din.adr.eq(current_adr), # recorded
|
||||||
|
@ -721,13 +699,15 @@ class NuBus(Module):
|
||||||
nub_adn = platform.request("ad_3v3_n") # V1.0: from CPLD ; V1.2: from shifters
|
nub_adn = platform.request("ad_3v3_n") # V1.0: from CPLD ; V1.2: from shifters
|
||||||
nub_idn = platform.request("id_3v3_n") # V1.0: from CPLD (4 bits) ; V1.2: from shifters (3 bits, /ID3 is always 0)
|
nub_idn = platform.request("id_3v3_n") # V1.0: from CPLD (4 bits) ; V1.2: from shifters (3 bits, /ID3 is always 0)
|
||||||
|
|
||||||
|
# idem between V1.0 and V1.2
|
||||||
|
self.specials += Tristate(nub_adn, ad_o_n, ad_oe, ad_i_n)
|
||||||
|
|
||||||
# Tri-state
|
# Tri-state
|
||||||
if (version == "V1.0"):
|
if (version == "V1.0"):
|
||||||
# tri-state communication with CPLD
|
# tri-state communication with CPLD
|
||||||
self.specials += Tristate(nub_tm0n, tm0_o_n, tmo_oe, tm0_i_n)
|
self.specials += Tristate(nub_tm0n, tm0_o_n, tmo_oe, tm0_i_n)
|
||||||
self.specials += Tristate(nub_tm1n, tm1_o_n, tmo_oe, tm1_i_n)
|
self.specials += Tristate(nub_tm1n, tm1_o_n, tmo_oe, tm1_i_n)
|
||||||
self.specials += Tristate(nub_ackn, ack_o_n, tmo_oe, ack_i_n)
|
self.specials += Tristate(nub_ackn, ack_o_n, tmo_oe, ack_i_n)
|
||||||
self.specials += Tristate(nub_adn, ad_o_n, ad_oe, ad_i_n)
|
|
||||||
self.specials += Tristate(nub_startn, start_o_n, master_oe, start_i_n)
|
self.specials += Tristate(nub_startn, start_o_n, master_oe, start_i_n)
|
||||||
elif (version == "V1.2"):
|
elif (version == "V1.2"):
|
||||||
# input only
|
# input only
|
||||||
|
@ -735,7 +715,6 @@ class NuBus(Module):
|
||||||
tm0_i_n.eq(nub_tm0n),
|
tm0_i_n.eq(nub_tm0n),
|
||||||
tm1_i_n.eq(nub_tm1n),
|
tm1_i_n.eq(nub_tm1n),
|
||||||
ack_i_n.eq(nub_ackn),
|
ack_i_n.eq(nub_ackn),
|
||||||
ad_i_n.eq(nub_adn),
|
|
||||||
start_i_n.eq(nub_startn),
|
start_i_n.eq(nub_startn),
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
|
@ -787,6 +766,7 @@ class NuBus(Module):
|
||||||
nf_grant = platform.request("grant") # V1.0: from cpld
|
nf_grant = platform.request("grant") # V1.0: from cpld
|
||||||
nf_nubus_master_dir = platform.request("nubus_master_dir") # V1.0: to cpld
|
nf_nubus_master_dir = platform.request("nubus_master_dir") # V1.0: to cpld
|
||||||
nf_fpga_to_cpld_signal = platform.request("fpga_to_cpld_signal") # V1.0: to cpld, 'rqstoen'
|
nf_fpga_to_cpld_signal = platform.request("fpga_to_cpld_signal") # V1.0: to cpld, 'rqstoen'
|
||||||
|
|
||||||
|
|
||||||
# NuBus90 signals, , for completeness
|
# NuBus90 signals, , for completeness
|
||||||
nub_clk2xn = ClockSignal(cd_nubus90)
|
nub_clk2xn = ClockSignal(cd_nubus90)
|
||||||
|
@ -800,20 +780,9 @@ class NuBus(Module):
|
||||||
nf_fpga_to_cpld_signal.eq(~rqst_oe),
|
nf_fpga_to_cpld_signal.eq(~rqst_oe),
|
||||||
]
|
]
|
||||||
|
|
||||||
if (usesampling):
|
|
||||||
self.sync += [
|
|
||||||
If((~nub_clk & nub_clk_prev[0]), # simultaneous with setting negedge
|
|
||||||
decoded_busy.eq(~decoded_busy & nub_ackn & ~nub_startn # beginning of transaction
|
|
||||||
| decoded_busy & nub_ackn & nub_resetn), # hold during cycle
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (version == "V1.2"):
|
if (version == "V1.2"):
|
||||||
self.nubus_oe = nubus_oe = Signal() # improveme
|
|
||||||
self.specials += Instance("nubus_cpldinfpga",
|
self.specials += Instance("nubus_cpldinfpga",
|
||||||
i_nubus_oe = nubus_oe, # improveme: handled in soc
|
i_nubus_oe = soc.hold_reset, # improveme, handled in SoC
|
||||||
i_tmoen = ~tmo_oe,
|
i_tmoen = ~tmo_oe,
|
||||||
i_nubus_master_dir = master_oe,
|
i_nubus_master_dir = master_oe,
|
||||||
i_rqst_oe_n = ~rqst_oe,
|
i_rqst_oe_n = ~rqst_oe,
|
||||||
|
|
|
@ -24,6 +24,8 @@ from litedram.phy import s7ddrphy
|
||||||
|
|
||||||
from litedram.frontend.dma import *
|
from litedram.frontend.dma import *
|
||||||
|
|
||||||
|
from liteeth.phy.rmii import LiteEthPHYRMII
|
||||||
|
|
||||||
from migen.genlib.cdc import BusSynchronizer
|
from migen.genlib.cdc import BusSynchronizer
|
||||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||||
|
|
||||||
|
@ -45,7 +47,8 @@ class _CRG(Module):
|
||||||
def __init__(self, platform, version, sys_clk_freq,
|
def __init__(self, platform, version, sys_clk_freq,
|
||||||
goblin=False,
|
goblin=False,
|
||||||
hdmi=False,
|
hdmi=False,
|
||||||
pix_clk=0):
|
pix_clk=0,
|
||||||
|
ethernet=False):
|
||||||
self.clock_domains.cd_sys = ClockDomain() # 100 MHz PLL, reset'ed by NuBus (via pll), SoC/Wishbone main clock
|
self.clock_domains.cd_sys = ClockDomain() # 100 MHz PLL, reset'ed by NuBus (via pll), SoC/Wishbone main clock
|
||||||
self.clock_domains.cd_sys4x = ClockDomain(reset_less=True)
|
self.clock_domains.cd_sys4x = ClockDomain(reset_less=True)
|
||||||
self.clock_domains.cd_sys4x_dqs = ClockDomain(reset_less=True)
|
self.clock_domains.cd_sys4x_dqs = ClockDomain(reset_less=True)
|
||||||
|
@ -59,7 +62,9 @@ class _CRG(Module):
|
||||||
else:
|
else:
|
||||||
self.clock_domains.cd_hdmi = ClockDomain()
|
self.clock_domains.cd_hdmi = ClockDomain()
|
||||||
self.clock_domains.cd_hdmi5x = ClockDomain()
|
self.clock_domains.cd_hdmi5x = ClockDomain()
|
||||||
|
if (ethernet):
|
||||||
|
self.clock_domains.cd_eth = ClockDomain()
|
||||||
|
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
clk48 = platform.request("clk48")
|
clk48 = platform.request("clk48")
|
||||||
|
@ -79,9 +84,10 @@ class _CRG(Module):
|
||||||
if (version == "V1.2"):
|
if (version == "V1.2"):
|
||||||
self.clock_domains.cd_bank34 = ClockDomain()
|
self.clock_domains.cd_bank34 = ClockDomain()
|
||||||
clk54 = platform.request("clk54")
|
clk54 = platform.request("clk54")
|
||||||
|
platform.add_platform_command("create_clock -name clk54 -period 18.51851851851851851 [get_nets clk54]")
|
||||||
self.clk54_bufg = Signal()
|
self.clk54_bufg = Signal()
|
||||||
self.specials += Instance("BUFG", i_I=clk54, o_O=self.clk54_bufg)
|
self.specials += Instance("BUFG", i_I=clk54, o_O=self.clk54_bufg)
|
||||||
self.comb += self.cd_native.clk.eq(self.clk54_bufg)
|
self.comb += self.cd_bank34.clk.eq(self.clk54_bufg)
|
||||||
else:
|
else:
|
||||||
clk54 = None
|
clk54 = None
|
||||||
|
|
||||||
|
@ -118,6 +124,11 @@ class _CRG(Module):
|
||||||
pll.create_clkout(self.cd_sys4x_dqs, 4*sys_clk_freq, phase=90)
|
pll.create_clkout(self.cd_sys4x_dqs, 4*sys_clk_freq, phase=90)
|
||||||
platform.add_platform_command("create_generated_clock -name sys4x90clk [get_pins {{{{MMCME2_ADV/CLKOUT{}}}}}]".format(num_clk))
|
platform.add_platform_command("create_generated_clock -name sys4x90clk [get_pins {{{{MMCME2_ADV/CLKOUT{}}}}}]".format(num_clk))
|
||||||
num_clk = num_clk + 1
|
num_clk = num_clk + 1
|
||||||
|
if (ethernet):
|
||||||
|
pll.create_clkout(self.cd_eth, 50e6, phase=90) # fixme: what if sys_clk_feq != 100e6?
|
||||||
|
platform.add_platform_command("create_generated_clock -name ethclk [get_pins {{{{MMCME2_ADV/CLKOUT{}}}}}]".format(num_clk))
|
||||||
|
num_clk = num_clk + 1
|
||||||
|
|
||||||
self.comb += pll.reset.eq(~rst_nubus_n) # | ~por_done
|
self.comb += pll.reset.eq(~rst_nubus_n) # | ~por_done
|
||||||
platform.add_false_path_constraints(clk48, self.cd_nubus.clk) # FIXME?
|
platform.add_false_path_constraints(clk48, self.cd_nubus.clk) # FIXME?
|
||||||
platform.add_false_path_constraints(self.cd_nubus.clk, clk48) # FIXME?
|
platform.add_false_path_constraints(self.cd_nubus.clk, clk48) # FIXME?
|
||||||
|
@ -171,7 +182,7 @@ class _CRG(Module):
|
||||||
|
|
||||||
|
|
||||||
class NuBusFPGA(SoCCore):
|
class NuBusFPGA(SoCCore):
|
||||||
def __init__(self, variant, version, sys_clk_freq, goblin, hdmi, goblin_res, **kwargs):
|
def __init__(self, variant, version, sys_clk_freq, goblin, hdmi, goblin_res, ethernet, **kwargs):
|
||||||
print(f"Building NuBusFPGA for board version {version}")
|
print(f"Building NuBusFPGA for board version {version}")
|
||||||
|
|
||||||
kwargs["cpu_type"] = "None"
|
kwargs["cpu_type"] = "None"
|
||||||
|
@ -183,6 +194,9 @@ class NuBusFPGA(SoCCore):
|
||||||
|
|
||||||
self.platform = platform = ztex213_nubus.Platform(variant = variant, version = version)
|
self.platform = platform = ztex213_nubus.Platform(variant = variant, version = version)
|
||||||
|
|
||||||
|
if (ethernet and (version == "V1.2")):
|
||||||
|
platform.add_extension(ztex213_nubus._rmii_eth_extpmod_io_v1_2)
|
||||||
|
|
||||||
use_goblin_alt = True
|
use_goblin_alt = True
|
||||||
if ((not use_goblin_alt) or (not hdmi)):
|
if ((not use_goblin_alt) or (not hdmi)):
|
||||||
from VintageBusFPGA_Common.goblin_fb import goblin_rounded_size, Goblin
|
from VintageBusFPGA_Common.goblin_fb import goblin_rounded_size, Goblin
|
||||||
|
@ -238,11 +252,12 @@ class NuBusFPGA(SoCCore):
|
||||||
"goblin_audio_ram" : 0xF0920000, # audio for goblin (RAM buffers)
|
"goblin_audio_ram" : 0xF0920000, # audio for goblin (RAM buffers)
|
||||||
"csr" : 0xF0A00000, # CSR
|
"csr" : 0xF0A00000, # CSR
|
||||||
"pingmaster": 0xF0B00000,
|
"pingmaster": 0xF0B00000,
|
||||||
|
"ethmac": 0xF0C00000,
|
||||||
"rom": 0xF0FF8000, # ROM at the end (32 KiB of it ATM)
|
"rom": 0xF0FF8000, # ROM at the end (32 KiB of it ATM)
|
||||||
#"END OF SLOT SPACE": 0xF0FFFFFF,
|
#"END OF SLOT SPACE": 0xF0FFFFFF,
|
||||||
}
|
}
|
||||||
self.mem_map.update(wb_mem_map)
|
self.mem_map.update(wb_mem_map)
|
||||||
self.submodules.crg = _CRG(platform=platform, version=version, sys_clk_freq=sys_clk_freq, goblin=goblin, hdmi=hdmi, pix_clk=litex.soc.cores.video.video_timings[goblin_res]["pix_clk"])
|
self.submodules.crg = _CRG(platform=platform, version=version, sys_clk_freq=sys_clk_freq, goblin=goblin, hdmi=hdmi, pix_clk=litex.soc.cores.video.video_timings[goblin_res]["pix_clk"], ethernet=ethernet)
|
||||||
|
|
||||||
## add our custom timings after the clocks have been defined
|
## add our custom timings after the clocks have been defined
|
||||||
xdc_timings_filename = None;
|
xdc_timings_filename = None;
|
||||||
|
@ -320,7 +335,7 @@ class NuBusFPGA(SoCCore):
|
||||||
#hold_reset_ctr = Signal(30, reset=960000000)
|
#hold_reset_ctr = Signal(30, reset=960000000)
|
||||||
hold_reset_ctr = Signal(5, reset=31)
|
hold_reset_ctr = Signal(5, reset=31)
|
||||||
self.sync.native += If(hold_reset_ctr>0, hold_reset_ctr.eq(hold_reset_ctr - 1))
|
self.sync.native += If(hold_reset_ctr>0, hold_reset_ctr.eq(hold_reset_ctr - 1))
|
||||||
hold_reset = Signal()
|
self.hold_reset = hold_reset = Signal() # in reset if high, out-of-reset if low
|
||||||
self.comb += hold_reset.eq(~(hold_reset_ctr == 0))
|
self.comb += hold_reset.eq(~(hold_reset_ctr == 0))
|
||||||
pad_nubus_oe = platform.request("nubus_oe")
|
pad_nubus_oe = platform.request("nubus_oe")
|
||||||
self.comb += pad_nubus_oe.eq(hold_reset)
|
self.comb += pad_nubus_oe.eq(hold_reset)
|
||||||
|
@ -476,25 +491,42 @@ class NuBusFPGA(SoCCore):
|
||||||
self.add_ram("goblin_accel_rom", origin=self.mem_map["goblin_accel_rom"], size=rounded_goblin_rom_len, contents=goblin_rom_data, mode="r")
|
self.add_ram("goblin_accel_rom", origin=self.mem_map["goblin_accel_rom"], size=rounded_goblin_rom_len, contents=goblin_rom_data, mode="r")
|
||||||
self.add_ram("goblin_accel_ram", origin=self.mem_map["goblin_accel_ram"], size=2**12, mode="rw")
|
self.add_ram("goblin_accel_ram", origin=self.mem_map["goblin_accel_ram"], size=2**12, mode="rw")
|
||||||
|
|
||||||
|
if (ethernet):
|
||||||
|
# we need the CRG to provide the cd_eth clock: "use refclk_cd as RMII reference clock (provided by user design) (no external clock).
|
||||||
|
self.ethphy = LiteEthPHYRMII(
|
||||||
|
clock_pads = self.platform.request("eth_clocks"),
|
||||||
|
pads = self.platform.request("eth"))
|
||||||
|
self.add_ethernet(phy=self.ethphy, data_width = 32)
|
||||||
|
print(f"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% {self.ethmac.interface.sram.ev.irq}") # FIXME HANDLEME
|
||||||
|
|
||||||
# for testing
|
# for testing
|
||||||
if (True):
|
if (False):
|
||||||
from nubus_master_tst import PingMaster
|
from nubus_master_tst import PingMaster
|
||||||
self.submodules.pingmaster = PingMaster(nubus=self.nubus, platform=self.platform)
|
self.submodules.pingmaster = PingMaster(nubus=self.nubus, platform=self.platform)
|
||||||
self.bus.add_slave("pingmaster_slv", self.pingmaster.bus_slv, SoCRegion(origin=self.mem_map.get("pingmaster", None), size=0x010, cached=False))
|
self.bus.add_slave("pingmaster_slv", self.pingmaster.bus_slv, SoCRegion(origin=self.mem_map.get("pingmaster", None), size=0x010, cached=False))
|
||||||
self.bus.add_master(name="pingmaster_mst", master=self.pingmaster.bus_mst)
|
self.bus.add_master(name="pingmaster_mst", master=self.pingmaster.bus_mst)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="SbusFPGA")
|
parser = argparse.ArgumentParser(description="NuBusFPGA")
|
||||||
parser.add_argument("--build", action="store_true", help="Build bitstream")
|
parser.add_argument("--build", action="store_true", help="Build bitstream")
|
||||||
parser.add_argument("--variant", default="ztex2.13a", help="ZTex board variant (default ztex2.13a)")
|
parser.add_argument("--variant", default="ztex2.13a", help="ZTex board variant (default ztex2.13a)")
|
||||||
parser.add_argument("--version", default="V1.0", help="NuBusFPGA board version (default V1.0)")
|
parser.add_argument("--version", default="V1.0", help="NuBusFPGA board version (default V1.0)")
|
||||||
parser.add_argument("--sys-clk-freq", default=100e6, help="NuBusFPGA system clock (default 100e6 = 100 MHz)")
|
parser.add_argument("--sys-clk-freq", default=100e6, help="NuBusFPGA system clock (default 100e6 = 100 MHz)")
|
||||||
parser.add_argument("--goblin", action="store_true", help="add a goblin framebuffer")
|
parser.add_argument("--goblin", action="store_true", help="add a goblin framebuffer")
|
||||||
parser.add_argument("--hdmi", action="store_true", help="The framebuffer uses HDMI (default to VGA)")
|
parser.add_argument("--hdmi", action="store_true", help="The framebuffer uses HDMI (default to VGA, required for V1.2)")
|
||||||
parser.add_argument("--goblin-res", default="640x480@60Hz", help="Specify the goblin resolution")
|
parser.add_argument("--goblin-res", default="640x480@60Hz", help="Specify the goblin resolution")
|
||||||
|
parser.add_argument("--ethernet", action="store_true", help="Add Ethernet (V1.2 w/ custom PMod only)")
|
||||||
builder_args(parser)
|
builder_args(parser)
|
||||||
vivado_build_args(parser)
|
vivado_build_args(parser)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if (args.ethernet and (args.version == "V1.0")):
|
||||||
|
print(" ***** ERROR ***** : Ethernet not supported on V1.0\n");
|
||||||
|
assert(False)
|
||||||
|
|
||||||
|
if ((not args.hdmi) and (args.version == "V1.2")):
|
||||||
|
print(" ***** ERROR ***** : VGA not supported on V1.2\n");
|
||||||
|
assert(False)
|
||||||
|
|
||||||
soc = NuBusFPGA(**soc_core_argdict(args),
|
soc = NuBusFPGA(**soc_core_argdict(args),
|
||||||
variant=args.variant,
|
variant=args.variant,
|
||||||
|
@ -502,7 +534,8 @@ def main():
|
||||||
sys_clk_freq=int(float(args.sys_clk_freq)),
|
sys_clk_freq=int(float(args.sys_clk_freq)),
|
||||||
goblin=args.goblin,
|
goblin=args.goblin,
|
||||||
hdmi=args.hdmi,
|
hdmi=args.hdmi,
|
||||||
goblin_res=args.goblin_res)
|
goblin_res=args.goblin_res,
|
||||||
|
ethernet=args.ethernet)
|
||||||
|
|
||||||
version_for_filename = args.version.replace(".", "_")
|
version_for_filename = args.version.replace(".", "_")
|
||||||
|
|
||||||
|
|
|
@ -104,8 +104,8 @@ _nubus_io_v1_2 = [
|
||||||
## extra 54 MHz clock reference for bank 34
|
## extra 54 MHz clock reference for bank 34
|
||||||
("clk54", 0, Pins("R3"), IOStandard("LVCMOS33")),
|
("clk54", 0, Pins("R3"), IOStandard("LVCMOS33")),
|
||||||
## leds on the NuBus board
|
## leds on the NuBus board
|
||||||
("user_led", 0, Pins("U9"), IOStandard("lvcmos33")), #LED0
|
("user_led", 0, Pins("V9"), IOStandard("lvcmos33")), #LED0
|
||||||
("user_led", 1, Pins("V9"), IOStandard("lvcmos33")), #LED1; both are overlapping with serial TX/RX
|
("user_led", 1, Pins("U9"), IOStandard("lvcmos33")), #LED1; both are overlapping with serial TX/RX
|
||||||
## serial header for console
|
## serial header for console
|
||||||
("serial", 0,
|
("serial", 0,
|
||||||
Subsignal("tx", Pins("V9")), # FIXME: might be the other way round
|
Subsignal("tx", Pins("V9")), # FIXME: might be the other way round
|
||||||
|
|
Loading…
Reference in New Issue
Block a user