Skip to content
4 changes: 2 additions & 2 deletions hammer/common/synopsys/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def get_synopsys_rm_tarball(self, product: str, settings_key: str = "") -> str:

synopsys_rm_tarball = os.path.join(self.get_setting("synopsys.rm_dir"), "%s-RM_%s.tar" % (product, self.get_setting(key)))
if not os.path.exists(synopsys_rm_tarball):
# TODO: convert these to logger calls
raise FileNotFoundError("Expected reference methodology tarball not found at %s. Use the Synopsys RM generator <https://solvnet.synopsys.com/rmgen> to generate a DC reference methodology. If these tarballs have been pre-downloaded, you can set synopsys.rm_dir instead of generating them yourself." % (synopsys_rm_tarball))
self.logger.warning("Expected reference methodology tarball not found at %s. Use the Synopsys RM generator <https://solvnet.synopsys.com/rmgen> to generate a DC reference methodology. If these tarballs have been pre-downloaded, you can set synopsys.rm_dir instead of generating them yourself." % (synopsys_rm_tarball))
return ""
else:
return synopsys_rm_tarball
7 changes: 7 additions & 0 deletions hammer/config/defaults.yml
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ vlsi.inputs:
# group (str) - Optional. The name of the clock group this clock belongs to. Clocks in the same group will not be marked as asynchronous.
# Clocks with no group specified will all be placed in separate groups and thus marked as asynchronous to each other and all other groups.

resets: [] # Reset ports of the top-level module.
# type: List[ResetPort]
# Reset struct members:
# name (str) - Name of the reset port.
# active_negated (bool) - Optional. If specified this reset is active when zero,
# synchronous (bool) - Optional. If specified this reset is synchronous with the clock,

default_output_load: "1 pF" # Default output pin load capacitance.
# type: CapacitanceValue

Expand Down
3 changes: 3 additions & 0 deletions hammer/config/defaults_types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ vlsi.inputs:
# Clock ports of the top-level module.
clocks: list[dict[str, Any]]

# Reset ports of the top-level module.
resets: list[dict[str, Any]]

# Default output pin load capacitance.
default_output_load: str

Expand Down
2 changes: 1 addition & 1 deletion hammer/technology/nangate45/defaults.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ vlsi:
placement_site: "FreePDK45_38x28_10R_NP_162NW_34O" # Set standard cell LEF placement site

technology.core:
stackup: "nangate45_3Ma_3Mb_2Mc_2Md" # This key should exist in the stackups list in the tech json
stackup: "nangate45_3Ma_2Mb_2Mc_2Md" # This key should exist in the stackups list in the tech json
std_cell_rail_layer: "metal1" # This should specify the TOPMOST metal layer the
# standard cells use for power rails
# Note that this is not usually stackup specific;
Expand Down
46 changes: 29 additions & 17 deletions hammer/technology/nangate45/nangate45.tech.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,20 @@
"lvs_decks": [],
"additional_lvs_text": "",

