diff --git a/hammer/common/synopsys/__init__.py b/hammer/common/synopsys/__init__.py index 02c71d8fd..6f4b62372 100644 --- a/hammer/common/synopsys/__init__.py +++ b/hammer/common/synopsys/__init__.py @@ -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 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 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 \ No newline at end of file diff --git a/hammer/config/defaults.yml b/hammer/config/defaults.yml index 4cf413149..90741f9dc 100644 --- a/hammer/config/defaults.yml +++ b/hammer/config/defaults.yml @@ -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 diff --git a/hammer/config/defaults_types.yml b/hammer/config/defaults_types.yml index ca48d07de..80edc7c49 100644 --- a/hammer/config/defaults_types.yml +++ b/hammer/config/defaults_types.yml @@ -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 diff --git a/hammer/technology/nangate45/defaults.yml b/hammer/technology/nangate45/defaults.yml index 6b2f65146..34a082eab 100644 --- a/hammer/technology/nangate45/defaults.yml +++ b/hammer/technology/nangate45/defaults.yml @@ -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; diff --git a/hammer/technology/nangate45/nangate45.tech.json b/hammer/technology/nangate45/nangate45.tech.json index 43b71e414..fd6d0b9d1 100644 --- a/hammer/technology/nangate45/nangate45.tech.json +++ b/hammer/technology/nangate45/nangate45.tech.json @@ -18,13 +18,21 @@ "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", + "verilog_sim" : "nangate45/Front_End/Verilog/NangateOpenCellLibrary_fixed.v", "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", @@ -41,10 +49,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, @@ -55,7 +64,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, @@ -66,7 +75,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, @@ -76,7 +85,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, @@ -86,7 +95,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, @@ -96,7 +105,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, @@ -105,7 +114,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, @@ -114,7 +123,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": "metal9", "index": 9, "direction": "horizontal", "min_width": 0.8, "max_width": 1073741.8235, "pitch": 1.6, @@ -122,7 +131,7 @@ {"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, @@ -130,7 +139,7 @@ {"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": [ @@ -147,6 +156,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"]} ] } diff --git a/hammer/technology/nangate45/sram-cache.json b/hammer/technology/nangate45/sram-cache.json index 8f6571c68..5cdc67a45 100644 --- a/hammer/technology/nangate45/sram-cache.json +++ b/hammer/technology/nangate45/sram-cache.json @@ -1,53 +1,123 @@ [{ "type" : "sram", - "name" : "fakeram45_1024x32", + "name" : "fakeram45_1RW_1024x32", "depth" : "1024", "width" : 32, "family" : "1RW", "vt" : "SRAM", + "mask" : "true", "mux" : 1, - "ports" : [{ - "address port name" : "addr_in", - "address port polarity" : "active high", - "chip enable port name" : "ce_in", - "chip enable port polarity" : "active high", - "clock port name" : "clk", - "clock port polarity" : "positive edge", - "input port name" : "wd_in", - "input port polarity" : "active high", - "mask granularity" : 1, - "mask port name" : "w_mask_in", - "mask port polarity": "active high", - "output port name" : "rd_out", - "output port polarity" : "active high", - "write enable port name" : "we_in", - "write enable port polarity" : "active high" - }], - "extra ports" : [] + "ports": [ + { + "address port name": "RW0_addr", + "address port polarity": "active high", + "chip enable port name": "RW0_en", + "chip enable port polarity": "active high", + "clock port name": "RW0_clk", + "clock port polarity": "positive edge", + "output port name": "RW0_rdata", + "output port polarity": "active high", + "input port name": "RW0_wdata", + "input port polarity": "active high", + "write enable port name": "RW0_wmode", + "write enable port polarity": "active high", + "mask port name" : "RW0_wmask", + "mask port polarity" : "active high", + "mask granularity" : 32 + } + ] },{ "type" : "sram", - "name" : "fakeram45_64x32", + "name" : "fakeram45_1RW_64x32", "depth" : "64", "width" : 32, "family" : "1RW", "vt" : "SRAM", + "mask" : "true", "mux" : 1, - "ports" : [{ - "address port name" : "addr_in", + "ports": [ + { + "address port name": "RW0_addr", + "address port polarity": "active high", + "chip enable port name": "RW0_en", + "chip enable port polarity": "active high", + "clock port name": "RW0_clk", + "clock port polarity": "positive edge", + "output port name": "RW0_rdata", + "output port polarity": "active high", + "input port name": "RW0_wdata", + "input port polarity": "active high", + "write enable port name": "RW0_wmode", + "write enable port polarity": "active high", + "mask port name" : "RW0_wmask", + "mask port polarity" : "active high", + "mask granularity" : 32 + } + ] +}, + +{ + "type" : "sram", + "name" : "fakeram45_1R1W_64x32", + "depth" : "64", + "width" : 32, + "family" : "1R1W", + "vt" : "SRAM", + "mask" : "false", + "ports": [ { + "address port name" : "R0_addr", + "address port polarity" : "active high", + "read enable port name" : "R0_en", + "read enable port polarity" : "active high", + "clock port name" : "R0_clk", + "clock port polarity" : "positive edge", + "output port name" : "R0_data", + "output port polarity" : "active high" + }, + { + "address port name" : "W0_addr", + "address port polarity" : "active high", + "write enable port name" : "W0_en", + "write enable port polarity" : "active high", + "clock port name" : "W0_clk", + "clock port polarity" : "positive edge", + "input port name" : "W0_data", + "input port polarity" : "active high" + } +] +}, + + +{ + "type" : "sram", + "name" : "fakeram45_2RmW_64x32", + "depth" : "64", + "width" : 32, + "family" : "2RmW", + "vt" : "SRAM", + "mask" : "true", + "ports": [ { + "address port name" : "R0_addr", + "address port polarity" : "active high", + "read enable port name" : "R0_en", + "read enable port polarity" : "active high", + "clock port name" : "R0_clk", + "clock port polarity" : "positive edge", + "output port name" : "R0_data", + "output port polarity" : "active high" + },{ + "address port name" : "W0_addr", "address port polarity" : "active high", - "chip enable port name" : "ce_in", - "chip enable port polarity" : "active high", - "clock port name" : "clk", + "write enable port name" : "W0_en", + "write enable port polarity" : "active high", + "clock port name" : "W0_clk", "clock port polarity" : "positive edge", - "input port name" : "wd_in", + "input port name" : "W0_data", "input port polarity" : "active high", - "mask granularity" : 1, - "mask port name" : "w_mask_in", - "mask port polarity": "active high", - "output port name" : "rd_out", - "output port polarity" : "active high", - "write enable port name" : "we_in", - "write enable port polarity" : "active high" - }], - "extra ports" : [] -}] + "mask port name" : "W0_wmask", + "mask port polarity" : "active high", + "mask granularity" : 32 + } +] +} +] diff --git a/hammer/technology/nangate45/sram_compiler/__init__.py b/hammer/technology/nangate45/sram_compiler/__init__.py index 9eebd803d..8bc12bf28 100644 --- a/hammer/technology/nangate45/sram_compiler/__init__.py +++ b/hammer/technology/nangate45/sram_compiler/__init__.py @@ -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 @@ -30,10 +32,17 @@ 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": + + if params.family == "1RW" or params.family == "1R1W" or params.family =="2RmW" : fam_code = params.family else: self.logger.error( @@ -47,7 +56,10 @@ def generate_sram(self, params: SRAMParameters, sram_name = "fakeram45_{d}x{w}".format( d=params.depth, w=params.width) - + sram_name_v="fakeram45_{fam}_{d}x{w}".format( + d=params.depth, + w=params.width, + fam=params.family) # NOTE: fakemem libs don't define a corner src_lib = "{}/{}.lib".format(base_dir, sram_name) dst_lib ="{}/{}_{}.lib".format(tech_cache_dir, sram_name, corner_str) @@ -61,12 +73,252 @@ 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_v) + with open(verilog_path, 'w') as f: + if params.family == "1R1W" : + specify = "" + for specify_j in range(0, params.width): + for specify_i in range(0, 2): + if specify_i == 0: + specify += "$setuphold(posedge ce_in, %s R0_data[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_j) + specify += "$setuphold(posedge ce_in, %s W0_data[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_j) + else: + specify += "$setuphold(posedge ce_in, %s R0_data[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_j) + specify += "$setuphold(posedge ce_in, %s W0_data[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_j) + specify += "(ce_in => R0_data[%d]) = 0;\n" % (specify_j) + specify += "(ce_in => W0_data[%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_in, %s R0_addr[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_k) + specify += "$setuphold(posedge ce_in, %s W0_addr[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_k) + else: + specify += "$setuphold(posedge ce_in, %s R0_addr[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_k) + specify += "$setuphold(posedge ce_in, %s W0_addr[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_k) + f.write(""" +`timescale 1ns/100fs +module {NAME} ( + input [{NUMADDR}-1:0] R0_addr, + input [{NUMADDR}-1:0] W0_addr, + input R0_clk, + input W0_clk, + output [{WORDLENGTH}-1:0] R0_data, + input [{WORDLENGTH}-1:0] W0_data, + + input R0_en, + input W0_en +); + + reg [{WORDLENGTH}-1:0] ram [0:{NUMWORDS}-1]; + + wire [{NUMADDR}-1:0] ram_R_0_addr; + wire [{WORDLENGTH}-1:0] ram_R_0_data; + wire [{WORDLENGTH}-1:0] ram_W_0_data; + wire [{NUMADDR}-1:0] ram_W_0_addr; + wire ram_W_0_mask; + wire ram_W_0_en; + reg [{NUMADDR}-1:0] ram_R_0_addr_pipe_0; + assign ram_R_0_addr = ram_R_0_addr_pipe_0; + assign ram_R_0_data = ram[ram_R_0_addr]; + assign ram_W_0_data = W0_data; + assign ram_W_0_addr = W0_addr; + assign ram_W_0_mask = 1'h1; + assign ram_W_0_en = W0_en ; + assign R0_data = ram_R_0_data; + + always @(posedge W0_clk) begin + if (ram_W_0_en & ram_W_0_mask) begin + ram[ram_W_0_addr] <= ram_W_0_data; + end + end + always @(posedge R0_clk) begin + if (R0_en ) begin + ram_R_0_addr_pipe_0 <= R0_addr; + end + end + +`ifndef SYNTHESIS +`ifdef RANDOMIZE_REG_INIT + +integer i; +initial begin + for (i = 0; i < {NUMWORDS}; i = i + 1) begin + ram[i] = {{{WORDLENGTH}{{$urandom()}}}}; +end +ram_R_0_addr_pipe_0 = {{{WORDLENGTH}{{$urandom()}}}}; +end // initial +`endif // RANDOMIZE +`endif // SYNTHESIS +endmodule +""".format(NUMADDR=math.ceil(math.log2(params.depth)), NUMWORDS=params.depth, WORDLENGTH=params.width, NAME=sram_name_v, + RAND_WIDTH=math.ceil(params.width / 32), specify=specify)) + elif params.family == "2RmW": + specify = "" + for specify_j in range(0, params.width): + for specify_i in range(0, 2): + if specify_i == 0: + specify += "$setuphold(posedge ce_in, %s R0_data[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_j) + specify += "$setuphold(posedge ce_in, %s W0_data[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_j) + else: + specify += "$setuphold(posedge ce_in, %s R0_data[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_j) + specify += "$setuphold(posedge ce_in, %s W0_data[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_j) + specify += "(ce_in => R0_data[%d]) = 0;\n" % (specify_j) + specify += "(ce_in => W0_data[%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_in, %s R0_addr[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_k) + specify += "$setuphold(posedge ce_in, %s W0_addr[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_k) + else: + specify += "$setuphold(posedge ce_in, %s R0_addr[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_k) + specify += "$setuphold(posedge ce_in, %s W0_addr[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_k) + f.write(""" +`timescale 1ns/100fs +module {NAME} ( + input [{NUMADDR}-1:0] R0_addr, + input [{NUMADDR}-1:0] W0_addr, + input R0_clk, + input W0_clk, + output [{WORDLENGTH}-1:0] R0_data, + input [{WORDLENGTH}-1:0] W0_data, + input W0_wmask, + input R0_en, + input W0_en +); + + reg [{WORDLENGTH}-1:0] ram [0:{NUMWORDS}-1]; + + wire [{NUMADDR}-1:0] ram_R_0_addr; + wire [{WORDLENGTH}-1:0] ram_R_0_data; + wire [{WORDLENGTH}-1:0] ram_W_0_data; + wire [{NUMADDR}-1:0] ram_W_0_addr; + wire ram_W_0_mask; + wire ram_W_0_en; + reg [{NUMADDR}-1:0] ram_R_0_addr_pipe_0; + assign ram_R_0_addr = ram_R_0_addr_pipe_0; + assign ram_R_0_data = ram[ram_R_0_addr]; + assign ram_W_0_data = W0_data; + assign ram_W_0_addr = W0_addr; + assign ram_W_0_en = W0_en ; + assign R0_data = ram_R_0_data; + assign ram_W_0_mask = W0_wmask; + + always @(posedge W0_clk) begin + if (ram_W_0_en & ram_W_0_mask) begin + ram[ram_W_0_addr] <= ram_W_0_data; + end + end + always @(posedge R0_clk) begin + if (R0_en ) begin + ram_R_0_addr_pipe_0 <= R0_addr; + end + end + +`ifndef SYNTHESIS +`ifdef RANDOMIZE_REG_INIT + +integer i; +initial begin + for (i = 0; i < {NUMWORDS}; i = i + 1) begin + ram[i] = {{{WORDLENGTH}{{$urandom()}}}}; +end +ram_R_0_addr_pipe_0 = {{{WORDLENGTH}{{$urandom()}}}}; +end // initial +`endif // RANDOMIZE +`endif // SYNTHESIS +endmodule +""".format(NUMADDR=math.ceil(math.log2(params.depth)), NUMWORDS=params.depth, WORDLENGTH=params.width, NAME=sram_name_v, + RAND_WIDTH=math.ceil(params.width / 32), specify=specify)) + elif 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_in, %s R0_data[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_j) + specify += "$setuphold(posedge ce_in, %s W0_data[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_j) + else: + specify += "$setuphold(posedge ce_in, %s R0_data[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_j) + specify += "$setuphold(posedge ce_in, %s W0_data[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_j) + specify += "(ce_in => R0_data[%d]) = 0;\n" % (specify_j) + specify += "(ce_in => W0_data[%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_in, %s R0_addr[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_k) + specify += "$setuphold(posedge ce_in, %s W0_addr[%d], 0, 0, NOTIFIER);\n" % ("posedge", specify_k) + else: + specify += "$setuphold(posedge ce_in, %s R0_addr[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_k) + specify += "$setuphold(posedge ce_in, %s W0_addr[%d], 0, 0, NOTIFIER);\n" % ("negedge", specify_k) + f.write(""" +`timescale 1ns/100fs +module {NAME} ( + input [{NUMADDR}-1:0] RW0_addr, + input RW0_clk, + input [{WORDLENGTH}-1:0] RW0_wdata, + output [{WORDLENGTH}-1:0] RW0_rdata, + input RW0_en, + input RW0_wmode, + input RW0_wmask +); + + reg [{WORDLENGTH}-1:0] ram [0:{NUMWORDS}-1]; + + wire ram_RW_0_r_en; + wire [{NUMADDR}-1:0] ram_RW_0_r_addr; + wire [{WORDLENGTH}-1:0] ram_RW_0_r_data; + wire [{WORDLENGTH}-1:0] ram_RW_0_w_data; + wire [{NUMADDR}-1:0] ram_RW_0_w_addr; + wire ram_RW_0_w_mask; + wire ram_RW_0_w_en; + reg ram_RW_0_r_en_pipe_0; + reg [{NUMADDR}-1:0] ram_RW_0_r_addr_pipe_0; + + assign ram_RW_0_r_en = ram_RW_0_r_en_pipe_0; + assign ram_RW_0_r_addr = ram_RW_0_r_addr_pipe_0; + assign ram_RW_0_r_data = ram[ram_RW_0_r_addr]; + + assign ram_RW_0_w_data = RW0_wdata; + assign ram_RW_0_w_addr = RW0_addr; + assign ram_RW_0_w_mask = RW0_wmask; + assign ram_RW_0_w_en = RW0_en & RW0_wmode; + + assign RW0_rdata = ram_RW_0_r_data; + + always @(posedge RW0_clk) begin + if (ram_RW_0_w_en & ram_RW_0_w_mask) begin + ram[ram_RW_0_w_addr] <= ram_RW_0_w_data; + end + ram_RW_0_r_en_pipe_0 <= RW0_en & ~RW0_wmode; + if (RW0_en & ~RW0_wmode) begin + ram_RW_0_r_addr_pipe_0 <= RW0_addr; + end + end + +`ifndef SYNTHESIS +`ifdef RANDOMIZE_REG_INIT + +integer i; +initial begin + for (i = 0; i < {NUMWORDS}; i = i + 1) begin + ram[i] = {{{WORDLENGTH}{{$urandom()}}}}; +end +ram_RW_0_r_en_pipe_0 = {{{WORDLENGTH}{{$urandom()}}}}; +ram_RW_0_r_addr_pipe_0 = {{{WORDLENGTH}{{$urandom()}}}}; +end // initial +`endif // RANDOMIZE +`endif // SYNTHESIS +endmodule +""".format(NUMADDR=math.ceil(math.log2(params.depth)), NUMWORDS=params.depth, WORDLENGTH=params.width, NAME=sram_name_v, + RAND_WIDTH=math.ceil(params.width / 32), specify=specify)) + return ExtraLibrary( prefix=None, library=Library( - name=sram_name, + name=sram_name_v, nldm_liberty_file=dst_lib, lef_file=dst_lef, + verilog_sim=verilog_path, corner=Corner( nmos="typical", pmos="typical", diff --git a/hammer/vlsi/constraints.py b/hammer/vlsi/constraints.py index e0c075e9a..3a5255433 100644 --- a/hammer/vlsi/constraints.py +++ b/hammer/vlsi/constraints.py @@ -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) diff --git a/hammer/vlsi/hammer_tool.py b/hammer/vlsi/hammer_tool.py index e9c254deb..b47c97329 100644 --- a/hammer/vlsi/hammer_tool.py +++ b/hammer/vlsi/hammer_tool.py @@ -1078,7 +1078,23 @@ def get_clock_ports(self) -> List[ClockPort]: assert clock.period is not None, f"Non-generated clock {clock.name} must have a period specified." output.append(clock) return output - + + def get_reset_ports(self) -> List[ResetPort]: + """" + Get the reset ports of the top-level module, as specified in vlsi.inputs.resets. + """ + resets = self.get_setting("vlsi.inputs.resets") + output = [] # type: List[ResetPort] + for reset_port in resets: + reset = ResetPort(name=reset_port["name"], active_negated=None, + synchronous=None) + if "active_negated" in reset_port: + reset = reset._replace(active_negated=bool(reset_port["active_negated"])) + if "path" in reset_port: + reset = reset._replace(synchronous=bool(reset_port["synchronous"])) + output.append(reset) + + return output def get_time_unit(self) -> TimeValue: """ Return the library time value.