diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 4f72d8f6..29624af0 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -21,8 +21,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp" self.name = name - hierarchy_layout.layout.__init__(self, name) hierarchy_spice.spice.__init__(self, name) + hierarchy_layout.layout.__init__(self, name) def get_layout_pins(self,inst): diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index cb2799f3..93c1c041 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -28,7 +28,10 @@ class spice(): # Spice format) self.conns = [] # Keep track of any comments to add the the spice - self.comments = [] + try: + self.commments + except: + self.comments = [] self.sp_read() @@ -38,7 +41,12 @@ class spice(): def add_comment(self, comment): """ Add a comment to the spice file """ - self.comments.append(comment) + try: + self.commments + except: + self.comments = [] + else: + self.comments.append(comment) def add_pin(self, name, pin_type="INOUT"): """ Adds a pin to the pins list. Default type is INOUT signal. """ diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index c2165b20..f46c0d04 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -11,16 +11,13 @@ class pand2(pgate.pgate): This is a simple buffer used for driving loads. """ def __init__(self, name, size=1, height=None): - self.size = size - - pgate.pgate.__init__(self, name, height) - debug.info(1, "Creating {}".format(self.name)) + debug.info(1, "reating pnand2 {}".format(name)) self.add_comment("size: {}".format(size)) - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - + self.size = size + + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) def create_netlist(self): self.add_pins() @@ -40,6 +37,7 @@ class pand2(pgate.pgate): self.place_insts() self.add_wires() self.add_layout_pins() + self.DRC_LVS() def add_pins(self): self.add_pin("A") @@ -129,4 +127,4 @@ class pand2(pgate.pgate): def get_cin(self): """Return the relative input capacitance of a single input""" return self.nand.get_cin() - \ No newline at end of file + diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index 9d73c004..02137ead 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -12,17 +12,15 @@ class pbuf(pgate.pgate): """ def __init__(self, name, size=4, height=None): + debug.info(1, "creating {0} with size of {1}".format(name,size)) + self.add_comment("size: {}".format(size)) + self.stage_effort = 4 self.size = size self.height = height + # Creates the netlist and layout pgate.pgate.__init__(self, name, height) - debug.info(1, "creating {0} with size of {1}".format(self.name,self.size)) - self.add_comment("size: {}".format(size)) - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() def create_netlist(self): diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index 02e4355c..7a60309f 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -13,6 +13,8 @@ class pdriver(pgate.pgate): """ def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None): + debug.info(1, "creating pdriver {}".format(name)) + self.stage_effort = 3 self.height = height self.neg_polarity = neg_polarity @@ -26,16 +28,9 @@ class pdriver(pgate.pgate): if self.size_list and self.neg_polarity: debug.error("Cannot specify both size_list and neg_polarity.", -1) + # Creates the netlist and layout pgate.pgate.__init__(self, name, height) - debug.info(1, "Creating {}".format(self.name)) - self.compute_sizes() - - self.add_comment("sizes: {}".format(str(self.size_list))) - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() def compute_sizes(self): # size_list specified @@ -63,6 +58,8 @@ class pdriver(pgate.pgate): def create_netlist(self): + self.compute_sizes() + self.add_comment("sizes: {}".format(str(self.size_list))) self.add_pins() self.add_modules() self.create_insts() @@ -75,7 +72,6 @@ class pdriver(pgate.pgate): self.width = self.inv_inst_list[-1].rx() self.height = self.inv_inst_list[0].height - self.DRC_LVS() def add_pins(self): self.add_pin("A") diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 073efa0c..dcc474d0 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -21,7 +21,20 @@ class pgate(design.design): b = factory.create(module_type="bitcell") self.height = b.height + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + self.DRC_LVS() + + def create_netlist(): + """ Pure virtual function """ + debug.error("Must over-ride create_netlist.",-1) + + def create_layout(): + """ Pure virtual function """ + debug.error("Must over-ride create_layout.",-1) + def connect_pin_to_rail(self,inst,pin,supply): """ Connects a ptx pin to a supply rail. """ source_pin = inst.get_pin(pin) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index b488013d..e3ff4a37 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -20,28 +20,18 @@ class pinv(pgate.pgate): """ def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True): - # We need to keep unique names because outputting to GDSII - # will use the last record with a given name. I.e., you will - # over-write a design in GDS if one has and the other doesn't - # have poly connected, for example. - pgate.pgate.__init__(self, name, height) - debug.info(2, "create pinv structure {0} with size of {1}".format(name, size)) + + debug.info(2, "creating pinv structure {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) - + self.size = size self.nmos_size = size self.pmos_size = beta*size self.beta = beta self.route_output = False - - self.create_netlist() - 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() + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) def create_netlist(self): """ Calls all functions related to the generation of the netlist """ diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index 06936895..446d8918 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -1,18 +1,21 @@ import debug -import design +import pgate from tech import drc from math import log from vector import vector from globals import OPTS from sram_factory import factory -class pinvbuf(design.design): +class pinvbuf(pgate.pgate): """ This is a simple inverter/buffer used for driving loads. It is used in the column decoder for 1:2 decoding and as the clock buffer. """ def __init__(self, name, size=4, height=None): + debug.info(1, "creating pinvbuf {}".format(name)) + self.add_comment("size: {}".format(size)) + self.stage_effort = 4 self.row_height = height # FIXME: Change the number of stages to support high drives. @@ -23,13 +26,8 @@ class pinvbuf(design.design): self.size = size self.predriver_size = max(int(self.size/(self.stage_effort/2)),1) - design.design.__init__(self, name) - debug.info(1, "Creating {}".format(self.name)) - self.add_comment("size: {}".format(size)) - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() + # Creates the netlist and layout + pgate.pgate.__init__(self, name) def create_netlist(self): @@ -48,7 +46,6 @@ class pinvbuf(design.design): self.offset_all_coordinates() - self.DRC_LVS() def add_pins(self): self.add_pin("A") diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 168bfaf2..f6441f41 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -14,8 +14,8 @@ class pnand2(pgate.pgate): """ def __init__(self, name, size=1, height=None): """ Creates a cell for a simple 2 input nand """ - pgate.pgate.__init__(self, name, height) - debug.info(2, "create pnand2 structure {0} with size of {1}".format(name, size)) + + debug.info(2, "creating pnand2 structure {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) self.size = size @@ -28,9 +28,8 @@ class pnand2(pgate.pgate): debug.check(size==1,"Size 1 pnand2 is only supported now.") self.tx_mults = 1 - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) def create_netlist(self): diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 95d02f29..80a92f66 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -14,8 +14,8 @@ class pnand3(pgate.pgate): """ def __init__(self, name, size=1, height=None): """ Creates a cell for a simple 3 input nand """ - pgate.pgate.__init__(self, name, height) - debug.info(2, "create pnand3 structure {0} with size of {1}".format(name, size)) + + debug.info(2, "creating pnand3 structure {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) # We have trouble pitch matching a 3x sizes to the bitcell... @@ -30,9 +30,8 @@ class pnand3(pgate.pgate): debug.check(size==1,"Size 1 pnand3 is only supported now.") self.tx_mults = 1 - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) def add_pins(self): diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 125261d1..49206333 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -13,8 +13,8 @@ class pnor2(pgate.pgate): """ def __init__(self, name, size=1, height=None): """ Creates a cell for a simple 2 input nor """ - pgate.pgate.__init__(self, name, height) - debug.info(2, "create pnor2 structure {0} with size of {1}".format(name, size)) + + debug.info(2, "creating pnor2 structure {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) self.nmos_size = size @@ -27,9 +27,8 @@ class pnor2(pgate.pgate): debug.check(size==1,"Size 1 pnor2 is only supported now.") self.tx_mults = 1 - self.create_netlist() - self.create_layout() - #self.DRC_LVS() + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) def add_pins(self): @@ -38,12 +37,11 @@ class pnor2(pgate.pgate): def create_netlist(self): self.add_pins() + self.create_ptx() + self.setup_layout_constants() 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.connect_rails() diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 5a7672d6..c2860ec6 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -12,20 +12,19 @@ class precharge(pgate.pgate): This module implements the precharge bitline cell used in the design. """ def __init__(self, name, size=1, bitcell_bl="bl", bitcell_br="br"): - pgate.pgate.__init__(self, name) - debug.info(2, "create single precharge cell: {0}".format(name)) + + debug.info(2, "creating precharge cell {0}".format(name)) self.bitcell = factory.create(module_type="bitcell") - self.beta = parameter["beta"] self.ptx_width = self.beta*parameter["min_tx_size"] self.width = self.bitcell.width self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() + + # Creates the netlist and layout + pgate.pgate.__init__(self, name) + def create_netlist(self): self.add_pins() @@ -40,7 +39,6 @@ class precharge(pgate.pgate): 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_bar", "vdd"]) diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py new file mode 100644 index 00000000..14c1f7d8 --- /dev/null +++ b/compiler/pgates/ptristate_inv.py @@ -0,0 +1,220 @@ +import contact +import pgate +import debug +from tech import drc, parameter, spice +from vector import vector +from math import ceil +from globals import OPTS +from utils import round_to_grid +import logical_effort +from sram_factory import factory + +class ptristate_inv(pgate.pgate): + """ + ptristate generates gds of a parametrically sized tristate inverter. + + There is some flexibility in the size, but we do not allow multiple fingers + to fit in the cell height. + + """ + + def __init__(self, name, size=1, height=None): + + debug.info(2, "creating ptristate inv {0} with size of {1}".format(name, size)) + self.add_comment("size: {}".format(size)) + + # We are 2x since there are two series devices + self.size = 2*size + self.nmos_size = size + self.beta = parameter["beta"] + self.pmos_size = self.beta*size + + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") + + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) + + def create_netlist(self): + """ Calls all functions related to the generation of the netlist """ + self.add_pins() + self.add_ptx() + self.create_ptx() + + def create_layout(self): + """ Calls all functions related to the generation of the layout """ + self.setup_layout_constants() + self.route_supply_rails() + self.place_ptx() + self.add_well_contacts() + self.extend_wells(self.well_pos) + self.connect_rails() + self.route_inputs() + self.route_outputs() + + def add_pins(self): + """ Adds pins for spice netlist """ + self.add_pin_list(["in", "out", "en", "en_bar", "vdd", "gnd"]) + + + def setup_layout_constants(self): + """ + Pre-compute some handy layout parameters. + """ + + # 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() + + # Two PMOS devices and a well contact. Separation between each. + # Enclosure space on the sides. + self.well_width = 2*self.pmos.active_width + drc("well_enclosure_active") + + # Add an extra space because we route the output on the right of the S/D + self.width = self.well_width + 0.5*self.m1_space + # Height is an input parameter, so it is not recomputed. + + # Make sure we can put a well above and below + self.top_bottom_space = max(contact.well.width, contact.well.height) + + + def add_ptx(self): + """ Create the PMOS and NMOS transistors. """ + self.nmos = factory.create(module_type="ptx", + width=self.nmos_width, + mults=1, + tx_type="nmos") + self.add_mod(self.nmos) + + self.pmos = factory.create(module_type="ptx", + width=self.pmos_width, + mults=1, + tx_type="pmos") + + self.add_mod(self.pmos) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top and bottom. """ + self.add_layout_pin_rect_center(text="gnd", + layer="metal1", + offset=vector(0.5*self.width,0), + width=self.width) + + self.add_layout_pin_rect_center(text="vdd", + layer="metal1", + offset=vector(0.5*self.width,self.height), + width=self.width) + + + def create_ptx(self): + """ + Create the PMOS and NMOS netlist. + """ + + # These are the inverter PMOS/NMOS + self.pmos1_inst=self.add_inst(name="ptri_pmos1", mod=self.pmos) + self.connect_inst(["vdd", "in", "n1", "vdd"]) + self.nmos1_inst=self.add_inst(name="ptri_nmos1", mod=self.nmos) + self.connect_inst(["gnd", "in", "n2", "gnd"]) + + + # These are the tristate PMOS/NMOS + self.pmos2_inst = self.add_inst(name="ptri_pmos2", mod=self.pmos) + self.connect_inst(["out", "en_bar", "n1", "vdd"]) + self.nmos2_inst=self.add_inst(name="ptri_nmos2", mod=self.nmos) + self.connect_inst(["out", "en", "n2", "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 + """ + + pmos_yoff = self.height - self.pmos.active_height - self.top_bottom_space - 0.5*contact.well.height + nmos_yoff = self.top_bottom_space + 0.5*contact.well.height + + # Tristate transistors + pmos1_pos = vector(self.pmos.active_offset.x, pmos_yoff) + self.pmos1_inst.place(pmos1_pos) + nmos1_pos = vector(self.pmos.active_offset.x, nmos_yoff) + self.nmos1_inst.place(nmos1_pos) + + # Inverter transistors + self.pmos2_pos = pmos1_pos + self.overlap_offset + self.pmos2_inst.place(self.pmos2_pos) + self.nmos2_pos = nmos1_pos + self.overlap_offset + self.nmos2_inst.place(self.nmos2_pos) + + # Output position will be in between the PMOS and NMOS + self.output_pos = vector(0, 0.5*(pmos_yoff + nmos_yoff + self.nmos.height)) + + # This will help with the wells + self.well_pos = vector(0,self.nmos1_inst.uy()) + + + def route_inputs(self): + """ Route the gates """ + + self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.output_pos.y, "in", position="farleft") + self.route_single_gate(self.pmos2_inst, "en_bar", position="left") + self.route_single_gate(self.nmos2_inst, "en", position="left") + + + def route_outputs(self): + """ Route the output (drains) together. """ + + nmos_drain_pin = self.nmos2_inst.get_pin("D") + pmos_drain_pin = self.pmos2_inst.get_pin("D") + + nmos_drain_pos = nmos_drain_pin.lr() + pmos_drain_pos = pmos_drain_pin.ur() + + self.add_layout_pin(text="out", + layer="metal1", + offset=nmos_drain_pos, + height=pmos_drain_pos.y-nmos_drain_pos.y) + + + def add_well_contacts(self): + """ Add n/p well taps to the layout and connect to supplies AFTER the wells are created """ + + layer_stack = ("active", "contact", "metal1") + + drain_pos = self.nmos1_inst.get_pin("S").center() + vdd_pos = self.get_pin("vdd").center() + self.nwell_contact=self.add_via_center(layers=layer_stack, + offset=vector(drain_pos.x,vdd_pos.y), + implant_type="n", + well_type="n") + + gnd_pos = self.get_pin("gnd").center() + self.pwell_contact=self.add_via_center(layers=layer_stack, + offset=vector(drain_pos.x,gnd_pos.y), + implant_type="p", + well_type="p") + + + + def connect_rails(self): + """ Connect the nmos and pmos to its respective power rails """ + + self.connect_pin_to_rail(self.nmos1_inst,"S","gnd") + self.connect_pin_to_rail(self.pmos1_inst,"S","vdd") + + + def analytical_delay(self, corner, slew, load=0.0): + from tech import spice + r = spice["min_tx_r"] + c_para = spice["min_tx_drain_c"] + return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) + + def analytical_power(self, corner, load): + """Returns dynamic and leakage power. Results in nW""" + #Power in this module currently not defined. Returns 0 nW (leakage and dynamic). + total_power = self.return_power() + return total_power + + def input_load(self): + return 9*spice["min_tx_gate_c"] diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 36fda7a0..c0686bbb 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -28,9 +28,7 @@ class ptx(design.design): name += "_c{}".format(num_contacts) # replace periods with underscore for newer spice compatibility name=name.replace('.','_') - - design.design.__init__(self, name) - debug.info(3, "create ptx2 structure {0}".format(name)) + debug.info(3, "creating ptx {0}".format(name)) self.tx_type = tx_type self.mults = mults @@ -39,6 +37,9 @@ class ptx(design.design): self.connect_poly = connect_poly self.num_contacts = num_contacts + # Do NOT create the netlist and layout (not a pgate) + design.design.__init__(self, name) + self.create_netlist() # We must always create ptx layout for pbitcell # some transistor sizes in other netlist depend on pbitcell diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index d652fa9f..70579cf5 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -1,4 +1,4 @@ -import design +import pgate import debug from tech import drc from vector import vector @@ -7,7 +7,7 @@ from globals import OPTS from sram_factory import factory import logical_effort -class single_level_column_mux(design.design): +class single_level_column_mux(pgate.gate): """ This module implements the columnmux bitline cell used in the design. Creates a single columnmux cell with the given integer size relative @@ -15,18 +15,14 @@ class single_level_column_mux(design.design): Column-mux transistors driven by the decoder must be sized for optimal speed """ def __init__(self, name, tx_size=8, bitcell_bl="bl", bitcell_br="br"): + + debug.info(2, "creating single column mux cell: {0}".format(name)) self.tx_size = int(tx_size) - - design.design.__init__(self, name) - debug.info(2, "create single column mux cell: {0}".format(name)) - self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() + pgate.pgate.__init__(self, name) def create_netlist(self): self.add_modules()