Skip to content

Commit

Permalink
Merge pull request #87 from siliconcompiler/ali/ram-fix
Browse files Browse the repository at this point in the history
Split rams into impl and wrappers
  • Loading branch information
gadfort authored Nov 6, 2024
2 parents f81fbff + 0f6b610 commit 8d2ce26
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 71 deletions.
33 changes: 24 additions & 9 deletions lambdalib/ramlib/rtl/la_dpram.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
62 changes: 62 additions & 0 deletions lambdalib/ramlib/rtl/la_dpram_impl.v
Original file line number Diff line number Diff line change
@@ -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
35 changes: 20 additions & 15 deletions lambdalib/ramlib/rtl/la_spram.v
Original file line number Diff line number Diff line change
Expand Up @@ -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<DW;i=i+1)
// if (ce & we & wmask[i])
// ram[addr[AW-1:0]][i] <= din[i];
.vss (vss),
.vdd (vdd),
.vddio (vddio),

// Re-writing as a mux for verilator
always @(posedge clk)
if (ce & we)
ram[addr[AW-1:0]] <= din[DW-1:0] & wmask[DW-1:0] | ram[addr[AW-1:0]] & ~wmask[DW-1:0];

// Read Port
always @(posedge clk) if (ce) dout[DW-1:0] <= ram[addr[AW-1:0]];
.ctrl (ctrl),
.test (test)
);

endmodule
64 changes: 64 additions & 0 deletions lambdalib/ramlib/rtl/la_spram_impl.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*****************************************************************************
* Function: Single Port RAM
* 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_spram" 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_spram_impl #(
parameter DW = 32, // Memory width
parameter AW = 10, // Address width (derived)
parameter PROP = "DEFAULT", // Pass through variable for hard macro
parameter CTRLW = 1, // Width of asic ctrl interface
parameter TESTW = 1 // Width of asic test interface
) ( // Memory interface
input clk, // write clock
input ce, // chip enable
input we, // write enable
input [DW-1:0] wmask, //per bit write mask
input [AW-1:0] addr, //write address
input [DW-1:0] din, //write data
output reg [DW-1:0] dout, //read output data
// Power signals
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 clk)
// for (i=0;i<DW;i=i+1)
// if (ce & we & wmask[i])
// ram[addr[AW-1:0]][i] <= din[i];

// Re-writing as a mux for verilator
always @(posedge clk)
if (ce & we)
ram[addr[AW-1:0]] <= din[DW-1:0] & wmask[DW-1:0] | ram[addr[AW-1:0]] & ~wmask[DW-1:0];

// Read Port
always @(posedge clk) if (ce) dout[DW-1:0] <= ram[addr[AW-1:0]];

endmodule
10 changes: 9 additions & 1 deletion lambdalib/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -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'))
Expand Down Expand Up @@ -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()

Expand Down
116 changes: 70 additions & 46 deletions lambdalib/utils/templates/la_spmemory.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 8d2ce26

Please sign in to comment.