diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index d316e713..2e9bc2bb 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -57,6 +57,9 @@ class geometry: def compute_boundary(self,offset=vector(0,0),mirror="",rotate=0): """ Transform with offset, mirror and rotation to get the absolute pin location. We must then re-find the ll and ur. The master is the cell instance. """ + if OPTS.netlist_only: + return + (ll,ur) = [vector(0,0),vector(self.width,self.height)] if mirror=="MX": ll=ll.scale(1,-1) @@ -192,6 +195,14 @@ class instance(geometry): mirror=self.mirror, rotate=self.rotate) + def place(self, offset, mirror="R0", rotate=0): + """ This updates the placement of an instance. """ + debug.info(3, "placing instance {}".format(self.name)) + # Update the placement of an already added instance + self.offset = vector(offset) + self.mirror = mirror + self.rotate = rotate + self.update_boundary() def get_pin(self,name,index=-1): diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 3aa21794..780baaf9 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -5,6 +5,7 @@ import debug from tech import drc, GDS from tech import layer as techlayer import os +from globals import OPTS from vector import vector from pin_layout import pin_layout import lef @@ -118,17 +119,6 @@ class layout(lef.lef): for pin in pin_list: pin.rect = [pin.ll() - offset, pin.ur() - offset] - def place_inst(self, name, offset, mirror="R0", rotate=0): - """ This updates the placement of an instance. """ - inst = self.get_inst(name) - debug.info(3, "placing instance {}".format(inst)) - # Update the placement of an already added instance - inst.offset = offset - inst.mirror = mirror - inst.rotate = rotate - inst.update_boundary() - return inst - def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0): """Adds an instance of a mod to this module""" self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) @@ -436,6 +426,10 @@ class layout(lef.lef): def gds_read(self): """Reads a GDSII file in the library and checks if it exists Otherwise, start a new layout for dynamic generation.""" + if OPTS.netlist_only: + self.gds = None + return + # open the gds file if it exists or else create a blank layout if os.path.isfile(self.gds_file): debug.info(3, "opening %s" % self.gds_file) diff --git a/compiler/example_config_freepdk45.py b/compiler/example_config_freepdk45.py index b114f4e6..32655f09 100644 --- a/compiler/example_config_freepdk45.py +++ b/compiler/example_config_freepdk45.py @@ -8,7 +8,7 @@ supply_voltages = [1.0] temperatures = [25] output_path = "temp" -output_name = "sram_2_16_1_freepdk45" +output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,num_words,num_banks,tech_name) #Below are some additions to test additional ports on sram #bitcell = "pbitcell" diff --git a/compiler/example_config_scn3me_subm.py b/compiler/example_config_scn3me_subm.py index 491fa4d2..356b4542 100644 --- a/compiler/example_config_scn3me_subm.py +++ b/compiler/example_config_scn3me_subm.py @@ -7,7 +7,6 @@ process_corners = ["TT"] supply_voltages = [ 5.0 ] temperatures = [ 25 ] - output_path = "temp" -output_name = "sram_2_16_1_scn3me_subm" +output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,num_words,num_banks,tech_name) diff --git a/compiler/globals.py b/compiler/globals.py index 02168e37..52b33450 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -142,22 +142,31 @@ def init_openram(config_file, is_unit_test=True): -def get_tool(tool_type, preferences): +def get_tool(tool_type, preferences, default_name=None): """ Find which tool we have from a list of preferences and return the - one selected and its full path. + one selected and its full path. If default is specified, + find that one only and error otherwise. """ debug.info(2,"Finding {} tool...".format(tool_type)) - for name in preferences: - exe_name = find_exe(name) - if exe_name != None: - debug.info(1, "Using {0}: {1}".format(tool_type,exe_name)) - return(name,exe_name) + if default_name: + exe_name=find_exe(default_name) + if exe_name == None: + debug.error("{0} not found. Cannot find {1} tool.".format(default_name,tool_type),2) else: - debug.info(1, "Could not find {0}, trying next {1} tool.".format(name,tool_type)) + debug.info(1, "Using {0}: {1}".format(tool_type,exe_name)) + return(default_name,exe_name) else: - return(None,"") + for name in preferences: + exe_name = find_exe(name) + if exe_name != None: + debug.info(1, "Using {0}: {1}".format(tool_type,exe_name)) + return(name,exe_name) + else: + debug.info(1, "Could not find {0}, trying next {1} tool.".format(name,tool_type)) + else: + return(None,"") def read_config(config_file, is_unit_test=True): @@ -207,7 +216,6 @@ def read_config(config_file, is_unit_test=True): # If we are only generating a netlist, we can't do DRC/LVS if OPTS.netlist_only: OPTS.check_lvsdrc=False - # If config didn't set output name, make a reasonable default. if (OPTS.output_name == ""): diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index cadb0b7e..9e711d22 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -22,17 +22,6 @@ class bank(design.design): def __init__(self, word_size, num_words, words_per_row, num_banks=1, name=""): - mod_list = ["bitcell", "decoder", "ms_flop_array", "wordline_driver", - "bitcell_array", "sense_amp_array", "precharge_array", - "column_mux_array", "write_driver_array", - "dff", "bank_select"] - from importlib import reload - for mod_name in mod_list: - config_mod_name = getattr(OPTS, mod_name) - class_file = reload(__import__(config_mod_name)) - mod_class = getattr(class_file , config_mod_name) - setattr (self, "mod_"+mod_name, mod_class) - if name == "": name = "bank_{0}_{1}".format(word_size, num_words) design.design.__init__(self, name) @@ -56,7 +45,8 @@ class bank(design.design): self.prefix="" self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): @@ -205,6 +195,19 @@ class bank(design.design): def add_modules(self): """ Create all the modules using the class loader """ + + mod_list = ["bitcell", "decoder", "ms_flop_array", "wordline_driver", + "bitcell_array", "sense_amp_array", "precharge_array", + "column_mux_array", "write_driver_array", + "dff", "bank_select"] + from importlib import reload + for mod_name in mod_list: + config_mod_name = getattr(OPTS, mod_name) + class_file = reload(__import__(config_mod_name)) + mod_class = getattr(class_file , config_mod_name) + setattr (self, "mod_"+mod_name, mod_class) + + self.bitcell = self.mod_bitcell() self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols, @@ -277,8 +280,7 @@ class bank(design.design): def place_bitcell_array(self): """ Placing Bitcell Array """ - self.place_inst(name="bitcell_array", - offset=vector(0,0)) + self.bitcell_array_inst.place(vector(0,0)) def create_precharge_array(self): @@ -303,8 +305,7 @@ class bank(design.design): # The wells must be far enough apart # The enclosure is for the well and the spacing is to the bitcell wells y_offset = self.bitcell_array.height + self.m2_gap - self.place_inst(name=self.precharge_array_inst[k].name, - offset=vector(0,y_offset)) + self.precharge_array_inst[k].place(vector(0,y_offset)) def create_column_mux_array(self): """ Creating Column Mux when words_per_row > 1 . """ @@ -338,8 +339,7 @@ class bank(design.design): for k in range(self.total_ports): y_offset = self.column_mux_height - self.place_inst(name=self.col_mux_array_inst[k].name, - offset=vector(0,y_offset).scale(-1,-1)) + self.col_mux_array_inst[k].place(vector(0,y_offset).scale(-1,-1)) def create_sense_amp_array(self): """ Creating Sense amp """ @@ -368,8 +368,7 @@ class bank(design.design): # FIXME: place for multiport for k in range(self.total_read): y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap - self.place_inst(name=self.sense_amp_array_inst[k].name, - offset=vector(0,y_offset).scale(-1,-1)) + self.sense_amp_array_inst[k].place(vector(0,y_offset).scale(-1,-1)) def create_write_driver_array(self): """ Creating Write Driver """ @@ -399,8 +398,7 @@ class bank(design.design): for k in range(self.total_write): y_offset = self.sense_amp_array.height + self.column_mux_height \ + self.m2_gap + self.write_driver_array.height - self.place_inst(name=self.write_driver_array_inst[k].name, - offset=vector(0,y_offset).scale(-1,-1)) + self.write_driver_array_inst[k].place(vector(0,y_offset).scale(-1,-1)) @@ -432,8 +430,7 @@ class bank(design.design): # FIXME: place for multiport for k in range(self.total_ports): x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) - self.place_inst(name=self.row_decoder_inst[k].name, - offset=vector(x_offset,0)) + self.row_decoder_inst[k].place(vector(x_offset,0)) def create_wordline_driver(self): @@ -461,8 +458,7 @@ class bank(design.design): for k in range(self.total_ports): # The wordline driver is placed to the right of the main decoder width. x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch - self.place_inst(name=self.wordline_driver_inst[k].name, - offset=vector(x_offset,0)) + self.wordline_driver_inst[k].place(vector(x_offset,0)) def create_column_decoder(self): @@ -508,8 +504,7 @@ class bank(design.design): # Place the col decoder right aligned with row decoder x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) - self.place_inst(name=self.col_decoder_inst[k].name, - offset=vector(x_off,y_off)) + self.col_decoder_inst[k].place(vector(x_off,y_off)) def create_bank_select(self): @@ -545,8 +540,7 @@ class bank(design.design): y_off = self.row_decoder_inst[0].by() y_off -= (self.bank_select.height + drc["well_to_well"]) self.bank_select_pos = vector(x_off,y_off) - self.place_inst(name=self.bank_select_inst[k].name, - offset=self.bank_select_pos) + self.bank_select_inst[k].place(self.bank_select_pos) def route_vdd_gnd(self): @@ -580,23 +574,25 @@ class bank(design.design): def route_bank_select(self): """ Route the bank select logic. """ - for input_name in self.input_control_signals+["bank_sel"]: - self.copy_layout_pin(self.bank_select_inst, input_name) + + for k in range(self.total_ports): + for input_name in self.input_control_signals+["bank_sel"]: + self.copy_layout_pin(self.bank_select_inst[k], input_name) - for gated_name in self.control_signals: - # Connect the inverter output to the central bus - out_pos = self.bank_select_inst[0].get_pin(gated_name).rc() - bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y) - self.add_path("metal3",[out_pos, bus_pos]) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=bus_pos, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=out_pos, - rotate=90) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=out_pos, - rotate=90) + for gated_name in self.control_signals: + # Connect the inverter output to the central bus + out_pos = self.bank_select_inst[k].get_pin(gated_name).rc() + bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y) + self.add_path("metal3",[out_pos, bus_pos]) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=bus_pos, + rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=out_pos, + rotate=90) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=out_pos, + rotate=90) def setup_routing_constraints(self): diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 511e2e7c..337deb48 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -18,9 +18,29 @@ class bank_select(design.design): def __init__(self, name="bank_select"): design.design.__init__(self, name) + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_modules() + + def create_layout(self): + self.calculate_module_offsets() + self.place_modules() + self.route_modules() + + self.DRC_LVS() + + + def add_pins(self): + # Number of control lines in the bus self.num_control_lines = 4 # The order of the control signals on the control bus: + # FIXME: Update for multiport (these names are not right) self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"] # These will be outputs of the gaters if this is multibank self.control_signals = ["gated_"+str for str in self.input_control_signals] @@ -31,14 +51,7 @@ class bank_select(design.design): self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") - self.create_modules() - self.calculate_module_offsets() - self.add_modules() - self.route_modules() - - self.DRC_LVS() - - def create_modules(self): + def add_modules(self): """ Create modules for later instantiation """ # 1x Inverter self.inv = pinv() @@ -67,15 +80,10 @@ class bank_select(design.design): self.height = self.yoffset_maxpoint + 2*self.m1_pitch self.width = self.xoffset_inv + self.inv4x.width - def add_modules(self): + def create_modules(self): - # bank select inverter - self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0) - - # bank select inverter (must be made unique if more than one OR) self.bank_sel_inv=self.add_inst(name="bank_sel_inv", - mod=self.inv, - offset=[self.xoffset_bank_sel_inv, 0]) + mod=self.inv) self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"]) self.logic_inst = [] @@ -87,6 +95,51 @@ class bank_select(design.design): name_nor = "nor_{}".format(input_name) name_inv = "inv_{}".format(input_name) + # These require OR (nor2+inv) gates since they are active low. + # (writes occur on clk low) + if input_name in ("clk_buf"): + + self.logic_inst.append(self.add_inst(name=name_nor, + mod=self.nor2)) + self.connect_inst([input_name, + "bank_sel_bar", + gated_name+"_temp_bar", + "vdd", + "gnd"]) + + # the rest are AND (nand2+inv) gates + else: + self.logic_inst.append(self.add_inst(name=name_nand, + mod=self.nand2)) + self.connect_inst([input_name, + "bank_sel", + gated_name+"_temp_bar", + "vdd", + "gnd"]) + + # They all get inverters on the output + self.inv_inst.append(self.add_inst(name=name_inv, + mod=self.inv4x)) + self.connect_inst([gated_name+"_temp_bar", + gated_name, + "vdd", + "gnd"]) + + def place_modules(self): + + # bank select inverter + self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0) + + # bank select inverter (must be made unique if more than one OR) + self.bank_sel_inv.place(vector(self.xoffset_bank_sel_inv, 0)) + + for i in range(self.num_control_lines): + + logic_inst = self.logic_inst[i] + inv_inst = self.inv_inst[i] + + input_name = self.input_control_signals[i] + y_offset = self.inv.height * i if i%2: y_offset += self.inv.height @@ -98,40 +151,18 @@ class bank_select(design.design): # (writes occur on clk low) if input_name in ("clk_buf"): - self.logic_inst.append(self.add_inst(name=name_nor, - mod=self.nor2, - offset=[self.xoffset_nor, y_offset], - mirror=mirror)) - self.connect_inst([input_name, - "bank_sel_bar", - gated_name+"_temp_bar", - "vdd", - "gnd"]) - + logic_inst.place(offset=[self.xoffset_nor, y_offset], + mirror=mirror) # the rest are AND (nand2+inv) gates else: - self.logic_inst.append(self.add_inst(name=name_nand, - mod=self.nand2, - offset=[self.xoffset_nand, y_offset], - mirror=mirror)) - bank_sel_signal = "bank_sel" - self.connect_inst([input_name, - "bank_sel", - gated_name+"_temp_bar", - "vdd", - "gnd"]) + logic_inst.place(offset=[self.xoffset_nand, y_offset], + mirror=mirror) # They all get inverters on the output - self.inv_inst.append(self.add_inst(name=name_inv, - mod=self.inv4x, - offset=[self.xoffset_inv, y_offset], - mirror=mirror)) - self.connect_inst([gated_name+"_temp_bar", - gated_name, - "vdd", - "gnd"]) - + inv_inst.place(offset=[self.xoffset_inv, y_offset], + mirror=mirror) + def route_modules(self): diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 4d3e0b20..0eb1fbf3 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -21,38 +21,27 @@ class bitcell_array(design.design): self.column_size = cols self.row_size = rows - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.cell = self.mod_bitcell() - self.add_mod(self.cell) - - # We increase it by a well enclosure so the precharges don't overlap our wells - self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width - self.width = self.column_size*self.cell.width + self.m1_width - self.create_netlist() - - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() # We don't offset this because we need to align # the replica bitcell in the control logic #self.offset_all_coordinates() - - def add_pins(self): - row_list = self.cell.list_all_wl_names() - column_list = self.cell.list_all_bitline_names() - for col in range(self.column_size): - for cell_column in column_list: - self.add_pin(cell_column+"[{0}]".format(col)) - for row in range(self.row_size): - for cell_row in row_list: - self.add_pin(cell_row+"[{0}]".format(row)) - self.add_pin("vdd") - self.add_pin("gnd") + + def create_netlist(self): + """ Create and connect the netlist """ + self.add_modules() + self.add_pins() + self.create_modules() def create_layout(self): + + # We increase it by a well enclosure so the precharges don't overlap our wells + self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width + self.width = self.column_size*self.cell.width + self.m1_width + xoffset = 0.0 for col in range(self.column_size): yoffset = 0.0 @@ -66,20 +55,38 @@ class bitcell_array(design.design): tempy = yoffset dir_key = "" - self.place_inst(name=name, - offset=[xoffset, tempy], - mirror=dir_key) + self.cell_inst[row,col].place(offset=[xoffset, tempy], + mirror=dir_key) yoffset += self.cell.height xoffset += self.cell.width self.add_layout_pins() self.DRC_LVS() - - def create_netlist(self): - """ Create and connect the netlist """ - self.add_pins() - + + def add_pins(self): + row_list = self.cell.list_all_wl_names() + column_list = self.cell.list_all_bitline_names() + for col in range(self.column_size): + for cell_column in column_list: + self.add_pin(cell_column+"[{0}]".format(col)) + for row in range(self.row_size): + for cell_row in row_list: + self.add_pin(cell_row+"[{0}]".format(row)) + self.add_pin("vdd") + self.add_pin("gnd") + + def add_modules(self): + """ Add the modules used in this design """ + + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.cell = self.mod_bitcell() + self.add_mod(self.cell) + + def create_modules(self): + """ Create the module instances used in this design """ self.cell_inst = {} for col in range(self.column_size): for row in range(self.row_size): @@ -87,8 +94,7 @@ class bitcell_array(design.design): self.cell_inst[row,col]=self.add_inst(name=name, mod=self.cell) self.connect_inst(self.cell.list_bitcell_pins(col, row)) - - + def add_layout_pins(self): """ Add the layout pins """ diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 26e5e717..02504313 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -24,17 +24,26 @@ class control_logic(design.design): debug.info(1, "Creating {}".format(self.name)) self.num_rows = num_rows - self.create_layout() - self.DRC_LVS() + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + def create_netlist(self): + self.setup_signal_busses() + self.add_pins() + self.add_modules() + self.create_modules() + def create_layout(self): """ Create layout and route between modules """ - self.setup_layout_offsets() - self.add_pins() - self.create_modules() - self.add_rails() - self.add_modules() - self.add_routing() + self.route_rails() + self.place_modules() + self.route_all() + + self.add_lvs_correspondence_points() + + self.DRC_LVS() def add_pins(self): @@ -46,8 +55,8 @@ class control_logic(design.design): self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") - def create_modules(self): - """ add all the required modules """ + def add_modules(self): + """ Add all the required modules """ dff = dff_inv() dff_height = dff.height @@ -83,14 +92,8 @@ class control_logic(design.design): self.add_mod(self.replica_bitline) - def setup_layout_offsets(self): - """ Setup layout offsets, determine the size of the busses etc """ - # These aren't for instantiating, but we use them to get the dimensions - #self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height) - - # Have the cell gap leave enough room to route an M2 wire. - # Some cells may have pwell/nwell spacing problems too when the wells are different heights. - #self.cell_gap = max(self.m2_pitch,drc["pwell_to_nwell"]) + def setup_signal_busses(self): + """ Setup bus names, determine the size of the busses etc """ # List of input control signals self.input_list =["csb","web"] @@ -104,7 +107,7 @@ class control_logic(design.design): self.supply_list = ["vdd", "gnd"] - def add_rails(self): + def route_rails(self): """ Add the input signal inverted tracks """ height = 4*self.inv1.height - self.m2_pitch offset = vector(self.ctrl_dff_array.width,0) @@ -112,7 +115,20 @@ class control_logic(design.design): self.rail_offsets = self.create_vertical_bus("metal2", self.m2_pitch, offset, self.internal_bus_list, height) - def add_modules(self): + def create_modules(self): + """ Create all the modules """ + self.create_dffs() + self.create_clk_row() + self.create_we_row() + # self.create_trien_row() + # self.create_trien_bar_row() + self.create_rbl_in_row() + self.create_sen_row() + self.create_rbl() + + + + def place_modules(self): """ Place all the modules """ # Keep track of all right-most instances to determine row boundary # and add the vdd/gnd pins @@ -120,20 +136,18 @@ class control_logic(design.design): # Add the control flops on the left of the bus - self.add_dffs() + self.place_dffs() # Add the logic on the right of the bus - self.add_clk_row(row=0) # clk is a double-high cell - self.add_we_row(row=2) - # self.add_trien_row(row=3) - # self.add_trien_bar_row(row=4) - self.add_rbl_in_row(row=3) - self.add_sen_row(row=4) - self.add_rbl(row=5) + self.place_clk_row(row=0) # clk is a double-high cell + self.place_we_row(row=2) + # self.place_trien_row(row=3) + # self.place_trien_bar_row(row=4) + self.place_rbl_in_row(row=3) + self.place_sen_row(row=4) + self.place_rbl(row=5) - self.add_lvs_correspondence_points() - # This offset is used for placement of the control logic in # the SRAM level. self.control_logic_center = vector(self.ctrl_dff_inst.rx(), self.rbl_inst.by()) @@ -142,9 +156,9 @@ class control_logic(design.design): self.height = self.rbl_inst.uy() + self.m3_pitch # Max of modules or logic rows self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch + - - def add_routing(self): + def route_all(self): """ Routing between modules """ self.route_dffs() #self.route_trien() @@ -156,84 +170,92 @@ class control_logic(design.design): self.route_supply() - def add_rbl(self,row): - """ Add the replica bitline """ + def create_rbl(self): + """ Create the replica bitline """ + self.rbl_inst=self.add_inst(name="replica_bitline", + mod=self.replica_bitline) + self.connect_inst(["rbl_in", "pre_s_en", "vdd", "gnd"]) + + def place_rbl(self,row): + """ Place the replica bitline """ y_off = row * self.inv1.height + 2*self.m1_pitch # Add the RBL above the rows # Add to the right of the control rows and routing channel self.replica_bitline_offset = vector(0, y_off) - self.rbl_inst=self.add_inst(name="replica_bitline", - mod=self.replica_bitline, - offset=self.replica_bitline_offset) - self.connect_inst(["rbl_in", "pre_s_en", "vdd", "gnd"]) + self.rbl_inst.place(self.replica_bitline_offset) - def add_clk_row(self,row): - """ Add the multistage clock buffer below the control flops """ - x_off = self.ctrl_dff_array.width + self.internal_bus_width - (y_off,mirror)=self.get_offset(row) - - clkbuf_offset = vector(x_off,y_off) + def create_clk_row(self): + """ Create the multistage clock buffer """ self.clkbuf_inst = self.add_inst(name="clkbuf", - mod=self.clkbuf, - offset=clkbuf_offset) - + mod=self.clkbuf) self.connect_inst(["clk","clk_buf_bar","clk_buf","vdd","gnd"]) + def place_clk_row(self,row): + """ Place the multistage clock buffer below the control flops """ + x_off = self.ctrl_dff_array.width + self.internal_bus_width + (y_off,mirror)=self.get_offset(row) + clkbuf_offset = vector(x_off,y_off) + self.clkbuf_inst.place(clkbuf_offset) self.row_end_inst.append(self.clkbuf_inst) - def add_rbl_in_row(self,row): - x_off = self.ctrl_dff_array.width + self.internal_bus_width - (y_off,mirror)=self.get_offset(row) - - - # input: clk_buf_bar,CS output: rbl_in_bar - self.rbl_in_bar_offset = vector(x_off, y_off) + def create_rbl_in_row(self): self.rbl_in_bar_inst=self.add_inst(name="nand3_rbl_in_bar", - mod=self.nand2, - offset=self.rbl_in_bar_offset, - mirror=mirror) + mod=self.nand2) self.connect_inst(["clk_buf_bar", "cs", "rbl_in_bar", "vdd", "gnd"]) - x_off += self.nand2.width # input: rbl_in_bar, output: rbl_in - self.rbl_in_offset = vector(x_off, y_off) self.rbl_in_inst=self.add_inst(name="inv_rbl_in", - mod=self.inv1, - offset=self.rbl_in_offset, - mirror=mirror) + mod=self.inv1) self.connect_inst(["rbl_in_bar", "rbl_in", "vdd", "gnd"]) - self.row_end_inst.append(self.rbl_in_inst) - - def add_sen_row(self,row): - """ The sense enable buffer gets placed to the far right of the - row. """ + + def place_rbl_in_row(self,row): x_off = self.ctrl_dff_array.width + self.internal_bus_width (y_off,mirror)=self.get_offset(row) + + self.rbl_in_bar_offset = vector(x_off, y_off) + self.rbl_in_bar_inst.place(offset=self.rbl_in_bar_offset, + mirror=mirror) + x_off += self.nand2.width + + self.rbl_in_offset = vector(x_off, y_off) + self.rbl_in_inst.place(offset=self.rbl_in_offset, + mirror=mirror) + self.row_end_inst.append(self.rbl_in_inst) + + def create_sen_row(self): + """ Create the sense enable buffer. """ # input: pre_s_en, output: pre_s_en_bar - self.pre_s_en_bar_offset = vector(x_off, y_off) self.pre_s_en_bar_inst=self.add_inst(name="inv_pre_s_en_bar", - mod=self.inv2, - offset=self.pre_s_en_bar_offset, - mirror=mirror) + mod=self.inv2) self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"]) - x_off += self.inv2.width - # BUFFER INVERTERS FOR S_EN # input: input: pre_s_en_bar, output: s_en - self.s_en_offset = vector(x_off, y_off) self.s_en_inst=self.add_inst(name="inv_s_en", - mod=self.inv8, - offset=self.s_en_offset, - mirror=mirror) + mod=self.inv8) self.connect_inst(["pre_s_en_bar", "s_en0", "vdd", "gnd"]) + def place_sen_row(self,row): + """ + The sense enable buffer gets placed to the far right of the + row. + """ + x_off = self.ctrl_dff_array.width + self.internal_bus_width + (y_off,mirror)=self.get_offset(row) + self.pre_s_en_bar_offset = vector(x_off, y_off) + self.pre_s_en_bar_inst.place(offset=self.pre_s_en_bar_offset, + mirror=mirror) + x_off += self.inv2.width + + self.s_en_offset = vector(x_off, y_off) + self.s_en_inst.place(offset=self.s_en_offset, + mirror=mirror) self.row_end_inst.append(self.s_en_inst) @@ -256,14 +278,16 @@ class control_logic(design.design): self.copy_layout_pin(self.ctrl_dff_inst, "din[1]", "web") - def add_dffs(self): + def create_dffs(self): """ Add the three input DFFs (with inverters) """ self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs", - mod=self.ctrl_dff_array, - offset=vector(0,0)) - + mod=self.ctrl_dff_array) self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list) + def place_dffs(self): + """ Place the input DFFs (with inverters) """ + self.ctrl_dff_inst.place(vector(0,0)) + def get_offset(self,row): """ Compute the y-offset and mirroring """ @@ -276,48 +300,53 @@ class control_logic(design.design): return (y_off,mirror) - def add_we_row(self,row): + def create_we_row(self): + # input: WE, CS output: w_en_bar + self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar", + mod=self.nand3) + self.connect_inst(["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"]) + + # input: w_en_bar, output: pre_w_en + self.pre_w_en_inst=self.add_inst(name="inv_pre_w_en", + mod=self.inv1) + self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"]) + + # BUFFER INVERTERS FOR W_EN + self.pre_w_en_bar_inst=self.add_inst(name="inv_pre_w_en_bar", + mod=self.inv2) + self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"]) + + self.w_en_inst=self.add_inst(name="inv_w_en2", + mod=self.inv8) + self.connect_inst(["pre_w_en_bar", "w_en0", "vdd", "gnd"]) + + + def place_we_row(self,row): x_off = self.ctrl_dff_inst.width + self.internal_bus_width (y_off,mirror)=self.get_offset(row) - # input: WE, CS output: w_en_bar w_en_bar_offset = vector(x_off, y_off) - self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar", - mod=self.nand3, - offset=w_en_bar_offset, - mirror=mirror) - self.connect_inst(["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"]) + self.w_en_bar_inst.place(offset=w_en_bar_offset, + mirror=mirror) x_off += self.nand3.width - # input: w_en_bar, output: pre_w_en pre_w_en_offset = vector(x_off, y_off) - self.pre_w_en_inst=self.add_inst(name="inv_pre_w_en", - mod=self.inv1, - offset=pre_w_en_offset, - mirror=mirror) - - self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"]) + self.pre_w_en_inst.place(offset=pre_w_en_offset, + mirror=mirror) x_off += self.inv1.width - # BUFFER INVERTERS FOR W_EN pre_w_en_bar_offset = vector(x_off, y_off) - self.pre_w_en_bar_inst=self.add_inst(name="inv_pre_w_en_bar", - mod=self.inv2, - offset=pre_w_en_bar_offset, - mirror=mirror) - self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"]) + self.pre_w_en_bar_inst.place(offset=pre_w_en_bar_offset, + mirror=mirror) x_off += self.inv2.width w_en_offset = vector(x_off, y_off) - self.w_en_inst=self.add_inst(name="inv_w_en2", - mod=self.inv8, - offset=w_en_offset, - mirror=mirror) - self.connect_inst(["pre_w_en_bar", "w_en0", "vdd", "gnd"]) + self.w_en_inst.place(offset=w_en_offset, + mirror=mirror) x_off += self.inv8.width self.row_end_inst.append(self.w_en_inst) - + def route_rbl_in(self): """ Connect the logic for the rbl_in generation """ diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index 18b5592d..b8a57f15 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -24,14 +24,23 @@ class delay_chain(design.design): # number of inverters including any fanout loads. self.fanout_list = fanout_list - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell = self.mod_bitcell() + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() self.add_pins() - self.create_module() - self.add_inverters() + self.create_inverters() + + def create_layout(self): + # Each stage is a a row + self.height = len(self.fanout_list)*self.inv.height + # The width is determined by the largest fanout plus the driver + self.width = (max(self.fanout_list)+1) * self.inv.width + + self.place_inverters() self.route_inverters() self.add_layout_pins() self.DRC_LVS() @@ -43,40 +52,27 @@ class delay_chain(design.design): self.add_pin("vdd") self.add_pin("gnd") - def create_module(self): - """ Add the inverter logical module """ + def add_modules(self): + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() self.inv = pinv(route_output=False) self.add_mod(self.inv) - # Each stage is a a row - self.height = len(self.fanout_list)*self.inv.height - # The width is determined by the largest fanout plus the driver - self.width = (max(self.fanout_list)+1) * self.inv.width - - - def add_inverters(self): - """ Add the inverters and connect them based on the stage list """ + def create_inverters(self): + """ Create the inverters and connect them based on the stage list """ self.driver_inst_list = [] self.rightest_load_inst = {} self.load_inst_map = {} for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list): - if stage_num % 2: - inv_mirror = "MX" - inv_offset = vector(0, (stage_num+1)* self.inv.height) - else: - inv_mirror = "R0" - inv_offset = vector(0, stage_num * self.inv.height) - # Add the inverter cur_driver=self.add_inst(name="dinv{}".format(stage_num), - mod=self.inv, - offset=inv_offset, - mirror=inv_mirror) + mod=self.inv) # keep track of the inverter instances so we can use them to get the pins self.driver_inst_list.append(cur_driver) - # Hook up the driver if stage_num+1==len(self.fanout_list): stageout_name = "out" @@ -91,11 +87,8 @@ class delay_chain(design.design): # Now add the dummy loads to the right self.load_inst_map[cur_driver]=[] for i in range(fanout_size): - inv_offset += vector(self.inv.width,0) cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num,i), - mod=self.inv, - offset=inv_offset, - mirror=inv_mirror) + mod=self.inv) # Fanout stage is always driven by driver and output is disconnected disconnect_name = "n_{0}_{1}".format(stage_num,i) self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"]) @@ -105,6 +98,29 @@ class delay_chain(design.design): else: # Keep track of the last one so we can add the the wire later self.rightest_load_inst[cur_driver]=cur_load + + def place_inverters(self): + """ Place the inverters and connect them based on the stage list """ + for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list): + if stage_num % 2: + inv_mirror = "MX" + inv_offset = vector(0, (stage_num+1)* self.inv.height) + else: + inv_mirror = "R0" + inv_offset = vector(0, stage_num * self.inv.height) + + # Add the inverter + cur_driver=self.driver_inst_list[stage_num] + cur_driver.place(offset=inv_offset, + mirror=inv_mirror) + + # Now add the dummy loads to the right + load_list = self.load_inst_map[cur_driver] + for i in range(fanout_size): + inv_offset += vector(self.inv.width,0) + load_list[i].place(offset=inv_offset, + mirror=inv_mirror) + def add_route(self, pin1, pin2): """ This guarantees that we route from the top to bottom row correctly. """ diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index fd21aa98..b1b1b361 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -20,27 +20,30 @@ class dff_array(design.design): design.design.__init__(self, name) debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns)) + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_dff_array() + + def create_layout(self): + self.width = self.columns * self.dff.width + self.height = self.rows * self.dff.height + + self.place_dff_array() + self.add_layout_pins() + self.DRC_LVS() + + def add_modules(self): from importlib import reload c = reload(__import__(OPTS.dff)) self.mod_dff = getattr(c, OPTS.dff) self.dff = self.mod_dff("dff") self.add_mod(self.dff) - - self.width = self.columns * self.dff.width - self.height = self.rows * self.dff.height - - self.create_netlist() - self.create_layout() - - def create_netlist(self): - self.add_pins() - self.create_dff_array() - def create_layout(self): - self.place_dff_array() - self.add_layout_pins() - self.DRC_LVS() - def add_pins(self): for row in range(self.rows): for col in range(self.columns): @@ -75,9 +78,8 @@ class dff_array(design.design): else: base = vector(col*self.dff.width,(row+1)*self.dff.height) mirror = "MX" - self.place_inst(name=name, - offset=base, - mirror=mirror) + self.dff_insts[row,col].place(offset=base, + mirror=mirror) def get_din_name(self, row, col): if self.columns == 1: diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 34d9b7f3..395216bf 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -26,29 +26,41 @@ class dff_buf(design.design): debug.check(inv1_size>=2, "Inverter must be greater than two for rail spacing DRC rules.") debug.check(inv2_size>=2, "Inverter must be greater than two for rail spacing DRC rules.") + self.inv1_size=inv1_size + self.inv2_size=inv2_size + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_modules() + + def create_layout(self): + self.width = self.dff.width + self.inv1.width + self.inv2.width + self.height = self.dff.height + + self.place_modules() + self.route_wires() + self.add_layout_pins() + self.DRC_LVS() + + def add_modules(self): from importlib import reload c = reload(__import__(OPTS.dff)) self.mod_dff = getattr(c, OPTS.dff) self.dff = self.mod_dff("dff") self.add_mod(self.dff) - self.inv1 = pinv(size=inv1_size,height=self.dff.height) + self.inv1 = pinv(size=self.inv1_size,height=self.dff.height) self.add_mod(self.inv1) - self.inv2 = pinv(size=inv2_size,height=self.dff.height) + self.inv2 = pinv(size=self.inv2_size,height=self.dff.height) self.add_mod(self.inv2) - self.width = self.dff.width + self.inv1.width + self.inv2.width - self.height = self.dff.height - - self.create_layout() - - def create_layout(self): - self.add_pins() - self.add_insts() - self.add_wires() - self.add_layout_pins() - self.DRC_LVS() + def add_pins(self): self.add_pin("D") @@ -58,26 +70,30 @@ class dff_buf(design.design): self.add_pin("vdd") self.add_pin("gnd") - def add_insts(self): - # Add the DFF + def create_modules(self): self.dff_inst=self.add_inst(name="dff_buf_dff", - mod=self.dff, - offset=vector(0,0)) + mod=self.dff) self.connect_inst(["D", "qint", "clk", "vdd", "gnd"]) - # Add INV1 to the right self.inv1_inst=self.add_inst(name="dff_buf_inv1", - mod=self.inv1, - offset=vector(self.dff_inst.rx(),0)) + mod=self.inv1) self.connect_inst(["qint", "Qb", "vdd", "gnd"]) - # Add INV2 to the right self.inv2_inst=self.add_inst(name="dff_buf_inv2", - mod=self.inv2, - offset=vector(self.inv1_inst.rx(),0)) + mod=self.inv2) self.connect_inst(["Qb", "Q", "vdd", "gnd"]) + + def place_modules(self): + # Add the DFF + self.dff_inst.place(vector(0,0)) + + # Add INV1 to the right + self.inv1_inst.place(vector(self.dff_inst.rx(),0)) - def add_wires(self): + # Add INV2 to the right + self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) + + def route_wires(self): # Route dff q to inv1 a q_pin = self.dff_inst.get_pin("Q") a1_pin = self.inv1_inst.get_pin("A") diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index f6b72ae8..cedf0404 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -20,21 +20,21 @@ class dff_buf_array(design.design): name = "dff_buf_array_{0}x{1}".format(rows, columns) design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) - - self.dff = dff_buf.dff_buf(inv1_size, inv2_size) - self.add_mod(self.dff) - - self.width = self.columns * self.dff.width - self.height = self.rows * self.dff.height - + self.inv1_size = inv1_size + self.inv2_size = inv2_size + self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): self.add_pins() + self.add_modules() self.create_dff_array() def create_layout(self): + self.width = self.columns * self.dff.width + self.height = self.rows * self.dff.height self.place_dff_array() self.add_layout_pins() self.DRC_LVS() @@ -51,6 +51,10 @@ class dff_buf_array(design.design): self.add_pin("vdd") self.add_pin("gnd") + def add_modules(self): + self.dff = dff_buf.dff_buf(self.inv1_size, self.inv2_size) + self.add_mod(self.dff) + def create_dff_array(self): self.dff_insts={} for row in range(self.rows): @@ -75,9 +79,8 @@ class dff_buf_array(design.design): else: base = vector(col*self.dff.width,(row+1)*self.dff.height) mirror = "MX" - self.place_inst(name=name, - offset=base, - mirror=mirror) + self.dff_insts[row,col].place(offset=base, + mirror=mirror) def get_din_name(self, row, col): if self.columns == 1: diff --git a/compiler/modules/dff_inv.py b/compiler/modules/dff_inv.py index cb5b0478..3a06c9c9 100644 --- a/compiler/modules/dff_inv.py +++ b/compiler/modules/dff_inv.py @@ -18,31 +18,30 @@ class dff_inv(design.design): name = "dff_inv_{0}".format(inv_size) design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) - + self.inv_size = inv_size + # This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width. # This causes a DRC in the pinv which assumes min width rails. This ensures the output # contact does not violate spacing to the rail in the NMOS. debug.check(inv_size>=2, "Inverter must be greater than two for rail spacing DRC rules.") - from importlib import reload - c = reload(__import__(OPTS.dff)) - self.mod_dff = getattr(c, OPTS.dff) - self.dff = self.mod_dff("dff") - self.add_mod(self.dff) - - self.inv1 = pinv(size=inv_size,height=self.dff.height) - self.add_mod(self.inv1) + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_modules() + + def create_layout(self): self.width = self.dff.width + self.inv1.width self.height = self.dff.height - self.create_layout() - - def create_layout(self): - self.add_pins() - self.add_insts() + self.place_modules() self.add_wires() self.add_layout_pins() + self.DRC_LVS() def add_pins(self): @@ -53,18 +52,31 @@ class dff_inv(design.design): self.add_pin("vdd") self.add_pin("gnd") - def add_insts(self): - # Add the DFF + def add_modules(self): + from importlib import reload + c = reload(__import__(OPTS.dff)) + self.mod_dff = getattr(c, OPTS.dff) + self.dff = self.mod_dff("dff") + self.add_mod(self.dff) + + self.inv1 = pinv(size=self.inv_size,height=self.dff.height) + self.add_mod(self.inv1) + + def create_modules(self): self.dff_inst=self.add_inst(name="dff_inv_dff", - mod=self.dff, - offset=vector(0,0)) + mod=self.dff) self.connect_inst(["D", "Q", "clk", "vdd", "gnd"]) - # Add INV1 to the right self.inv1_inst=self.add_inst(name="dff_inv_inv1", - mod=self.inv1, - offset=vector(self.dff_inst.rx(),0)) + mod=self.inv1) self.connect_inst(["Q", "Qb", "vdd", "gnd"]) + + def place_modules(self): + # Place the DFF + self.dff_inst.place(vector(0,0)) + + # Place the INV1 to the right + self.inv1_inst.place(vector(self.dff_inst.rx(),0)) def add_wires(self): diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index a6cef392..c88fabd3 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -20,21 +20,29 @@ class dff_inv_array(design.design): name = "dff_inv_array_{0}x{1}".format(rows, columns) design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) + self.inv_size = inv_size + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() - self.dff = dff_inv.dff_inv(inv_size) - self.add_mod(self.dff) - + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_dff_array() + + def create_layout(self): self.width = self.columns * self.dff.width self.height = self.rows * self.dff.height - self.create_layout() - - def create_layout(self): - self.add_pins() - self.create_dff_array() + self.place_dff_array() self.add_layout_pins() self.DRC_LVS() + def add_modules(self): + self.dff = dff_inv.dff_inv(self.inv_size) + self.add_mod(self.dff) + def add_pins(self): for row in range(self.rows): for col in range(self.columns): @@ -52,16 +60,8 @@ class dff_inv_array(design.design): for row in range(self.rows): for col in range(self.columns): name = "Xdff_r{0}_c{1}".format(row,col) - if (row % 2 == 0): - base = vector(col*self.dff.width,row*self.dff.height) - mirror = "R0" - else: - base = vector(col*self.dff.width,(row+1)*self.dff.height) - mirror = "MX" self.dff_insts[row,col]=self.add_inst(name=name, - mod=self.dff, - offset=base, - mirror=mirror) + mod=self.dff) self.connect_inst([self.get_din_name(row,col), self.get_dout_name(row,col), self.get_dout_bar_name(row,col), @@ -69,6 +69,19 @@ class dff_inv_array(design.design): "vdd", "gnd"]) + def place_dff_array(self): + for row in range(self.rows): + for col in range(self.columns): + name = "Xdff_r{0}_c{1}".format(row,col) + if (row % 2 == 0): + base = vector(col*self.dff.width,row*self.dff.height) + mirror = "R0" + else: + base = vector(col*self.dff.width,(row+1)*self.dff.height) + mirror = "MX" + self.dff_insts[row,col].place(offset=base, + mirror=mirror) + def get_din_name(self, row, col): if self.columns == 1: din_name = "din[{0}]".format(row) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index b74af940..38136b62 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -37,7 +37,8 @@ class hierarchical_decoder(design.design): (self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): @@ -302,8 +303,7 @@ class hierarchical_decoder(design.design): else: base= vector(-self.pre2_4.width, num * self.pre2_4.height) - self.place_inst(name="pre[{0}]".format(num), - offset=base) + self.pre2x4_inst[num].place(base) def place_pre3x8(self,num): @@ -315,8 +315,7 @@ class hierarchical_decoder(design.design): height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height offset = vector(-self.pre3_8.width, height) - self.place_inst(name="pre3x8[{0}]".format(num), - offset=offset) + self.pre3x8_inst[num].place(offset) def create_row_decoder(self): @@ -396,7 +395,6 @@ class hierarchical_decoder(design.design): x_off = self.internal_routing_width + self.nand3.width for row in range(self.rows): - name = self.INV_FORMAT.format(row) if (row % 2 == 0): inv_row_height = self.inv.height * row mirror = "R0" @@ -407,9 +405,8 @@ class hierarchical_decoder(design.design): y_dir = -1 y_off = inv_row_height offset = vector(x_off,y_off) - self.place_inst(name=name, - offset=offset, - mirror=mirror) + self.inv_inst[row].place(offset=offset, + mirror=mirror) def place_row_decoder(self): """ @@ -448,9 +445,8 @@ class hierarchical_decoder(design.design): y_dir = -1 mirror = "MX" - self.place_inst(name=name, - offset=[self.internal_routing_width, y_off], - mirror=mirror) + self.nand_inst[row].place(offset=[self.internal_routing_width, y_off], + mirror=mirror) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 6522e233..d0699cc3 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -49,7 +49,7 @@ class hierarchical_predecode(design.design): else: debug.error("Invalid number of predecode inputs: {}".format(inputs),-1) - def setup_constraints(self): + def setup_layout_constraints(self): self.height = self.number_of_outputs * self.nand.height @@ -88,7 +88,6 @@ class hierarchical_predecode(design.design): def create_input_inverters(self): """ Create the input inverters to invert input signals for the decode stage. """ - self.in_inst = [] for inv_num in range(self.number_of_inputs): name = "Xpre_inv[{0}]".format(inv_num) @@ -100,9 +99,7 @@ class hierarchical_predecode(design.design): def place_input_inverters(self): """ Place the input inverters to invert input signals for the decode stage. """ - for inv_num in range(self.number_of_inputs): - name = "Xpre_inv[{0}]".format(inv_num) if (inv_num % 2 == 0): y_off = inv_num * (self.inv.height) mirror = "R0" @@ -110,13 +107,11 @@ class hierarchical_predecode(design.design): y_off = (inv_num + 1) * (self.inv.height) mirror="MX" offset = vector(self.x_off_inv_1, y_off) - self.place_inst(name=name, - offset=offset, - mirror=mirror) + self.in_inst[inv_num].place(offset=offset, + mirror=mirror) def create_output_inverters(self): """ Create inverters for the inverted output decode signals. """ - self.inv_inst = [] for inv_num in range(self.number_of_outputs): name = "Xpre_nand_inv[{}]".format(inv_num) @@ -129,9 +124,7 @@ class hierarchical_predecode(design.design): def place_output_inverters(self): """ Place inverters for the inverted output decode signals. """ - for inv_num in range(self.number_of_outputs): - name = "Xpre_nand_inv[{}]".format(inv_num) if (inv_num % 2 == 0): y_off = inv_num * self.inv.height mirror = "R0" @@ -139,9 +132,8 @@ class hierarchical_predecode(design.design): y_off =(inv_num + 1)*self.inv.height mirror = "MX" offset = vector(self.x_off_inv_2, y_off) - self.place_inst(name=name, - offset=offset, - mirror=mirror) + self.inv_inst[inv_num].place(offset=offset, + mirror=mirror) def create_nand_array(self,connections): """ Create the NAND stage for the decodes """ @@ -158,7 +150,6 @@ class hierarchical_predecode(design.design): """ Place the NAND stage for the decodes """ for nand_input in range(self.number_of_outputs): inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs) - name = "Xpre{0}_nand[{1}]".format(inout,nand_input) if (nand_input % 2 == 0): y_off = nand_input * self.inv.height mirror = "R0" @@ -166,9 +157,8 @@ class hierarchical_predecode(design.design): y_off = (nand_input + 1) * self.inv.height mirror = "MX" offset = vector(self.x_off_nand, y_off) - self.place_inst(name=name, - offset=offset, - mirror=mirror) + self.nand_inst[nand_input].place(offset=offset, + mirror=mirror) def route(self): diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index ce994a82..5a31cf9e 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -3,6 +3,7 @@ import debug import design from vector import vector from hierarchical_predecode import hierarchical_predecode +from globals import OPTS class hierarchical_predecode2x4(hierarchical_predecode): """ @@ -11,13 +12,13 @@ class hierarchical_predecode2x4(hierarchical_predecode): def __init__(self): hierarchical_predecode.__init__(self, 2) - self.add_pins() - self.create_modules() - self.setup_constraints() self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): + self.add_pins() + self.create_modules() self.create_input_inverters() self.create_output_inverters() connections =[["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"], @@ -33,6 +34,7 @@ class hierarchical_predecode2x4(hierarchical_predecode): 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs 4) a set of NAND gates for inversion """ + self.setup_layout_constraints() self.route_rails() self.place_input_inverters() self.place_output_inverters() diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index 78462077..e1b37ec0 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -3,6 +3,7 @@ import debug import design from vector import vector from hierarchical_predecode import hierarchical_predecode +from globals import OPTS class hierarchical_predecode3x8(hierarchical_predecode): """ @@ -11,13 +12,13 @@ class hierarchical_predecode3x8(hierarchical_predecode): def __init__(self): hierarchical_predecode.__init__(self, 3) - self.add_pins() - self.create_modules() - self.setup_constraints() self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): + self.add_pins() + self.create_modules() self.create_input_inverters() self.create_output_inverters() connections=[["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"], @@ -38,6 +39,7 @@ class hierarchical_predecode3x8(hierarchical_predecode): 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs 4) a set of NAND gates for inversion """ + self.setup_layout_constraints() self.route_rails() self.place_input_inverters() self.place_output_inverters() diff --git a/compiler/modules/ms_flop_array.py b/compiler/modules/ms_flop_array.py index 653c0c38..061ad9be 100644 --- a/compiler/modules/ms_flop_array.py +++ b/compiler/modules/ms_flop_array.py @@ -20,27 +20,31 @@ class ms_flop_array(design.design): design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) + self.words_per_row = int(self.columns / self.word_size) + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_ms_flop_array() + + def create_layout(self): + self.width = self.columns * self.ms.width + self.height = self.ms.height + + self.place_ms_flop_array() + self.add_layout_pins() + self.DRC_LVS() + + def add_modules(self): from importlib import reload c = reload(__import__(OPTS.ms_flop)) self.mod_ms_flop = getattr(c, OPTS.ms_flop) self.ms = self.mod_ms_flop("ms_flop") self.add_mod(self.ms) - - self.width = self.columns * self.ms.width - self.height = self.ms.height - self.words_per_row = int(self.columns / self.word_size) - - self.create_netlist() - self.create_layout() - - def create_netlist(self): - self.add_pins() - self.create_ms_flop_array() - - def create_layout(self): - self.place_ms_flop_array() - self.add_layout_pins() - self.DRC_LVS() def add_pins(self): for i in range(self.word_size): @@ -67,16 +71,15 @@ class ms_flop_array(design.design): def place_ms_flop_array(self): for i in range(0,self.columns,self.words_per_row): - name = "Xdff{0}".format(i) + index = int(i/self.words_per_row) if (i % 2 == 0 or self.words_per_row>1): base = vector(i*self.ms.width,0) mirror = "R0" else: base = vector((i+1)*self.ms.width,0) mirror = "MY" - self.place_inst(name=name, - offset=base, - mirror=mirror) + self.ms_inst[index].place(offset=base, + mirror=mirror) def add_layout_pins(self): diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index eed25978..880288c6 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -3,7 +3,7 @@ import debug from tech import drc from vector import vector from precharge import precharge - +from globals import OPTS class precharge_array(design.design): """ @@ -20,15 +20,13 @@ class precharge_array(design.design): debug.info(1, "Creating {0}".format(self.name)) self.columns = columns - - self.pc_cell = precharge(name="precharge", size=size, bitcell_bl=bitcell_bl, bitcell_br=bitcell_br) - self.add_mod(self.pc_cell) - - self.width = self.columns * self.pc_cell.width - self.height = self.pc_cell.height + self.size = size + self.bitcell_bl = bitcell_bl + self.bitcell_br = bitcell_br self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() def add_pins(self): """Adds pins for spice file""" @@ -39,15 +37,26 @@ class precharge_array(design.design): self.add_pin("vdd") def create_netlist(self): + self.add_modules() self.add_pins() self.create_insts() def create_layout(self): + self.width = self.columns * self.pc_cell.width + self.height = self.pc_cell.height + self.place_insts() self.add_layout_pins() self.DRC_LVS() - + def add_modules(self): + self.pc_cell = precharge(name="precharge", + size=self.size, + bitcell_bl=self.bitcell_bl, + bitcell_br=self.bitcell_br) + self.add_mod(self.pc_cell) + + def add_layout_pins(self): self.add_layout_pin(text="en", @@ -91,7 +100,5 @@ class precharge_array(design.design): def place_insts(self): """ Places precharge array by horizontally tiling the precharge cell""" for i in range(self.columns): - name = "pre_column_{0}".format(i) offset = vector(self.pc_cell.width * i, 0) - inst = self.place_inst(name=name, - offset=offset) + self.local_insts[i].place(offset) diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index 941304e8..ee3e89d4 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -18,27 +18,26 @@ class replica_bitline(design.design): def __init__(self, delay_stages, delay_fanout, bitcell_loads, name="replica_bitline"): design.design.__init__(self, name) - from importlib import reload - g = reload(__import__(OPTS.delay_chain)) - self.mod_delay_chain = getattr(g, OPTS.delay_chain) - - g = reload(__import__(OPTS.replica_bitcell)) - self.mod_replica_bitcell = getattr(g, OPTS.replica_bitcell) - - for pin in ["en", "out", "vdd", "gnd"]: - self.add_pin(pin) self.bitcell_loads = bitcell_loads self.delay_stages = delay_stages self.delay_fanout = delay_fanout - self.create_modules() - self.calculate_module_offsets() + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): self.add_modules() + self.add_pins() + self.create_modules() + + def create_layout(self): + self.calculate_module_offsets() + self.place_modules() self.route() + self.add_layout_pins() self.offset_all_coordinates() - - self.add_layout_pins() #self.add_lvs_correspondence_points() @@ -48,6 +47,10 @@ class replica_bitline(design.design): self.DRC_LVS() + def add_pins(self): + for pin in ["en", "out", "vdd", "gnd"]: + self.add_pin(pin) + def calculate_module_offsets(self): """ Calculate all the module offsets """ @@ -74,8 +77,16 @@ class replica_bitline(design.design): - def create_modules(self): - """ Create modules for later instantiation """ + def add_modules(self): + """ Add the modules for later usage """ + + from importlib import reload + g = reload(__import__(OPTS.delay_chain)) + self.mod_delay_chain = getattr(g, OPTS.delay_chain) + + g = reload(__import__(OPTS.replica_bitcell)) + self.mod_replica_bitcell = getattr(g, OPTS.replica_bitcell) + self.bitcell = self.replica_bitcell = self.mod_replica_bitcell() self.add_mod(self.bitcell) @@ -93,38 +104,48 @@ class replica_bitline(design.design): self.access_tx = ptx(tx_type="pmos") self.add_mod(self.access_tx) - def add_modules(self): - """ Add all of the module instances in the logical netlist """ + def create_modules(self): + """ Create all of the module instances in the logical netlist """ + # This is the threshold detect inverter on the output of the RBL self.rbl_inv_inst=self.add_inst(name="rbl_inv", - mod=self.inv, - offset=self.rbl_inv_offset, - rotate=180) + mod=self.inv) self.connect_inst(["bl[0]", "out", "vdd", "gnd"]) self.tx_inst=self.add_inst(name="rbl_access_tx", - mod=self.access_tx, - offset=self.access_tx_offset) + mod=self.access_tx) # D, G, S, B self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"]) # add the well and poly contact self.dc_inst=self.add_inst(name="delay_chain", - mod=self.delay_chain, - offset=self.delay_chain_offset) + mod=self.delay_chain) self.connect_inst(["en", "delayed_en", "vdd", "gnd"]) self.rbc_inst=self.add_inst(name="bitcell", - mod=self.replica_bitcell, - offset=self.bitcell_offset, - mirror="MX") + mod=self.replica_bitcell) self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"]) self.rbl_inst=self.add_inst(name="load", - mod=self.rbl, - offset=self.rbl_offset) + mod=self.rbl) self.connect_inst(["bl[0]", "br[0]"] + ["gnd"]*self.bitcell_loads + ["vdd", "gnd"]) + def place_modules(self): + """ Add all of the module instances in the logical netlist """ + + # This is the threshold detect inverter on the output of the RBL + self.rbl_inv_inst.place(offset=self.rbl_inv_offset, + rotate=180) + + self.tx_inst.place(self.access_tx_offset) + + self.dc_inst.place(self.delay_chain_offset) + + self.rbc_inst.place(offset=self.bitcell_offset, + mirror="MX") + + self.rbl_inst.place(self.rbl_offset) + @@ -248,8 +269,6 @@ class replica_bitline(design.design): self.copy_layout_pin(self.dc_inst,"vdd") self.copy_layout_pin(self.rbc_inst,"vdd") - - # Connect the WL and vdd pins directly to the center and right vdd rails # Connect RBL vdd pins to center and right rails rbl_vdd_pins = self.rbl_inst.get_pins("vdd") @@ -269,9 +288,6 @@ class replica_bitline(design.design): offset=end, rotate=90) - - - # Add via for the inverter pin = self.rbl_inv_inst.get_pin("vdd") start = vector(self.left_vdd_pin.cx(),pin.cy()) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 5a513583..47bcf023 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -14,44 +14,46 @@ class sense_amp_array(design.design): design.design.__init__(self, "sense_amp_array") debug.info(1, "Creating {0}".format(self.name)) + self.word_size = word_size + self.words_per_row = words_per_row + self.row_size = self.word_size * self.words_per_row + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_sense_amp_array() + + def create_layout(self): + self.height = self.amp.height + self.width = self.amp.width * self.word_size * self.words_per_row + + self.place_sense_amp_array() + self.add_layout_pins() + self.route_rails() + self.DRC_LVS() + + def add_pins(self): + for i in range(0,self.word_size): + self.add_pin("data[{0}]".format(i)) + self.add_pin("bl[{0}]".format(i)) + self.add_pin("br[{0}]".format(i)) + self.add_pin("en") + self.add_pin("vdd") + self.add_pin("gnd") + + def add_modules(self): from importlib import reload c = reload(__import__(OPTS.sense_amp)) self.mod_sense_amp = getattr(c, OPTS.sense_amp) self.amp = self.mod_sense_amp("sense_amp") self.add_mod(self.amp) - self.word_size = word_size - self.words_per_row = words_per_row - self.row_size = self.word_size * self.words_per_row - - self.height = self.amp.height - self.width = self.amp.width * self.word_size * self.words_per_row - - self.create_netlist() - self.create_layout() - self.DRC_LVS() - - def add_pins(self): - - for i in range(0,self.word_size): - self.add_pin("data[{0}]".format(i)) - self.add_pin("bl[{0}]".format(i)) - self.add_pin("br[{0}]".format(i)) - - self.add_pin("en") - self.add_pin("vdd") - self.add_pin("gnd") - - def create_netlist(self): - self.add_pins() - self.create_sense_amp_array() - def create_layout(self): - self.place_sense_amp_array() - self.add_layout_pins() - self.route_rails() - - def create_sense_amp_array(self): self.local_insts = [] for i in range(0,self.word_size): @@ -68,11 +70,8 @@ class sense_amp_array(design.design): amp_spacing = self.amp.width * self.words_per_row for i in range(0,self.word_size): - - name = "sa_d{0}".format(i) amp_position = vector(amp_spacing * i, 0) - self.place_inst(name=name, - offset=amp_position) + self.local_insts[i].place(amp_position) def add_layout_pins(self): diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 4913bfb7..d9be378f 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -6,7 +6,7 @@ from tech import drc import debug import math from vector import vector - +from globals import OPTS class single_level_column_mux_array(design.design): """ @@ -20,23 +20,14 @@ class single_level_column_mux_array(design.design): self.columns = columns self.word_size = word_size self.words_per_row = int(self.columns / self.word_size) + self.create_netlist() - self.create_layout() - - def add_pins(self): - for i in range(self.columns): - self.add_pin("bl[{}]".format(i)) - self.add_pin("br[{}]".format(i)) - for i in range(self.words_per_row): - self.add_pin("sel[{}]".format(i)) - for i in range(self.word_size): - self.add_pin("bl_out[{}]".format(i)) - self.add_pin("br_out[{}]".format(i)) - self.add_pin("gnd") + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): - self.add_pins() self.add_modules() + self.add_pins() self.create_array() def create_layout(self): @@ -51,6 +42,17 @@ class single_level_column_mux_array(design.design): self.DRC_LVS() + def add_pins(self): + for i in range(self.columns): + self.add_pin("bl[{}]".format(i)) + self.add_pin("br[{}]".format(i)) + for i in range(self.words_per_row): + self.add_pin("sel[{}]".format(i)) + for i in range(self.word_size): + self.add_pin("bl_out[{}]".format(i)) + self.add_pin("br_out[{}]".format(i)) + self.add_pin("gnd") + def add_modules(self): # FIXME: Why is this 8x? @@ -87,8 +89,7 @@ class single_level_column_mux_array(design.design): for col_num in range(self.columns): name = "XMUX{0}".format(col_num) x_off = vector(col_num * self.mux.width, self.route_height) - self.place_inst(name=name, - offset=x_off) + self.mux_inst[col_num].place(x_off) def add_layout_pins(self): diff --git a/compiler/modules/tri_gate_array.py b/compiler/modules/tri_gate_array.py index 22004a9f..f8b939af 100644 --- a/compiler/modules/tri_gate_array.py +++ b/compiler/modules/tri_gate_array.py @@ -14,31 +14,34 @@ class tri_gate_array(design.design): design.design.__init__(self, "tri_gate_array") debug.info(1, "Creating {0}".format(self.name)) + self.columns = columns + self.word_size = word_size + self.words_per_row = int(self.columns / self.word_size) + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_array() + + def create_layout(self): + self.width = (self.columns / self.words_per_row) * self.tri.width + self.height = self.tri.height + + self.place_array() + self.add_layout_pins() + self.DRC_LVS() + + def add_modules(self): from importlib import reload c = reload(__import__(OPTS.tri_gate)) self.mod_tri_gate = getattr(c, OPTS.tri_gate) self.tri = self.mod_tri_gate("tri_gate") self.add_mod(self.tri) - - self.columns = columns - self.word_size = word_size - - self.words_per_row = int(self.columns / self.word_size) - self.width = (self.columns / self.words_per_row) * self.tri.width - self.height = self.tri.height - self.create_netlist() - self.create_layout() - - def create_netlist(self): - self.add_pins() - self.create_array() - - def create_layout(self): - self.place_array() - self.add_layout_pins() - self.DRC_LVS() - def add_pins(self): """create the name of pins depend on the word size""" for i in range(self.word_size): @@ -63,10 +66,8 @@ class tri_gate_array(design.design): def place_array(self): """ Place the tri gate to the array """ for i in range(0,self.columns,self.words_per_row): - name = "Xtri_gate{0}".format(i) base = vector(i*self.tri.width, 0) - self.place_inst(name=name, - offset=base) + self.tri_inst[i].place(base) def add_layout_pins(self): diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 4aa7526b..709e3e45 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -20,11 +20,23 @@ class wordline_driver(design.design): design.design.__init__(self, "wordline_driver") self.rows = rows + self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_drivers() + + def create_layout(self): + self.place_drivers() + self.route_layout() + self.route_vdd_gnd() self.offset_all_coordinates() self.DRC_LVS() - + def add_pins(self): # inputs to wordline_driver. for i in range(self.rows): @@ -36,16 +48,6 @@ class wordline_driver(design.design): self.add_pin("vdd") self.add_pin("gnd") - def create_netlist(self): - self.add_pins() - self.add_modules() - self.create_drivers() - - def create_layout(self): - self.place_drivers() - self.route_layout() - self.route_vdd_gnd() - def add_modules(self): self.inv = pinv() @@ -128,10 +130,6 @@ class wordline_driver(design.design): for row in range(self.rows): - name_inv1 = "wl_driver_inv_en{}".format(row) - name_nand = "wl_driver_nand{}".format(row) - name_inv2 = "wl_driver_inv{}".format(row) - if (row % 2): y_offset = self.inv.height*(row + 1) inst_mirror = "MX" @@ -144,17 +142,14 @@ class wordline_driver(design.design): inv2_offset=[inv2_xoffset, y_offset] # add inv1 based on the info above - self.place_inst(name=name_inv1, - offset=inv1_offset, - mirror=inst_mirror) + self.inv1_inst[row].place(offset=inv1_offset, + mirror=inst_mirror) # add nand 2 - self.place_inst(name=name_nand, - offset=nand2_offset, - mirror=inst_mirror) + self.nand_inst[row].place(offset=nand2_offset, + mirror=inst_mirror) # add inv2 - self.place_inst(name=name_inv2, - offset=inv2_offset, - mirror=inst_mirror) + self.inv2_inst[row].place(offset=inv2_offset, + mirror=inst_mirror) def route_layout(self): diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 2d5bea87..965f1735 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -15,21 +15,27 @@ class write_driver_array(design.design): design.design.__init__(self, "write_driver_array") debug.info(1, "Creating {0}".format(self.name)) - from importlib import reload - c = reload(__import__(OPTS.write_driver)) - self.mod_write_driver = getattr(c, OPTS.write_driver) - self.driver = self.mod_write_driver("write_driver") - self.add_mod(self.driver) - self.columns = columns self.word_size = word_size self.words_per_row = int(columns / word_size) - self.width = self.columns * self.driver.width - self.height = self.height = self.driver.height - self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() + + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_write_array() + + def create_layout(self): + self.width = self.columns * self.driver.width + self.height = self.driver.height + + self.place_write_array() + self.add_layout_pins() + self.DRC_LVS() def add_pins(self): for i in range(self.word_size): @@ -41,14 +47,12 @@ class write_driver_array(design.design): self.add_pin("vdd") self.add_pin("gnd") - def create_netlist(self): - self.add_pins() - self.create_write_array() - - def create_layout(self): - self.place_write_array() - self.add_layout_pins() - self.DRC_LVS() + def add_modules(self): + from importlib import reload + c = reload(__import__(OPTS.write_driver)) + self.mod_write_driver = getattr(c, OPTS.write_driver) + self.driver = self.mod_write_driver("write_driver") + self.add_mod(self.driver) def create_write_array(self): self.driver_insts = {} @@ -66,11 +70,9 @@ class write_driver_array(design.design): def place_write_array(self): for i in range(0,self.columns,self.words_per_row): - name = "Xwrite_driver{}".format(i) + index = int(i/self.words_per_row) base = vector(i * self.driver.width,0) - - self.place_inst(name=name, - offset=base) + self.driver_insts[index].place(base) def add_layout_pins(self): diff --git a/compiler/options.py b/compiler/options.py index 5360515d..04d546d4 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -24,14 +24,18 @@ class options(optparse.Values): check_lvsdrc = True # Variable to select the variant of spice spice_name = "" - # Should we print out the banner at startup - print_banner = True + # The spice executable being used which is derived from the user PATH. + spice_exe = "" + # Variable to select the variant of drc, lvs, pex + drc_name = "" + lvs_name = "" + pex_name = "" # The DRC/LVS/PEX executable being used which is derived from the user PATH. drc_exe = None lvs_exe = None pex_exe = None - # The spice executable being used which is derived from the user PATH. - spice_exe = "" + # Should we print out the banner at startup + print_banner = True # Run with extracted parasitics use_pex = False # Remove noncritical memory cells for characterization speed-up diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index a2b7e26c..ac6ace3b 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -29,16 +29,47 @@ class pbitcell(pgate.pgate): self.num_read = num_read self.create_netlist() - self.create_layout() - + if not OPTS.netlist_only: + self.create_layout() + + # FIXME: Why is this static set here? pbitcell.width = self.width pbitcell.height = self.height + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_storage() + + if(self.num_readwrite > 0): + self.create_readwrite_ports() + if(self.num_write > 0): + self.create_write_ports() + if(self.num_read > 0): + self.create_read_ports() + + def create_layout(self): + self.calculate_spacing() + self.calculate_postions() + + self.place_storage() + self.route_storage() + self.route_rails() + + if(self.num_readwrite > 0): + self.place_readwrite_ports() + if(self.num_write > 0): + self.place_write_ports() + if(self.num_read > 0): + self.place_read_ports() + self.extend_well() + + self.offset_all_coordinates() + self.DRC_LVS() + def add_pins(self): - """ - Adding pins for pbitcell module - """ for k in range(self.num_readwrite): self.add_pin("rwbl{}".format(k)) self.add_pin("rwbl_bar{}".format(k)) @@ -59,33 +90,8 @@ class pbitcell(pgate.pgate): self.add_pin("vdd") self.add_pin("gnd") - - def create_netlist(self): - self.add_pins() - - def create_layout(self): - self.create_ptx() - self.calculate_spacing() - self.calculate_postions() - self.add_storage() - self.add_rails() - if(self.num_readwrite > 0): - self.add_readwrite_ports() - if(self.num_write > 0): - self.add_write_ports() - if(self.num_read > 0): - self.add_read_ports() - self.extend_well() - self.offset_all_coordinates() - self.DRC_LVS() - - def create_ptx(self): - """ - Calculate transistor sizes and create ptx for read/write, write, and read ports - """ - - """ calculate transistor sizes """ + def add_modules(self): # if there are any read/write ports, then the inverter nmos is sized based the number of them if(self.num_readwrite > 0): inverter_nmos_width = self.num_readwrite*3*parameter["min_tx_size"] @@ -129,11 +135,9 @@ class pbitcell(pgate.pgate): def calculate_spacing(self): - """ - Calculate transistor spacings - """ + """ Calculate transistor spacings """ - """ calculate metal contact extensions over transistor active """ + # calculate metal contact extensions over transistor active self.inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) self.readwrite_nmos_contact_extension = 0.5*(self.readwrite_nmos.active_contact.height - self.readwrite_nmos.active_height) self.write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height) @@ -142,7 +146,7 @@ class pbitcell(pgate.pgate): # calculate the distance threshold for different gate contact spacings self.gate_contact_thres = drc["poly_to_active"] - drc["minwidth_metal2"] - """ calculations for horizontal transistor to tansistor spacing """ + #calculations for horizontal transistor to tansistor spacing # inverter spacings self.inverter_to_inverter_spacing = contact.poly.height + drc["minwidth_metal1"] self.inverter_to_write_spacing = drc["pwell_to_nwell"] + 2*drc["well_enclosure_active"] @@ -264,9 +268,9 @@ class pbitcell(pgate.pgate): self.height = self.topmost_ypos - self.botmost_ypos - array_vdd_overlap - def add_storage(self): + def place_storage(self): """ - Creates the crossed coupled inverters that act as storage for the bitcell. + Places the crossed coupled inverters that act as storage for the bitcell. The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". """ @@ -276,26 +280,14 @@ class pbitcell(pgate.pgate): inverter_pmos_ypos = self.inverter_nmos.active_height + self.inverter_gap # create active for nmos - self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", - mod=self.inverter_nmos, - offset=[left_inverter_xpos,0]) - self.connect_inst(["Q_bar", "Q", "gnd", "gnd"]) - - self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right", - mod=self.inverter_nmos, - offset=[right_inverter_xpos,0]) - self.connect_inst(["gnd", "Q_bar", "Q", "gnd"]) + self.inverter_nmos_left.place([left_inverter_xpos,0]) + self.inverter_nmos_right.place([right_inverter_xpos,0]) # create active for pmos - self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left", - mod=self.inverter_pmos, - offset=[left_inverter_xpos, inverter_pmos_ypos]) - self.connect_inst(["Q_bar", "Q", "vdd", "vdd"]) - - self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right", - mod=self.inverter_pmos, - offset=[right_inverter_xpos, inverter_pmos_ypos]) - self.connect_inst(["vdd", "Q_bar", "Q", "vdd"]) + self.inverter_pmos_left.place([left_inverter_xpos, inverter_pmos_ypos]) + self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos]) + + def route_storage(self): # connect input (gate) of inverters self.add_path("poly", [self.inverter_nmos_left.get_pin("G").uc(), self.inverter_pmos_left.get_pin("G").bc()]) @@ -327,13 +319,38 @@ class pbitcell(pgate.pgate): self.left_building_edge = -self.inverter_tile_width self.right_building_edge = self.inverter_tile_width - - def add_rails(self): + + def create_storage(self): + """ + Creates the crossed coupled inverters that act as storage for the bitcell. + The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". + """ + + # create active for nmos + self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", + mod=self.inverter_nmos) + self.connect_inst(["Q_bar", "Q", "gnd", "gnd"]) + + self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right", + mod=self.inverter_nmos) + self.connect_inst(["gnd", "Q_bar", "Q", "gnd"]) + + # create active for pmos + self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left", + mod=self.inverter_pmos) + self.connect_inst(["Q_bar", "Q", "vdd", "vdd"]) + + self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right", + mod=self.inverter_pmos) + self.connect_inst(["vdd", "Q_bar", "Q", "vdd"]) + + + def route_rails(self): """ Add gnd and vdd rails and connects them to the inverters """ - """ Add rails for vdd and gnd """ + # Add rails for vdd and gnd self.gnd_position = vector(self.leftmost_xpos, -self.rail_tile_height) self.gnd = self.add_layout_pin(text="gnd", layer="metal1", @@ -350,7 +367,7 @@ class pbitcell(pgate.pgate): width=self.width, height=drc["minwidth_metal1"]) - """ Connect inverters to rails """ + # Connect inverters to rails # connect inverter nmos to gnd gnd_pos_left = vector(self.inverter_nmos_left.get_pin("S").bc().x, self.gnd_position.y) self.add_path("metal1", [self.inverter_nmos_left.get_pin("S").bc(), gnd_pos_left]) @@ -366,9 +383,9 @@ class pbitcell(pgate.pgate): self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) - def add_readwrite_ports(self): + def create_readwrite_ports(self): """ - Adds read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell. + Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell. A read or write is enabled by setting a Read-Write-Wordline (RWWL) high, subsequently turning on the transistor. The transistor is connected between a Read-Write-Bitline (RWBL) and the storage component of the cell (Q). In a write operation, driving RWBL high or low sets the value of the cell. @@ -376,20 +393,38 @@ class pbitcell(pgate.pgate): This is a differential design, so each write port has a mirrored port that connects RWBL_bar to Q_bar. """ - """ Define variables relevant to write transistors """ - # define offset correction due to rotation of the ptx module - readwrite_rotation_correct = self.readwrite_nmos.active_height - # define write transistor variables as empty arrays based on the number of write ports self.readwrite_nmos_left = [None] * self.num_readwrite self.readwrite_nmos_right = [None] * self.num_readwrite + + # iterate over the number of read/write ports + for k in range(0,self.num_readwrite): + # add read/write transistors + self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), + mod=self.readwrite_nmos) + self.connect_inst(["Q", "rwwl{}".format(k), "rwbl{}".format(k), "gnd"]) + + self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k), + mod=self.readwrite_nmos) + self.connect_inst(["Q_bar", "rwwl{}".format(k), "rwbl_bar{}".format(k), "gnd"]) + + + def place_readwrite_ports(self): + """ + Places read/write ports in the bit cell. + """ + + # Define variables relevant to write transistors self.rwwl_positions = [None] * self.num_readwrite self.rwbl_positions = [None] * self.num_readwrite self.rwbl_bar_positions = [None] * self.num_readwrite + # define offset correction due to rotation of the ptx module + readwrite_rotation_correct = self.readwrite_nmos.active_height + # iterate over the number of read/write ports for k in range(0,self.num_readwrite): - """ Add transistors """ + # Add transistors # calculate read/write transistor offsets left_readwrite_transistor_xpos = self.left_building_edge \ - self.inverter_to_write_spacing \ @@ -402,19 +437,13 @@ class pbitcell(pgate.pgate): + readwrite_rotation_correct # add read/write transistors - self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), - mod=self.readwrite_nmos, - offset=[left_readwrite_transistor_xpos,0], - rotate=90) - self.connect_inst(["Q", "rwwl{}".format(k), "rwbl{}".format(k), "gnd"]) + self.readwrite_nmos_left[k].place(offset=[left_readwrite_transistor_xpos,0], + rotate=90) - self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k), - mod=self.readwrite_nmos, - offset=[right_readwrite_transistor_xpos,0], - rotate=90) - self.connect_inst(["Q_bar", "rwwl{}".format(k), "rwbl_bar{}".format(k), "gnd"]) + self.readwrite_nmos_right[k].place(offset=[right_readwrite_transistor_xpos,0], + rotate=90) - """ Add RWWL lines """ + # Add RWWL lines # calculate RWWL position rwwl_ypos = self.gnd_position.y - (k+1)*self.rowline_tile_height self.rwwl_positions[k] = vector(self.leftmost_xpos, rwwl_ypos) @@ -426,7 +455,7 @@ class pbitcell(pgate.pgate): width=self.width, height=contact.m1m2.width) - """ Source/RWBL/RWBL_bar connections """ + # Source/RWBL/RWBL_bar connections # add metal1-to-metal2 contacts on top of read/write transistor source pins for connection to WBL and WBL_bar offset_left = self.readwrite_nmos_left[k].get_pin("S").center() self.add_contact_center(layers=("metal1", "via1", "metal2"), @@ -453,7 +482,7 @@ class pbitcell(pgate.pgate): width=drc["minwidth_metal2"], height=self.height) - """ Gate/RWWL connections """ + # Gate/RWWL connections # add poly-to-meltal2 contacts to connect gate of read/write transistors to RWWL (contact next to gate) # contact must be placed a metal1 width below the source pin to avoid drc from source pin routings if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): @@ -502,7 +531,7 @@ class pbitcell(pgate.pgate): self.add_path("metal2", [left_gate_contact, left_rwwl_contact]) self.add_path("metal2", [right_gate_contact, right_rwwl_contact]) - """ Drain/Storage connections """ + # Drain/Storage connections # this path only needs to be drawn once on the last iteration of the loop if(k == self.num_readwrite-1): # add contacts to connect gate of inverters to drain of read/write transistors @@ -536,34 +565,56 @@ class pbitcell(pgate.pgate): # end if # end for - """ update furthest left and right transistor edges """ + # update furthest left and right transistor edges self.left_building_edge = left_readwrite_transistor_xpos - self.readwrite_nmos.active_height self.right_building_edge = right_readwrite_transistor_xpos + - - def add_write_ports(self): + def create_write_ports(self): """ - Adds write ports to the bit cell. A differential pair of transistors can write only. + Creates write ports in the bit cell. A differential pair of transistors can write only. A write is enabled by setting a Write-Rowline (WWL) high, subsequently turning on the transistor. The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q). In a write operation, driving WBL high or low sets the value of the cell. This is a differential design, so each write port has a mirrored port that connects WBL_bar to Q_bar. """ - """ Define variables relevant to write transistors """ + # Define variables relevant to write transistors # define offset correction due to rotation of the ptx module write_rotation_correct = self.write_nmos.active_height # define write transistor variables as empty arrays based on the number of write ports self.write_nmos_left = [None] * self.num_write self.write_nmos_right = [None] * self.num_write - self.wwl_positions = [None] * self.num_write - self.wbl_positions = [None] * self.num_write - self.wbl_bar_positions = [None] * self.num_write # iterate over the number of write ports for k in range(0,self.num_write): - """ Add transistors """ + # add write transistors + self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k), + mod=self.write_nmos) + self.connect_inst(["Q", "wwl{}".format(k), "wbl{}".format(k), "gnd"]) + + self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k), + mod=self.write_nmos) + self.connect_inst(["Q_bar", "wwl{}".format(k), "wbl_bar{}".format(k), "gnd"]) + + + def place_write_ports(self): + """ + Places write ports in the bit cell. + """ + + # Define variables relevant to write transistors + self.wwl_positions = [None] * self.num_write + self.wbl_positions = [None] * self.num_write + self.wbl_bar_positions = [None] * self.num_write + + # define offset correction due to rotation of the ptx module + write_rotation_correct = self.write_nmos.active_height + + # iterate over the number of write ports + for k in range(0,self.num_write): + # Add transistors # calculate write transistor offsets left_write_transistor_xpos = self.left_building_edge \ - (not self.readwrite_port_flag)*self.inverter_to_write_spacing \ @@ -578,19 +629,13 @@ class pbitcell(pgate.pgate): + write_rotation_correct # add write transistors - self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k), - mod=self.write_nmos, - offset=[left_write_transistor_xpos,0], - rotate=90) - self.connect_inst(["Q", "wwl{}".format(k), "wbl{}".format(k), "gnd"]) + self.write_nmos_left[k].place(offset=[left_write_transistor_xpos,0], + rotate=90) - self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k), - mod=self.write_nmos, - offset=[right_write_transistor_xpos,0], - rotate=90) - self.connect_inst(["Q_bar", "wwl{}".format(k), "wbl_bar{}".format(k), "gnd"]) + self.write_nmos_right[k].place(offset=[right_write_transistor_xpos,0], + rotate=90) - """ Add WWL lines """ + # Add WWL lines # calculate WWL position wwl_ypos = self.gnd_position.y \ - self.num_readwrite*self.rowline_tile_height \ @@ -604,7 +649,7 @@ class pbitcell(pgate.pgate): width=self.width, height=contact.m1m2.width) - """ Source/WBL/WBL_bar connections """ + # Source/WBL/WBL_bar connections # add metal1-to-metal2 contacts on top of write transistor source pins for connection to WBL and WBL_bar offset_left = self.write_nmos_left[k].get_pin("S").center() self.add_contact_center(layers=("metal1", "via1", "metal2"), @@ -631,7 +676,7 @@ class pbitcell(pgate.pgate): width=drc["minwidth_metal2"], height=self.height) - """ Gate/WWL connections """ + # Gate/WWL connections # add poly-to-meltal2 contacts to connect gate of write transistors to WWL (contact next to gate) # contact must be placed a metal width below the source pin to avoid drc from source pin routings if(self.write_nmos_contact_extension > self.gate_contact_thres): @@ -680,7 +725,7 @@ class pbitcell(pgate.pgate): self.add_path("metal2", [left_gate_contact, left_wwl_contact]) self.add_path("metal2", [right_gate_contact, right_wwl_contact]) - """ Drain/Storage connections """ + # Drain/Storage connections # this path only needs to be drawn once on the last iteration of the loop if(k == self.num_write-1): # add contacts to connect gate of inverters to drain of write transistors @@ -711,17 +756,15 @@ class pbitcell(pgate.pgate): midR1 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.write_nmos_right[k].get_pin("D").rc().y) self.add_path("metal1", [right_storage_contact, midR0], width=contact.poly.second_layer_width) self.add_path("metal1", [midR0+vector(0,0.5*contact.poly.second_layer_width), midR1, self.write_nmos_right[k].get_pin("D").rc()]) - # end if - # end for - """ update furthest left and right transistor edges """ + # update furthest left and right transistor edges self.left_building_edge = left_write_transistor_xpos - self.write_nmos.active_height self.right_building_edge = right_write_transistor_xpos - def add_read_ports(self): + def create_read_ports(self): """ - Adds read ports to the bit cell. A differential pair of ports can read only. + Creates read ports in the bit cell. A differential pair of ports can read only. Two transistors function as a read port, denoted as the "read transistor" and the "read-access transistor". The read transistor is connected to RWL (gate), RBL (drain), and the read-access transistor (source). The read-access transistor is connected to Q_bar (gate), gnd (source), and the read transistor (drain). @@ -731,7 +774,42 @@ class pbitcell(pgate.pgate): using sense amps. This is a differential design, so each read port has a mirrored port that connects RBL_bar to Q. """ - """ Define variables relevant to read transistors """ + # define read transistor variables as empty arrays based on the number of read ports + self.read_nmos_left = [None] * self.num_read + self.read_nmos_right = [None] * self.num_read + self.read_access_nmos_left = [None] * self.num_read + self.read_access_nmos_right = [None] * self.num_read + + # iterate over the number of read ports + for k in range(0,self.num_read): + # add read-access transistors + self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), + mod=self.read_nmos) + self.connect_inst(["RA_to_R_left{}".format(k), " Q_bar", "gnd", "gnd"]) + + self.read_access_nmos_right[k] = self.add_inst(name="read_access_nmos_right{}".format(k), + mod=self.read_nmos) + self.connect_inst(["RA_to_R_right{}".format(k), "Q", "gnd", "gnd"]) + + # add read transistors + self.read_nmos_left[k] = self.add_inst(name="read_nmos_left{}".format(k), + mod=self.read_nmos) + self.connect_inst(["rbl{}".format(k), "rwl{}".format(k), "RA_to_R_left{}".format(k), "gnd"]) + + self.read_nmos_right[k] = self.add_inst(name="read_nmos_right{}".format(k), + mod=self.read_nmos) + self.connect_inst(["rbl_bar{}".format(k), "rwl{}".format(k), "RA_to_R_right{}".format(k), "gnd"]) + + def place_read_ports(self): + """ + Places the read ports in the bit cell. + """ + + # Define variables relevant to read transistors + self.rwl_positions = [None] * self.num_read + self.rbl_positions = [None] * self.num_read + self.rbl_bar_positions = [None] * self.num_read + # define offset correction due to rotation of the ptx module read_rotation_correct = self.read_nmos.active_height @@ -749,7 +827,7 @@ class pbitcell(pgate.pgate): # iterate over the number of read ports for k in range(0,self.num_read): - """ Add transistors """ + # Add transistors # calculate transistor offsets left_read_transistor_xpos = self.left_building_edge \ - self.write_to_read_spacing \ @@ -787,7 +865,7 @@ class pbitcell(pgate.pgate): rotate=90) self.connect_inst(["rbl_bar{}".format(k), "rwl{}".format(k), "RA_to_R_right{}".format(k), "gnd"]) - """ Add RWL lines """ + # Add RWL lines # calculate RWL position rwl_ypos = self.gnd_position.y \ - self.num_readwrite*self.rowline_tile_height \ @@ -802,7 +880,7 @@ class pbitcell(pgate.pgate): width=self.width, height=contact.m1m2.width) - """ Drain of read transistor / RBL & RBL_bar connection """ + # Drain of read transistor / RBL & RBL_bar connection # add metal1-to-metal2 contacts on top of read transistor drain pins for connection to RBL and RBL_bar offset_left = self.read_nmos_left[k].get_pin("D").center() self.add_contact_center(layers=("metal1", "via1", "metal2"), @@ -829,7 +907,7 @@ class pbitcell(pgate.pgate): width=drc["minwidth_metal2"], height=self.height) - """ Gate of read transistor / RWL connection """ + # Gate of read transistor / RWL connection # add poly-to-meltal2 contacts to connect gate of read transistors to RWL (contact next to gate) if(self.read_nmos_contact_extension > self.gate_contact_thres): contact_xpos = self.read_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width @@ -874,7 +952,7 @@ class pbitcell(pgate.pgate): self.add_path("metal2", [left_gate_contact, left_rwl_contact]) self.add_path("metal2", [right_gate_contact, right_rwl_contact]) - """ Source of read-access transistor / GND connection """ + # Source of read-access transistor / GND connection # connect source of read-access transistor to GND (metal1 path) gnd_offset_left = vector(self.read_access_nmos_left[k].get_pin("S").bc().x, self.gnd_position.y) self.add_path("metal1", [self.read_access_nmos_left[k].get_pin("S").bc(), gnd_offset_left]) @@ -882,7 +960,7 @@ class pbitcell(pgate.pgate): gnd_offset_right = vector(self.read_access_nmos_right[k].get_pin("S").bc().x, self.gnd_position.y) self.add_path("metal1", [self.read_access_nmos_right[k].get_pin("S").bc(), gnd_offset_right]) - """ Gate of read-access transistor / storage connection """ + # Gate of read-access transistor / storage connection # add poly-to-metal1 contacts to connect gate of read-access transistors to output of inverters (contact next to gate) if(self.read_nmos_contact_extension > self.gate_contact_thres): contact_xpos = self.read_nmos_left[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width @@ -931,7 +1009,7 @@ class pbitcell(pgate.pgate): self.add_path("metal1", [right_gate_contact, midR0, midR1, midR2, right_inverter_offset]) # end for - + def extend_well(self): """ Connects wells between ptx modules to avoid drc spacing issues. @@ -939,7 +1017,7 @@ class pbitcell(pgate.pgate): the well connections must be done piecewise to avoid pwell and nwell overlap. """ - """ extend pwell to encompass entire nmos region of the cell up to the height of the inverter nmos well """ + # extend pwell to encompass entire nmos region of the cell up to the height of the inverter nmos well offset = vector(self.leftmost_xpos, self.botmost_ypos) well_height = -self.botmost_ypos + self.inverter_nmos.cell_well_height - drc["well_enclosure_active"] self.add_rect(layer="pwell", @@ -947,7 +1025,9 @@ class pbitcell(pgate.pgate): width=self.width, height=well_height) - """ extend pwell over read/write and write transistors to the height of the write transistor well (read/write and write transistors are the same height) """ + # extend pwell over read/write and write transistors to the + # height of the write transistor well (read/write and write + # transistors are the same height) if(self.num_write > 0): # calculate the edge of the write transistor well closest to the center left_write_well_xpos = self.write_nmos_left[0].offset.x + drc["well_enclosure_active"] @@ -973,7 +1053,7 @@ class pbitcell(pgate.pgate): width=write_well_width, height=write_well_height) - """ extend pwell over the read transistors to the height of the bitcell """ + # extend pwell over the read transistors to the height of the bitcell if(self.num_read > 0): # calculate the edge of the read transistor well clostest to the center left_read_well_xpos = self.read_nmos_left[0].offset.x + drc["well_enclosure_active"] @@ -995,7 +1075,7 @@ class pbitcell(pgate.pgate): width=read_well_width, height=read_well_height) - """ extend nwell to encompass inverter_pmos """ + # extend nwell to encompass inverter_pmos # calculate offset of the left pmos well inverter_well_xpos = -self.inverter_tile_width - drc["well_enclosure_active"] inverter_well_ypos = self.inverter_nmos.active_height + self.inverter_gap - drc["well_enclosure_active"] @@ -1012,7 +1092,7 @@ class pbitcell(pgate.pgate): height=well_height) - """ add well contacts """ + # add well contacts # connect pimplants to gnd offset = vector(0, self.gnd_position.y + 0.5*contact.well.second_layer_width) self.add_contact_center(layers=("active", "contact", "metal1"), diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index ef67180b..e2e87053 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -41,40 +41,49 @@ class pinv(pgate.pgate): self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() # for run-time, we won't check every transitor DRC/LVS independently # but this may be uncommented for debug purposes #self.DRC_LVS() - def add_pins(self): - """ Adds pins for spice netlist """ - self.add_pin_list(["A", "Z", "vdd", "gnd"]) - def create_netlist(self): """ Calls all functions related to the generation of the netlist """ self.add_pins() + self.determine_tx_mults() + self.add_ptx() + self.create_ptx() def create_layout(self): """ Calls all functions related to the generation of the layout """ - - self.determine_tx_mults() - self.create_ptx() self.setup_layout_constants() - self.add_supply_rails() - self.add_ptx() + self.route_supply_rails() + self.place_ptx() self.add_well_contacts() self.extend_wells(self.well_pos) self.connect_rails() self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", rotate=0) self.route_outputs() + def add_pins(self): + """ Adds pins for spice netlist """ + self.add_pin_list(["A", "Z", "vdd", "gnd"]) + def determine_tx_mults(self): """ Determines the number of fingers needed to achieve the size within the height constraint. This may fail if the user has a tight height. """ + + # This may make the result differ when the layout is created... + if OPTS.netlist_only: + self.tx_mults = 1 + self.nmos_width = self.nmos_size*drc["minwidth_tx"] + self.pmos_width = self.pmos_size*drc["minwidth_tx"] + return + # Do a quick sanity check and bail if unlikely feasible height # Sanity check. can we make an inverter in the height with minimum tx sizes? # Assume we need 3 metal 1 pitches (2 power rails, one between the tx for the drain) @@ -138,7 +147,7 @@ class pinv(pgate.pgate): - def create_ptx(self): + def add_ptx(self): """ Create the PMOS and NMOS transistors. """ self.nmos = ptx(width=self.nmos_width, mults=self.tx_mults, @@ -154,7 +163,7 @@ class pinv(pgate.pgate): connect_active=True) self.add_mod(self.pmos) - def add_supply_rails(self): + def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", layer="metal1", @@ -166,26 +175,34 @@ class pinv(pgate.pgate): offset=vector(0.5*self.width,self.height), width=self.width) - def add_ptx(self): + def create_ptx(self): """ - Add PMOS and NMOS to the layout at the upper-most and lowest position + Create the PMOS and NMOS netlist. + """ + + self.pmos_inst=self.add_inst(name="pinv_pmos", + mod=self.pmos) + self.connect_inst(["Z", "A", "vdd", "vdd"]) + + self.nmos_inst=self.add_inst(name="pinv_nmos", + mod=self.nmos) + self.connect_inst(["Z", "A", "gnd", "gnd"]) + + + def place_ptx(self): + """ + Place PMOS and NMOS to the layout at the upper-most and lowest position to provide maximum routing in channel """ # place PMOS so it is half a poly spacing down from the top self.pmos_pos = self.pmos.active_offset.scale(1,0) \ + vector(0, self.height-self.pmos.active_height-self.top_bottom_space) - self.pmos_inst=self.add_inst(name="pinv_pmos", - mod=self.pmos, - offset=self.pmos_pos) - self.connect_inst(["Z", "A", "vdd", "vdd"]) + self.pmos_inst.place(self.pmos_pos) # place NMOS so that it is half a poly spacing up from the bottom self.nmos_pos = self.nmos.active_offset.scale(1,0) + vector(0,self.top_bottom_space) - self.nmos_inst=self.add_inst(name="pinv_nmos", - mod=self.nmos, - offset=self.nmos_pos) - self.connect_inst(["Z", "A", "gnd", "gnd"]) + self.nmos_inst.place(self.nmos_pos) # Output position will be in between the PMOS and NMOS drains diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index b7238474..e55fb649 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -36,7 +36,8 @@ class pinvbuf(design.design): debug.info(1, "Creating {}".format(self.name)) self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): @@ -49,7 +50,7 @@ class pinvbuf(design.design): self.width = 2*self.inv1.width + self.inv2.width self.height = 2*self.inv1.height - self.place_insts() + self.place_modules() self.route_wires() self.add_layout_pins() @@ -96,23 +97,19 @@ class pinvbuf(design.design): mod=self.inv2) self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) - def place_insts(self): + def place_modules(self): # Add INV1 to the right (capacitance shield) - self.place_inst(name="buf_inv1", - offset=vector(0,0)) + self.inv1_inst.place(vector(0,0)) # Add INV2 to the right - self.place_inst(name="buf_inv2", - offset=vector(self.inv1_inst.rx(),0)) + self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) # Add INV3 to the right - self.place_inst(name="buf_inv3", - offset=vector(self.inv2_inst.rx(),0)) + self.inv3_inst.place(vector(self.inv2_inst.rx(),0)) # Add INV4 to the bottom - self.place_inst(name="buf_inv4", - offset=vector(self.inv2_inst.rx(),2*self.inv2.height), - mirror = "MX") + self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),2*self.inv2.height), + mirror = "MX") def route_wires(self): diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index fd684b63..99ae9f02 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -36,32 +36,34 @@ class pnand2(pgate.pgate): self.tx_mults = 1 self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() #self.DRC_LVS() - def add_pins(self): - """ Adds pins for spice netlist """ - self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) - - def create_netlist(self): self.add_pins() + self.add_ptx() + self.create_ptx() def create_layout(self): """ Calls all functions related to the generation of the layout """ - self.create_ptx() self.setup_layout_constants() - self.add_supply_rails() - self.add_ptx() + self.route_supply_rails() + self.place_ptx() self.connect_rails() self.add_well_contacts() self.extend_wells(self.well_pos) self.route_inputs() self.route_output() - def create_ptx(self): + def add_pins(self): + """ Adds pins for spice netlist """ + self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) + + + def add_ptx(self): """ Create the PMOS and NMOS transistors. """ self.nmos = ptx(width=self.nmos_width, mults=self.tx_mults, @@ -105,7 +107,7 @@ class pnand2(pgate.pgate): self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, drc["poly_extend_active"], self.poly_space) - def add_supply_rails(self): + def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", layer="metal1", @@ -117,37 +119,47 @@ class pnand2(pgate.pgate): offset=vector(0.5*self.width,self.height), width=self.width) - def add_ptx(self): + def create_ptx(self): """ - Add PMOS and NMOS to the layout at the upper-most and lowest position + Add PMOS and NMOS to the netlist. + """ + + self.pmos1_inst=self.add_inst(name="pnand2_pmos1", + mod=self.pmos) + self.connect_inst(["vdd", "A", "Z", "vdd"]) + + self.pmos2_inst = self.add_inst(name="pnand2_pmos2", + mod=self.pmos) + self.connect_inst(["Z", "B", "vdd", "vdd"]) + + self.nmos1_inst=self.add_inst(name="pnand2_nmos1", + mod=self.nmos) + self.connect_inst(["Z", "B", "net1", "gnd"]) + + self.nmos2_inst=self.add_inst(name="pnand2_nmos2", + mod=self.nmos) + self.connect_inst(["net1", "A", "gnd", "gnd"]) + + + def place_ptx(self): + """ + Place PMOS and NMOS to the layout at the upper-most and lowest position to provide maximum routing in channel """ pmos1_pos = vector(self.pmos.active_offset.x, self.height - self.pmos.active_height - self.top_bottom_space) - self.pmos1_inst=self.add_inst(name="pnand2_pmos1", - mod=self.pmos, - offset=pmos1_pos) - self.connect_inst(["vdd", "A", "Z", "vdd"]) + self.pmos1_inst.place(pmos1_pos) self.pmos2_pos = pmos1_pos + self.overlap_offset - self.pmos2_inst = self.add_inst(name="pnand2_pmos2", - mod=self.pmos, - offset=self.pmos2_pos) - self.connect_inst(["Z", "B", "vdd", "vdd"]) + self.pmos2_inst.place(self.pmos2_pos) nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space) - self.nmos1_inst=self.add_inst(name="pnand2_nmos1", - mod=self.nmos, - offset=nmos1_pos) - self.connect_inst(["Z", "B", "net1", "gnd"]) + self.nmos1_inst.place(nmos1_pos) self.nmos2_pos = nmos1_pos + self.overlap_offset - self.nmos2_inst=self.add_inst(name="pnand2_nmos2", - mod=self.nmos, - offset=self.nmos2_pos) - self.connect_inst(["net1", "A", "gnd", "gnd"]) + self.nmos2_inst.place(self.nmos2_pos) # Output position will be in between the PMOS and NMOS self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height)) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 3df9628d..984ee417 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -39,8 +39,8 @@ class pnand3(pgate.pgate): self.tx_mults = 1 self.create_netlist() - self.create_layout() - #self.DRC_LVS() + if not OPTS.netlist_only: + self.create_layout() def add_pins(self): @@ -49,21 +49,22 @@ class pnand3(pgate.pgate): def create_netlist(self): self.add_pins() + self.add_ptx() + self.create_ptx() def create_layout(self): """ Calls all functions related to the generation of the layout """ - self.create_ptx() self.setup_layout_constants() - self.add_supply_rails() - self.add_ptx() + self.route_supply_rails() + self.place_ptx() self.connect_rails() self.add_well_contacts() self.extend_wells(self.well_pos) self.route_inputs() self.route_output() - def create_ptx(self): + def add_ptx(self): """ Create the PMOS and NMOS transistors. """ self.nmos = ptx(width=self.nmos_width, mults=self.tx_mults, @@ -103,7 +104,7 @@ class pnand3(pgate.pgate): self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, drc["poly_extend_active"], self.poly_space) - def add_supply_rails(self): + def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", layer="metal1", @@ -115,50 +116,61 @@ class pnand3(pgate.pgate): offset=vector(0.5*self.width,self.height), width=self.width) - def add_ptx(self): + def create_ptx(self): """ - Add PMOS and NMOS to the layout at the upper-most and lowest position + Create the PMOS and NMOS in the netlist. + """ + + self.pmos1_inst=self.add_inst(name="pnand3_pmos1", + mod=self.pmos) + self.connect_inst(["vdd", "A", "Z", "vdd"]) + + self.pmos2_inst = self.add_inst(name="pnand3_pmos2", + mod=self.pmos) + self.connect_inst(["Z", "B", "vdd", "vdd"]) + + self.pmos3_inst = self.add_inst(name="pnand3_pmos3", + mod=self.pmos) + self.connect_inst(["Z", "C", "vdd", "vdd"]) + + self.nmos1_inst=self.add_inst(name="pnand3_nmos1", + mod=self.nmos) + self.connect_inst(["Z", "C", "net1", "gnd"]) + + self.nmos2_inst=self.add_inst(name="pnand3_nmos2", + mod=self.nmos) + self.connect_inst(["net1", "B", "net2", "gnd"]) + + self.nmos3_inst=self.add_inst(name="pnand3_nmos3", + mod=self.nmos) + self.connect_inst(["net2", "A", "gnd", "gnd"]) + + + def place_ptx(self): + """ + Place the PMOS and NMOS in the layout at the upper-most and lowest position to provide maximum routing in channel """ pmos1_pos = vector(self.pmos.active_offset.x, self.height - self.pmos.active_height - self.top_bottom_space) - self.pmos1_inst=self.add_inst(name="pnand3_pmos1", - mod=self.pmos, - offset=pmos1_pos) - self.connect_inst(["vdd", "A", "Z", "vdd"]) + self.pmos1_inst.place(pmos1_pos) pmos2_pos = pmos1_pos + self.overlap_offset - self.pmos2_inst = self.add_inst(name="pnand3_pmos2", - mod=self.pmos, - offset=pmos2_pos) - self.connect_inst(["Z", "B", "vdd", "vdd"]) + self.pmos2_inst.place(pmos2_pos) self.pmos3_pos = pmos2_pos + self.overlap_offset - self.pmos3_inst = self.add_inst(name="pnand3_pmos3", - mod=self.pmos, - offset=self.pmos3_pos) - self.connect_inst(["Z", "C", "vdd", "vdd"]) + self.pmos3_inst.place(self.pmos3_pos) nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space) - self.nmos1_inst=self.add_inst(name="pnand3_nmos1", - mod=self.nmos, - offset=nmos1_pos) - self.connect_inst(["Z", "C", "net1", "gnd"]) + self.nmos1_inst.place(nmos1_pos) nmos2_pos = nmos1_pos + self.overlap_offset - self.nmos2_inst=self.add_inst(name="pnand3_nmos2", - mod=self.nmos, - offset=nmos2_pos) - self.connect_inst(["net1", "B", "net2", "gnd"]) + self.nmos2_inst.place(nmos2_pos) - self.nmos3_pos = nmos2_pos + self.overlap_offset - self.nmos3_inst=self.add_inst(name="pnand3_nmos3", - mod=self.nmos, - offset=self.nmos3_pos) - self.connect_inst(["net2", "A", "gnd", "gnd"]) + self.nmos3_inst.place(self.nmos3_pos) # This should be placed at the top of the NMOS well self.well_pos = vector(0,self.nmos1_inst.uy()) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 3d06f889..3ddca616 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -32,38 +32,38 @@ class precharge(pgate.pgate): self.bitcell_br = bitcell_br self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.add_ptx() + self.create_ptx() + + def create_layout(self): + self.place_ptx() + self.connect_poly() + self.route_en() + self.place_nwell_and_contact() + self.route_vdd_rail() + self.route_bitlines() + self.connect_to_bitlines() + self.DRC_LVS() def add_pins(self): self.add_pin_list(["bl", "br", "en", "vdd"]) - def create_netlist(self): - self.add_pins() - - def create_layout(self): - self.create_ptx() - self.add_ptx() - self.connect_poly() - self.add_en() - self.add_nwell_and_contact() - self.add_vdd_rail() - self.add_bitlines() - self.connect_to_bitlines() - self.DRC_LVS() - - def create_ptx(self): + def add_ptx(self): """Initializes the upper and lower pmos""" self.pmos = ptx(width=self.ptx_width, tx_type="pmos") self.add_mod(self.pmos) - # Compute the other pmos2 location, but determining offset to overlap the - # source and drain pins - self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() - def add_vdd_rail(self): + def route_vdd_rail(self): + """Adds a vdd rail at the top of the cell""" # adds the rail across the width of the cell vdd_position = vector(0, self.height - self.m1_width) @@ -87,31 +87,43 @@ class precharge(pgate.pgate): offset=vdd_pos.scale(0,1)) - def add_ptx(self): - """Adds both the upper_pmos and lower_pmos to the module""" + def create_ptx(self): + """Create both the upper_pmos and lower_pmos to the module""" + + self.lower_pmos_inst=self.add_inst(name="lower_pmos", + mod=self.pmos) + self.connect_inst(["bl", "en", "br", "vdd"]) + + self.upper_pmos1_inst=self.add_inst(name="upper_pmos1", + mod=self.pmos) + self.connect_inst(["bl", "en", "vdd", "vdd"]) + + self.upper_pmos2_inst=self.add_inst(name="upper_pmos2", + mod=self.pmos) + self.connect_inst(["br", "en", "vdd", "vdd"]) + + + def place_ptx(self): + """Place both the upper_pmos and lower_pmos to the module""" + + # Compute the other pmos2 location, but determining offset to overlap the + # source and drain pins + self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() + # adds the lower pmos to layout #base = vector(self.width - 2*self.pmos.width + self.overlap_offset.x, 0) self.lower_pmos_position = vector(self.bitcell.get_pin(self.bitcell_bl).lx(), self.pmos.active_offset.y) - self.lower_pmos_inst=self.add_inst(name="lower_pmos", - mod=self.pmos, - offset=self.lower_pmos_position) - self.connect_inst(["bl", "en", "br", "vdd"]) + self.lower_pmos_inst.place(self.lower_pmos_position) # adds the upper pmos(s) to layout ydiff = self.pmos.height + 2*self.m1_space + contact.poly.width self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff) - self.upper_pmos1_inst=self.add_inst(name="upper_pmos1", - mod=self.pmos, - offset=self.upper_pmos1_pos) - self.connect_inst(["bl", "en", "vdd", "vdd"]) + self.upper_pmos1_inst.place(self.upper_pmos1_pos) upper_pmos2_pos = self.upper_pmos1_pos + self.overlap_offset - self.upper_pmos2_inst=self.add_inst(name="upper_pmos2", - mod=self.pmos, - offset=upper_pmos2_pos) - self.connect_inst(["br", "en", "vdd", "vdd"]) - + self.upper_pmos2_inst.place(upper_pmos2_pos) + def connect_poly(self): """Connects the upper and lower pmos together""" @@ -131,7 +143,7 @@ class precharge(pgate.pgate): width=xlength, height=self.poly_width) - def add_en(self): + def route_en(self): """Adds the en input rail, en contact/vias, and connects to the pmos""" # adds the en contact to connect the gates to the en rail on metal1 offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space) @@ -146,7 +158,7 @@ class precharge(pgate.pgate): end=offset.scale(0,1)+vector(self.width,0)) - def add_nwell_and_contact(self): + def place_nwell_and_contact(self): """Adds a nwell tap to connect to the vdd rail""" # adds the contact from active to metal1 well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \ @@ -165,7 +177,7 @@ class precharge(pgate.pgate): height=self.height) - def add_bitlines(self): + def route_bitlines(self): """Adds both bit-line and bit-line-bar to the module""" # adds the BL on metal 2 offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 57451ee4..e7fcd5f4 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -3,6 +3,7 @@ import debug from tech import drc, info, spice from vector import vector from contact import contact +from globals import OPTS import path class ptx(design.design): @@ -40,13 +41,9 @@ class ptx(design.design): self.num_contacts = num_contacts self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() - self.translate_all(self.active_offset) - - # for run-time, we won't check every transitor DRC independently - # but this may be uncommented for debug purposes - #self.DRC() def create_layout(self): @@ -56,6 +53,11 @@ class ptx(design.design): self.add_well_implant() self.add_poly() self.add_active_contacts() + self.translate_all(self.active_offset) + + # for run-time, we won't check every transitor DRC independently + # but this may be uncommented for debug purposes + #self.DRC() def create_netlist(self): self.add_pin_list(["D", "G", "S", "B"]) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index c4deedb8..604c9312 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -17,21 +17,19 @@ class single_level_column_mux(design.design): design.design.__init__(self, name) debug.info(2, "create single column mux cell: {0}".format(name)) - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell = self.mod_bitcell() - - self.ptx_width = tx_size * drc["minwidth_tx"] + self.tx_size = tx_size self.create_netlist() - self.create_layout() + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): - self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"]) + self.add_modules() + self.add_pins() self.add_ptx() def create_layout(self): + self.pin_height = 2*self.m2_width self.width = self.bitcell.width self.height = self.nmos_upper.uy() + self.pin_height @@ -39,7 +37,23 @@ class single_level_column_mux(design.design): self.add_bitline_pins() self.connect_bitlines() self.add_wells() + + def add_modules(self): + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() + + # Adds nmos_lower,nmos_upper to the module + self.ptx_width = self.tx_size * drc["minwidth_tx"] + self.nmos = ptx(width=self.ptx_width) + self.add_mod(self.nmos) + + + def add_pins(self): + self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"]) + def add_bitline_pins(self): """ Add the top and bottom pins to this cell """ @@ -70,10 +84,6 @@ class single_level_column_mux(design.design): def add_ptx(self): """ Create the two pass gate NMOS transistors to switch the bitlines""" - # Adds nmos_lower,nmos_upper to the module - self.nmos = ptx(width=self.ptx_width) - self.add_mod(self.nmos) - # 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", diff --git a/compiler/sram.py b/compiler/sram.py index 1f1adb79..653a3a03 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -38,18 +38,9 @@ class sram(): debug.error("Invalid number of banks.",-1) self.s.create_netlist() - self.s.create_layout() + if not OPTS.netlist_only: + self.s.create_layout() - # Can remove the following, but it helps for debug! - self.s.add_lvs_correspondence_points() - - self.s.offset_all_coordinates() - highest_coord = self.s.find_highest_coords() - self.s.width = highest_coord[0] - self.s.height = highest_coord[1] - - self.s.DRC_LVS(final_verification=True) - if not OPTS.is_unit_test: print_time("SRAM creation", datetime.datetime.now(), start_time) diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index fe51d94f..e2a62e42 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -22,18 +22,16 @@ class sram_1bank(sram_base): def create_netlist(self): self.compute_sizes() - self.add_modules() - # Must run this after add modules to get control pin names - self.add_pins() + sram_base.create_netlist(self) self.create_modules() - + def create_modules(self): """ This adds the modules for a single bank SRAM with control logic. """ - self.bank_inst = self.create_bank() + self.bank_inst=self.create_bank(0) self.control_logic_inst = self.create_control_logic() @@ -43,7 +41,7 @@ class sram_1bank(sram_base): self.col_addr_dff_inst = self.create_col_addr_dff() self.data_dff_inst = self.create_data_dff() - + def place_modules(self): """ This places the modules for a single bank SRAM with control @@ -60,14 +58,12 @@ class sram_1bank(sram_base): # up to the row address DFFs. control_pos = vector(-self.control_logic.width - 2*self.m2_pitch, self.bank.bank_center.y - self.control_logic.control_logic_center.y) - self.place_inst(name=self.control_logic_inst.name, - offset=control_pos) + self.control_logic_inst.place(control_pos) # The row address bits are placed above the control logic aligned on the right. row_addr_pos = vector(self.control_logic_inst.rx() - self.row_addr_dff.width, self.control_logic_inst.uy()) - self.place_inst(name=self.row_addr_dff_inst.name, - offset=row_addr_pos) + self.row_addr_dff_inst.place(row_addr_pos) # This is M2 pitch even though it is on M1 to help stem via spacings on the trunk data_gap = -self.m2_pitch*(self.word_size+1) @@ -77,8 +73,7 @@ class sram_1bank(sram_base): if self.col_addr_dff: col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width, data_gap - self.col_addr_dff.height) - self.place_inst(name=self.col_addr_dff_inst.name, - offset=col_addr_pos) + self.col_addr_dff_inst.place(col_addr_pos) # Add the data flops below the bank to the right of the center of bank: # This relies on the center point of the bank: @@ -87,12 +82,11 @@ class sram_1bank(sram_base): # sense amps. data_pos = vector(self.bank.bank_center.x, data_gap - self.data_dff.height) - self.place_inst(self.data_dff.name, - offset=data_pos) + self.data_dff_inst.place(data_pos) # two supply rails are already included in the bank, so just 2 here. - self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch - self.height = self.bank.height + # self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch + # self.height = self.bank.height def add_layout_pins(self): """ diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 7023407d..da6403db 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -117,16 +117,31 @@ class sram_base(design): def create_netlist(self): - """ Netlist creation """ + """ Netlist creation """ + + # Must create the control logic before pins to get the pins self.add_modules() self.add_pins() - + + # This is for the lib file if we don't create layout + self.width=0 + self.height=0 + def create_layout(self): """ Layout creation """ self.place_modules() self.route() self.add_lvs_correspondence_points() + self.offset_all_coordinates() + + highest_coord = self.find_highest_coords() + self.width = highest_coord[0] + self.height = highest_coord[1] + + self.DRC_LVS(final_verification=True) + + def compute_bus_sizes(self): """ Compute the independent bus widths shared between two and four bank SRAMs """ @@ -296,9 +311,8 @@ class sram_base(design): - def create_bank(self): + def create_bank(self,bank_num): """ Create a bank """ - bank_num = len(self.bank_insts) self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num), mod=self.bank)) @@ -341,8 +355,7 @@ class sram_base(design): else: bank_mirror = "R0" - self.place_inst(name=bank_inst.name, - offset=position, + bank_inst.place(offset=position, mirror=bank_mirror, rotate=bank_rotation) diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 15b64f93..41b2ab83 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 """ Run regresion tests on a parameterized bitcell """ @@ -13,9 +13,7 @@ import debug OPTS = globals.OPTS -#@unittest.skip("SKIPPING 04_pbitcell_test") - - +@unittest.skip("SKIPPING 04_pbitcell_test") class pbitcell_test(openram_test): def runTest(self): diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index e8c16607..92e58673 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -#@unittest.skip("SKIPPING 05_pbitcell_array_test") +@unittest.skip("SKIPPING 05_pbitcell_array_test") class pbitcell_array_test(openram_test): def runTest(self): diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py old mode 100644 new mode 100755 index e7ce8460..fad2b100 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -11,8 +11,8 @@ import globals from globals import OPTS import debug -@unittest.skip("Multiported Bank not working yet") -class single_bank_test(openram_test): +@unittest.skip("SKIPPING 19_psingle_bank_test") +class psingle_bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) diff --git a/compiler/verify/__init__.py b/compiler/verify/__init__.py index 2199e765..91c52a90 100644 --- a/compiler/verify/__init__.py +++ b/compiler/verify/__init__.py @@ -22,9 +22,9 @@ if not OPTS.check_lvsdrc: OPTS.pex_exe = None else: debug.info(1, "Finding DRC/LVS/PEX tools.") - OPTS.drc_exe = get_tool("DRC",["calibre","assura","magic"]) - OPTS.lvs_exe = get_tool("LVS",["calibre","assura","netgen"]) - OPTS.pex_exe = get_tool("PEX",["calibre","magic"]) + OPTS.drc_exe = get_tool("DRC", ["calibre","assura","magic"], OPTS.drc_name) + OPTS.lvs_exe = get_tool("LVS", ["calibre","assura","netgen"], OPTS.lvs_name) + OPTS.pex_exe = get_tool("PEX", ["calibre","magic"], OPTS.pex_name) if OPTS.check_lvsdrc and OPTS.tech_name == "freepdk45": debug.check(OPTS.drc_exe[0]!="magic","Magic does not support FreePDK45 for DRC.") diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 112b1257..cf3508f4 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -1,59 +1,15 @@ """ This is a DRC/LVS/PEX interface file for magic + netgen. -This assumes you have the SCMOS magic rules installed. Get these from: -ftp://ftp.mosis.edu/pub/sondeen/magic/new/beta/current.tar.gz -and install them in: -cd /opt/local/lib/magic/sys -tar zxvf current.tar.gz -ln -s 2001a current - -1. magic can perform drc with the following: -#!/bin/sh -magic -dnull -noconsole << EOF -tech load SCN3ME_SUBM.30 -#scalegrid 1 2 -gds rescale no -gds polygon subcell true -gds warning default -gds read $1 -load $1 -writeall force -drc count -drc why -quit -noprompt -EOF - -2. magic can perform extraction with the following: -#!/bin/sh -rm -f $1.ext -rm -f $1.spice -magic -dnull -noconsole << EOF -tech load SCN3ME_SUBM.30 -#scalegrid 1 2 -gds rescale no -gds polygon subcell true -gds warning default -gds read $1 -extract -ext2spice scale off -ext2spice -quit -noprompt -EOF - -3. netgen can perform LVS with: -#!/bin/sh -netgen -noconsole <