"libraries": [{
"lef_file": "nangate45/lef/NangateOpenCellLibrary.tech.lef",
"libraries": [
{"milkyway_techfile" : "nangate45/Back_End/virtuoso/technology.tf",
"provides": [{"lib_type": "milkyway_techfile"}]
},
{
"lef_file": "nangate45/Back_End/lef/NangateOpenCellLibrary.tech.lef",
"provides": [{"lib_type": "technology"}]
},{
"nldm_liberty_file": "nangate45/lib/NangateOpenCellLibrary_typical.lib",
"lef_file": "nangate45/lef/NangateOpenCellLibrary.macro.lef",
"gds_file": "nangate45/gds/NangateOpenCellLibrary.gds",
},
{
"ccs_library_file" : "nangate45/fix_scan/NangateOpenCellLibrary_typical_ccs_scan.db",
"ccs_liberty_file" : "nangate45/Front_End/Liberty/CCS/NangateOpenCellLibrary_typical_ccs.lib",
"nldm_liberty_file": "nangate45/Front_End/Liberty/NLDM/NangateOpenCellLibrary_typical.lib",
"lef_file": "nangate45/Back_End/lef/NangateOpenCellLibrary.macro.lef",
"gds_file": "nangate45/Back_End/gds/NangateOpenCellLibrary.gds",
"corner": {
"nmos": "typical",
"pmos": "typical",
Expand All @@ -41,10 +48,11 @@
}],
"stackups": [{
"name": "nangate45_3Ma_2Mb_2Mc_2Md",
"grid_unit": "0.0005",
"metals": [{
"name": "metal1", "index": 1, "direction": "horizontal",
"min_width": 0.07, "max_width": 1073741.8235, "pitch": 0.14,
"offset": 0.07, "power_strap_widths_and_spacings": []
"offset": 0.07, "power_strap_widths_and_spacings": [], "grid_unit": "0.0005"
},{
"name": "metal2", "index": 2, "direction": "vertical",
"min_width": 0.07, "max_width": 1073741.8235, "pitch": 0.14,
Expand All @@ -55,7 +63,7 @@
{"width_at_least": 0.5, "min_spacing": 0.5},
{"width_at_least": 0.9, "min_spacing": 0.9},
{"width_at_least": 1.5, "min_spacing": 1.5}
]
], "grid_unit": "0.0005"
},{
"name": "metal3", "index": 3, "direction": "horizontal",
"min_width": 0.07, "max_width": 1073741.8235, "pitch": 0.14,
Expand All @@ -66,7 +74,7 @@
{"width_at_least": 0.5, "min_spacing": 0.5},
{"width_at_least": 0.9, "min_spacing": 0.9},
{"width_at_least": 1.5, "min_spacing": 1.5}
]
], "grid_unit": "0.0005"
},{
"name": "metal4", "index": 4, "direction": "vertical",
"min_width": 0.14, "max_width": 1073741.8235, "pitch": 0.28,
Expand All @@ -76,7 +84,7 @@
{"width_at_least": 0.5, "min_spacing": 0.5},
{"width_at_least": 0.9, "min_spacing": 0.9},
{"width_at_least": 1.5, "min_spacing": 1.5}
]
], "grid_unit": "0.0005"
},{
"name": "metal5", "index": 5, "direction": "horizontal",
"min_width": 0.14, "max_width": 1073741.8235, "pitch": 0.28,
Expand All @@ -86,7 +94,7 @@
{"width_at_least": 0.5, "min_spacing": 0.5},
{"width_at_least": 0.9, "min_spacing": 0.9},
{"width_at_least": 1.5, "min_spacing": 1.5}
]
], "grid_unit": "0.0005"
},{
"name": "metal6", "index": 6, "direction": "vertical",
"min_width": 0.14, "max_width": 1073741.8235, "pitch": 0.28,
Expand All @@ -96,7 +104,7 @@
{"width_at_least": 0.5, "min_spacing": 0.5},
{"width_at_least": 0.9, "min_spacing": 0.9},
{"width_at_least": 1.5, "min_spacing": 1.5}
]
], "grid_unit": "0.0005"
},{
"name": "metal7", "index": 7, "direction": "horizontal",
"min_width": 0.4, "max_width": 1073741.8235, "pitch": 0.8,
Expand All @@ -105,7 +113,7 @@
{"width_at_least": 0.5, "min_spacing": 0.5},
{"width_at_least": 0.9, "min_spacing": 0.9},
{"width_at_least": 1.5, "min_spacing": 1.5}
]
], "grid_unit": "0.0005"
},{
"name": "metal8", "index": 8, "direction": "vertical",
"min_width": 0.4, "max_width": 1073741.8235, "pitch": 0.8,
Expand All @@ -114,23 +122,23 @@
{"width_at_least": 0.5, "min_spacing": 0.5},
{"width_at_least": 0.9, "min_spacing": 0.9},
{"width_at_least": 1.5, "min_spacing": 1.5}
]
], "grid_unit": "0.0005"
},{
"name": "metal9", "index": 9, "direction": "horizontal",
"min_width": 0.8, "max_width": 1073741.8235, "pitch": 1.6,
"offset": 0.07, "power_strap_widths_and_spacings": [
{"width_at_least": 0.0, "min_spacing": 0.8},
{"width_at_least": 0.9, "min_spacing": 0.9},
{"width_at_least": 1.5, "min_spacing": 1.5}
]
], "grid_unit": "0.0005"
},{
"name": "metal10", "index": 10, "direction": "vertical",
"min_width": 0.8, "max_width": 1073741.8235, "pitch": 1.6,
"offset": 0.095, "power_strap_widths_and_spacings": [
{"width_at_least": 0.0, "min_spacing": 0.8},
{"width_at_least": 0.9, "min_spacing": 0.9},
{"width_at_least": 1.5, "min_spacing": 1.5}
]
], "grid_unit": "0.0005"
}]
}],
"sites": [
Expand All @@ -147,6 +155,10 @@
"FILLCELL_X8","FILLCELL_X16","FILLCELL_X32"
]},
{"cell_type": "tiehicell", "name": ["LOGIC1_X1"]},
{"cell_type": "tielocell", "name": ["LOGIC0_X1"]}
{"cell_type": "tielocell", "name": ["LOGIC0_X1"]},
{"cell_type": "driver", "name": ["BUF_X1","BUF_X16","BUF_X2","BUF_X32","BUF_X4","BUF_X8"], "input_ports": ["A"], "output_ports": ["Z"]},
{"cell_type": "ctsbuffer", "name": ["CLKBUF_X1","CLKBUF_X2","CLKBUF_X3"]},
{"cell_type": "decap", "name": ["FILLCELL_X32"]},
{"cell_type": "endcap", "name": ["FILLCELL_X1"]}
]
}
171 changes: 170 additions & 1 deletion hammer/technology/nangate45/sram_compiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# See LICENSE for licence details.

