diff --git a/sbus-to-ztex-gateware-migen/sbus_to_fpga_export.py b/sbus-to-ztex-gateware-migen/sbus_to_fpga_export.py new file mode 100644 index 0000000..f772f5e --- /dev/null +++ b/sbus-to-ztex-gateware-migen/sbus_to_fpga_export.py @@ -0,0 +1,133 @@ +import os +import json +import inspect +from shutil import which +from sysconfig import get_platform + +from migen import * + +from litex.soc.interconnect.csr import CSRStatus + +from litex.build.tools import generated_banner + +from litex.soc.doc.rst import reflow +from litex.soc.doc.module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts +from litex.soc.doc.csr import DocumentedCSRRegion +from litex.soc.interconnect.csr import _CompoundCSR + +# for generating a timestamp in the description field, if none is otherwise given +import datetime +import time + +def _get_rw_functions_c(name, csr_name, reg_base, area_base, nwords, busword, alignment, read_only, with_access_functions): + reg_name = name + "_" + csr_name + r = "" + + addr_str = "CSR_{}_ADDR".format(reg_name.upper()) + size_str = "CSR_{}_SIZE".format(reg_name.upper()) + r += "#define {} (CSR_{}_BASE + {}L)\n".format(addr_str, name.upper(), hex(reg_base - area_base)) + r += "#define {} {}\n".format(size_str, nwords) + + size = nwords*busword//8 + if size > 8: + # downstream should select appropriate `csr_[rd|wr]_buf_uintX()` pair! + return r + elif size > 4: + ctype = "uint64_t" + elif size > 2: + ctype = "uint32_t" + elif size > 1: + ctype = "uint16_t" + else: + ctype = "uint8_t" + + stride = alignment//8; + if with_access_functions: + r += "static inline {} {}_read(struct sbusfpga_sdram_softc *sc) {{\n".format(ctype, reg_name) + if nwords > 1: + r += "\t{} r = bus_space_read_4(sc->sc_bustag, sc>sc_bhregs_{}, {}L);\n".format(ctype, name, hex(reg_base - area_base)) + for sub in range(1, nwords): + r += "\tr <<= {};\n".format(busword) + r += "\tr |= bus_space_read_4(sc->sc_bustag, sc>sc_bhregs_{}, {}L);\n".format(name, hex(reg_base - area_base + sub*stride)) + r += "\treturn r;\n}\n" + else: + r += "\treturn bus_space_read_4(sc->sc_bustag, sc>sc_bhregs_{}, {}L);\n}}\n".format(name, hex(reg_base - area_base)) + + if not read_only: + r += "static inline void {}_write(struct sbusfpga_sdram_softc *sc, {} v) {{\n".format(reg_name, ctype) + for sub in range(nwords): + shift = (nwords-sub-1)*busword + if shift: + v_shift = "v >> {}".format(shift) + else: + v_shift = "v" + r += "\tbus_space_write_4(sc->sc_bustag, sc>sc_bhregs_{}, {}L, {});\n".format(name, hex(reg_base - area_base + sub*stride), v_shift) + r += "}\n" + return r + + +def get_csr_header(regions, constants, csr_base=None, with_access_functions=True): + alignment = constants.get("CONFIG_CSR_ALIGNMENT", 32) + r = generated_banner("//") + #if with_access_functions: # FIXME + # r += "#include \n" + r += "#ifndef __GENERATED_CSR_H\n#define __GENERATED_CSR_H\n" + #if with_access_functions: + # r += "#include \n" + # r += "#include \n" + # r += "#ifndef CSR_ACCESSORS_DEFINED\n" + # r += "#include \n" + # r += "#endif /* ! CSR_ACCESSORS_DEFINED */\n" + csr_base = csr_base if csr_base is not None else regions[next(iter(regions))].origin + r += "#ifndef CSR_BASE\n" + r += "#define CSR_BASE {}L\n".format(hex(csr_base)) + r += "#endif\n" + for name, region in regions.items(): + origin = region.origin - csr_base + r += "\n/* "+name+" */\n" + r += "#ifndef CSR_"+name.upper()+"_BASE\n" + r += "#define CSR_"+name.upper()+"_BASE (CSR_BASE + "+hex(origin)+"L)\n" + r += "#endif\n" + if not isinstance(region.obj, Memory): + for csr in region.obj: + nr = (csr.size + region.busword - 1)//region.busword + r += _get_rw_functions_c(name, csr.name, origin, region.origin - csr_base, nr, region.busword, alignment, + getattr(csr, "read_only", False), with_access_functions) + origin += alignment//8*nr + if hasattr(csr, "fields"): + for field in csr.fields.fields: + offset = str(field.offset) + size = str(field.size) + r += "#define CSR_"+name.upper()+"_"+csr.name.upper()+"_"+field.name.upper()+"_OFFSET "+offset+"\n" + r += "#define CSR_"+name.upper()+"_"+csr.name.upper()+"_"+field.name.upper()+"_SIZE "+size+"\n" + if with_access_functions and csr.size <= 32: # FIXME: Implement extract/read functions for csr.size > 32-bit. + reg_name = name + "_" + csr.name.lower() + field_name = reg_name + "_" + field.name.lower() + r += "static inline uint32_t " + field_name + "_extract(struct sbusfpga_sdram_softc *sc, uint32_t oldword) {\n" + r += "\tuint32_t mask = ((1 << " + size + ")-1);\n" + r += "\treturn ( (oldword >> " + offset + ") & mask );\n}\n" + r += "static inline uint32_t " + field_name + "_read(struct sbusfpga_sdram_softc *sc) {\n" + r += "\tuint32_t word = " + reg_name + "_read(sc);\n" + r += "\treturn " + field_name + "_extract(word);\n" + r += "}\n" + if not getattr(csr, "read_only", False): + r += "static inline uint32_t " + field_name + "_replace(struct sbusfpga_sdram_softc *sc, uint32_t oldword, uint32_t plain_value) {\n" + r += "\tuint32_t mask = ((1 << " + size + ")-1);\n" + r += "\treturn (oldword & (~(mask << " + offset + "))) | (mask & plain_value)<< " + offset + " ;\n}\n" + r += "static inline void " + field_name + "_write(struct sbusfpga_sdram_softc *sc, uint32_t plain_value) {\n" + r += "\tuint32_t oldword = " + reg_name + "_read(sc);\n" + r += "\tuint32_t newword = " + field_name + "_replace(sc, oldword, plain_value);\n" + r += "\t" + reg_name + "_write(sc, newword);\n" + r += "}\n" + + r += "\n#endif\n" + return r + + +def get_csr_forth_header(csr_regions, mem_regions, constants, csr_base=None): + r = "\\ auto-generated base regions for CSRs in the PROM\n" + for name, region in csr_regions.items(): + r += "h# " + hex(region.origin).replace("0x", "") + " constant " + "sbusfpga_csraddr_{}".format(name) + "\n" + for name, region in mem_regions.items(): + r += "h# " + hex(region.origin).replace("0x", "") + " constant " + "sbusfpga_regionaddr_{}".format(name) + "\n" + return r diff --git a/sbus-to-ztex-gateware-migen/sbus_to_fpga_soc.py b/sbus-to-ztex-gateware-migen/sbus_to_fpga_soc.py index 698020a..1a3b8b6 100644 --- a/sbus-to-ztex-gateware-migen/sbus_to_fpga_soc.py +++ b/sbus-to-ztex-gateware-migen/sbus_to_fpga_soc.py @@ -18,6 +18,8 @@ from litedram.phy import s7ddrphy from sbus_to_fpga_fsm import *; +import sbus_to_fpga_export; + _sbus_sbus = [ ("SBUS_3V3_CLK", 0, Pins("D15"), IOStandard("lvttl")), ("SBUS_3V3_ASs", 0, Pins("T4"), IOStandard("lvttl")), @@ -98,7 +100,7 @@ class _CRG(Module): self.submodules.pll_idelay = pll_idelay = S7PLL(speedgrade=-1) pll_idelay.register_clkin(clk48, 48e6) - pll_idelay.create_clkout(self.cd_idelay, 200e6) + pll_idelay.create_clkout(self.cd_idelay, 200e6, margin = 0) self.comb += pll_idelay.reset.eq(~rst_sbus) # | ~por_done self.submodules.idelayctrl = S7IDELAYCTRL(self.cd_idelay) @@ -122,6 +124,13 @@ class SBusFPGA(SoCCore): clk_freq=sys_clk_freq, csr_paging=0x1000, # default is 0x800 **kwargs) + + # This mem-map is also exposed in the FSM (matched prefixes) + # and in the PROM (to tell NetBSD where everything is) + # Currently it is a straight mapping between the two: + # the physical address here are used as offset in the SBus + # reserved area of 256 MiB + # Anything at 0x10000000 is therefore unreachable directly wb_mem_map = { "prom": 0x00000000, "csr" : 0x00040000, @@ -180,66 +189,8 @@ class SBusFPGA(SoCCore): hold_reset = Signal(reset=1) self.comb += hold_reset.eq(~(hold_reset_ctr == 0)) - - # FIFO to send data & address from SBus to the Wishbone - ##sbus_to_wishbone_wr_fifo = AsyncFIFOBuffered(width=32+30, depth=16) - ##sbus_to_wishbone_wr_fifo = ClockDomainsRenamer({"write": "sbus", "read": "sys"})(sbus_to_wishbone_wr_fifo) - ##self.submodules += sbus_to_wishbone_wr_fifo - - # FIFOs to send address / receive data from SBus to the Wishbone - ##sbus_to_wishbone_rd_fifo_addr = AsyncFIFOBuffered(width=30, depth=16) - ##sbus_to_wishbone_rd_fifo_addr = ClockDomainsRenamer({"write": "sbus", "read": "sys"})(sbus_to_wishbone_rd_fifo_addr) - ##self.submodules += sbus_to_wishbone_rd_fifo_addr - ##sbus_to_wishbone_rd_fifo_data = AsyncFIFOBuffered(width=32+1, depth=16) - ##sbus_to_wishbone_rd_fifo_data = ClockDomainsRenamer({"write": "sys", "read": "sbus"})(sbus_to_wishbone_rd_fifo_data) - ##self.submodules += sbus_to_wishbone_rd_fifo_data - - # SBus to Wishbone, 'Slave' on the SBus side, 'Master' on the Wishbone side - ##self.submodules.sbus_to_wishbone = SBusToWishbone(platform=self.platform, - ## wr_fifo=sbus_to_wishbone_wr_fifo, - ## rd_fifo_addr=sbus_to_wishbone_rd_fifo_addr, - ## rd_fifo_data=sbus_to_wishbone_rd_fifo_data, - ## wishbone=wishbone.Interface(data_width=self.bus.data_width)) - - - # FIFO to send data & address from Wishbone to the SBus - ##wishbone_to_sbus_wr_fifo = AsyncFIFOBuffered(width=32+30, depth=16) - ##wishbone_to_sbus_wr_fifo = ClockDomainsRenamer({"write": "sys", "read": "sbus"})(wishbone_to_sbus_wr_fifo) - ##self.submodules += wishbone_to_sbus_wr_fifo - - # FIFOs to send address / receive data from Wishbone to the SBus - ##wishbone_to_sbus_rd_fifo_addr = AsyncFIFOBuffered(width=30, depth=4) - ##wishbone_to_sbus_rd_fifo_addr = ClockDomainsRenamer({"write": "sys", "read": "sbus"})(wishbone_to_sbus_rd_fifo_addr) - ##self.submodules += wishbone_to_sbus_rd_fifo_addr - ##wishbone_to_sbus_rd_fifo_data = AsyncFIFOBuffered(width=32+1, depth=4) - ##wishbone_to_sbus_rd_fifo_data = ClockDomainsRenamer({"write": "sbus", "read": "sys"})(wishbone_to_sbus_rd_fifo_data) - ##self.submodules += wishbone_to_sbus_rd_fifo_data - - # Wishbone to SBus, 'Master' on the SBus side, 'Slave' on the Wishbone side - ##self.submodules.wishbone_to_sbus = WishboneToSBus(platform=self.platform, - ## soc=self, - ## wr_fifo=wishbone_to_sbus_wr_fifo, - ## rd_fifo_addr=wishbone_to_sbus_rd_fifo_addr, - ## rd_fifo_data=wishbone_to_sbus_rd_fifo_data, - ## wishbone=wishbone.Interface(data_width=self.bus.data_width)) - - ##_sbus_bus = SBusFPGABus(platform=self.platform, - ## prom=prom, - ## hold_reset=hold_reset, - ## wr_fifo=sbus_to_wishbone_wr_fifo, - ## rd_fifo_addr=sbus_to_wishbone_rd_fifo_addr, - ## rd_fifo_data=sbus_to_wishbone_rd_fifo_data, - ## master_wr_fifo=wishbone_to_sbus_wr_fifo, - ## master_rd_fifo_addr=wishbone_to_sbus_rd_fifo_addr, - ## master_rd_fifo_data=wishbone_to_sbus_rd_fifo_data) - ##self.submodules.sbus_bus = ClockDomainsRenamer("sbus")(_sbus_bus) - - #wishbone_slave = wishbone.Interface(data_width=self.bus.data_width) - #wishbone_master = wishbone.Interface(data_width=self.bus.data_width) - - #wishbone_slave = wishbone.Interface(data_width=self.bus.data_width) - #wishbone_master = wishbone.Interface(data_width=self.bus.data_width) - + # Interface SBus to wishbone + # we need to cross clock domains wishbone_slave_sbus = wishbone.Interface(data_width=self.bus.data_width) wishbone_master_sys = wishbone.Interface(data_width=self.bus.data_width) self.submodules.wishbone_master_sbus = wishbone.WishboneDomainCrossingMaster(platform=self.platform, slave=wishbone_master_sys, cd_master="sbus", cd_slave="sys") @@ -253,11 +204,6 @@ class SBusFPGA(SoCCore): #self.submodules.sbus_bus = _sbus_bus self.submodules.sbus_bus = ClockDomainsRenamer("sbus")(_sbus_bus) - ##self.bus.add_master(name="SBusBridgeToWishbone", master=self.sbus_to_wishbone.wishbone) - ##self.bus.add_slave(name="usb_fake_dma", slave=self.wishbone_to_sbus.wishbone, region=SoCRegion(origin=self.mem_map.get("usb_fake_dma", None), size=0x03ffffff, cached=False)) - - #self.bus.add_master(name="SBusBridgeToWishbone", master=self.sbus_bus.wishbone_master) - #self.bus.add_slave(name="usb_fake_dma", slave=self.sbus_bus.wishbone_slave, region=SoCRegion(origin=self.mem_map.get("usb_fake_dma", None), size=0x03ffffff, cached=False)) self.bus.add_master(name="SBusBridgeToWishbone", master=wishbone_master_sys) self.bus.add_slave(name="usb_fake_dma", slave=self.wishbone_slave_sys, region=SoCRegion(origin=self.mem_map.get("usb_fake_dma", None), size=0x03ffffff, cached=False)) @@ -273,18 +219,6 @@ class SBusFPGA(SoCCore): l2_cache_size = 0 ) -# self.soc = Module() - # self.soc.mem_regions = self.mem_regions = {} - # region = litex.soc.integration.soc.SoCRegion(origin=0x0, size=0x0) - # region.length = 0 - # self.mem_regions['csr'] = region - # self.soc.constants = self.constants = {} - # self.soc.csr_regions = self.csr_regions = {} - # self.soc.cpu_type = self.cpu_type = None - -# def do_finalize(self): -# self.platform.add_period_constraint(self.platform.lookup_request("SBUS_3V3_CLK", loose=True), 1e9/25e6) - def main(): parser = argparse.ArgumentParser(description="SbusFPGA") parser.add_argument("--build", action="store_true", help="Build bitstream") @@ -298,5 +232,20 @@ def main(): builder = Builder(soc, **builder_argdict(args)) builder.build(**vivado_build_argdict(args), run=args.build) + # Generate modified CSR registers definitions/access functions to netbsd_csr.h. + csr_contents = sbus_to_fpga_export.get_csr_header( + regions = soc.csr_regions, + constants = soc.constants, + csr_base = soc.mem_regions['csr'].origin) + write_to_file(os.path.join("netbsd_csr.h"), csr_contents) + + # tells the prom where to find what + csr_forth_contents = sbus_to_fpga_export.get_csr_forth_header( + csr_regions = soc.csr_regions, + mem_regions = soc.mem_regions, + constants = soc.constants, + csr_base = soc.mem_regions['csr'].origin) + write_to_file(os.path.join("prom_csr.fth"), csr_forth_contents) + if __name__ == "__main__": main()