From ac1e968351f43e019a0adf48c5d162972e54fda2 Mon Sep 17 00:00:00 2001 From: Ilya Epifanov Date: Tue, 28 Apr 2020 22:10:20 +0200 Subject: [PATCH 01/17] Can now pass `--seed` to `nextpnr-ecp5` via `TrellisToolchain` `kwargs` --- litex/build/lattice/trellis.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/litex/build/lattice/trellis.py b/litex/build/lattice/trellis.py index fa19b9192..3aba0a849 100644 --- a/litex/build/lattice/trellis.py +++ b/litex/build/lattice/trellis.py @@ -117,11 +117,11 @@ nextpnr_ecp5_architectures = { _build_template = [ "yosys -l {build_name}.rpt {build_name}.ys", "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config \ - --{architecture} --package {package} --speed {speed_grade} {timefailarg} {ignoreloops}", + --{architecture} --package {package} --speed {speed_grade} {timefailarg} {ignoreloops} {seed}", "ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit" ] -def _build_script(source, build_template, build_name, architecture, package, speed_grade, timingstrict, ignoreloops): +def _build_script(source, build_template, build_name, architecture, package, speed_grade, timingstrict, ignoreloops, seed): if sys.platform in ("win32", "cygwin"): script_ext = ".bat" script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n" @@ -140,7 +140,8 @@ def _build_script(source, build_template, build_name, architecture, package, spe speed_grade = speed_grade, timefailarg = "--timing-allow-fail" if not timingstrict else "", ignoreloops = "--ignore-loops" if ignoreloops else "", - fail_stmt = fail_stmt) + fail_stmt = fail_stmt, + seed = f"--seed {seed}" if seed is not None else "") script_file = "build_" + build_name + script_ext tools.write_to_file(script_file, script_contents, force_unix=False) @@ -186,6 +187,7 @@ class LatticeTrellisToolchain: nowidelut = False, timingstrict = False, ignoreloops = False, + seed = None, **kwargs): # Create build directory @@ -217,7 +219,7 @@ class LatticeTrellisToolchain: # Generate build script script = _build_script(False, self.build_template, build_name, architecture, package, - speed_grade, timingstrict, ignoreloops) + speed_grade, timingstrict, ignoreloops, seed) # Run if run: From 83f4dcb2c6950e33a32f08abc61aed3abc9b6e1b Mon Sep 17 00:00:00 2001 From: Ilya Epifanov Date: Tue, 28 Apr 2020 22:13:53 +0200 Subject: [PATCH 02/17] Added `imac` config for CPUs which implements the most basic working riscv32imac feature set, implemented for VexRiscv --- litex/soc/cores/cpu/__init__.py | 1 + litex/soc/cores/cpu/vexriscv/core.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/litex/soc/cores/cpu/__init__.py b/litex/soc/cores/cpu/__init__.py index 21f7ce5d5..5497bac85 100644 --- a/litex/soc/cores/cpu/__init__.py +++ b/litex/soc/cores/cpu/__init__.py @@ -61,6 +61,7 @@ CPU_VARIANTS = { "minimal" : ["min",], "lite" : ["light", "zephyr", "nuttx"], "standard": [None, "std"], + "imac": [], "full": [], "linux" : [], "linuxd" : [], diff --git a/litex/soc/cores/cpu/vexriscv/core.py b/litex/soc/cores/cpu/vexriscv/core.py index 8e27e5d3f..1ed0494b5 100644 --- a/litex/soc/cores/cpu/vexriscv/core.py +++ b/litex/soc/cores/cpu/vexriscv/core.py @@ -25,6 +25,8 @@ CPU_VARIANTS = { "lite+debug": "VexRiscv_LiteDebug", "standard": "VexRiscv", "standard+debug": "VexRiscv_Debug", + "imac": "VexRiscv_IMAC", + "imac+debug": "VexRiscv_IMACDebug", "full": "VexRiscv_Full", "full+debug": "VexRiscv_FullDebug", "linux": "VexRiscv_Linux", @@ -47,6 +49,8 @@ GCC_FLAGS = { "lite+debug": "-march=rv32i -mabi=ilp32", "standard": "-march=rv32im -mabi=ilp32", "standard+debug": "-march=rv32im -mabi=ilp32", + "imac": "-march=rv32imac -mabi=ilp32", + "imac+debug": "-march=rv32imac -mabi=ilp32", "full": "-march=rv32im -mabi=ilp32", "full+debug": "-march=rv32im -mabi=ilp32", "linux": "-march=rv32ima -mabi=ilp32", From 64b505156e41a12063a50b5884449ab972d6c02d Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 28 Apr 2020 15:14:27 +0100 Subject: [PATCH 03/17] Add RDIMM side-B inversion support Signed-off-by: David Shah --- litex/soc/software/bios/sdram.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/litex/soc/software/bios/sdram.c b/litex/soc/software/bios/sdram.c index 8d8ddfefe..94df63168 100644 --- a/litex/soc/software/bios/sdram.c +++ b/litex/soc/software/bios/sdram.c @@ -269,6 +269,13 @@ void sdrwlon(void) sdram_dfii_pi0_address_write(DDRX_MR1 | (1 << 7)); sdram_dfii_pi0_baddress_write(1); command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS); + +#ifdef SDRAM_PHY_DDR4_RDIMM + sdram_dfii_pi0_address_write((DDRX_MR1 | (1 << 7)) ^ 0x2BF8) ; + sdram_dfii_pi0_baddress_write(1 ^ 0xF); + command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS); +#endif + ddrphy_wlevel_en_write(1); } @@ -277,6 +284,13 @@ void sdrwloff(void) sdram_dfii_pi0_address_write(DDRX_MR1); sdram_dfii_pi0_baddress_write(1); command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS); + +#ifdef SDRAM_PHY_DDR4_RDIMM + sdram_dfii_pi0_address_write(DDRX_MR1 ^ 0x2BF8); + sdram_dfii_pi0_baddress_write(1 ^ 0xF); + command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS); +#endif + ddrphy_wlevel_en_write(0); } @@ -1158,6 +1172,12 @@ void sdrmrwr(char reg, int value) { sdram_dfii_pi0_address_write(value); sdram_dfii_pi0_baddress_write(reg); command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS); + +#ifdef SDRAM_PHY_DDR4_RDIMM + sdram_dfii_pi0_address_write(value ^ 0x2BF8); + sdram_dfii_pi0_baddress_write(reg ^ 0xF); + command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS); +#endif } static void sdrmpron(char mpr) From f1a50a21388f5b6cabfa3bf0c481feade73957b9 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 8 May 2020 11:54:51 +0200 Subject: [PATCH 04/17] integration/soc: add add_uartbone method (to add a UARTBone aka UART Wishbone bridge). --- litex/soc/cores/uart.py | 4 +++- litex/soc/integration/soc.py | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/litex/soc/cores/uart.py b/litex/soc/cores/uart.py index e6315016d..07fdac305 100644 --- a/litex/soc/cores/uart.py +++ b/litex/soc/cores/uart.py @@ -239,11 +239,13 @@ class UART(Module, AutoCSR, UARTInterface): self.ev.rx.trigger.eq(~rx_fifo.source.valid) ] -class UARTWishboneBridge(WishboneStreamingBridge): +class UARTBone(WishboneStreamingBridge): def __init__(self, pads, clk_freq, baudrate=115200): self.submodules.phy = RS232PHY(pads, clk_freq, baudrate) WishboneStreamingBridge.__init__(self, self.phy, clk_freq) +class UARTWishboneBridge(UARTBone): pass + # UART Multiplexer --------------------------------------------------------------------------------- class UARTMultiplexer(Module): diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index 8d5107625..d45b32fdd 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -926,13 +926,9 @@ class LiteXSoC(SoC): if name == "stub": self.comb += self.uart.sink.ready.eq(1) - # Bridge - elif name in ["bridge"]: - self.submodules.uart = uart.UARTWishboneBridge( - pads = self.platform.request("serial"), - clk_freq = self.sys_clk_freq, - baudrate = baudrate) - self.bus.add_master(name="uart_bridge", master=self.uart.wishbone) + # UARTBone / Bridge + elif name in ["uartbone", "bridge"]: + self.add_uartbone(baudrate=baudrate) # Crossover elif name in ["crossover"]: @@ -986,6 +982,15 @@ class LiteXSoC(SoC): else: self.add_constant("UART_POLLING") + # Add UARTbone --------------------------------------------------------------------------------- + def add_uartbone(self, name="serial", baudrate=115200): + from litex.soc.cores import uart + self.submodules.uartbone = uart.UARTBone( + pads = self.platform.request(name), + clk_freq = self.sys_clk_freq, + baudrate = baudrate) + self.bus.add_master(name="uartbone", master=self.uartbone.wishbone) + # Add SDRAM ------------------------------------------------------------------------------------ def add_sdram(self, name, phy, module, origin, size=None, l2_cache_size = 8192, From 90c485fcc88751c8b123c9ae1f57319d9939f387 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 8 May 2020 13:15:44 +0200 Subject: [PATCH 05/17] integration/soc: add clock_domain parameter to add_etherbone. To allow using a sys_clk < 125MHz with a 1Gbps link. --- litex/soc/integration/soc.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index d45b32fdd..012c486d7 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -1138,7 +1138,7 @@ class LiteXSoC(SoC): eth_tx_clk) # Add Etherbone -------------------------------------------------------------------------------- - def add_etherbone(self, name="etherbone", phy=None, + def add_etherbone(self, name="etherbone", phy=None, clock_domain=None, mac_address = 0x10e2d5000000, ip_address = "192.168.1.50", udp_port = 1234): @@ -1151,9 +1151,21 @@ class LiteXSoC(SoC): mac_address = mac_address, ip_address = ip_address, clk_freq = self.clk_freq) + if clock_domain is not None: # FIXME: Could probably be avoided. + ethcore = ClockDomainsRenamer("eth_tx")(ethcore) self.submodules += ethcore + + # Clock domain renaming + if clock_domain is not None: # FIXME: Could probably be avoided. + self.clock_domains.cd_etherbone = ClockDomain("etherbone") + self.comb += self.cd_etherbone.clk.eq(ClockSignal(clock_domain)) + self.comb += self.cd_etherbone.rst.eq(ResetSignal(clock_domain)) + clock_domain = "etherbone" + else: + clock_domain = "sys" + # Etherbone - etherbone = LiteEthEtherbone(ethcore.udp, udp_port) + etherbone = LiteEthEtherbone(ethcore.udp, udp_port, cd=clock_domain) setattr(self.submodules, name, etherbone) self.add_wb_master(etherbone.wishbone.bus) # Timing constraints From 05869beb722e6862118d5879fde57e90a8dc04e2 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 8 May 2020 13:17:59 +0200 Subject: [PATCH 06/17] cores/led: add LedChaser (now that LiteX is running on FPGA mining boards let's use fancy led blinks :)) --- litex/soc/cores/led.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 litex/soc/cores/led.py diff --git a/litex/soc/cores/led.py b/litex/soc/cores/led.py new file mode 100644 index 000000000..727000e0b --- /dev/null +++ b/litex/soc/cores/led.py @@ -0,0 +1,35 @@ +# This file is Copyright (c) 2020 Florent Kermarrec +# License: BSD + +from migen import * +from migen.genlib.misc import WaitTimer + +from litex.soc.interconnect.csr import * + +# Led Chaser --------------------------------------------------------------------------------------- + +class LedChaser(Module, AutoCSR): + def __init__(self, pads, sys_clk_freq, period=1e0): + self.control = CSRStorage(fields=[ + CSRField("mode", size=2, values=[ + ("``0b0``", "Chaser mode."), + ("``0b1``", "CPU mode."), + ]) + ]) + self.value = CSRStorage(len(pads), description="Control value when in CPU mode.") + + # # # + + n = len(pads) + chaser = Signal(n) + timer = WaitTimer(int(period*sys_clk_freq/(2*n))) + self.submodules += timer + self.comb += timer.wait.eq(~timer.done) + self.sync += If(timer.done, chaser.eq(Cat(~chaser[-1], chaser))) + self.comb += [ + If(self.control.fields.mode, + pads.eq(self.value.storage) + ).Else( + pads.eq(chaser) + ) + ] From fbbbdf03b583a5571069fb747170e6749ad118e6 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 8 May 2020 22:13:47 +0200 Subject: [PATCH 07/17] core/led: simplify LedChaser (to have the same user interface than GPIOOut). --- litex/soc/cores/led.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/litex/soc/cores/led.py b/litex/soc/cores/led.py index 727000e0b..54c1d2a08 100644 --- a/litex/soc/cores/led.py +++ b/litex/soc/cores/led.py @@ -8,27 +8,26 @@ from litex.soc.interconnect.csr import * # Led Chaser --------------------------------------------------------------------------------------- +_CHASER_MODE = 0 +_CONTROL_MODE = 1 + class LedChaser(Module, AutoCSR): def __init__(self, pads, sys_clk_freq, period=1e0): - self.control = CSRStorage(fields=[ - CSRField("mode", size=2, values=[ - ("``0b0``", "Chaser mode."), - ("``0b1``", "CPU mode."), - ]) - ]) - self.value = CSRStorage(len(pads), description="Control value when in CPU mode.") + self._out = CSRStorage(len(pads), description="Led Output(s) Control.") # # # - n = len(pads) - chaser = Signal(n) - timer = WaitTimer(int(period*sys_clk_freq/(2*n))) + n = len(pads) + chaser = Signal(n) + mode = Signal(reset=_CHASER_MODE) + timer = WaitTimer(int(period*sys_clk_freq/(2*n))) self.submodules += timer self.comb += timer.wait.eq(~timer.done) self.sync += If(timer.done, chaser.eq(Cat(~chaser[-1], chaser))) + self.sync += If(self._out.re, mode.eq(_CONTROL_MODE)) self.comb += [ - If(self.control.fields.mode, - pads.eq(self.value.storage) + If(mode == _CONTROL_MODE, + pads.eq(self._out.storage) ).Else( pads.eq(chaser) ) From 9b782bd7da28b00b5f8b292ca0741503484b7ed7 Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Fri, 8 May 2020 21:00:24 -0700 Subject: [PATCH 08/17] diamond: clock constraint improvements Specify NET or PORT for freq constraints Add equivalent timing closure check that diamond ui uses, and default to asserting check has passed --- litex/build/lattice/diamond.py | 71 ++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/litex/build/lattice/diamond.py b/litex/build/lattice/diamond.py index cf44d096b..e48f4852b 100644 --- a/litex/build/lattice/diamond.py +++ b/litex/build/lattice/diamond.py @@ -4,6 +4,7 @@ # License: BSD import os +import re import sys import subprocess import shutil @@ -40,18 +41,29 @@ def _format_lpf(signame, pin, others, resname): return "\n".join(lpf) -def _build_lpf(named_sc, named_pc, build_name): +def _build_lpf(named_sc, named_pc, cc, build_name): lpf = [] lpf.append("BLOCK RESETPATHS;") lpf.append("BLOCK ASYNCPATHS;") + clk_ports = set() for sig, pins, others, resname in named_sc: if len(pins) > 1: for i, p in enumerate(pins): lpf.append(_format_lpf(sig + "[" + str(i) + "]", p, others, resname)) else: lpf.append(_format_lpf(sig, pins[0], others, resname)) + if sig in cc.keys(): + clk_ports.add(sig) if named_pc: - lpf.append("\n\n".join(named_pc)) + lpf.append("\n".join(named_pc)) + + # NOTE: The lpf is only used post-synthesis. Currently Synplify is fed no constraints, + # so defaults to inferring clocks and trying to hit 200MHz on all of them. + # NOTE: For additional options (PAR_ADJ, etc), use add_platform_command + for clk, freq in cc.items(): + sig_type = "PORT" if clk in clk_ports else "NET" + lpf.append("FREQUENCY {} \"{}\" {} MHz;".format(sig_type, clk, freq)) + tools.write_to_file(build_name + ".lpf", "\n".join(lpf)) # Project (.tcl) ----------------------------------------------------------------------------------- @@ -67,13 +79,15 @@ def _build_tcl(device, sources, vincpaths, build_name): "-synthesis \"synplify\"" ])) + def tcl_path(path): return path.replace("\\", "/") + # Add include paths - vincpath = ';'.join(map(lambda x: x.replace('\\', '/'), vincpaths)) + vincpath = ";".join(map(lambda x: tcl_path(x), vincpaths)) tcl.append("prj_impl option {include path} {\"" + vincpath + "\"}") # Add sources for filename, language, library in sources: - tcl.append("prj_src add \"" + filename.replace("\\", "/") + "\" -work " + library) + tcl.append("prj_src add \"{}\" -work {}".format(tcl_path(filename), library)) # Set top level tcl.append("prj_impl option top \"{}\"".format(build_name)) @@ -89,6 +103,7 @@ def _build_tcl(device, sources, vincpaths, build_name): tcl.append("prj_run Export -impl impl -task Bitgen") if _produces_jedec(device): tcl.append("prj_run Export -impl impl -task Jedecgen") + tools.write_to_file(build_name + ".tcl", "\n".join(tcl)) # Script ------------------------------------------------------------------------------------------- @@ -130,6 +145,35 @@ def _run_script(script): if subprocess.call(shell + [script]) != 0: raise OSError("Subprocess failed") +# This operates the same way tmcheck does, but tmcheck isn't usable without gui +def _check_timing(build_name): + lines = open("impl/{}_impl.par".format(build_name), "r").readlines() + runs = [None, None] + for i in range(len(lines)-1): + if lines[i].startswith("Level/") and lines[i+1].startswith("Cost "): + runs[0] = i + 2 + if lines[i].startswith("* : Design saved.") and runs[0] is not None: + runs[1] = i + break + assert all(map(lambda x: x is not None, runs)) + + p = re.compile(r"(^\s*\S+\s+\*?\s+[0-9]+\s+)(\S+)(\s+\S+\s+)(\S+)(\s+.*)") + for l in lines[runs[0]:runs[1]]: + m = p.match(l) + if m is None: continue + limit = 1e-8 + setup = m.group(2) + hold = m.group(4) + # if there were no freq constraints in lpf, ratings will be dashed. + # results will likely be terribly unreliable, so bail + assert not setup == hold == "-", "No timing constraints were provided" + setup, hold = map(float, (setup, hold)) + if setup > limit and hold > limit: + # at least one run met timing + # XXX is this necessarily the run from which outputs will be used? + return + raise Exception("Failed to meet timing") + # LatticeDiamondToolchain -------------------------------------------------------------------------- class LatticeDiamondToolchain: @@ -149,12 +193,14 @@ class LatticeDiamondToolchain: special_overrides = common.lattice_ecp5_special_overrides def __init__(self): + self.period_constraints = [] self.false_paths = set() # FIXME: use it def build(self, platform, fragment, build_dir = "build", build_name = "top", run = True, + timingstrict = True, **kwargs): # Create build directory @@ -174,8 +220,15 @@ class LatticeDiamondToolchain: v_output.write(v_file) platform.add_source(v_file) + cc = {} + for (clk, freq) in self.period_constraints: + clk_name = v_output.ns.get_name(clk) + if clk_name in cc and cc[clk_name] != freq: + raise ConstraintError("Differing period constraints on {}".format(clk_name)) + cc[clk_name] = freq + # Generate design constraints file (.lpf) - _build_lpf(named_sc, named_pc, build_name) + _build_lpf(named_sc, named_pc, cc, build_name) # Generate design script file (.tcl) _build_tcl(platform.device, platform.sources, platform.verilog_include_paths, build_name) @@ -186,16 +239,18 @@ class LatticeDiamondToolchain: # Run if run: _run_script(script) + if timingstrict: + _check_timing(build_name) os.chdir(cwd) return v_output.ns def add_period_constraint(self, platform, clk, period): - clk.attr.add("keep") # TODO: handle differential clk - platform.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format( - freq=str(float(1/period)*1000), clk="{clk}"), clk=clk) + clk.attr.add("keep") + freq = str(float(1/period)*1000) + self.period_constraints.append((clk, freq)) def add_false_path_constraint(self, platform, from_, to): from_.attr.add("keep") From eeee179dd87e62c68502e5781bd43c17ce5de975 Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Sat, 9 May 2020 02:16:24 -0700 Subject: [PATCH 09/17] diamond: close project when done Avoids ".recovery file is present" prompt. --- litex/build/lattice/diamond.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/litex/build/lattice/diamond.py b/litex/build/lattice/diamond.py index e48f4852b..da993c5c9 100644 --- a/litex/build/lattice/diamond.py +++ b/litex/build/lattice/diamond.py @@ -104,6 +104,9 @@ def _build_tcl(device, sources, vincpaths, build_name): if _produces_jedec(device): tcl.append("prj_run Export -impl impl -task Jedecgen") + # Cleanly close the project + tcl.append("prj_project close") + tools.write_to_file(build_name + ".tcl", "\n".join(tcl)) # Script ------------------------------------------------------------------------------------------- From 2d70220b80882ffb1043946ebba5b420ec8bab56 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 9 May 2020 19:43:37 +0200 Subject: [PATCH 10/17] bios: Fix warning on 64-bit This fixes an incorrect printf format specifier Signed-off-by: Benjamin Herrenschmidt --- litex/soc/software/bios/sdram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litex/soc/software/bios/sdram.c b/litex/soc/software/bios/sdram.c index e7e754c65..2b89c0980 100644 --- a/litex/soc/software/bios/sdram.c +++ b/litex/soc/software/bios/sdram.c @@ -903,7 +903,7 @@ static void memspeed(void) end = timer0_value_read(); read_speed = (8*MEMTEST_DATA_SIZE*(CONFIG_CLOCK_FREQUENCY/1000000))/(start - end); - printf("Memspeed Writes: %dMbps Reads: %dMbps\n", write_speed, read_speed); + printf("Memspeed Writes: %ldMbps Reads: %ldMbps\n", write_speed, read_speed); } int memtest(void) From e4fa4bbcf7482283e0e3e8496637ed474c4f5a99 Mon Sep 17 00:00:00 2001 From: Ilia Sergachev Date: Sun, 10 May 2020 11:32:34 +0200 Subject: [PATCH 11/17] integration/soc: fix add_adapter for slaves --- litex/soc/integration/soc.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index 012c486d7..d6e2daa77 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -282,7 +282,7 @@ class SoCBusHandler(Module): return is_io # Add Master/Slave ----------------------------------------------------------------------------- - def add_adapter(self, name, interface): + def add_adapter(self, name, interface, is_master): if interface.data_width != self.data_width: self.logger.info("{} Bus {} from {}-bit to {}-bit.".format( colorer(name), @@ -290,7 +290,8 @@ class SoCBusHandler(Module): colorer(interface.data_width), colorer(self.data_width))) new_interface = wishbone.Interface(data_width=self.data_width) - self.submodules += wishbone.Converter(interface, new_interface) + args = (interface, new_interface) if is_master else (new_interface, interface) + self.submodules += wishbone.Converter(*args) return new_interface else: return interface @@ -304,7 +305,7 @@ class SoCBusHandler(Module): colorer("already declared", color="red"))) self.logger.error(self) raise - master = self.add_adapter(name, master) + master = self.add_adapter(name, master, True) self.masters[name] = master self.logger.info("{} {} as Bus Master.".format( colorer(name, color="underline"), @@ -336,7 +337,7 @@ class SoCBusHandler(Module): colorer("already declared", color="red"))) self.logger.error(self) raise - slave = self.add_adapter(name, slave) + slave = self.add_adapter(name, slave, False) self.slaves[name] = slave self.logger.info("{} {} as Bus Slave.".format( colorer(name, color="underline"), From 59d88a880ca2313cd3b8f875abcf29297209a224 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 11 May 2020 08:47:34 +0200 Subject: [PATCH 12/17] integration/soc/add_adapter: rename is_master to direction. --- litex/soc/integration/soc.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index d6e2daa77..c6cc1e0f8 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -282,7 +282,8 @@ class SoCBusHandler(Module): return is_io # Add Master/Slave ----------------------------------------------------------------------------- - def add_adapter(self, name, interface, is_master): + def add_adapter(self, name, interface, direction="m2s"): + assert direction in ["m2s", "s2m"] if interface.data_width != self.data_width: self.logger.info("{} Bus {} from {}-bit to {}-bit.".format( colorer(name), @@ -290,8 +291,11 @@ class SoCBusHandler(Module): colorer(interface.data_width), colorer(self.data_width))) new_interface = wishbone.Interface(data_width=self.data_width) - args = (interface, new_interface) if is_master else (new_interface, interface) - self.submodules += wishbone.Converter(*args) + if direction == "m2s": + converter = wishbone.Converter(master=interface, slave=new_interface) + if direction == "s2m": + converter = wishbone.Converter(master=new_interface, slave=interface) + self.submodules += converter return new_interface else: return interface @@ -305,7 +309,7 @@ class SoCBusHandler(Module): colorer("already declared", color="red"))) self.logger.error(self) raise - master = self.add_adapter(name, master, True) + master = self.add_adapter(name, master, "m2s") self.masters[name] = master self.logger.info("{} {} as Bus Master.".format( colorer(name, color="underline"), @@ -337,7 +341,7 @@ class SoCBusHandler(Module): colorer("already declared", color="red"))) self.logger.error(self) raise - slave = self.add_adapter(name, slave, False) + slave = self.add_adapter(name, slave, "s2m") self.slaves[name] = slave self.logger.info("{} {} as Bus Slave.".format( colorer(name, color="underline"), From ea7fe383a377a27c4afb2e0389139e334ca061db Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 11 May 2020 09:26:12 +0200 Subject: [PATCH 13/17] lattice/trellis: simplify seed support and add it to trellis_args. --- litex/build/lattice/trellis.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/litex/build/lattice/trellis.py b/litex/build/lattice/trellis.py index 3aba0a849..36b5cbc3e 100644 --- a/litex/build/lattice/trellis.py +++ b/litex/build/lattice/trellis.py @@ -117,7 +117,7 @@ nextpnr_ecp5_architectures = { _build_template = [ "yosys -l {build_name}.rpt {build_name}.ys", "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config \ - --{architecture} --package {package} --speed {speed_grade} {timefailarg} {ignoreloops} {seed}", + --{architecture} --package {package} --speed {speed_grade} {timefailarg} {ignoreloops} --seed {seed}", "ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit" ] @@ -141,7 +141,7 @@ def _build_script(source, build_template, build_name, architecture, package, spe timefailarg = "--timing-allow-fail" if not timingstrict else "", ignoreloops = "--ignore-loops" if ignoreloops else "", fail_stmt = fail_stmt, - seed = f"--seed {seed}" if seed is not None else "") + seed = seed) script_file = "build_" + build_name + script_ext tools.write_to_file(script_file, script_contents, force_unix=False) @@ -187,7 +187,7 @@ class LatticeTrellisToolchain: nowidelut = False, timingstrict = False, ignoreloops = False, - seed = None, + seed = 1, **kwargs): # Create build directory @@ -246,10 +246,13 @@ def trellis_args(parser): help="fail if timing not met, i.e., do NOT pass '--timing-allow-fail' to nextpnr") parser.add_argument("--nextpnr-ignoreloops", action="store_true", help="ignore combinational loops in timing analysis, i.e. pass '--ignore-loops' to nextpnr") + parser.add_argument("--nextpnr-seed", default=1, type=int, + help="seed to pass to nextpnr") def trellis_argdict(args): return { "nowidelut": args.yosys_nowidelut, "timingstrict": args.nextpnr_timingstrict, "ignoreloops": args.nextpnr_ignoreloops, + "seed": args.nextpnr_seed, } From c9e36d7fddcf8fa2e3ee3b8c47770e03f2333e38 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 11 May 2020 09:33:26 +0200 Subject: [PATCH 14/17] lattice/icestorm: add ignoreloops/seed support (similar to trellis) and icestorm_args. --- litex/build/lattice/icestorm.py | 40 ++++++++++++++++++++++++--------- litex/build/lattice/trellis.py | 16 ++++++------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/litex/build/lattice/icestorm.py b/litex/build/lattice/icestorm.py index 2822a9b53..059363c04 100644 --- a/litex/build/lattice/icestorm.py +++ b/litex/build/lattice/icestorm.py @@ -93,32 +93,35 @@ def parse_device(device): _build_template = [ "yosys -l {build_name}.rpt {build_name}.ys", "nextpnr-ice40 --json {build_name}.json --pcf {build_name}.pcf --asc {build_name}.txt \ - --pre-pack {build_name}_pre_pack.py --{architecture} --package {package} {timefailarg}", + --pre-pack {build_name}_pre_pack.py --{architecture} --package {package} {timefailarg} {ignoreloops} --seed {seed}", "icepack -s {build_name}.txt {build_name}.bin" ] -def _build_script(build_template, build_name, architecture, package, timingstrict): +def _build_script(build_template, build_name, architecture, package, timingstrict, ignoreloops, seed): if sys.platform in ("win32", "cygwin"): script_ext = ".bat" - build_script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n" + script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n" fail_stmt = " || exit /b" else: script_ext = ".sh" - build_script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n" + script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n" fail_stmt = "" for s in build_template: s_fail = s + "{fail_stmt}\n" # Required so Windows scripts fail early. - build_script_contents += s_fail.format( + script_contents += s_fail.format( build_name = build_name, architecture = architecture, package = package, timefailarg = "--timing-allow-fail" if not timingstrict else "", - fail_stmt = fail_stmt) + ignoreloops = "--ignore-loops" if ignoreloops else "", + fail_stmt = fail_stmt, + seed = seed) - build_script_file = "build_" + build_name + script_ext - tools.write_to_file(build_script_file, build_script_contents,force_unix=False) - return build_script_file + script_file = "build_" + build_name + script_ext + tools.write_to_file(script_file, script_contents, force_unix=False) + + return script_file def _run_script(script): if sys.platform in ("win32", "cygwin"): @@ -158,6 +161,8 @@ class LatticeIceStormToolchain: synth_opts = "", run = True, timingstrict = False, + ignoreloops = False, + seed = 1, **kwargs): # Create build directory @@ -190,7 +195,7 @@ class LatticeIceStormToolchain: (family, architecture, package) = parse_device(platform.device) # Generate build script - script = _build_script(self.build_template, build_name, architecture, package, timingstrict) + script = _build_script(self.build_template, build_name, architecture, package, timingstrict, ignoreloops, seed) # Run if run: @@ -207,3 +212,18 @@ class LatticeIceStormToolchain: raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns" .format(self.clocks[clk], period)) self.clocks[clk] = period + +def icestorm_args(parser): + parser.add_argument("--nextpnr-timingstrict", action="store_true", + help="fail if timing not met, i.e., do NOT pass '--timing-allow-fail' to nextpnr") + parser.add_argument("--nextpnr-ignoreloops", action="store_true", + help="ignore combinational loops in timing analysis, i.e. pass '--ignore-loops' to nextpnr") + parser.add_argument("--nextpnr-seed", default=1, type=int, + help="seed to pass to nextpnr") + +def icestorm_argdict(args): + return { + "timingstrict": args.nextpnr_timingstrict, + "ignoreloops": args.nextpnr_ignoreloops, + "seed": args.nextpnr_seed, + } \ No newline at end of file diff --git a/litex/build/lattice/trellis.py b/litex/build/lattice/trellis.py index 36b5cbc3e..38e4cdaeb 100644 --- a/litex/build/lattice/trellis.py +++ b/litex/build/lattice/trellis.py @@ -134,14 +134,14 @@ def _build_script(source, build_template, build_name, architecture, package, spe for s in build_template: s_fail = s + "{fail_stmt}\n" # Required so Windows scripts fail early. script_contents += s_fail.format( - build_name = build_name, - architecture = architecture, - package = package, - speed_grade = speed_grade, - timefailarg = "--timing-allow-fail" if not timingstrict else "", - ignoreloops = "--ignore-loops" if ignoreloops else "", - fail_stmt = fail_stmt, - seed = seed) + build_name = build_name, + architecture = architecture, + package = package, + speed_grade = speed_grade, + timefailarg = "--timing-allow-fail" if not timingstrict else "", + ignoreloops = "--ignore-loops" if ignoreloops else "", + fail_stmt = fail_stmt, + seed = seed) script_file = "build_" + build_name + script_ext tools.write_to_file(script_file, script_contents, force_unix=False) From 1e610600f6a088941700d6736e0621010527ef64 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 11 May 2020 10:50:25 +0200 Subject: [PATCH 15/17] build/lattice/diamond/clock_constraints: review and improve similarities with the others build backends. --- litex/build/lattice/diamond.py | 48 +++++++++++++++------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/litex/build/lattice/diamond.py b/litex/build/lattice/diamond.py index da993c5c9..9e6ee704d 100644 --- a/litex/build/lattice/diamond.py +++ b/litex/build/lattice/diamond.py @@ -6,6 +6,7 @@ import os import re import sys +import math import subprocess import shutil @@ -41,28 +42,26 @@ def _format_lpf(signame, pin, others, resname): return "\n".join(lpf) -def _build_lpf(named_sc, named_pc, cc, build_name): +def _build_lpf(named_sc, named_pc, clocks, vns, build_name): lpf = [] lpf.append("BLOCK RESETPATHS;") lpf.append("BLOCK ASYNCPATHS;") - clk_ports = set() for sig, pins, others, resname in named_sc: if len(pins) > 1: for i, p in enumerate(pins): lpf.append(_format_lpf(sig + "[" + str(i) + "]", p, others, resname)) else: lpf.append(_format_lpf(sig, pins[0], others, resname)) - if sig in cc.keys(): - clk_ports.add(sig) if named_pc: lpf.append("\n".join(named_pc)) - # NOTE: The lpf is only used post-synthesis. Currently Synplify is fed no constraints, - # so defaults to inferring clocks and trying to hit 200MHz on all of them. - # NOTE: For additional options (PAR_ADJ, etc), use add_platform_command - for clk, freq in cc.items(): - sig_type = "PORT" if clk in clk_ports else "NET" - lpf.append("FREQUENCY {} \"{}\" {} MHz;".format(sig_type, clk, freq)) + # Note: .lpf is only used post-synthesis, Synplify constraints clocks by default to 200MHz. + for clk, period in clocks.items(): + clk_name = vns.get_name(clk) + lpf.append("FREQUENCY {} \"{}\" {} MHz;".format( + "PORT" if clk_name in [name for name, _, _, _ in named_sc] else "NET", + clk_name, + str(1e3/period))) tools.write_to_file(build_name + ".lpf", "\n".join(lpf)) @@ -104,7 +103,7 @@ def _build_tcl(device, sources, vincpaths, build_name): if _produces_jedec(device): tcl.append("prj_run Export -impl impl -task Jedecgen") - # Cleanly close the project + # Close project tcl.append("prj_project close") tools.write_to_file(build_name + ".tcl", "\n".join(tcl)) @@ -148,7 +147,6 @@ def _run_script(script): if subprocess.call(shell + [script]) != 0: raise OSError("Subprocess failed") -# This operates the same way tmcheck does, but tmcheck isn't usable without gui def _check_timing(build_name): lines = open("impl/{}_impl.par".format(build_name), "r").readlines() runs = [None, None] @@ -166,13 +164,13 @@ def _check_timing(build_name): if m is None: continue limit = 1e-8 setup = m.group(2) - hold = m.group(4) - # if there were no freq constraints in lpf, ratings will be dashed. + hold = m.group(4) + # If there were no freq constraints in lpf, ratings will be dashed. # results will likely be terribly unreliable, so bail assert not setup == hold == "-", "No timing constraints were provided" setup, hold = map(float, (setup, hold)) if setup > limit and hold > limit: - # at least one run met timing + # At least one run met timing # XXX is this necessarily the run from which outputs will be used? return raise Exception("Failed to meet timing") @@ -196,7 +194,7 @@ class LatticeDiamondToolchain: special_overrides = common.lattice_ecp5_special_overrides def __init__(self): - self.period_constraints = [] + self.clocks = {} self.false_paths = set() # FIXME: use it def build(self, platform, fragment, @@ -223,15 +221,8 @@ class LatticeDiamondToolchain: v_output.write(v_file) platform.add_source(v_file) - cc = {} - for (clk, freq) in self.period_constraints: - clk_name = v_output.ns.get_name(clk) - if clk_name in cc and cc[clk_name] != freq: - raise ConstraintError("Differing period constraints on {}".format(clk_name)) - cc[clk_name] = freq - # Generate design constraints file (.lpf) - _build_lpf(named_sc, named_pc, cc, build_name) + _build_lpf(named_sc, named_pc, self.clocks, v_output.ns, build_name) # Generate design script file (.tcl) _build_tcl(platform.device, platform.sources, platform.verilog_include_paths, build_name) @@ -250,10 +241,13 @@ class LatticeDiamondToolchain: return v_output.ns def add_period_constraint(self, platform, clk, period): - # TODO: handle differential clk clk.attr.add("keep") - freq = str(float(1/period)*1000) - self.period_constraints.append((clk, freq)) + period = math.floor(period*1e3)/1e3 # round to lowest picosecond + if clk in self.clocks: + if period != self.clocks[clk]: + raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns" + .format(self.clocks[clk], period)) + self.clocks[clk] = period def add_false_path_constraint(self, platform, from_, to): from_.attr.add("keep") From 2c40967b5a8ea3f844de3f1e4da3a11c601c3245 Mon Sep 17 00:00:00 2001 From: Arnaud Durand Date: Mon, 11 May 2020 22:12:40 +0200 Subject: [PATCH 16/17] Enable 1x mode on SPI flash --- litex/soc/integration/soc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index 012c486d7..26c2b8b91 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -1185,10 +1185,10 @@ class LiteXSoC(SoC): # Add SPI Flash -------------------------------------------------------------------------------- def add_spi_flash(self, name="spiflash", mode="4x", dummy_cycles=None, clk_freq=None): assert dummy_cycles is not None # FIXME: Get dummy_cycles from SPI Flash - assert mode in ["4x"] # FIXME: Add 1x support. + assert mode in ["1x", "4x"] if clk_freq is None: clk_freq = self.clk_freq/2 # FIXME: Get max clk_freq from SPI Flash spiflash = SpiFlash( - pads = self.platform.request(name + mode), + pads = self.platform.request(name if mode == "1x" else name + mode), dummy = dummy_cycles, div = ceil(self.clk_freq/clk_freq), with_bitbang = True, From e2176cefc2ac7b0916eaf25a3f0ddebdcfbae212 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 11 May 2020 22:39:17 +0200 Subject: [PATCH 17/17] soc: remove with_wishbone (a SoC always always has a Bus) and expose more bus parameters. --- litex/soc/integration/soc.py | 4 ++-- litex/soc/integration/soc_core.py | 27 +++++++++++---------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index c6cc1e0f8..64819fcad 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -758,8 +758,8 @@ class SoC(Module): def add_csr_bridge(self, origin): self.submodules.csr_bridge = wishbone2csr.WB2CSR( bus_csr = csr_bus.Interface( - address_width = self.csr.address_width, - data_width = self.csr.data_width)) + address_width = self.csr.address_width, + data_width = self.csr.data_width)) csr_size = 2**(self.csr.address_width + 2) csr_region = SoCRegion(origin=origin, size=csr_size, cached=False) self.bus.add_slave("csr", self.csr_bridge.wishbone, csr_region) diff --git a/litex/soc/integration/soc_core.py b/litex/soc/integration/soc_core.py index f61555a31..68499ad2b 100644 --- a/litex/soc/integration/soc_core.py +++ b/litex/soc/integration/soc_core.py @@ -61,6 +61,11 @@ class SoCCore(LiteXSoC): } def __init__(self, platform, clk_freq, + # Bus parameters + bus_standard = "wishbone", + bus_data_width = 32, + bus_address_width = 32, + bus_timeout = 1e6, # CPU parameters cpu_type = "vexriscv", cpu_reset_address = None, @@ -92,18 +97,15 @@ class SoCCore(LiteXSoC): with_timer = True, # Controller parameters with_ctrl = True, - # Wishbone parameters - with_wishbone = True, - wishbone_timeout_cycles = 1e6, # Others **kwargs): # New LiteXSoC class ---------------------------------------------------------------------------- LiteXSoC.__init__(self, platform, clk_freq, - bus_standard = "wishbone", - bus_data_width = 32, - bus_address_width = 32, - bus_timeout = wishbone_timeout_cycles, + bus_standard = bus_standard, + bus_data_width = bus_data_width, + bus_address_width = bus_address_width, + bus_timeout = bus_timeout, bus_reserved_regions = {}, csr_data_width = csr_data_width, @@ -127,9 +129,6 @@ class SoCCore(LiteXSoC): cpu_reset_address = None if cpu_reset_address == "None" else cpu_reset_address cpu_variant = cpu.check_format_cpu_variant(cpu_variant) - if not with_wishbone: - self.mem_map["csr"] = 0x00000000 - self.cpu_type = cpu_type self.cpu_variant = cpu_variant self.cpu_cls = cpu_cls @@ -141,9 +140,6 @@ class SoCCore(LiteXSoC): self.csr_data_width = csr_data_width - self.with_wishbone = with_wishbone - self.wishbone_timeout_cycles = wishbone_timeout_cycles - self.wb_slaves = {} # Modules instances ------------------------------------------------------------------------ @@ -187,9 +183,8 @@ class SoCCore(LiteXSoC): if with_timer: self.add_timer(name="timer0") - # Add Wishbone to CSR bridge - if with_wishbone: - self.add_csr_bridge(self.mem_map["csr"]) + # Add CSR bridge + self.add_csr_bridge(self.mem_map["csr"]) # Methods --------------------------------------------------------------------------------------