import os, tempfile, subprocess
import math
import importlib.resources

from hammer.vlsi import MMMCCorner, MMMCCornerType, HammerTool, \
HammerToolStep, HammerSRAMGeneratorTool, SRAMParameters
Expand Down Expand Up @@ -30,7 +32,13 @@ def generate_sram(self, params: SRAMParameters,

self.validate_openroad_installation()
openroad = self.openroad_flow_path()
base_dir = os.path.join(openroad, "flow/designs/src/tinyRocket")

# Check what type of installation we have
if not os.path.exists(openroad + "flow/designs/src/tinyRocket"):
# This uses the OpenRoad repository
base_dir = os.path.join(openroad, "test/Nangate45/")
else:
base_dir = os.path.join(openroad, "flow/designs/src/tinyRocket")

tech_cache_dir = os.path.abspath(self.technology.cache_dir)
if params.family == "1RW":
Expand Down Expand Up @@ -61,12 +69,173 @@ def generate_sram(self, params: SRAMParameters,
if not os.path.exists(dst_lef):
os.symlink(src_lef, dst_lef)

# Generate Verilog file from template
verilog_path = "{t}/{n}.v".format(t=tech_cache_dir, n=sram_name)
with open(verilog_path, 'w') as f:
if params.family == "1RW":
specify = ""
for specify_j in range(0, params.width):
for specify_i in range(0, 2):
if specify_i == 0:
specify += "$setuphold(posedge CE, %s I[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_j)
else:
specify += "$setuphold(posedge CE, %s I[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_j)
specify += "(CE => O[%d]) = 0;\n" % (specify_j)
for specify_k in range(0, math.ceil(math.log2(params.depth))):
for specify_i in range(0, 2):
if specify_i == 0:
specify += "$setuphold(posedge CE, %s A[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_k)
else:
specify += "$setuphold(posedge CE, %s A[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_k)
f.write("""
`timescale 1ns/100fs

module {NAME} (A,CE,WEB,OEB,CSB,I,O);

input CE;
input WEB;
input OEB;
input CSB;

input [{NUMADDR}-1:0] A;
input [{WORDLENGTH}-1:0] I;
output [{WORDLENGTH}-1:0] O;

reg [{WORDLENGTH}-1:0] memory[{NUMWORDS}-1:0];
reg [{WORDLENGTH}-1:0] data_out;
wire [{WORDLENGTH}-1:0] O;

wire RE;
wire WE;
and u1 (RE, ~CSB, ~OEB);
and u2 (WE, ~CSB, ~WEB);

// Initialization for simulation
integer i;
initial begin
for (i = 0; i < {NUMWORDS}; i = i + 1) begin
memory[i] = {{{RAND_WIDTH}{{$urandom()}}}};
end
data_out = {{{RAND_WIDTH}{{$urandom()}}}};
end

always @ (posedge CE) begin
if (RE)
data_out <= memory[A];
if (WE)
memory[A] <= I;
end

reg NOTIFIER;
specify
{specify}
endspecify

assign O = data_out;

endmodule
""".format(NUMADDR=math.ceil(math.log2(params.depth)), NUMWORDS=params.depth, WORDLENGTH=params.width, NAME=sram_name,
RAND_WIDTH=math.ceil(params.width / 32), specify=specify))
else:
specify = ""

for specify_j in range(0, params.width):
for specify_i in range(0, 2):
if specify_i == 0:
specify += "$setuphold(posedge CE1, %s I1[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_j)
specify += "$setuphold(posedge CE2, %s I2[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_j)
else:
specify += "$setuphold(posedge CE1, %s I1[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_j)
specify += "$setuphold(posedge CE2, %s I2[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_j)
specify += "(CE1 => O1[%d]) = 0;\n" % (specify_j)
specify += "(CE2 => O2[%d]) = 0;\n" % (specify_j)
for specify_k in range(0, math.ceil(math.log2(params.depth))):
for specify_i in range(0, 2):
if specify_i == 0:
specify += "$setuphold(posedge CE1, %s A1[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_k)
specify += "$setuphold(posedge CE2, %s A2[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_k)
else:
specify += "$setuphold(posedge CE1, %s A1[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_k)
specify += "$setuphold(posedge CE2, %s A2[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_k)
f.write("""
`timescale 1ns/100fs

module {NAME} (A1,A2,CE1,CE2,WEB1,WEB2,OEB1,OEB2,CSB1,CSB2,I1,I2,O1,O2);

input CE1;
input CE2;
input WEB1;
input WEB2;
input OEB1;
input OEB2;
input CSB1;
input CSB2;

input [{NUMADDR}-1:0] A1;
input [{NUMADDR}-1:0] A2;
input [{WORDLENGTH}-1:0] I1;
input [{WORDLENGTH}-1:0] I2;
output [{WORDLENGTH}-1:0] O1;
output [{WORDLENGTH}-1:0] O2;

reg [{WORDLENGTH}-1:0] memory[{NUMWORDS}-1:0];
reg [{WORDLENGTH}-1:0] data_out1;
reg [{WORDLENGTH}-1:0] data_out2;
wire [{WORDLENGTH}-1:0] O1;
wire [{WORDLENGTH}-1:0] O2;

wire RE1;
wire RE2;
wire WE1;
wire WE2;
and u1 (RE1, ~CSB1, ~OEB1);
and u2 (RE2, ~CSB2, ~OEB2);
and u3 (WE1, ~CSB1, ~WEB1);
and u4 (WE2, ~CSB2, ~WEB2);

// Initialization for simulation
integer i;
initial begin
for (i = 0; i < {NUMWORDS}; i = i + 1) begin
memory[i] = {{{RAND_WIDTH}{{$urandom()}}}};
end
data_out1 = {{{RAND_WIDTH}{{$urandom()}}}};
data_out2 = {{{RAND_WIDTH}{{$urandom()}}}};
end

always @ (posedge CE1) begin
if (RE1)
data_out1 <= memory[A1];
if (WE1)
memory[A1] <= I1;
end

always @ (posedge CE2) begin
if (RE2)
data_out2 <= memory[A2];
if (WE2)
memory[A2] <= I2;
end

reg NOTIFIER;
specify
{specify}
endspecify

assign O1 = data_out1;
assign O2 = data_out2;

endmodule
""".format(NUMADDR=math.ceil(math.log2(params.depth)), NUMWORDS=params.depth, WORDLENGTH=params.width, NAME=sram_name,
RAND_WIDTH=math.ceil(params.width / 32), specify=specify))

return ExtraLibrary(
prefix=None,
library=Library(
name=sram_name,
nldm_liberty_file=dst_lib,
lef_file=dst_lef,
verilog_sim=verilog_path,
corner=Corner(
nmos="typical",
pmos="typical",
Expand Down
6 changes: 6 additions & 0 deletions hammer/vlsi/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,12 @@ def name_bump(self, definition: BumpsDefinition, assignment: BumpAssignment) ->
('group', Optional[str])
])

ResetPort = NamedTuple('ResetPort', [
('name', str),
('active_negated', Optional[bool]),
('synchronous', Optional[bool]),
])

OutputLoadConstraint = NamedTuple('OutputLoadConstraint', [
('name', str),
('load', CapacitanceValue)
Expand Down
Loading