From a3195c08272ae08480b6b939e1be93e6148fbfc3 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 13 Jul 2020 12:37:56 -0700 Subject: [PATCH 1/6] Add words_per_row and others in config file. --- compiler/openram.py | 5 ++++- compiler/options.py | 3 ++- compiler/sram/sram_config.py | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/openram.py b/compiler/openram.py index 4989feff..f046fdb1 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -54,7 +54,10 @@ from sram_config import sram_config c = sram_config(word_size=OPTS.word_size, num_words=OPTS.num_words, write_size=OPTS.write_size, - num_spare_rows=OPTS.num_spare_rows) + num_banks=OPTS.num_banks, + words_per_row=OPTS.words_per_row, + num_spare_rows=OPTS.num_spare_rows, + num_spare_cols=OPTS.num_spare_cols) debug.print_raw("Words per row: {}".format(c.words_per_row)) output_extensions = ["lvs", "sp", "v", "lib", "py", "html", "log"] diff --git a/compiler/options.py b/compiler/options.py index d229b96e..d97ea300 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -44,8 +44,9 @@ class options(optparse.Values): # word_size = 0 # You can manually specify banks, but it is better to auto-detect it. num_banks = 1 + words_per_row = None num_spare_rows = 0 - # num_spare_cols = 0 + num_spare_cols = 0 ################### # Optimization options diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index 3af2fd9c..fa70d730 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -14,7 +14,7 @@ from sram_factory import factory class sram_config: """ This is a structure that is used to hold the SRAM configuration options. """ - def __init__(self, word_size, num_words, write_size = None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=None): + def __init__(self, word_size, num_words, write_size = None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=0): self.word_size = word_size self.num_words = num_words self.write_size = write_size From 2011974e010908f4dd8f7162993577ca5aeb2dd1 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 13 Jul 2020 12:49:24 -0700 Subject: [PATCH 2/6] Make drc and lvs errors a member variable. Run only once. --- compiler/base/hierarchy_design.py | 18 +++++++++------- compiler/characterizer/lib.py | 6 +----- .../example_config_1rw_2mux_scn4m_subm.py | 21 +++++++++++++++++++ compiler/sram/sram_base.py | 2 +- 4 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 compiler/example_configs/example_config_1rw_2mux_scn4m_subm.py diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 001093dd..5ae2f26e 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -72,26 +72,28 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): self.lvs_write(tempspice) self.gds_write(tempgds) # Final verification option does not allow nets to be connected by label. - num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) - num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) + self.drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) + self.lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) # force_check is used to determine decoder height and other things, so we shouldn't fail # if that flag is set if OPTS.inline_lvsdrc and not force_check: - debug.check(num_drc_errors == 0, + debug.check(self.drc_errors == 0, "DRC failed for {0} with {1} error(s)".format(self.name, - num_drc_errors)) - debug.check(num_lvs_errors == 0, + self.drc_errors)) + debug.check(self.lvs_errors == 0, "LVS failed for {0} with {1} errors(s)".format(self.name, - num_lvs_errors)) + self.lvs_errors)) if OPTS.purge_temp: os.remove(tempspice) os.remove(tempgds) - return (num_drc_errors, num_lvs_errors) else: - return ("skipped", "skipped") + self.drc_errors = "skipped" + self.lvs_errors = "skipped" + + return (self.drc_errors, self.lvs_errors) def DRC(self, final_verification=False): """Checks DRC for a module""" diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 92882db7..525f2180 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -658,11 +658,7 @@ class lib: # information of checks # run it only the first time - try: - datasheet.write("{0},{1},".format(self.drc_errors, self.lvs_errors)) - except AttributeError: - (self.drc_errors, self.lvs_errors) = self.sram.DRC_LVS(final_verification=True) - datasheet.write("{0},{1},".format(self.drc_errors, self.lvs_errors)) + datasheet.write("{0},{1},".format(self.sram.drc_errors, self.sram.lvs_errors)) # write area datasheet.write(str(self.sram.width * self.sram.height) + ',') diff --git a/compiler/example_configs/example_config_1rw_2mux_scn4m_subm.py b/compiler/example_configs/example_config_1rw_2mux_scn4m_subm.py new file mode 100644 index 00000000..a09edfdb --- /dev/null +++ b/compiler/example_configs/example_config_1rw_2mux_scn4m_subm.py @@ -0,0 +1,21 @@ +word_size = 4 +num_words = 64 +words_per_row = 2 + +num_rw_ports = 1 +num_r_ports = 0 +num_w_ports = 0 + +tech_name = "scn4m_subm" +nominal_corners_only = False +process_corners = ["TT"] +supply_voltages = [5.0] +temperatures = [25] + +# route_supplies = True +check_lvsdrc = True + +output_path = "temp" +output_name = "sram_1rw_{0}_{1}_{2}".format(word_size, + num_words, + tech_name) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 89ecfaaf..b16c28fa 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -129,7 +129,7 @@ class sram_base(design, verilog, lef): start_time = datetime.datetime.now() # We only enable final verification if we have routed the design - self.DRC_LVS(final_verification=OPTS.route_supplies) + self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=True) if not OPTS.is_unit_test: print_time("Verification", datetime.datetime.now(), start_time) From 716798baae3a7f1e5e912de9d9d5a33223d07fd0 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 13 Jul 2020 13:01:00 -0700 Subject: [PATCH 3/6] Convert all DRC and LVS routines to set member variables for drc_errors and lvs_errors. --- compiler/base/hierarchy_design.py | 35 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 5ae2f26e..38e30981 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -58,14 +58,17 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # No layout to check if OPTS.netlist_only: - return ("skipped", "skipped") + self.drc_errors = "skipped" + self.lvs_errors = "skipped" # Unit tests will check themselves. - if not force_check and OPTS.is_unit_test: - return ("skipped", "skipped") - if not force_check and not OPTS.check_lvsdrc: - return ("skipped", "skipped") + elif not force_check and OPTS.is_unit_test: + self.drc_errors = "skipped" + self.lvs_errors = "skipped" + elif not force_check and not OPTS.check_lvsdrc: + self.drc_errors = "skipped" + self.lvs_errors = "skipped" # Do not run if disabled in options. - if (OPTS.inline_lvsdrc or force_check or final_verification): + elif (OPTS.inline_lvsdrc or force_check or final_verification): tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) @@ -104,9 +107,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # No layout to check if OPTS.netlist_only: - return "skipped" - - if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): + self.drc_errors = "skipped" + elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) self.gds_write(tempgds) num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification) @@ -117,9 +119,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): if OPTS.purge_temp: os.remove(tempgds) - return num_errors else: - return "skipped" + self.drc_errors = "skipped" + + return self.drc_errors def LVS(self, final_verification=False): """Checks LVS for a module""" @@ -130,9 +133,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # No layout to check if OPTS.netlist_only: - return "skipped" - - if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): + self.lvs_errors = "skipped" + elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) self.lvs_write(tempspice) @@ -145,9 +147,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): os.remove(tempspice) os.remove(tempgds) - return num_errors else: - return "skipped" + self.lvs_errors = "skipped" + + return self.lvs_errors def init_graph_params(self): """Initializes parameters relevant to the graph creation""" From e49236f8fcf4fb2571722ca9820631af93c1c101 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 13 Jul 2020 14:08:00 -0700 Subject: [PATCH 4/6] Default drc and lvs errors is skipped. --- compiler/base/hierarchy_design.py | 32 ++++++++----------------------- compiler/pgates/ptx.py | 14 ++++++-------- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 38e30981..79b5a53a 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -36,6 +36,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): else: self.lvs_file = self.sp_file + self.drc_errors = "skipped" + self.lvs_errors = "skipped" + self.name = name hierarchy_spice.spice.__init__(self, name) hierarchy_layout.layout.__init__(self, name) @@ -58,15 +61,12 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # No layout to check if OPTS.netlist_only: - self.drc_errors = "skipped" - self.lvs_errors = "skipped" + return # Unit tests will check themselves. elif not force_check and OPTS.is_unit_test: - self.drc_errors = "skipped" - self.lvs_errors = "skipped" + return elif not force_check and not OPTS.check_lvsdrc: - self.drc_errors = "skipped" - self.lvs_errors = "skipped" + return # Do not run if disabled in options. elif (OPTS.inline_lvsdrc or force_check or final_verification): @@ -92,12 +92,6 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): os.remove(tempspice) os.remove(tempgds) - else: - self.drc_errors = "skipped" - self.lvs_errors = "skipped" - - return (self.drc_errors, self.lvs_errors) - def DRC(self, final_verification=False): """Checks DRC for a module""" import verify @@ -107,7 +101,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # No layout to check if OPTS.netlist_only: - self.drc_errors = "skipped" + return elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) self.gds_write(tempgds) @@ -119,11 +113,6 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): if OPTS.purge_temp: os.remove(tempgds) - else: - self.drc_errors = "skipped" - - return self.drc_errors - def LVS(self, final_verification=False): """Checks LVS for a module""" import verify @@ -133,7 +122,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # No layout to check if OPTS.netlist_only: - self.lvs_errors = "skipped" + return elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) @@ -146,11 +135,6 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): if OPTS.purge_temp: os.remove(tempspice) os.remove(tempgds) - - else: - self.lvs_errors = "skipped" - - return self.lvs_errors def init_graph_params(self): """Initializes parameters relevant to the graph creation""" diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 8be003b2..ad741870 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -12,7 +12,6 @@ from vector import vector from sram_factory import factory import contact import logical_effort -import os from globals import OPTS from pgate import pgate @@ -120,11 +119,10 @@ class ptx(design.design): def create_netlist(self): pin_list = ["D", "G", "S", "B"] if self.tx_type == "nmos": - body_dir = 'GROUND' + body_dir = "GROUND" else: - # Assumed that the check for either pmos or nmos is done elsewhere. - body_dir = 'POWER' - dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir] + body_dir = "POWER" + dir_list = ["INOUT", "INPUT", "INOUT", body_dir] self.add_pin_list(pin_list, dir_list) # Just make a guess since these will actually @@ -135,9 +133,9 @@ class ptx(design.design): # sky130 simulation cannot use the mult parameter in simulation (self.tx_width, self.mults) = pgate.bin_width(self.tx_type, self.tx_width) main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc("minwidth_poly")) + self.mults, + self.tx_width, + drc("minwidth_poly")) # Perimeters are in microns # Area is in u since it is microns square area_str = "pd={0:.2f} ps={0:.2f} as={1:.2f}u ad={1:.2f}u".format(perimeter_sd, From 2b7d89d2c155a2ffd7918c93309bd38dcf50912b Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 13 Jul 2020 14:59:31 -0700 Subject: [PATCH 5/6] Fix netlist_only in sky130 --- compiler/pgates/pinv_dec.py | 4 ---- compiler/pgates/ptx.py | 4 ++-- compiler/pgates/single_level_column_mux.py | 26 ++++++++++++++-------- compiler/verify/__init__.py | 6 +++-- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py index 3ce6ad80..4d4f2229 100644 --- a/compiler/pgates/pinv_dec.py +++ b/compiler/pgates/pinv_dec.py @@ -13,10 +13,6 @@ from vector import vector from globals import OPTS from sram_factory import factory -if(OPTS.tech_name == "sky130"): - from tech import nmos_bins, pmos_bins - - class pinv_dec(pinv.pinv): """ This is another version of pinv but with layout for the decoder. diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index ad741870..ce8591fd 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -129,7 +129,7 @@ class ptx(design.design): # be decided in the layout later. area_sd = 2.5 * self.poly_width * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width - if OPTS.tech_name == "sky130" and OPTS.lvs_exe[0] == "calibre": + if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre": # sky130 simulation cannot use the mult parameter in simulation (self.tx_width, self.mults) = pgate.bin_width(self.tx_type, self.tx_width) main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], @@ -150,7 +150,7 @@ class ptx(design.design): self.spice_device = main_str + area_str self.spice.append("\n* ptx " + self.spice_device) - if OPTS.tech_name == "sky130" and OPTS.lvs_exe[0] == "calibre": + if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre": # sky130 requires mult parameter too self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type], self.mults, diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index b594f2e4..ca7c3e51 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -56,7 +56,8 @@ class single_level_column_mux(pgate.pgate): self.pin_height = 2 * self.pin_width self.width = self.bitcell.width self.height = self.nmos_upper.uy() + self.pin_height - + + self.place_ptx() self.connect_poly() self.add_bitline_pins() self.connect_bitlines() @@ -104,20 +105,27 @@ class single_level_column_mux(pgate.pgate): """ Create the two pass gate NMOS transistors to switch the bitlines""" # Space it in the center - nmos_lower_position = self.nmos.active_offset.scale(0, 1) \ - + vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0) self.nmos_lower = self.add_inst(name="mux_tx1", - mod=self.nmos, - offset=nmos_lower_position) + mod=self.nmos) self.connect_inst(["bl", "sel", "bl_out", "gnd"]) # This aligns it directly above the other tx with gates abutting - nmos_upper_position = nmos_lower_position \ - + vector(0, self.nmos.active_height + max(self.active_space, self.poly_space)) self.nmos_upper = self.add_inst(name="mux_tx2", - mod=self.nmos, - offset=nmos_upper_position) + mod=self.nmos) self.connect_inst(["br", "sel", "br_out", "gnd"]) + + def place_ptx(self): + """ Create the two pass gate NMOS transistors to switch the bitlines""" + + # Space it in the center + nmos_lower_position = self.nmos.active_offset.scale(0, 1) \ + + vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0) + self.nmos_lower.place(nmos_lower_position) + + # This aligns it directly above the other tx with gates abutting + nmos_upper_position = nmos_lower_position \ + + vector(0, self.nmos.active_height + max(self.active_space, self.poly_space)) + self.nmos_upper.place(nmos_upper_position) if OPTS.tech_name == "sky130": self.add_implants() diff --git a/compiler/verify/__init__.py b/compiler/verify/__init__.py index a28581d8..4d9eb151 100644 --- a/compiler/verify/__init__.py +++ b/compiler/verify/__init__.py @@ -30,13 +30,15 @@ if not OPTS.check_lvsdrc: OPTS.drc_exe = None OPTS.lvs_exe = None OPTS.pex_exe = None + if OPTS.tech_name == "sky130": + OPTS.magic_exe = None else: debug.info(1, "Finding DRC/LVS/PEX tools.") OPTS.drc_exe = get_tool("DRC", ["calibre", "assura", "magic"], drc_name) OPTS.lvs_exe = get_tool("LVS", ["calibre", "assura", "netgen"], lvs_name) OPTS.pex_exe = get_tool("PEX", ["calibre", "magic"], pex_name) if OPTS.tech_name == "sky130": - OPTS.magic_exe = get_tool("GDS", ["magic"], None) + OPTS.magic_exe = get_tool("GDS", ["magic"]) if not OPTS.drc_exe: from .none import run_drc, print_drc_stats @@ -71,7 +73,7 @@ else: debug.warning("Did not find a supported PEX tool.") if OPTS.tech_name == "sky130": - if "magic"==OPTS.magic_exe[0]: + if OPTS.magic_exe and "magic"==OPTS.magic_exe[0]: from .magic import filter_gds else: debug.warning("Did not find Magic.") From e502ee02be2a2fa2cf9b524fa73e1bb3f1062519 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 13 Jul 2020 15:51:46 -0700 Subject: [PATCH 6/6] Place before computing height of col mux. --- compiler/pgates/single_level_column_mux.py | 30 ++++++++++------------ 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index ca7c3e51..d2dbacc1 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -39,7 +39,6 @@ class single_level_column_mux(pgate.pgate): return "br" def create_netlist(self): - self.add_modules() self.add_pins() self.add_ptx() @@ -54,16 +53,18 @@ class single_level_column_mux(pgate.pgate): self.pin_pitch = getattr(self, "{}_pitch".format(self.pin_layer)) self.pin_width = getattr(self, "{}_width".format(self.pin_layer)) self.pin_height = 2 * self.pin_width + + self.place_ptx() + self.width = self.bitcell.width self.height = self.nmos_upper.uy() + self.pin_height - self.place_ptx() self.connect_poly() self.add_bitline_pins() self.connect_bitlines() self.add_pn_wells() - def add_modules(self): + def add_ptx(self): self.bitcell = factory.create(module_type="bitcell") # Adds nmos_lower,nmos_upper to the module @@ -72,6 +73,16 @@ class single_level_column_mux(pgate.pgate): width=self.ptx_width) self.add_mod(self.nmos) + # Space it in the center + self.nmos_lower = self.add_inst(name="mux_tx1", + mod=self.nmos) + self.connect_inst(["bl", "sel", "bl_out", "gnd"]) + + # This aligns it directly above the other tx with gates abutting + self.nmos_upper = self.add_inst(name="mux_tx2", + mod=self.nmos) + self.connect_inst(["br", "sel", "br_out", "gnd"]) + def add_pins(self): self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"]) @@ -100,19 +111,6 @@ class single_level_column_mux(pgate.pgate): layer=self.pin_layer, offset=br_pos, height=self.pin_height) - - def add_ptx(self): - """ Create the two pass gate NMOS transistors to switch the bitlines""" - - # Space it in the center - self.nmos_lower = self.add_inst(name="mux_tx1", - mod=self.nmos) - self.connect_inst(["bl", "sel", "bl_out", "gnd"]) - - # This aligns it directly above the other tx with gates abutting - self.nmos_upper = self.add_inst(name="mux_tx2", - mod=self.nmos) - self.connect_inst(["br", "sel", "br_out", "gnd"]) def place_ptx(self): """ Create the two pass gate NMOS transistors to switch the bitlines"""