From c0c6c854ecd4a875be15a71cf91c38830ca5eede Mon Sep 17 00:00:00 2001 From: azaidy Date: Wed, 6 Nov 2024 10:51:40 -0500 Subject: [PATCH 1/2] Split rams into impl and wrappers --- lambdalib/ramlib/rtl/la_dpram.v | 33 ++++++++++---- lambdalib/ramlib/rtl/la_dpram_impl.v | 62 +++++++++++++++++++++++++++ lambdalib/ramlib/rtl/la_spram.v | 35 ++++++++------- lambdalib/ramlib/rtl/la_spram_impl.v | 64 ++++++++++++++++++++++++++++ 4 files changed, 170 insertions(+), 24 deletions(-) create mode 100644 lambdalib/ramlib/rtl/la_dpram_impl.v create mode 100644 lambdalib/ramlib/rtl/la_spram_impl.v diff --git a/lambdalib/ramlib/rtl/la_dpram.v b/lambdalib/ramlib/rtl/la_dpram.v index aeca602..b0bb77f 100644 --- a/lambdalib/ramlib/rtl/la_dpram.v +++ b/lambdalib/ramlib/rtl/la_dpram.v @@ -47,16 +47,31 @@ module la_dpram #( input [TESTW-1:0] test // pass through ASIC test interface ); - // Generic RTL RAM - reg [DW-1:0] ram[(2**AW)-1:0]; - integer i; + la_dpram_impl #( + .DW (DW), + .AW (AW), + .PROP (PROP), + .CTRLW (CTRLW), + .TESTW (TESTW) + ) ram ( + .wr_clk (wr_clk), + .wr_ce (wr_ce), + .wr_we (wr_we), + .wr_wmask (wr_wmask), + .wr_addr (wr_addr), + .wr_din (wr_din), - // Write port - always @(posedge wr_clk) - for (i = 0; i < DW; i = i + 1) - if (wr_ce & wr_we & wr_wmask[i]) ram[wr_addr[AW-1:0]][i] <= wr_din[i]; + .rd_clk (rd_clk), + .rd_ce (rd_ce), + .rd_addr (rd_addr), + .rd_dout (rd_dout), - // Read Port - always @(posedge rd_clk) if (rd_ce) rd_dout[DW-1:0] <= ram[rd_addr[AW-1:0]]; + .vss (vss), + .vdd (vdd), + .vddio (vddio), + + .ctrl (ctrl), + .test (test) + ); endmodule diff --git a/lambdalib/ramlib/rtl/la_dpram_impl.v b/lambdalib/ramlib/rtl/la_dpram_impl.v new file mode 100644 index 0000000..0123281 --- /dev/null +++ b/lambdalib/ramlib/rtl/la_dpram_impl.v @@ -0,0 +1,62 @@ +/***************************************************************************** + * Function: Dual Port RAM (One write port + One read port) + * Copyright: Lambda Project Authors. All rights Reserved. + * License: MIT (see LICENSE file in Lambda repository) + * + * Docs: + * + * This is a wrapper for selecting from a set of hardened memory macros. + * + * A synthesizable reference model is used when the PROP is DEFAULT. The + * synthesizable model does not implement the cfg and test interface and should + * only be used for basic testing and for synthesizing for FPGA devices. + * Advanced ASIC development should rely on complete functional models + * supplied on a per macro basis. + * + * Technologoy specific implementations of "la_dpram" would generally include + * one or more hardcoded instantiations of RAM modules with a generate + * statement relying on the "PROP" to select between the list of modules + * at build time. + * + ****************************************************************************/ + +module la_dpram_impl #( + parameter DW = 32, // Memory width + parameter AW = 10, // address width (derived) + parameter PROP = "DEFAULT", // pass through variable for hard macro + parameter CTRLW = 128, // width of asic ctrl interface + parameter TESTW = 128 // width of asic test interface +) ( // Write port + input wr_clk, // write clock + input wr_ce, // write chip-enable + input wr_we, // write enable + input [DW-1:0] wr_wmask, // write mask + input [AW-1:0] wr_addr, // write address + input [DW-1:0] wr_din, //write data in + // Read port + input rd_clk, // read clock + input rd_ce, // read chip-enable + input [AW-1:0] rd_addr, // read address + output reg [DW-1:0] rd_dout, //read data out + // Power signal + input vss, // ground signal + input vdd, // memory core array power + input vddio, // periphery/io power + // Generic interfaces + input [CTRLW-1:0] ctrl, // pass through ASIC control interface + input [TESTW-1:0] test // pass through ASIC test interface +); + + // Generic RTL RAM + reg [DW-1:0] ram[(2**AW)-1:0]; + integer i; + + // Write port + always @(posedge wr_clk) + for (i = 0; i < DW; i = i + 1) + if (wr_ce & wr_we & wr_wmask[i]) ram[wr_addr[AW-1:0]][i] <= wr_din[i]; + + // Read Port + always @(posedge rd_clk) if (rd_ce) rd_dout[DW-1:0] <= ram[rd_addr[AW-1:0]]; + +endmodule diff --git a/lambdalib/ramlib/rtl/la_spram.v b/lambdalib/ramlib/rtl/la_spram.v index a4dda67..b14c8fe 100644 --- a/lambdalib/ramlib/rtl/la_spram.v +++ b/lambdalib/ramlib/rtl/la_spram.v @@ -43,22 +43,27 @@ module la_spram #( input [TESTW-1:0] test // pass through ASIC test interface ); - // Generic RTL RAM - reg [DW-1:0] ram[(2**AW)-1:0]; - integer i; + la_spram_impl #( + .DW (DW), + .AW (AW), + .PROP (PROP), + .CTRLW (CTRLW), + .TESTW (TESTW), + ) ram ( + .clk (clk), + .ce (ce), + .we (we), + .wmask (wmask), + .addr (addr), + .din (din), + .dout (dout), - // Write port - // always @(posedge clk) - // for (i=0;i Date: Wed, 6 Nov 2024 15:19:22 -0500 Subject: [PATCH 2/2] update template generator --- lambdalib/ramlib/rtl/la_spram.v | 2 +- lambdalib/utils/__init__.py | 10 +- lambdalib/utils/templates/la_spmemory.v | 116 ++++++++++++++---------- 3 files changed, 80 insertions(+), 48 deletions(-) diff --git a/lambdalib/ramlib/rtl/la_spram.v b/lambdalib/ramlib/rtl/la_spram.v index b14c8fe..e741596 100644 --- a/lambdalib/ramlib/rtl/la_spram.v +++ b/lambdalib/ramlib/rtl/la_spram.v @@ -48,7 +48,7 @@ module la_spram #( .AW (AW), .PROP (PROP), .CTRLW (CTRLW), - .TESTW (TESTW), + .TESTW (TESTW) ) ram ( .clk (clk), .ce (ce), diff --git a/lambdalib/utils/__init__.py b/lambdalib/utils/__init__.py index fc63a78..1579cae 100644 --- a/lambdalib/utils/__init__.py +++ b/lambdalib/utils/__init__.py @@ -1,9 +1,10 @@ from jinja2 import Template import os +import math from collections import OrderedDict -def write_la_spram(fout, memories, control_signals=None, la_type='ram'): +def write_la_spram(fout, memories, control_signals=None, la_type='ram', minbits=None): template_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates', 'la_spmemory.v')) @@ -33,6 +34,13 @@ def write_la_spram(fout, memories, control_signals=None, la_type='ram'): selection_table = OrderedDict(sorted(selection_table.items(), reverse=True)) for aw, items in selection_table.items(): selection_table[aw] = OrderedDict(sorted(items.items(), reverse=True)) + + if minbits is not None: + depth = 2**aw + dw = int(math.floor(minbits / depth)) + if dw > 0: + selection_table[aw][dw] = "SOFT" + selection_table[min(selection_table.keys()) - 1] = {0: "SOFT"} widths_table.sort() depths_table.sort() diff --git a/lambdalib/utils/templates/la_spmemory.v b/lambdalib/utils/templates/la_spmemory.v index 75231b2..87e58f5 100644 --- a/lambdalib/utils/templates/la_spmemory.v +++ b/lambdalib/utils/templates/la_spmemory.v @@ -56,61 +56,85 @@ module la_sp{{ type }} (MEM_PROP == "{{ memory }}") ? {{ depth }} :{% endfor %} 0; - // Create memories - localparam MEM_ADDRS = 2**(AW - MEM_DEPTH) < 1 ? 1 : 2**(AW - MEM_DEPTH); - - {% if control_signals %}// Control signals{% for line in control_signals %} - {{ line }}{% endfor %}{% endif %} - generate - genvar o; - for (o = 0; o < DW; o = o + 1) begin: OUTPUTS - wire [MEM_ADDRS-1:0] mem_outputs; - assign dout[o] = |mem_outputs; + if (MEM_PROP == "SOFT") begin: isoft + la_spram_impl #( + .DW(DW), + .AW(AW), + .PROP(PROP), + .CTRLW(CTRLW), + .TESTW(TESTW) + ) memory( + .clk(clk), + .ce(ce), + .we(we), + .wmask(wmask), + .addr(addr), + .din(din), + .dout(dout), + .vss(vss), + .vdd(vdd), + .vddio(vddio), + .ctrl(ctrl), + .test(test) + ); end + if (MEM_PROP != "SOFT") begin: itech + // Create memories + localparam MEM_ADDRS = 2**(AW - MEM_DEPTH) < 1 ? 1 : 2**(AW - MEM_DEPTH); - genvar a; - for (a = 0; a < MEM_ADDRS; a = a + 1) begin: ADDR - wire selected; - wire [MEM_DEPTH-1:0] mem_addr; + {% if control_signals %}// Control signals{% for line in control_signals %} + {{ line }}{% endfor %}{% endif %} - if (MEM_ADDRS == 1) begin: FITS - assign selected = 1'b1; - assign mem_addr = addr; - end else begin: NOFITS - assign selected = addr[AW-1:MEM_DEPTH] == a; - assign mem_addr = addr[MEM_DEPTH-1:0]; + genvar o; + for (o = 0; o < DW; o = o + 1) begin: OUTPUTS + wire [MEM_ADDRS-1:0] mem_outputs; + assign dout[o] = |mem_outputs; end - genvar n; - for (n = 0; n < DW; n = n + MEM_WIDTH) begin: WORD - wire [MEM_WIDTH-1:0] mem_din; - wire [MEM_WIDTH-1:0] mem_dout; - wire [MEM_WIDTH-1:0] mem_wmask; + genvar a; + for (a = 0; a < MEM_ADDRS; a = a + 1) begin: ADDR + wire selected; + wire [MEM_DEPTH-1:0] mem_addr; - genvar i; - for (i = 0; i < MEM_WIDTH; i = i + 1) begin: WORD_SELECT - if (n + i < DW) begin: ACTIVE - assign mem_din[i] = din[n + i]; - assign mem_wmask[i] = wmask[n + i]; - assign OUTPUTS[n + i].mem_outputs[a] = selected ? mem_dout[i] : 1'b0; - end - else begin: INACTIVE - assign mem_din[i] = 1'b0; - assign mem_wmask[i] = 1'b0; - end + if (MEM_ADDRS == 1) begin: FITS + assign selected = 1'b1; + assign mem_addr = addr; + end else begin: NOFITS + assign selected = addr[AW-1:MEM_DEPTH] == a; + assign mem_addr = addr[MEM_DEPTH-1:0]; end - wire ce_in; - wire we_in; - assign ce_in = ce && selected; - assign we_in = we && selected; - {% for memory, inst_name in inst_map.items() %} - if (MEM_PROP == "{{ memory }}") begin: i{{ memory }} - {{ inst_name }} memory ({% for port, net in port_mapping[memory] %} - .{{ port }}({{ net }}){% if loop.nextitem is defined %},{% endif %}{% endfor %} - ); - end{% endfor %} + genvar n; + for (n = 0; n < DW; n = n + MEM_WIDTH) begin: WORD + wire [MEM_WIDTH-1:0] mem_din; + wire [MEM_WIDTH-1:0] mem_dout; + wire [MEM_WIDTH-1:0] mem_wmask; + + genvar i; + for (i = 0; i < MEM_WIDTH; i = i + 1) begin: WORD_SELECT + if (n + i < DW) begin: ACTIVE + assign mem_din[i] = din[n + i]; + assign mem_wmask[i] = wmask[n + i]; + assign OUTPUTS[n + i].mem_outputs[a] = selected ? mem_dout[i] : 1'b0; + end + else begin: INACTIVE + assign mem_din[i] = 1'b0; + assign mem_wmask[i] = 1'b0; + end + end + + wire ce_in; + wire we_in; + assign ce_in = ce && selected; + assign we_in = we && selected; + {% for memory, inst_name in inst_map.items() %} + if (MEM_PROP == "{{ memory }}") begin: i{{ memory }} + {{ inst_name }} memory ({% for port, net in port_mapping[memory] %} + .{{ port }}({{ net }}){% if loop.nextitem is defined %},{% endif %}{% endfor %} + ); + end{% endfor %} + end end end endgenerate