From 71f8fc7cb558e35ae448f541cb12e58bad5d6738 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Thu, 28 Oct 2021 09:48:58 +0200 Subject: [PATCH 1/7] fhdl/memory: First Cleanup/Re-organization pass. - Reorganize a bit (move Memory initialization to Memory declaration block). - Use f-strings. - Add separators. - Add comments. --- litex/gen/fhdl/memory.py | 138 +++++++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 50 deletions(-) diff --git a/litex/gen/fhdl/memory.py b/litex/gen/fhdl/memory.py index 49a66b25a..47af628cd 100644 --- a/litex/gen/fhdl/memory.py +++ b/litex/gen/fhdl/memory.py @@ -1,3 +1,10 @@ +# +# This file is part of LiteX (Adapted from Migen for LiteX usage). +# +# This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq +# This file is Copyright (c) 2021 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + from migen.fhdl.structure import * from migen.fhdl.module import * from migen.fhdl.bitcontainer import bits_for @@ -6,21 +13,22 @@ from migen.fhdl.verilog import _printexpr as verilog_printexpr from migen.fhdl.specials import * def memory_emit_verilog(memory, ns, add_data_file): - r = "" + # Helpers. + # -------- def gn(e): if isinstance(e, Memory): return ns.get_name(e) else: return verilog_printexpr(ns, e)[0] - adrbits = bits_for(memory.depth-1) - r += "reg [" + str(memory.width-1) + ":0] " \ - + gn(memory) \ - + "[0:" + str(memory.depth-1) + "];\n" - - adr_regs = {} + # Parameters. + # ----------- + adrbits = bits_for(memory.depth-1) + adr_regs = {} data_regs = {} + # Ports Transformations. + # ---------------------- # https://github.com/enjoy-digital/litex/issues/1003 # FIXME: Verify behaviour with the different FPGA toolchains. clocks = [port.clock for port in memory.ports] @@ -28,69 +36,99 @@ def memory_emit_verilog(memory, ns, add_data_file): for port in memory.ports: port.mode = READ_FIRST - for port in memory.ports: - if not port.async_read: - if port.mode == WRITE_FIRST: - adr_reg = Signal(name_override="memadr") - r += "reg [" + str(adrbits-1) + ":0] " \ - + gn(adr_reg) + ";\n" - adr_regs[id(port)] = adr_reg - else: - data_reg = Signal(name_override="memdat") - r += "reg [" + str(memory.width-1) + ":0] " \ - + gn(data_reg) + ";\n" - data_regs[id(port)] = data_reg + # Memory Declaration/Initialization. + # ---------------------------------- + r = f"reg [{memory.width-1}:0] {gn(memory)}[0:{memory.depth-1}];\n" + if memory.init is not None: + content = "" + formatter = f"{{:0{int(memory.width/4)}x}}\n" + for d in memory.init: + content += formatter.format(d) + memory_filename = add_data_file(f"{gn(memory)}.init", content) + r += "initial begin\n" + r += f"\t$readmemh(\"{memory_filename}\", {gn(memory)});\n" + r += "end\n\n" + + # Port Intermediate Signals. + # -------------------------- for port in memory.ports: - r += "always @(posedge " + gn(port.clock) + ") begin\n" + # No Intermediate Signal for Async Read. + if port.async_read: + continue + + # Create Address Register in Write-First mode. + if port.mode in [WRITE_FIRST]: + adr_reg = Signal(name_override="memadr") + r += f"reg [{adrbits-1}:0] {gn(adr_reg)};\n" + adr_regs[id(port)] = adr_reg + + # Create Data Register in Read-First/No Change mode. + if port.mode in [READ_FIRST, NO_CHANGE]: + data_reg = Signal(name_override="memdat") + r += f"reg [{memory.width-1}:0] {gn(data_reg)};\n" + data_regs[id(port)] = data_reg + + # Ports Write/Read Logic. + # ----------------------- + for port in memory.ports: + r += f"always @(posedge {gn(port.clock)}) begin\n" + # Write Logic. if port.we is not None: + # Split Write Logic when Granularity. if port.we_granularity: n = memory.width//port.we_granularity for i in range(n): m = i*port.we_granularity M = (i+1)*port.we_granularity-1 - sl = "[" + str(M) + ":" + str(m) + "]" - r += "\tif (" + gn(port.we) + "[" + str(i) + "])\n" - r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "]" + sl + " <= " + gn(port.dat_w) + sl + ";\n" + sl = f"[{M}:{m}]" + r += f"\tif ({gn(port.we)}[{i}])\n" + r += f"\t\t{gn(memory)}[{gn(port.adr)}]{sl} <= {gn(port.dat_w)}{sl};\n" + # Else use common Write Logic. else: - r += "\tif (" + gn(port.we) + ")\n" - r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "] <= " + gn(port.dat_w) + ";\n" + r += f"\tif ({gn(port.we)})\n" + r += f"\t\t{gn(memory)}[{gn(port.adr)}] <= {gn(port.dat_w)};\n" + + # Read Logic. if not port.async_read: - if port.mode == WRITE_FIRST: - rd = "\t" + gn(adr_regs[id(port)]) + " <= " + gn(port.adr) + ";\n" - else: - bassign = gn(data_regs[id(port)]) + " <= " + gn(memory) + "[" + gn(port.adr) + "];\n" + # In Write-First mode, Read from Address Register. + if port.mode in [WRITE_FIRST]: + rd = f"\t{gn(adr_regs[id(port)])} <= {gn(port.adr)};\n" + + # In Write-First/No Change mode: + if port.mode in [READ_FIRST, NO_CHANGE]: + bassign = f"{gn(data_regs[id(port)])} <= {gn(memory)} [{gn(port.adr)}];\n" + # Always Read in Read-First mode. if port.mode == READ_FIRST: - rd = "\t" + bassign + rd = f"\t{bassign}" + # Only Read in No-Change mode when no Write. elif port.mode == NO_CHANGE: - rd = "\tif (!" + gn(port.we) + ")\n" \ - + "\t\t" + bassign + rd = f"\tif (!{gn(port.we)})\n\t\t{bassign}" + + # Add Read-Enable Logic. if port.re is None: r += rd else: - r += "\tif (" + gn(port.re) + ")\n" + r += f"\tif ({gn(port.re)})\n" r += "\t" + rd.replace("\n\t", "\n\t\t") r += "end\n\n" + # Ports Read Mapping. + # ------------------- for port in memory.ports: + # Direct (Asynchronous) Read on Async-Read mode. if port.async_read: - r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(port.adr) + "];\n" - else: - if port.mode == WRITE_FIRST: - r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(adr_regs[id(port)]) + "];\n" - else: - r += "assign " + gn(port.dat_r) + " = " + gn(data_regs[id(port)]) + ";\n" + r += f"assign {gn(port.dat_r)} = {gn(memory)}[{gn(port.adr)}];\n" + continue + + # Write-First mode: Do Read through Address Register. + if port.mode in [WRITE_FIRST]: + r += f"assign {gn(port.dat_r)} = {gn(memory)}[{gn(adr_regs[id(port)])}];\n" + + # Read-First/No-Change mode: Data already Read on Data Register. + if port.mode in [READ_FIRST, NO_CHANGE]: + r += f"assign {gn(port.dat_r)} = {gn(data_regs[id(port)])};\n" + r += "\n" - if memory.init is not None: - content = "" - formatter = "{:0" + str(int(memory.width / 4)) + "X}\n" - for d in memory.init: - content += formatter.format(d) - memory_filename = add_data_file(gn(memory) + ".init", content) - - r += "initial begin\n" - r += "\t$readmemh(\"" + memory_filename + "\", " + gn(memory) + ");\n" - r += "end\n\n" - return r From b6c4f6ae2477a64bc67241ac337622f3d3476bc8 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Thu, 28 Oct 2021 10:51:58 +0200 Subject: [PATCH 2/7] fhdl/memory: Add initial Memory description. Gives an overview of the generated Verilog Memory, will be useful for debug/improve inference. --- litex/gen/fhdl/memory.py | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/litex/gen/fhdl/memory.py b/litex/gen/fhdl/memory.py index 47af628cd..b96a0b8e7 100644 --- a/litex/gen/fhdl/memory.py +++ b/litex/gen/fhdl/memory.py @@ -23,6 +23,7 @@ def memory_emit_verilog(memory, ns, add_data_file): # Parameters. # ----------- + r = "" adrbits = bits_for(memory.depth-1) adr_regs = {} data_regs = {} @@ -36,9 +37,34 @@ def memory_emit_verilog(memory, ns, add_data_file): for port in memory.ports: port.mode = READ_FIRST - # Memory Declaration/Initialization. - # ---------------------------------- - r = f"reg [{memory.width-1}:0] {gn(memory)}[0:{memory.depth-1}];\n" + # Memory Description. + # ------------------- + r += "//" + "-"*80 + "\n" + r += f"// Memory {gn(memory)}: {memory.depth}-words x {memory.width}-bit\n" + r += "//" + "-"*80 + "\n" + for n, port in enumerate(memory.ports): + r += f"// Port {n} | " + if port.async_read: + r += "Read: Async | " + else: + r += "Read: Sync | " + if port.we is None: + r += "Write: ---- | " + else: + r += "Write: Sync | " + r += "Mode: " + if port.mode == WRITE_FIRST: + r += "Write-First | " + elif port.mode == READ_FIRST: + r += "Read-First | " + elif port.mode == NO_CHANGE: + r += "No-Change | " + r += f"Write-Granularity: {port.we_granularity} " + r += "\n" + + # Memory Logic Declaration/Initialization. + # ---------------------------------------- + r += f"reg [{memory.width-1}:0] {gn(memory)}[0:{memory.depth-1}];\n" if memory.init is not None: content = "" formatter = f"{{:0{int(memory.width/4)}x}}\n" @@ -48,7 +74,7 @@ def memory_emit_verilog(memory, ns, add_data_file): r += "initial begin\n" r += f"\t$readmemh(\"{memory_filename}\", {gn(memory)});\n" - r += "end\n\n" + r += "end\n" # Port Intermediate Signals. # -------------------------- @@ -111,7 +137,7 @@ def memory_emit_verilog(memory, ns, add_data_file): else: r += f"\tif ({gn(port.re)})\n" r += "\t" + rd.replace("\n\t", "\n\t\t") - r += "end\n\n" + r += "end\n" # Ports Read Mapping. # ------------------- @@ -128,7 +154,6 @@ def memory_emit_verilog(memory, ns, add_data_file): # Read-First/No-Change mode: Data already Read on Data Register. if port.mode in [READ_FIRST, NO_CHANGE]: r += f"assign {gn(port.dat_r)} = {gn(data_regs[id(port)])};\n" - - r += "\n" + r += "//" + "-"*80 + "\n\n" return r From 95e5f20bd8db1737e5d1ff809bd4aa055c931c42 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Thu, 28 Oct 2021 11:11:09 +0200 Subject: [PATCH 3/7] fhdl/memory: Simplify Write Logic generation when with granularity. --- litex/gen/fhdl/memory.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/litex/gen/fhdl/memory.py b/litex/gen/fhdl/memory.py index b96a0b8e7..1f5ded7b3 100644 --- a/litex/gen/fhdl/memory.py +++ b/litex/gen/fhdl/memory.py @@ -103,13 +103,11 @@ def memory_emit_verilog(memory, ns, add_data_file): if port.we is not None: # Split Write Logic when Granularity. if port.we_granularity: - n = memory.width//port.we_granularity - for i in range(n): - m = i*port.we_granularity - M = (i+1)*port.we_granularity-1 - sl = f"[{M}:{m}]" - r += f"\tif ({gn(port.we)}[{i}])\n" - r += f"\t\t{gn(memory)}[{gn(port.adr)}]{sl} <= {gn(port.dat_w)}{sl};\n" + for n in range(memory.width//port.we_granularity): + r += f"\tif ({gn(port.we)}[{n}])\n" + lbit = n*port.we_granularity + hbit = (n+1)*port.we_granularity-1 + r += f"\t\t{gn(memory)}[{gn(port.adr)}][{hbit}:{lbit}] <= {gn(port.dat_w)}[{hbit}:{lbit}];\n" # Else use common Write Logic. else: r += f"\tif ({gn(port.we)})\n" From 3d4e45145d13422fa864f27ef16a48011da69b16 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Thu, 28 Oct 2021 11:25:53 +0200 Subject: [PATCH 4/7] fhdl/memory: Simplify logic generation and improve intermediate address/data register naming. --- litex/gen/fhdl/memory.py | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/litex/gen/fhdl/memory.py b/litex/gen/fhdl/memory.py index 1f5ded7b3..f4e2d4a73 100644 --- a/litex/gen/fhdl/memory.py +++ b/litex/gen/fhdl/memory.py @@ -24,7 +24,6 @@ def memory_emit_verilog(memory, ns, add_data_file): # Parameters. # ----------- r = "" - adrbits = bits_for(memory.depth-1) adr_regs = {} data_regs = {} @@ -78,35 +77,33 @@ def memory_emit_verilog(memory, ns, add_data_file): # Port Intermediate Signals. # -------------------------- - for port in memory.ports: + for n, port in enumerate(memory.ports): # No Intermediate Signal for Async Read. if port.async_read: continue # Create Address Register in Write-First mode. if port.mode in [WRITE_FIRST]: - adr_reg = Signal(name_override="memadr") - r += f"reg [{adrbits-1}:0] {gn(adr_reg)};\n" - adr_regs[id(port)] = adr_reg + adr_regs[n] = Signal(name_override=f"{gn(memory)}_adr{n}") + r += f"reg [{bits_for(memory.depth-1)-1}:0] {gn(adr_regs[n])};\n" # Create Data Register in Read-First/No Change mode. if port.mode in [READ_FIRST, NO_CHANGE]: - data_reg = Signal(name_override="memdat") - r += f"reg [{memory.width-1}:0] {gn(data_reg)};\n" - data_regs[id(port)] = data_reg + data_regs[n] = Signal(name_override=f"{gn(memory)}_dat{n}") + r += f"reg [{memory.width-1}:0] {gn(data_regs[n])};\n" # Ports Write/Read Logic. # ----------------------- - for port in memory.ports: + for n, port in enumerate(memory.ports): r += f"always @(posedge {gn(port.clock)}) begin\n" # Write Logic. if port.we is not None: # Split Write Logic when Granularity. if port.we_granularity: - for n in range(memory.width//port.we_granularity): - r += f"\tif ({gn(port.we)}[{n}])\n" - lbit = n*port.we_granularity - hbit = (n+1)*port.we_granularity-1 + for i in range(memory.width//port.we_granularity): + r += f"\tif ({gn(port.we)}[{i}])\n" + lbit = i*port.we_granularity + hbit = (i+1)*port.we_granularity-1 r += f"\t\t{gn(memory)}[{gn(port.adr)}][{hbit}:{lbit}] <= {gn(port.dat_w)}[{hbit}:{lbit}];\n" # Else use common Write Logic. else: @@ -117,11 +114,11 @@ def memory_emit_verilog(memory, ns, add_data_file): if not port.async_read: # In Write-First mode, Read from Address Register. if port.mode in [WRITE_FIRST]: - rd = f"\t{gn(adr_regs[id(port)])} <= {gn(port.adr)};\n" + rd = f"\t{gn(adr_regs[n])} <= {gn(port.adr)};\n" # In Write-First/No Change mode: if port.mode in [READ_FIRST, NO_CHANGE]: - bassign = f"{gn(data_regs[id(port)])} <= {gn(memory)} [{gn(port.adr)}];\n" + bassign = f"{gn(data_regs[n])} <= {gn(memory)} [{gn(port.adr)}];\n" # Always Read in Read-First mode. if port.mode == READ_FIRST: rd = f"\t{bassign}" @@ -139,7 +136,7 @@ def memory_emit_verilog(memory, ns, add_data_file): # Ports Read Mapping. # ------------------- - for port in memory.ports: + for n, port in enumerate(memory.ports): # Direct (Asynchronous) Read on Async-Read mode. if port.async_read: r += f"assign {gn(port.dat_r)} = {gn(memory)}[{gn(port.adr)}];\n" @@ -147,11 +144,11 @@ def memory_emit_verilog(memory, ns, add_data_file): # Write-First mode: Do Read through Address Register. if port.mode in [WRITE_FIRST]: - r += f"assign {gn(port.dat_r)} = {gn(memory)}[{gn(adr_regs[id(port)])}];\n" + r += f"assign {gn(port.dat_r)} = {gn(memory)}[{gn(adr_regs[n])}];\n" # Read-First/No-Change mode: Data already Read on Data Register. if port.mode in [READ_FIRST, NO_CHANGE]: - r += f"assign {gn(port.dat_r)} = {gn(data_regs[id(port)])};\n" + r += f"assign {gn(port.dat_r)} = {gn(data_regs[n])};\n" r += "//" + "-"*80 + "\n\n" return r From d86cd94c715adb2c1487e44e71a032896151fbf3 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Thu, 28 Oct 2021 12:11:40 +0200 Subject: [PATCH 5/7] efinix/memory: Avoid specific memory_efinix generator by applying FullMemoryWE on the design. --- litex/build/efinix/efinity.py | 7 +- litex/gen/fhdl/memory_efinix.py | 132 -------------------------------- litex/gen/fhdl/verilog.py | 11 +-- 3 files changed, 8 insertions(+), 142 deletions(-) delete mode 100644 litex/gen/fhdl/memory_efinix.py diff --git a/litex/build/efinix/efinity.py b/litex/build/efinix/efinity.py index 6f50a4f97..77ff913f7 100644 --- a/litex/build/efinix/efinity.py +++ b/litex/build/efinix/efinity.py @@ -18,12 +18,12 @@ import datetime from xml.dom import expatbuilder import xml.etree.ElementTree as et -from litex.build.generic_platform import * - from migen.fhdl.structure import _Fragment from migen.fhdl.tools import * from migen.fhdl.namer import build_namespace +from migen.fhdl.simplify import FullMemoryWE +from litex.build.generic_platform import * from litex.build.generic_platform import Pins, IOStandard, Misc from litex.build import tools @@ -268,6 +268,9 @@ class EfinityToolchain: os.makedirs(build_dir, exist_ok=True) os.chdir(build_dir) + # Apply FullMemoryWE on design (Efiniy does not infer memories correctly otherwise). + FullMemoryWE()(fragment) + # Finalize design if not isinstance(fragment, _Fragment): fragment = fragment.get_fragment() diff --git a/litex/gen/fhdl/memory_efinix.py b/litex/gen/fhdl/memory_efinix.py deleted file mode 100644 index 406438146..000000000 --- a/litex/gen/fhdl/memory_efinix.py +++ /dev/null @@ -1,132 +0,0 @@ -from migen.fhdl.structure import * -from migen.fhdl.module import * -from migen.fhdl.bitcontainer import bits_for -from migen.fhdl.tools import * -from migen.fhdl.verilog import _printexpr as verilog_printexpr -from migen.fhdl.specials import * - -def memory_emit_verilog(memory, ns, add_data_file): - r = "" - def gn(e): - if isinstance(e, Memory): - return ns.get_name(e) - else: - return verilog_printexpr(ns, e)[0] - adrbits = bits_for(memory.depth-1) - - for i in range(memory.width // 8): - r += "reg [" + str((memory.width//4)-1) + ":0] " \ - + gn(memory) + '_efx_' + str(i) \ - + "[0:" + str(memory.depth-1) + "];\n" - - adr_regs = {} - data_regs = {} - for port in memory.ports: - if not port.async_read: - if port.mode == WRITE_FIRST: - adr_reg = Signal(name_override="memadr") - r += "reg [" + str(adrbits-1) + ":0] " \ - + gn(adr_reg) + ";\n" - adr_regs[id(port)] = adr_reg - else: - data_reg = Signal(name_override="memdat") - r += "reg [" + str(memory.width-1) + ":0] " \ - + gn(data_reg) + ";\n" - data_regs[id(port)] = data_reg - - for port in memory.ports: - r += "always @(posedge " + gn(port.clock) + ") begin\n" - if port.we is not None: - if port.we_granularity: - n = memory.width//port.we_granularity - for i in range(n): - if (i > 0): - r += "always @(posedge " + gn(port.clock) + ") begin\n" - m = i*port.we_granularity - M = (i+1)*port.we_granularity-1 - sl = "[" + str(M) + ":" + str(m) + "]" - r += "\tif (" + gn(port.we) + "[" + str(i) + "])\n" - r += "\t\t" + gn(memory) + '_efx_' + str(i) + "[" + gn(port.adr) + "]" + " <= " + gn(port.dat_w) + sl + ";\n" - r += "end\n" - else: - r += "\tif (" + gn(port.we) + ")\n" - r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "] <= " + gn(port.dat_w) + ";\n" - if not port.async_read: - if port.mode == WRITE_FIRST: - r += "always @(posedge " + gn(port.clock) + ") begin\n" - rd = "\t" + gn(adr_regs[id(port)]) + " <= " + gn(port.adr) + ";\n" - else: - bassign = "" - for i in range(memory.width // 8): - m = i*port.we_granularity - M = (i+1)*port.we_granularity-1 - sl = "[" + str(M) + ":" + str(m) + "]" - bassign += gn(data_regs[id(port)]) + sl + " <= " + gn(memory) + "_efx_" + str(i) + "[" + gn(port.adr) + "];\n" - if port.mode == READ_FIRST: - rd = "\t" + bassign - elif port.mode == NO_CHANGE: - rd = "\tif (!" + gn(port.we) + ")\n" \ - + "\t\t" + bassign - if port.re is None: - r += rd - else: - r += "\tif (" + gn(port.re) + ")\n" - r += "\t" + rd.replace("\n\t", "\n\t\t") - r += "end\n\n" - - for port in memory.ports: - if port.async_read: - r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(port.adr) + "];\n" - else: - if port.mode == WRITE_FIRST: - for i in range(memory.width // 8): - m = i*port.we_granularity - M = (i+1)*port.we_granularity-1 - sl = "[" + str(M) + ":" + str(m) + "]" - r += "assign " + gn(port.dat_r) + sl + " = " + gn(memory) + "_efx_" + str(i) + "[" + gn(adr_regs[id(port)]) + "];\n" - else: - r += "assign " + gn(port.dat_r) + " = " + gn(data_regs[id(port)]) + ";\n" - r += "\n" - - if memory.init is not None: - content_7_0 = "" - content_15_8 = "" - content_23_16 = "" - content_31_24 = "" - formatter = "{:0" + str(int(memory.width / 4)) + "X}\n" - - init_7_0 = [] - init_15_8 = [] - init_23_16 = [] - init_31_24 = [] - - for w in memory.init: - init_7_0.append(w & 0xff) - init_15_8.append((w >> 8) & 0xff) - init_23_16.append((w >> 16) & 0xff) - init_31_24.append((w >> 24) & 0xff) - - for d in init_7_0: - content_7_0 += formatter.format(d) - - for d in init_15_8: - content_15_8 += formatter.format(d) - - for d in init_23_16: - content_23_16 += formatter.format(d) - - for d in init_31_24: - content_31_24 += formatter.format(d) - - memory_filename1 = add_data_file(gn(memory) + "_efx_1.init", content_7_0) - memory_filename2 = add_data_file(gn(memory) + "_efx_2.init", content_15_8) - memory_filename3 = add_data_file(gn(memory) + "_efx_3.init", content_23_16) - memory_filename4 = add_data_file(gn(memory) + "_efx_4.init", content_31_24) - r += "initial begin\n" - r += "\t$readmemh(\"" + memory_filename1 + "\", " + gn(memory)+ "_efx_0" + ");\n" - r += "\t$readmemh(\"" + memory_filename2 + "\", " + gn(memory)+ "_efx_1" + ");\n" - r += "\t$readmemh(\"" + memory_filename3 + "\", " + gn(memory)+ "_efx_2" + ");\n" - r += "\t$readmemh(\"" + memory_filename4 + "\", " + gn(memory)+ "_efx_3" + ");\n" - r += "end\n\n" - - return r diff --git a/litex/gen/fhdl/verilog.py b/litex/gen/fhdl/verilog.py index f60e0e310..33020394c 100644 --- a/litex/gen/fhdl/verilog.py +++ b/litex/gen/fhdl/verilog.py @@ -439,15 +439,10 @@ def _print_specials(overrides, specials, ns, add_data_file, attr_translate): attr = _print_attribute(special.attr, attr_translate) if attr: r += attr + " " - # Replace Migen Memory's emit_verilog with our implementation. + # Replace Migen Memory's emit_verilog with LiteX's implementation. if isinstance(special, Memory): - from litex.build.efinix.platform import EfinixPlatform - if isinstance(special.platform, EfinixPlatform) and (special.width == 32): # FIXME: Improve. - from litex.gen.fhdl.memory_efinix import memory_emit_verilog - pr = memory_emit_verilog(special, ns, add_data_file) - else: - from litex.gen.fhdl.memory import memory_emit_verilog - pr = memory_emit_verilog(special, ns, add_data_file) + from litex.gen.fhdl.memory import memory_emit_verilog + pr = memory_emit_verilog(special, ns, add_data_file) else: pr = call_special_classmethod(overrides, special, "emit_verilog", ns, add_data_file) if pr is None: From 576bb67332c9a16f33d3bbf65568e3ed6b3e013c Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Thu, 28 Oct 2021 14:19:02 +0200 Subject: [PATCH 6/7] fhdl/memory: Simplify Write Logic (Avoid specific cases on write granuarity). --- litex/gen/fhdl/memory.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/litex/gen/fhdl/memory.py b/litex/gen/fhdl/memory.py index f4e2d4a73..ca07b1f2a 100644 --- a/litex/gen/fhdl/memory.py +++ b/litex/gen/fhdl/memory.py @@ -29,13 +29,19 @@ def memory_emit_verilog(memory, ns, add_data_file): # Ports Transformations. # ---------------------- - # https://github.com/enjoy-digital/litex/issues/1003 - # FIXME: Verify behaviour with the different FPGA toolchains. + + # Set Port Mode to Read-First when several Ports with different Clocks. + # FIXME: Verify behaviour with the different FPGA toolchains, try to avoid it. clocks = [port.clock for port in memory.ports] if clocks.count(clocks[0]) != len(clocks): for port in memory.ports: port.mode = READ_FIRST + # Set Port Granularity when 0. + for port in memory.ports: + if port.we_granularity == 0: + port.we_granularity = memory.width + # Memory Description. # ------------------- r += "//" + "-"*80 + "\n" @@ -98,17 +104,13 @@ def memory_emit_verilog(memory, ns, add_data_file): r += f"always @(posedge {gn(port.clock)}) begin\n" # Write Logic. if port.we is not None: - # Split Write Logic when Granularity. - if port.we_granularity: - for i in range(memory.width//port.we_granularity): - r += f"\tif ({gn(port.we)}[{i}])\n" - lbit = i*port.we_granularity - hbit = (i+1)*port.we_granularity-1 - r += f"\t\t{gn(memory)}[{gn(port.adr)}][{hbit}:{lbit}] <= {gn(port.dat_w)}[{hbit}:{lbit}];\n" - # Else use common Write Logic. - else: - r += f"\tif ({gn(port.we)})\n" - r += f"\t\t{gn(memory)}[{gn(port.adr)}] <= {gn(port.dat_w)};\n" + # Split Write Logic. + for i in range(memory.width//port.we_granularity): + wbit = f"[{i}]" if memory.width != port.we_granularity else "" + r += f"\tif ({gn(port.we)}{wbit})\n" + lbit = i*port.we_granularity + hbit = (i+1)*port.we_granularity-1 + r += f"\t\t{gn(memory)}[{gn(port.adr)}][{hbit}:{lbit}] <= {gn(port.dat_w)}[{hbit}:{lbit}];\n" # Read Logic. if not port.async_read: From 08a9392c54525a8c5454a1a41e28c0e9f1d7b347 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Thu, 28 Oct 2021 14:34:52 +0200 Subject: [PATCH 7/7] fhdl/memory: Simplify Read Logic. --- litex/gen/fhdl/memory.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/litex/gen/fhdl/memory.py b/litex/gen/fhdl/memory.py index ca07b1f2a..19fd6793f 100644 --- a/litex/gen/fhdl/memory.py +++ b/litex/gen/fhdl/memory.py @@ -118,15 +118,14 @@ def memory_emit_verilog(memory, ns, add_data_file): if port.mode in [WRITE_FIRST]: rd = f"\t{gn(adr_regs[n])} <= {gn(port.adr)};\n" - # In Write-First/No Change mode: + # In Read-First/No Change mode: if port.mode in [READ_FIRST, NO_CHANGE]: - bassign = f"{gn(data_regs[n])} <= {gn(memory)} [{gn(port.adr)}];\n" - # Always Read in Read-First mode. - if port.mode == READ_FIRST: - rd = f"\t{bassign}" + rd = "" # Only Read in No-Change mode when no Write. - elif port.mode == NO_CHANGE: - rd = f"\tif (!{gn(port.we)})\n\t\t{bassign}" + if port.mode == NO_CHANGE: + rd += f"\tif (!{gn(port.we)})\n\t" + # Read-First/No-Change Read logic. + rd += f"\t{gn(data_regs[n])} <= {gn(memory)}[{gn(port.adr)}];\n" # Add Read-Enable Logic. if port.re is None: