Skip to content

Commit a8099fa

Browse files
authored
Merge pull request #46 from siliconcompiler/add_spram_template
add jinja template to build lamdbalib memories
2 parents da6f95a + f31fd3c commit a8099fa

File tree

5 files changed

+169
-2
lines changed

5 files changed

+169
-2
lines changed

.github/workflows/bin/format_verilog.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ find . \( \
1515
-or -name "*.vh" \
1616
\) -not \( \
1717
-path "./siliconcompiler/*" \
18+
-or -path "./lambdalib/utils/templates/*" \
1819
\) >> $FILES
1920

2021
verible-verilog-format \

lambdalib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import glob
44
import os
55
import shutil
6-
__version__ = "0.2.0"
6+
__version__ = "0.2.1"
77

88
_libraries = (
99
'iolib',

lambdalib/utils/__init__.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from jinja2 import Template
2+
import os
3+
from collections import OrderedDict
4+
5+
6+
def write_la_spram(fout, memories, control_signals=None, la_type='ram'):
7+
template_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
8+
'templates',
9+
'la_spmemory.v'))
10+
11+
widths_table = []
12+
depths_table = []
13+
memory_port_map = {}
14+
selection_table = {}
15+
memory_inst_map = {}
16+
17+
for memory, info in memories.items():
18+
widths_table.append(
19+
(memory, info['DW'])
20+
)
21+
depths_table.append(
22+
(memory, info['AW'])
23+
)
24+
25+
memory_port_map[memory] = sorted(info["port_map"])
26+
if "inst_name" not in info:
27+
memory_inst_map[memory] = memory
28+
else:
29+
memory_inst_map[memory] = info["inst_name"]
30+
31+
selection_table.setdefault(info['AW'], {})[info['DW']] = memory
32+
33+
selection_table = OrderedDict(sorted(selection_table.items(), reverse=True))
34+
for aw, items in selection_table.items():
35+
selection_table[aw] = OrderedDict(sorted(items.items(), reverse=True))
36+
widths_table.sort()
37+
depths_table.sort()
38+
39+
with open(template_path) as f:
40+
template = Template(f.read())
41+
42+
fout.write(template.render(
43+
type=la_type,
44+
width_table=widths_table,
45+
depth_table=depths_table,
46+
selection_table=selection_table,
47+
inst_map=memory_inst_map,
48+
port_mapping=memory_port_map,
49+
control_signals=control_signals))
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*****************************************************************************
2+
* Function: Single Port {{ type }}
3+
* Copyright: Lambda Project Authors. All rights Reserved.
4+
* License: MIT (see LICENSE file in Lambda repository)
5+
*
6+
* Docs:
7+
*
8+
* This is a wrapper for selecting from a set of hardened memory macros.
9+
*
10+
* A synthesizable reference model is used when the TYPE is DEFAULT. The
11+
* synthesizable model does not implement the cfg and test interface and should
12+
* only be used for basic testing and for synthesizing for FPGA devices.
13+
* Advanced ASIC development should rely on complete functional models
14+
* supplied on a per macro basis.
15+
*
16+
* Technologoy specific implementations of "la_sp{{ type }}" would generally include
17+
* one ore more hardcoded instantiations of {{ type }} modules with a generate
18+
* statement relying on the "TYPE" to select between the list of modules
19+
* at build time.
20+
*
21+
****************************************************************************/
22+
23+
module la_sp{{ type }}
24+
#(parameter DW = 32, // Memory width
25+
parameter AW = 10, // Address width (derived)
26+
parameter TYPE = "DEFAULT", // Pass through variable for hard macro
27+
parameter CTRLW = 128, // Width of asic ctrl interface
28+
parameter TESTW = 128 // Width of asic test interface
29+
)
30+
(// Memory interface
31+
input clk, // write clock
32+
input ce, // chip enable
33+
input we, // write enable
34+
input [DW-1:0] wmask, //per bit write mask
35+
input [AW-1:0] addr, //write address
36+
input [DW-1:0] din, //write data
37+
output [DW-1:0] dout, //read output data
38+
// Power signals
39+
input vss, // ground signal
40+
input vdd, // memory core array power
41+
input vddio, // periphery/io power
42+
// Generic interfaces
43+
input [CTRLW-1:0] ctrl, // pass through ASIC control interface
44+
input [TESTW-1:0] test // pass through ASIC test interface
45+
);
46+
47+
// Determine which memory to select
48+
localparam MEM_TYPE = (TYPE != "DEFAULT") ? TYPE :{% for aw, dw_select in selection_table.items() %}
49+
{% 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 %}
50+
51+
localparam MEM_WIDTH = {% for memory, width in width_table %}
52+
(MEM_TYPE == "{{ memory }}") ? {{ width }} :{% endfor %}
53+
0;
54+
55+
localparam MEM_DEPTH = {% for memory, depth in depth_table %}
56+
(MEM_TYPE == "{{ memory }}") ? {{ depth }} :{% endfor %}
57+
0;
58+
59+
// Create memories
60+
localparam MEM_ADDRS = 2**(AW - MEM_DEPTH) < 1 ? 1 : 2**(AW - MEM_DEPTH);
61+
62+
{% if control_signals %}// Control signals{% for line in control_signals %}
63+
{{ line }}{% endfor %}{% endif %}
64+
65+
generate
66+
genvar o;
67+
for (o = 0; o < DW; o = o + 1) begin: OUTPUTS
68+
wire [MEM_ADDRS-1:0] mem_outputs;
69+
assign dout[o] = |mem_outputs;
70+
end
71+
72+
genvar a;
73+
for (a = 0; a < MEM_ADDRS; a = a + 1) begin: ADDR
74+
wire selected;
75+
wire [MEM_DEPTH-1:0] mem_addr;
76+
77+
if (MEM_ADDRS == 1) begin: FITS
78+
assign selected = 1'b1;
79+
assign mem_addr = addr;
80+
end else begin: NOFITS
81+
assign selected = addr[AW-1:MEM_DEPTH] == a;
82+
assign mem_addr = addr[MEM_DEPTH-1:0];
83+
end
84+
85+
genvar n;
86+
for (n = 0; n < DW; n = n + MEM_WIDTH) begin: WORD
87+
wire [MEM_WIDTH-1:0] mem_din;
88+
wire [MEM_WIDTH-1:0] mem_dout;
89+
wire [MEM_WIDTH-1:0] mem_wmask;
90+
91+
genvar i;
92+
for (i = 0; i < MEM_WIDTH; i = i + 1) begin: WORD_SELECT
93+
if (n + i < DW) begin: ACTIVE
94+
assign mem_din[i] = din[n + i];
95+
assign mem_wmask[i] = wmask[n + i];
96+
assign OUTPUTS[n + i].mem_outputs[a] = selected ? mem_dout[i] : 1'b0;
97+
end
98+
else begin: INACTIVE
99+
assign mem_din[i] = 1'b0;
100+
assign mem_wmask[i] = 1'b0;
101+
end
102+
end
103+
104+
wire ce_in;
105+
wire we_in;
106+
assign ce_in = ce && selected;
107+
assign we_in = we && selected;
108+
{% for memory, inst_name in inst_map.items() %}
109+
{% if loop.previtem is defined %}else {% endif %}if (MEM_TYPE == "{{ memory }}")
110+
{{ inst_name }} memory ({% for port, net in port_mapping[memory] %}
111+
.{{ port }}({{ net }}){% if loop.nextitem is defined %},{% endif %}{% endfor %}
112+
);{% endfor %}
113+
end
114+
end
115+
endgenerate
116+
endmodule

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ urls = {Homepage = "https://github.com/siliconcompiler/lambdalib"}
2020
requires-python = ">= 3.8"
2121
license = {file = "LICENSE"}
2222
dependencies = [
23-
"siliconcompiler >= 0.20.2"
23+
"siliconcompiler >= 0.20.2",
24+
"Jinja2 >= 3.1.3"
2425
]
2526
dynamic = ['version']
2627

0 commit comments

Comments
 (0)