From f9886f72803e35c67acfe4d6743b62aea35263e9 Mon Sep 17 00:00:00 2001 From: Romain Dolbeau Date: Sat, 16 Sep 2023 15:14:35 +0200 Subject: [PATCH] add micro-sd HW support --- nubus-to-ztex-gateware/do_V1.2 | 14 ++- nubus-to-ztex-gateware/nubus_to_fpga_soc.py | 126 ++++++++++++++++---- nubus-to-ztex-gateware/ztex213_nubus.py | 17 ++- 3 files changed, 129 insertions(+), 28 deletions(-) diff --git a/nubus-to-ztex-gateware/do_V1.2 b/nubus-to-ztex-gateware/do_V1.2 index cfc5e48..e6b10a8 100644 --- a/nubus-to-ztex-gateware/do_V1.2 +++ b/nubus-to-ztex-gateware/do_V1.2 @@ -2,7 +2,7 @@ source /opt/Xilinx/Vivado/2020.1/settings64.sh 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 # --ethernet +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 --sdcard --flash # --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 @@ -11,3 +11,15 @@ python3 nubus_to_fpga_soc.py --build --csr-csv csr.csv --csr-json csr.json --var # --hdmi grep -A10 'Design Timing Summary' build/ztex213_nubus_V1_2/gateware/ztex213_nubus_V1_2_timing.rpt + + +### +# For 16 MiB Flash NOR (W25Q128.V): +# truncate -s $((16*1024*1024)) vid_decl_rom.bin +# flashrom -c W25Q128.V -p linux_spi:dev=/dev/spidev0.0,spispeed=2000 -l nubus_prom.layout -i ROM -w vid_decl_rom.bin +# +# where nubus_prom.layout contains: +### +# 00000000:00007fff ROM +# 00008000:00ffffff VDISK +### diff --git a/nubus-to-ztex-gateware/nubus_to_fpga_soc.py b/nubus-to-ztex-gateware/nubus_to_fpga_soc.py index d8dcf12..2250bf7 100644 --- a/nubus-to-ztex-gateware/nubus_to_fpga_soc.py +++ b/nubus-to-ztex-gateware/nubus_to_fpga_soc.py @@ -37,6 +37,7 @@ from VintageBusFPGA_Common.goblin_accel import * # Wishbone stuff from VintageBusFPGA_Common.cdc_wb import WishboneDomainCrossingMaster from VintageBusFPGA_Common.fpga_blk_dma import * +from VintageBusFPGA_Common.fpga_sd_dma import * from nubus_mem_wb import NuBus2Wishbone from nubus_memfifo_wb import NuBus2WishboneFIFO @@ -181,8 +182,26 @@ class _CRG(Module): -class NuBusFPGA(SoCCore): - def __init__(self, variant, version, sys_clk_freq, goblin, hdmi, goblin_res, ethernet, **kwargs): +class NuBusFPGA(SoCCore): # Add SDCard ----------------------------------------------------------------------------------- + # WiP + def add_sdcard_custom(self, name="sdcard", mode="read+write"): + # Imports. + from litesdcard.phy import SDPHY + from litesdcard.core import SDCore + + # Checks. + assert mode in ["read", "write", "read+write"] + + # Pads. + sdcard_pads = self.platform.request(name) + + # Core. + self.check_if_exists("sdphy") + self.check_if_exists("sdcore") + self.sdphy = SDPHY(sdcard_pads, self.platform.device, self.clk_freq, cmd_timeout=10e-1, data_timeout=10e-1) + self.sdcore = SDCore(self.sdphy) + + def __init__(self, variant, version, sys_clk_freq, goblin, hdmi, goblin_res, sdcard, flash, ethernet, **kwargs): print(f"Building NuBusFPGA for board version {version}") kwargs["cpu_type"] = "None" @@ -193,6 +212,9 @@ class NuBusFPGA(SoCCore): self.sys_clk_freq = sys_clk_freq self.platform = platform = ztex213_nubus.Platform(variant = variant, version = version) + + if (flash and (version == "V1.2")): + platform.add_extension(ztex213_nubus._flashtemp_pmod_io_v1_2) if (ethernet and (version == "V1.2")): platform.add_extension(ztex213_nubus._rmii_eth_extpmod_io_v1_2) @@ -253,7 +275,9 @@ class NuBusFPGA(SoCCore): "csr" : 0xF0A00000, # CSR "pingmaster": 0xF0B00000, "ethmac": 0xF0C00000, + #"spiflash": 0xF0D00000, # testing "rom": 0xF0FF8000, # ROM at the end (32 KiB of it ATM) + "spiflash": 0xF0FF8000, # FIXME currently the flash is in the ROM spot, limited to 32 KiB #"END OF SLOT SPACE": 0xF0FFFFFF, } self.mem_map.update(wb_mem_map) @@ -263,6 +287,8 @@ class NuBusFPGA(SoCCore): xdc_timings_filename = None; #if (version == "V1.0"): # xdc_timings_filename = "/home/dolbeau/nubus-to-ztex-gateware/nubus_fpga_V1_0_timings.xdc" + if (version == "V1.2"): + xdc_timings_filename = "nubus_fpga_V1_2_timings.xdc" if (xdc_timings_filename != None): xdc_timings_file = open(xdc_timings_filename) @@ -274,14 +300,25 @@ class NuBusFPGA(SoCCore): #print(fix_line) platform.add_platform_command(fix_line) - rom_file = "rom_{}.bin".format(version.replace(".", "_")) - rom_data = soc_core.get_mem_data(filename_or_regions=rom_file, endianness="little") # "big" - # rom = Array(rom_data) - #print("\n****************************************\n") - #for i in range(len(rom)): - # print(hex(rom[i])) - #print("\n****************************************\n") - self.add_ram("rom", origin=self.mem_map["rom"], size=2**15, contents=rom_data, mode="r") ## 32 KiB, must match mmap + if (not flash): + rom_file = "rom_{}.bin".format(version.replace(".", "_")) + rom_data = soc_core.get_mem_data(filename_or_regions=rom_file, endianness="little") # "big" + # rom = Array(rom_data) + #print("\n****************************************\n") + #for i in range(len(rom)): + # print(hex(rom[i])) + #print("\n****************************************\n") + self.add_ram("rom", origin=self.mem_map["rom"], size=2**15, contents=rom_data, mode="r") ## 32 KiB, must match mmap + + if (flash): + from litespi.modules.generated_modules import W25Q128JV + from litespi.opcodes import SpiNorFlashOpCodes as Codes + self.add_spi_flash(mode="4x", + clk_freq = sys_clk_freq/4, # Fixme; PHY freq ? + module=W25Q128JV(Codes.READ_1_1_4), + region_size = 0x00008000, # 32 KiB + with_mmap=True, with_master=False) + #from wb_test import WA2D #self.submodules.wa2d = WA2D(self.platform) @@ -379,15 +416,18 @@ class NuBusFPGA(SoCCore): self.comb += irq_line.eq(fb_irq) # active low, enable if one is low else: # details for usesampling in the NuBus python object - usesampling = True + usesampling = False wishbone_master_sys = wishbone.Interface(data_width=self.bus.data_width) if (not usesampling): # we need an extra CDC self.submodules.wishbone_master_nubus = WishboneDomainCrossingMaster(platform=self.platform, slave=wishbone_master_sys, cd_master="nubus", cd_slave="sys") # for non-sampling only nubus_writemaster_sys = wishbone.Interface(data_width=self.bus.data_width) wishbone_slave_nubus = wishbone.Interface(data_width=self.bus.data_width) - self.submodules.wishbone_slave_sys = WishboneDomainCrossingMaster(platform=self.platform, slave=wishbone_slave_nubus, cd_master="sys", cd_slave="nubus", force_delay=6) # force delay needed to avoid back-to-back transaction running into issue https://github.com/alexforencich/verilog-wishbone/issues/4 - - + self.submodules.wishbone_slave_sys = WishboneDomainCrossingMaster(platform=self.platform, slave=wishbone_slave_nubus, cd_master="sys", cd_slave="nubus", force_delay=9) # force delay needed to avoid back-to-back transaction running into issue https://github.com/alexforencich/verilog-wishbone/issues/4 + #led0 = platform.request("user_led", 0) + #led1 = platform.request("user_led", 1) + #self.comb += [ led0.eq(self.wishbone_slave_sys.stb), + # led1.eq(self.wishbone_slave_sys.cyc), ] + burst_size=4 data_width = burst_size * 4 @@ -407,9 +447,7 @@ class NuBusFPGA(SoCCore): ("dmaaddress", 32), ] - self.submodules.tosbus_fifo = ClockDomainsRenamer({"read": "nubus", "write": "sys"})(AsyncFIFOBuffered(width=layout_len(self.tosbus_layout), depth=1024//data_width)) - self.submodules.fromsbus_fifo = ClockDomainsRenamer({"write": "nubus", "read": "sys"})(AsyncFIFOBuffered(width=layout_len(self.fromsbus_layout), depth=512//data_width)) - self.submodules.fromsbus_req_fifo = ClockDomainsRenamer({"read": "nubus", "write": "sys"})(AsyncFIFOBuffered(width=layout_len(self.fromsbus_req_layout), depth=512//data_width)) + irq_line = self.platform.request("nmrq_3v3_n") # active low fb_irq = Signal(reset = 1) # active low dma_irq = Signal(reset = 1) # active low @@ -422,7 +460,13 @@ class NuBusFPGA(SoCCore): #] self.comb += irq_line.eq(fb_irq & dma_irq & audio_irq) # active low, enable if one is low + + self.submodules.tosbus_fifo = ClockDomainsRenamer({"read": "nubus", "write": "sys"})(AsyncFIFOBuffered(width=layout_len(self.tosbus_layout), depth=1024//data_width)) + self.submodules.fromsbus_fifo = ClockDomainsRenamer({"write": "nubus", "read": "sys"})(AsyncFIFOBuffered(width=layout_len(self.fromsbus_layout), depth=512//data_width)) + self.submodules.fromsbus_req_fifo = ClockDomainsRenamer({"read": "nubus", "write": "sys"})(AsyncFIFOBuffered(width=layout_len(self.fromsbus_req_layout), depth=512//data_width)) + + #if (not sdcard): # fixme: temporay exclusion self.submodules.exchange_with_mem = ExchangeWithMem(soc=self, platform=platform, tosbus_fifo=self.tosbus_fifo, @@ -434,8 +478,20 @@ class NuBusFPGA(SoCCore): burst_size=burst_size, do_checksum = False, clock_domain="nubus") - self.comb += dma_irq.eq(self.exchange_with_mem.irq) + #else: + # self.add_sdcard_custom() + # self.submodules.exchange_with_sd = ExchangeWithSD(soc=self, + # platform=platform, + # tosbus_fifo=self.tosbus_fifo, + # fromsbus_fifo=self.fromsbus_fifo, + # fromsbus_req_fifo=self.fromsbus_req_fifo, + # sd_source=self.sdcore.source, + # sd_sink=self.sdcore.sink, + # burst_size=burst_size, + # clock_domain="nubus") + # self.comb += dma_irq.eq(self.exchange_with_sd.irq) + self.submodules.nubus = nubus_full_unified.NuBus(soc=self, version=version, @@ -491,13 +547,17 @@ 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_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 + if (sdcard): + self.add_sdcard() + # irq? + + 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 if (False): @@ -515,14 +575,28 @@ def main(): 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, required for V1.2)") parser.add_argument("--goblin-res", default="640x480@60Hz", help="Specify the goblin resolution") + parser.add_argument("--sdcard", action="store_true", help="add a sdcard controller (V1.2 only)") + parser.add_argument("--flash", action="store_true", help="add a Flash device [V1.2+FLASHTEMP PMod]") parser.add_argument("--ethernet", action="store_true", help="Add Ethernet (V1.2 w/ custom PMod only)") builder_args(parser) vivado_build_args(parser) args = parser.parse_args() + if (args.sdcard and (args.version == "V1.0")): + print(" ***** ERROR ***** : Ethernet not supported on V1.0\n"); + assert(False) + + if (args.flash and (args.version == "V1.0")): + print(" ***** ERROR ***** : Flash not supported on V1.0\n"); + assert(False) + if (args.ethernet and (args.version == "V1.0")): print(" ***** ERROR ***** : Ethernet not supported on V1.0\n"); assert(False) + + if (args.ethernet and args.flash): + print(" ***** ERROR ***** : Only one PMod usable on V1.2\n"); + assert(False) if ((not args.hdmi) and (args.version == "V1.2")): print(" ***** ERROR ***** : VGA not supported on V1.2\n"); @@ -535,6 +609,8 @@ def main(): goblin=args.goblin, hdmi=args.hdmi, goblin_res=args.goblin_res, + sdcard=args.sdcard, + flash=args.flash, ethernet=args.ethernet) version_for_filename = args.version.replace(".", "_") diff --git a/nubus-to-ztex-gateware/ztex213_nubus.py b/nubus-to-ztex-gateware/ztex213_nubus.py index bbfce56..a590e21 100644 --- a/nubus-to-ztex-gateware/ztex213_nubus.py +++ b/nubus-to-ztex-gateware/ztex213_nubus.py @@ -214,17 +214,30 @@ connectors_v1_2 = [ ("P1", "M1 L1 N2 N1 R2 P2 T1 R1 P4 P3 P5 N5"), # check sequence! currently in pmod-* order ] +# Extension +def flashtemp_pmod_io(pmod): + return [ + # completely inconsistent with the SBus entry as P1 is in a different order... + ("spiflash4x", 0, + Subsignal("cs_n", Pins(f"{pmod}:2")), + Subsignal("clk", Pins(f"{pmod}:5")), + Subsignal("dq", Pins(f"{pmod}:7 {pmod}:4 {pmod}:6 {pmod}:3")), + IOStandard("LVCMOS33") + ), +] +_flashtemp_pmod_io_v1_2 = flashtemp_pmod_io("P1") + # Ethernet ---------------------------------------------------------------------------------------------- # custom not-quite-pmod def rmii_eth_extpmod_io(extpmod): return [ ("eth_clocks", 0, - Subsignal("ref_clk", Pins(f"{extpmod}:10")), + Subsignal("ref_clk", Pins(f"{extpmod}:8")), IOStandard("LVCMOS33"), ), ("eth", 0, Subsignal("rst_n", Pins(f"{extpmod}:3")), - Subsignal("rx_data", Pins(f"{extpmod}:8 {extpmod}:11")), + Subsignal("rx_data", Pins(f"{extpmod}:11 {extpmod}:10")), Subsignal("crs_dv", Pins(f"{extpmod}:6")), Subsignal("tx_en", Pins(f"{extpmod}:2")), Subsignal("tx_data", Pins(f"{extpmod}:0 {extpmod}:1")),