Skip to content

Commit

Permalink
Merge pull request #46 from siliconcompiler/add_spram_template
Browse files Browse the repository at this point in the history
add jinja template to build lamdbalib memories
  • Loading branch information
gadfort authored Apr 19, 2024
2 parents da6f95a + f31fd3c commit a8099fa
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 2 deletions.
1 change: 1 addition & 0 deletions .github/workflows/bin/format_verilog.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ find . \( \
-or -name "*.vh" \
\) -not \( \
-path "./siliconcompiler/*" \
-or -path "./lambdalib/utils/templates/*" \
\) >> $FILES

verible-verilog-format \
Expand Down
2 changes: 1 addition & 1 deletion lambdalib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import glob
import os
import shutil
__version__ = "0.2.0"
__version__ = "0.2.1"

_libraries = (
'iolib',
Expand Down
49 changes: 49 additions & 0 deletions lambdalib/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from jinja2 import Template
import os
from collections import OrderedDict


def write_la_spram(fout, memories, control_signals=None, la_type='ram'):
template_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
'templates',
'la_spmemory.v'))

widths_table = []
depths_table = []
memory_port_map = {}
selection_table = {}
memory_inst_map = {}

for memory, info in memories.items():
widths_table.append(
(memory, info['DW'])
)
depths_table.append(
(memory, info['AW'])
)

memory_port_map[memory] = sorted(info["port_map"])
if "inst_name" not in info:
memory_inst_map[memory] = memory
else:
memory_inst_map[memory] = info["inst_name"]

selection_table.setdefault(info['AW'], {})[info['DW']] = memory

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))
widths_table.sort()
depths_table.sort()

with open(template_path) as f:
template = Template(f.read())

fout.write(template.render(
type=la_type,
width_table=widths_table,
depth_table=depths_table,
selection_table=selection_table,
inst_map=memory_inst_map,
port_mapping=memory_port_map,
control_signals=control_signals))
116 changes: 116 additions & 0 deletions lambdalib/utils/templates/la_spmemory.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*****************************************************************************
* Function: Single Port {{ type }}
* 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 TYPE 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_sp{{ type }}" would generally include
* one ore more hardcoded instantiations of {{ type }} modules with a generate
* statement relying on the "TYPE" to select between the list of modules
* at build time.
*
****************************************************************************/

module la_sp{{ type }}
#(parameter DW = 32, // Memory width
parameter AW = 10, // Address width (derived)
parameter TYPE = "DEFAULT", // Pass through variable for hard macro
parameter CTRLW = 128, // Width of asic ctrl interface
parameter TESTW = 128 // 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 [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
);

// Determine which memory to select
localparam MEM_TYPE = (TYPE != "DEFAULT") ? TYPE :{% for aw, dw_select in selection_table.items() %}
{% if loop.nextitem is defined %}(AW {% if loop.previtem is defined %}=={% else %}>={% endif %} {{ aw }}) ? {% endif %}{% for dw, memory in dw_select.items() %}{% if loop.nextitem is defined %}(DW {% if loop.previtem is defined %}=={% else %}>={% endif %} {{dw}}) ? {% endif %}"{{ memory}}"{% if loop.nextitem is defined %} : {% endif%}{% endfor %}{% if loop.nextitem is defined %} :{% else %};{% endif %}{% endfor %}

localparam MEM_WIDTH = {% for memory, width in width_table %}
(MEM_TYPE == "{{ memory }}") ? {{ width }} :{% endfor %}
0;

localparam MEM_DEPTH = {% for memory, depth in depth_table %}
(MEM_TYPE == "{{ 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;
end

genvar a;
for (a = 0; a < MEM_ADDRS; a = a + 1) begin: ADDR
wire selected;
wire [MEM_DEPTH-1:0] mem_addr;

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

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 loop.previtem is defined %}else {% endif %}if (MEM_TYPE == "{{ memory }}")
{{ inst_name }} memory ({% for port, net in port_mapping[memory] %}
.{{ port }}({{ net }}){% if loop.nextitem is defined %},{% endif %}{% endfor %}
);{% endfor %}
end
end
endgenerate
endmodule
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ urls = {Homepage = "https://github.com/siliconcompiler/lambdalib"}
requires-python = ">= 3.8"
license = {file = "LICENSE"}
dependencies = [
"siliconcompiler >= 0.20.2"
"siliconcompiler >= 0.20.2",
"Jinja2 >= 3.1.3"
]
dynamic = ['version']

Expand Down

0 comments on commit a8099fa

Please sign in to comment.