From c1733ea2fff2ba6c4a9e1c386cac6693d0e780d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Thu, 12 Sep 2024 09:48:15 +0200 Subject: [PATCH 01/11] build: io: make oe2 of DDRTristate optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit make oe2 of DDRTristate optional. Signed-off-by: Fin Maaß --- litex/build/efinix/common.py | 2 +- litex/build/io.py | 8 ++++---- litex/build/lattice/common.py | 3 ++- litex/build/xilinx/common.py | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/litex/build/efinix/common.py b/litex/build/efinix/common.py index 50d78a8d98..1eba70f4e7 100644 --- a/litex/build/efinix/common.py +++ b/litex/build/efinix/common.py @@ -275,7 +275,7 @@ def lower(dr): class EfinixDDRTristateImpl(LiteXModule): def __init__(self, io, o1, o2, oe1, oe2, i1, i2, clk): - assert oe1 == oe2 + assert oe2 is None assert_is_signal_or_clocksignal(clk) platform = LiteXContext.platform io_name = platform.get_pin_name(io) diff --git a/litex/build/io.py b/litex/build/io.py index b83cac4780..20d53443a6 100644 --- a/litex/build/io.py +++ b/litex/build/io.py @@ -190,20 +190,20 @@ def __init__(self, io, o1, o2, oe1, oe2, i1, i2, clk): _oe = Signal() _i = Signal() self.specials += DDROutput(o1, o2, _o, clk) - self.specials += DDROutput(oe1, oe2, _oe, clk) + self.specials += DDROutput(oe1, oe2, _oe, clk) if oe2 is not None else SDROutput(oe1, _oe, clk) self.specials += DDRInput(_i, i1, i2, clk) self.specials += Tristate(io, _o, _oe, _i) class DDRTristate(Special): - def __init__(self, io, o1, o2, oe1, oe2, i1, i2, clk=None): + def __init__(self, io, o1, o2, oe1, oe2=None, i1=None, i2=None, clk=None): Special.__init__(self) self.io = io self.o1 = o1 self.o2 = o2 self.oe1 = oe1 self.oe2 = oe2 - self.i1 = i1 - self.i2 = i2 + self.i1 = i1 if i1 is not None else Signal() + self.i2 = i2 if i2 is not None else Signal() self.clk = clk if clk is not None else ClockSignal() def iter_expressions(self): diff --git a/litex/build/lattice/common.py b/litex/build/lattice/common.py index 3f3f78e637..cec5ca51dc 100644 --- a/litex/build/lattice/common.py +++ b/litex/build/lattice/common.py @@ -305,11 +305,12 @@ def lower(dr): class LatticeNXDDRTristateImpl(Module): def __init__(self, io, o1, o2, oe1, oe2, i1, i2, clk): + assert oe2 is None _o = Signal() _oe = Signal() _i = Signal() self.specials += DDROutput(o1, o2, _o, clk) - self.specials += SDROutput(oe1 | oe2, _oe, clk) + self.specials += SDROutput(oe1, _oe, clk) self.specials += DDRInput(_i, i1, i2, clk) self.specials += Tristate(io, _o, _oe, _i) _oe.attr.add("syn_useioff") diff --git a/litex/build/xilinx/common.py b/litex/build/xilinx/common.py index d0d6bf0f63..0f2231f70b 100644 --- a/litex/build/xilinx/common.py +++ b/litex/build/xilinx/common.py @@ -164,7 +164,7 @@ def __init__(self, io, o1, o2, oe1, oe2, i1, i2, clk): _oe_n = Signal() _i = Signal() self.specials += DDROutput(o1, o2, _o, clk) - self.specials += DDROutput(~oe1, ~oe2, _oe_n, clk) + self.specials += DDROutput(~oe1, ~oe2, _oe_n, clk) if oe2 is not None else SDROutput(~oe1, _oe_n, clk) self.specials += DDRInput(_i, i1, i2, clk) self.specials += Instance("IOBUF", io_IO = io, From ed510bb9df87e0d7b96e44164ccd8d46ea7e5a6d Mon Sep 17 00:00:00 2001 From: Matthias Breithaupt Date: Mon, 25 Nov 2024 15:09:59 +0100 Subject: [PATCH 02/11] liblitespi: add 4k erase function Signed-off-by: Matthias Breithaupt --- litex/soc/software/liblitespi/spiflash.c | 9 +++++++++ litex/soc/software/liblitespi/spiflash.h | 1 + 2 files changed, 10 insertions(+) diff --git a/litex/soc/software/liblitespi/spiflash.c b/litex/soc/software/liblitespi/spiflash.c index fd9f2b9545..70d1a1bfd1 100644 --- a/litex/soc/software/liblitespi/spiflash.c +++ b/litex/soc/software/liblitespi/spiflash.c @@ -238,6 +238,15 @@ void spiflash_erase_range(uint32_t addr, uint32_t len) } } +void spiflash_erase_4k_sector(uint32_t addr) +{ + w_buf[0] = 0x20; + w_buf[1] = addr>>16; + w_buf[2] = addr>>8; + w_buf[3] = addr>>0; + transfer_cmd(w_buf, r_buf, 4); +} + int spiflash_write_stream(uint32_t addr, uint8_t *stream, uint32_t len) { int res = 0; diff --git a/litex/soc/software/liblitespi/spiflash.h b/litex/soc/software/liblitespi/spiflash.h index 2ac19525d9..a12e727525 100644 --- a/litex/soc/software/liblitespi/spiflash.h +++ b/litex/soc/software/liblitespi/spiflash.h @@ -14,6 +14,7 @@ void spiflash_memspeed(void); void spiflash_init(void); int spiflash_write_stream(uint32_t addr, uint8_t *stream, uint32_t len); void spiflash_erase_range(uint32_t addr, uint32_t len); +void spiflash_erase_4k_sector(uint32_t addr); #ifdef __cplusplus } From e91d4d1a397f2e70fc2aa27286701d9ed47c4932 Mon Sep 17 00:00:00 2001 From: Matthias Breithaupt Date: Mon, 25 Nov 2024 15:09:32 +0100 Subject: [PATCH 03/11] Add efinix SEU interface Signed-off-by: Matthias Breithaupt --- litex/build/efinix/ifacewriter.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/litex/build/efinix/ifacewriter.py b/litex/build/efinix/ifacewriter.py index ef65fc21b0..c542901e0d 100644 --- a/litex/build/efinix/ifacewriter.py +++ b/litex/build/efinix/ifacewriter.py @@ -629,6 +629,32 @@ def get_pin_name(pin): cmds.append(f"# ---------- END REMOTE UPDATE ---------\n") return "\n".join(cmds) + def generate_seu(self, block, verbose=True): + name = block["name"] + pins = block["pins"] + enable = block["enable"] + mode = block["mode"].upper() + + def get_pin_name(pin): + return pin.backtrace[-1][0] + + cmds = [] + cmds.append(f"# ---------- SINGLE-EVENT UPSET ---------") + cmds.append(f'design.set_device_property("seu", "ENA_DETECT", "{enable}", "SEU")') + if enable: + cmds.append(f'design.set_device_property("seu", "CONFIG_PIN", "{get_pin_name(pins.CONFIG)}", "SEU")') + cmds.append(f'design.set_device_property("seu", "DONE_PIN", "{get_pin_name(pins.DONE)}", "SEU")') + cmds.append(f'design.set_device_property("seu", "ERROR_PIN", "{get_pin_name(pins.ERROR)}", "SEU")') + cmds.append(f'design.set_device_property("seu", "INJECT_ERROR_PIN", "{get_pin_name(pins.INJECT_ERROR)}", "SEU")') + cmds.append(f'design.set_device_property("seu", "RST_PIN", "{get_pin_name(pins.RST)}", "SEU")') + if mode == "MANUAL": + cmds.append(f'design.set_device_property("seu", "START_PIN", "{get_pin_name(pins.START)}", "SEU")') + cmds.append(f'design.set_device_property("seu", "MODE", "{mode}", "SEU")') + if mode == "AUTO" and hasattr(block, "wait_interval"): + cmds.append(f'design.set_device_property("seu", "WAIT_INTERVAL", "{block["wait_interval"]}", "SEU")') + cmds.append(f"# ---------- END SINGLE-EVENT UPSET ---------\n") + return "\n".join(cmds) + def generate(self, partnumber): output = "" for block in self.blocks: @@ -653,6 +679,8 @@ def generate(self, partnumber): output += self.generate_spiflash(block) if block["type"] == "REMOTE_UPDATE": output += self.generate_remote_update(block) + if block["type"] == "SEU": + output += self.generate_seu(block) return output def footer(self): From 070c4cd3875491d43c4c0f6736140ac2177878ec Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Tue, 26 Nov 2024 17:40:03 +0100 Subject: [PATCH 04/11] cores/cpu/vexiiriscv: Add PMP support The RISC-V PMP feature can now be enabled via --vexii-args="--pmp-size=8" for instance. TOR support can be disabled via --pmp-tor-disable to save area / timings --- litex/soc/cores/cpu/vexiiriscv/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litex/soc/cores/cpu/vexiiriscv/core.py b/litex/soc/cores/cpu/vexiiriscv/core.py index 5a97c46421..d3cbbe090f 100755 --- a/litex/soc/cores/cpu/vexiiriscv/core.py +++ b/litex/soc/cores/cpu/vexiiriscv/core.py @@ -156,7 +156,7 @@ def args_read(args): vdir = get_data_mod("cpu", "vexiiriscv").data_location ndir = os.path.join(vdir, "ext", "VexiiRiscv") - NaxRiscv.git_setup("VexiiRiscv", ndir, "https://github.com/SpinalHDL/VexiiRiscv.git", "dev", "ca10ab58", args.update_repo) + NaxRiscv.git_setup("VexiiRiscv", ndir, "https://github.com/SpinalHDL/VexiiRiscv.git", "dev", "b4269ddc", args.update_repo) if not args.cpu_variant: args.cpu_variant = "standard" From 05dd84a5d6026f9759cfed25e20998ebb4c4613a Mon Sep 17 00:00:00 2001 From: awaittrot <189866689+awaittrot@users.noreply.github.com> Date: Wed, 27 Nov 2024 22:18:24 +0900 Subject: [PATCH 05/11] liblitespi: fix typo --- litex/soc/software/liblitespi/spiram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litex/soc/software/liblitespi/spiram.c b/litex/soc/software/liblitespi/spiram.c index 7e49ab2a88..9de0bb5f6f 100644 --- a/litex/soc/software/liblitespi/spiram.c +++ b/litex/soc/software/liblitespi/spiram.c @@ -98,7 +98,7 @@ static void spiram_master_write(uint32_t val, size_t len, size_t width, uint32_t while (!spiram_rx_ready()); /* Clear RX queue. */ - spiflash_core_master_rxtx_read(); + spiram_core_master_rxtx_read(); /* Clear CS. */ spiram_core_master_cs_write(0); From 30aeaf544aa2953c69f7bc67020b818fffd01390 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Thu, 28 Nov 2024 13:32:34 +0100 Subject: [PATCH 06/11] build/vhd2v_converter: Create build_dir if not existing. --- litex/build/vhd2v_converter.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/litex/build/vhd2v_converter.py b/litex/build/vhd2v_converter.py index 1fb2bd647c..9e378b493f 100644 --- a/litex/build/vhd2v_converter.py +++ b/litex/build/vhd2v_converter.py @@ -152,6 +152,11 @@ def do_finalize(self): if len(v_list) != 0: inst_name += f"_{len(v_list)}" + + # Create build_dir if not existing. + if not os.path.exists(self._build_dir): + os.makedirs(self._build_dir) + verilog_out = os.path.join(self._build_dir, f"{inst_name}.v") ip_params = dict() From 3ba92171224139414ffab10117dc3911a5e6ea11 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 2 Dec 2024 16:34:59 +0100 Subject: [PATCH 07/11] build/xilinx/vivado: Move false path generation to _build_false_path_constraints. --- litex/build/xilinx/vivado.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/litex/build/xilinx/vivado.py b/litex/build/xilinx/vivado.py index a9f3e4f18f..2b048a444d 100644 --- a/litex/build/xilinx/vivado.py +++ b/litex/build/xilinx/vivado.py @@ -176,16 +176,8 @@ def get_clk_type(clk): self.platform.add_platform_command( "create_clock -name {name} -period " + str(period) + " [get_" + get_clk_type(clk) + " {clk}]", name=name, clk=clk) - for _from, _to in sorted(self.false_paths, key=lambda x: (x[0].duid, x[1].duid)): - self.platform.add_platform_command( - "set_clock_groups " - "-group [get_clocks -include_generated_clocks -of [get_" + get_clk_type(_from) + " {_from}]] " - "-group [get_clocks -include_generated_clocks -of [get_" + get_clk_type(_to) + " {_to}]] " - "-asynchronous", - _from=_from, _to=_to) - # Make sure add_*_constraint cannot be used again + # Make sure add_period_constraint cannot be used again. self.clocks.clear() - self.false_paths.clear() def _build_false_path_constraints(self): self.platform.add_platform_command(_xdc_separator("False path constraints")) @@ -194,7 +186,7 @@ def _build_false_path_constraints(self): "set_false_path -quiet " "-through [get_nets -hierarchical -filter {{mr_ff == TRUE}}]" ) - # The asychronous reset input to the AsyncResetSynchronizer is a false path + # The asynchronous reset input to the AsyncResetSynchronizer is a false path self.platform.add_platform_command( "set_false_path -quiet " "-to [get_pins -filter {{REF_PIN_NAME == PRE}} " @@ -208,6 +200,22 @@ def _build_false_path_constraints(self): "-to [get_pins -filter {{REF_PIN_NAME == D}} " "-of_objects [get_cells -hierarchical -filter {{ars_ff2 == TRUE}}]]" ) + # Add false paths between clocks + def get_clk_type(clk): + return { + False: "nets", + True: "ports", + }[hasattr(clk, "port")] + for _from, _to in sorted(self.false_paths, key=lambda x: (x[0].duid, x[1].duid)): + self.platform.add_platform_command( + "set_clock_groups " + "-group [get_clocks -include_generated_clocks -of [get_" + get_clk_type(_from) + " {_from}]] " + "-group [get_clocks -include_generated_clocks -of [get_" + get_clk_type(_to) + " {_to}]] " + "-asynchronous", + _from=_from, _to=_to) + # Make sure add_false_path_constraint cannot be used again. + self.false_paths.clear() + def build_timing_constraints(self, vns): # FIXME: -> self ? From 0ae90a96533ab7d22fc59ef688fdf39fc5a1110f Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 2 Dec 2024 16:39:07 +0100 Subject: [PATCH 08/11] build/xilinx/vivado: Add/Improve comments to _build_clock_constraints/_build_false_path_constraints. --- litex/build/xilinx/vivado.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/litex/build/xilinx/vivado.py b/litex/build/xilinx/vivado.py index 2b048a444d..7f64c74103 100644 --- a/litex/build/xilinx/vivado.py +++ b/litex/build/xilinx/vivado.py @@ -164,35 +164,45 @@ def build_io_constraints(self): # Timing Constraints (in xdc file) ------------------------------------------------------------- def _build_clock_constraints(self): + # Add clock constraints to the XDC file. self.platform.add_platform_command(_xdc_separator("Clock constraints")) + + # Determine whether a clock is defined as a net or a port. def get_clk_type(clk): return { - False : "nets", + False : "nets", True : "ports", }[hasattr(clk, "port")] + + # Add create_clock commands for each clock in the design. for clk, [period, name] in sorted(self.clocks.items(), key=lambda x: x[0].duid): if name is None: name = clk self.platform.add_platform_command( "create_clock -name {name} -period " + str(period) + " [get_" + get_clk_type(clk) + " {clk}]", name=name, clk=clk) - # Make sure add_period_constraint cannot be used again. + + # Clear clock constraints after generation. self.clocks.clear() def _build_false_path_constraints(self): + # Add false path constraints to the XDC file. self.platform.add_platform_command(_xdc_separator("False path constraints")) - # The asynchronous input to a MultiReg is a false path + + # Mark asynchronous inputs to MultiReg as false paths. self.platform.add_platform_command( "set_false_path -quiet " "-through [get_nets -hierarchical -filter {{mr_ff == TRUE}}]" ) - # The asynchronous reset input to the AsyncResetSynchronizer is a false path + + # Mark asynchronous reset inputs to AsyncResetSynchronizer as false paths. self.platform.add_platform_command( "set_false_path -quiet " "-to [get_pins -filter {{REF_PIN_NAME == PRE}} " "-of_objects [get_cells -hierarchical -filter {{ars_ff1 == TRUE || ars_ff2 == TRUE}}]]" ) - # clock_period-2ns to resolve metastability on the wire between the AsyncResetSynchronizer FFs + + # Set a maximum delay for metastability resolution in AsyncResetSynchronizer. self.platform.add_platform_command( "set_max_delay 2 -quiet " "-from [get_pins -filter {{REF_PIN_NAME == C}} " @@ -200,11 +210,12 @@ def _build_false_path_constraints(self): "-to [get_pins -filter {{REF_PIN_NAME == D}} " "-of_objects [get_cells -hierarchical -filter {{ars_ff2 == TRUE}}]]" ) - # Add false paths between clocks + + # Add false paths between asynchronous clock domains. def get_clk_type(clk): return { - False: "nets", - True: "ports", + False : "nets", + True : "ports", }[hasattr(clk, "port")] for _from, _to in sorted(self.false_paths, key=lambda x: (x[0].duid, x[1].duid)): self.platform.add_platform_command( @@ -213,9 +224,9 @@ def get_clk_type(clk): "-group [get_clocks -include_generated_clocks -of [get_" + get_clk_type(_to) + " {_to}]] " "-asynchronous", _from=_from, _to=_to) - # Make sure add_false_path_constraint cannot be used again. - self.false_paths.clear() + # Clear false path constraints after generation. + self.false_paths.clear() def build_timing_constraints(self, vns): # FIXME: -> self ? From 42cf2ca5d0ba993c02e80b28a469848b428894e1 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 2 Dec 2024 17:27:05 +0100 Subject: [PATCH 09/11] gen/fhdl/namer: Return Non if sig is None. --- litex/gen/fhdl/namer.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/litex/gen/fhdl/namer.py b/litex/gen/fhdl/namer.py index 3db8606a2c..fea8458ca4 100644 --- a/litex/gen/fhdl/namer.py +++ b/litex/gen/fhdl/namer.py @@ -413,6 +413,11 @@ def __init__(self, name_dict, reserved_keywords=set()): self.clock_domains = dict() def get_name(self, sig): + # Return None if sig is None. + # --------------------------- + if sig is None: + return None + # Handle Clock and Reset Signals. # ------------------------------- if isinstance(sig, (ClockSignal, ResetSignal)): From 4e775a7bd6c2ab89cc9a867f84f0947700721f7d Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 2 Dec 2024 17:27:44 +0100 Subject: [PATCH 10/11] build/generic_toolchain/add_false_path: Only add keep attribute if from_/to are Signals. --- litex/build/generic_toolchain.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/litex/build/generic_toolchain.py b/litex/build/generic_toolchain.py index 488b92178f..fd1c0af37c 100644 --- a/litex/build/generic_toolchain.py +++ b/litex/build/generic_toolchain.py @@ -8,7 +8,7 @@ import os import math -from migen.fhdl.structure import _Fragment +from migen.fhdl.structure import Signal, _Fragment from litex.gen import LiteXContext @@ -178,7 +178,9 @@ def add_period_constraint(self, platform, clk, period, keep=True, name=None): def add_false_path_constraint(self, platform, from_, to, keep=True): if keep: - from_.attr.add("keep") - to.attr.add("keep") + if isinstance(from_, Signal): + from_.attr.add("keep") + if isinstance(to, Signal): + to.attr.add("keep") if (to, from_) not in self.false_paths: self.false_paths.add((from_, to)) From ec06bf59552289a0b2d271a51a4cf1d761e43288 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 2 Dec 2024 17:28:38 +0100 Subject: [PATCH 11/11] build/xilinx/vivado: Allow passing str to add_false_path_constraint to allow mising Signals and str and simplify design constraints. --- litex/build/xilinx/vivado.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/litex/build/xilinx/vivado.py b/litex/build/xilinx/vivado.py index 7f64c74103..2292b0918e 100644 --- a/litex/build/xilinx/vivado.py +++ b/litex/build/xilinx/vivado.py @@ -199,31 +199,44 @@ def _build_false_path_constraints(self): self.platform.add_platform_command( "set_false_path -quiet " "-to [get_pins -filter {{REF_PIN_NAME == PRE}} " - "-of_objects [get_cells -hierarchical -filter {{ars_ff1 == TRUE || ars_ff2 == TRUE}}]]" + "-of_objects [get_cells -hierarchical -filter {{ars_ff1 == TRUE || ars_ff2 == TRUE}}]]" ) # Set a maximum delay for metastability resolution in AsyncResetSynchronizer. self.platform.add_platform_command( "set_max_delay 2 -quiet " "-from [get_pins -filter {{REF_PIN_NAME == C}} " - "-of_objects [get_cells -hierarchical -filter {{ars_ff1 == TRUE}}]] " + "-of_objects [get_cells -hierarchical -filter {{ars_ff1 == TRUE}}]] " "-to [get_pins -filter {{REF_PIN_NAME == D}} " - "-of_objects [get_cells -hierarchical -filter {{ars_ff2 == TRUE}}]]" + "-of_objects [get_cells -hierarchical -filter {{ars_ff2 == TRUE}}]]" ) # Add false paths between asynchronous clock domains. def get_clk_type(clk): return { - False : "nets", - True : "ports", + False: "nets", + True: "ports", }[hasattr(clk, "port")] - for _from, _to in sorted(self.false_paths, key=lambda x: (x[0].duid, x[1].duid)): + + for _from, _to in sorted(self.false_paths, key=lambda x: (str(x[0]), str(x[1]))): + if isinstance(_from, str): + _from_cmd = f"[get_clocks {_from}]" + else: + _from_cmd = f"[get_clocks -include_generated_clocks -of [get_{get_clk_type(_from)} {{_from}}]]" + + if isinstance(_to, str): + _to_cmd = f"[get_clocks {_to}]" + else: + _to_cmd = f"[get_clocks -include_generated_clocks -of [get_{get_clk_type(_to)} {{_to}}]]" + self.platform.add_platform_command( "set_clock_groups " - "-group [get_clocks -include_generated_clocks -of [get_" + get_clk_type(_from) + " {_from}]] " - "-group [get_clocks -include_generated_clocks -of [get_" + get_clk_type(_to) + " {_to}]] " + f"-group {_from_cmd} " + f"-group {_to_cmd} " "-asynchronous", - _from=_from, _to=_to) + _from = _from if not isinstance(_from, str) else None, + _to = _to if not isinstance(_to, str) else None, + ) # Clear false path constraints after generation. self.false_paths.clear()