From b24475b07daf99361b20e779dca0b43d7a955e47 Mon Sep 17 00:00:00 2001 From: Franck Jullien Date: Wed, 22 Sep 2021 09:47:47 +0200 Subject: [PATCH] Add an hacked no we memory for Efinix Efinity synthesizer cannot infer RAM blocks with write enable. In order to workaround this (at least for the Litex SoC intergrated RAM/ROM) a dirty modified Memory class has been created. This class needs to be rewrite ! --- litex/gen/fhdl/memory.py | 175 +++++++++++++++++++++++++++++ litex/soc/integration/soc.py | 8 +- litex/soc/integration/soc_core.py | 6 +- litex/soc/interconnect/axi.py | 2 +- litex/soc/interconnect/wishbone.py | 9 +- 5 files changed, 191 insertions(+), 9 deletions(-) create mode 100644 litex/gen/fhdl/memory.py diff --git a/litex/gen/fhdl/memory.py b/litex/gen/fhdl/memory.py new file mode 100644 index 000000000..4ed681750 --- /dev/null +++ b/litex/gen/fhdl/memory.py @@ -0,0 +1,175 @@ +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.tracer import get_obj_var_name +from migen.fhdl.verilog import _printexpr as verilog_printexpr +from migen.fhdl.specials import Special, _MemoryPort, _MemoryLocation + +(READ_FIRST, WRITE_FIRST, NO_CHANGE) = range(3) + +class Memory(Special): + def __init__(self, width, depth, init=None, name=None): + Special.__init__(self) + self.width = width + self.depth = depth + self.ports = [] + self.init = init + self.name_override = get_obj_var_name(name, "mem") + + def __getitem__(self, index): + # simulation only + return _MemoryLocation(self, index) + + def get_port(self, write_capable=False, async_read=False, + has_re=False, we_granularity=0, mode=WRITE_FIRST, + clock_domain="sys"): + if we_granularity >= self.width: + we_granularity = 0 + adr = Signal(max=self.depth) + dat_r = Signal(self.width) + if write_capable: + if we_granularity: + we = Signal(self.width//we_granularity) + else: + we = Signal() + dat_w = Signal(self.width) + else: + we = None + dat_w = None + if has_re: + re = Signal() + else: + re = None + mp = _MemoryPort(adr, dat_r, we, dat_w, + async_read, re, we_granularity, mode, + clock_domain) + self.ports.append(mp) + return mp + + @staticmethod + def 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) + '_' + 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) + '_' + 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) + "_" + 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) + "_" + 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) + "1.init", content_7_0) + memory_filename2 = add_data_file(gn(memory) + "2.init", content_15_8) + memory_filename3 = add_data_file(gn(memory) + "3.init", content_23_16) + memory_filename4 = add_data_file(gn(memory) + "4.init", content_31_24) + r += "initial begin\n" + r += "\t$readmemh(\"" + memory_filename1 + "\", " + gn(memory)+ "_0" + ");\n" + r += "\t$readmemh(\"" + memory_filename2 + "\", " + gn(memory)+ "_1" + ");\n" + r += "\t$readmemh(\"" + memory_filename3 + "\", " + gn(memory)+ "_2" + ");\n" + r += "\t$readmemh(\"" + memory_filename4 + "\", " + gn(memory)+ "_3" + ");\n" + r += "end\n\n" + + return r diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index cc8173522..ddbb9fda1 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -815,7 +815,7 @@ class SoC(Module): self.check_if_exists(name) setattr(self.submodules, name, SoCController(**kwargs)) - def add_ram(self, name, origin, size, contents=[], mode="rw"): + def add_ram(self, name, origin, size, contents=[], mode="rw", no_we=False): ram_cls = { "wishbone": wishbone.SRAM, "axi-lite": axi.AXILiteSRAM, @@ -825,7 +825,7 @@ class SoC(Module): "axi-lite": axi.AXILiteInterface, }[self.bus.standard] ram_bus = interface_cls(data_width=self.bus.data_width) - ram = ram_cls(size, bus=ram_bus, init=contents, read_only=(mode == "r")) + ram = ram_cls(size, bus=ram_bus, init=contents, read_only=(mode == "r"), no_we=no_we) self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode)) self.check_if_exists(name) self.logger.info("RAM {} {} {}.".format( @@ -834,8 +834,8 @@ class SoC(Module): self.bus.regions[name])) setattr(self.submodules, name, ram) - def add_rom(self, name, origin, size, contents=[], mode="r"): - self.add_ram(name, origin, size, contents, mode=mode) + def add_rom(self, name, origin, size, contents=[], mode="r", no_we=False): + self.add_ram(name, origin, size, contents, mode=mode, no_we=no_we) def init_rom(self, name, contents=[], auto_size=True): self.logger.info("Initializing ROM {} with contents (Size: {}).".format( diff --git a/litex/soc/integration/soc_core.py b/litex/soc/integration/soc_core.py index 94c3b3ae9..043ce4ead 100644 --- a/litex/soc/integration/soc_core.py +++ b/litex/soc/integration/soc_core.py @@ -79,10 +79,12 @@ class SoCCore(LiteXSoC): integrated_rom_size = 0, integrated_rom_mode = "r", integrated_rom_init = [], + integrated_rom_no_we = False, # SRAM parameters integrated_sram_size = 0x2000, integrated_sram_init = [], + integrated_sram_no_we = False, # MAIN_RAM parameters integrated_main_ram_size = 0, @@ -197,11 +199,11 @@ class SoCCore(LiteXSoC): # Add integrated ROM if integrated_rom_size: - self.add_rom("rom", self.cpu.reset_address, integrated_rom_size, integrated_rom_init, integrated_rom_mode) + self.add_rom("rom", self.cpu.reset_address, integrated_rom_size, integrated_rom_init, integrated_rom_mode, no_we=integrated_rom_no_we) # Add integrated SRAM if integrated_sram_size: - self.add_ram("sram", self.mem_map["sram"], integrated_sram_size) + self.add_ram("sram", self.mem_map["sram"], integrated_sram_size, no_we=integrated_sram_no_we) # Add integrated MAIN_RAM (only useful when no external SRAM/SDRAM is available) if integrated_main_ram_size: diff --git a/litex/soc/interconnect/axi.py b/litex/soc/interconnect/axi.py index b40532f0d..0117b49e4 100644 --- a/litex/soc/interconnect/axi.py +++ b/litex/soc/interconnect/axi.py @@ -783,7 +783,7 @@ class AXILite2CSR(Module): # AXILite SRAM ------------------------------------------------------------------------------------- class AXILiteSRAM(Module): - def __init__(self, mem_or_size, read_only=None, init=None, bus=None): + def __init__(self, mem_or_size, read_only=None, init=None, bus=None, no_we=False): if bus is None: bus = AXILiteInterface() self.bus = bus diff --git a/litex/soc/interconnect/wishbone.py b/litex/soc/interconnect/wishbone.py index 85d9c6b5f..88d0bb79a 100644 --- a/litex/soc/interconnect/wishbone.py +++ b/litex/soc/interconnect/wishbone.py @@ -329,7 +329,7 @@ class Converter(Module): # Wishbone SRAM ------------------------------------------------------------------------------------ class SRAM(Module): - def __init__(self, mem_or_size, read_only=None, init=None, bus=None): + def __init__(self, mem_or_size, read_only=None, init=None, bus=None, no_we=False): if bus is None: bus = Interface() self.bus = bus @@ -338,7 +338,12 @@ class SRAM(Module): assert(mem_or_size.width <= bus_data_width) self.mem = mem_or_size else: - self.mem = Memory(bus_data_width, mem_or_size//(bus_data_width//8), init=init) + if no_we: + from litex.gen.fhdl.memory import Memory as NoWeMemory + self.mem = NoWeMemory(bus_data_width, mem_or_size//(bus_data_width//8), init=init) + else: + self.mem = Memory(bus_data_width, mem_or_size//(bus_data_width//8), init=init) + if read_only is None: if hasattr(self.mem, "bus_read_only"): read_only = self.mem.bus_read_only