diff --git a/compiler/pinv.py b/compiler/pinv.py index 6e659d8e..afe9aa53 100644 --- a/compiler/pinv.py +++ b/compiler/pinv.py @@ -22,7 +22,7 @@ class pinv(design.design): unique_id = 1 - def __init__(self, size=1, beta=parameter["pinv_beta"], height=bitcell.height, route_output=True): + def __init__(self, size=1, beta=parameter["beta"], height=bitcell.height, 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 @@ -46,11 +46,11 @@ class pinv(design.design): #self.DRC_LVS() def add_pins(self): - """Adds pins for spice netlist""" + """ Adds pins for spice netlist """ self.add_pin_list(["A", "Z", "vdd", "gnd"]) def create_layout(self): - """Calls all functions related to the generation of the layout""" + """ Calls all functions related to the generation of the layout """ # These aren't for instantiating, but we use them to get the dimensions self.poly_contact = contact.contact(("poly", "contact", "metal1")) @@ -61,15 +61,7 @@ class pinv(design.design): self.setup_layout_constants() self.add_supply_rails() self.add_ptx() - - # These aren't for instantiating, but we use them to get the dimensions - # self.nwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"), - # dimensions=(1, self.pmos.num_contacts)) - # self.pwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"), - # dimensions=(1, self.nmos.num_contacts)) - self.extend_wells() - #self.extend_active() self.add_well_contacts() self.connect_rails() self.route_input_gate() @@ -142,8 +134,6 @@ class pinv(design.design): # This will help with the wells and the input/output placement self.middle_position = vector(0,0.5*self.height) - # This will balance the PMOS and NMOS size, roughly. - #self.middle_position = vector(0,1.0/(self.beta+1)*self.height) def create_ptx(self): @@ -181,18 +171,18 @@ class pinv(design.design): """ # 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_cell_space) + self.pmos_pos = self.pmos.active_offset.scale(1,0) \ + + vector(0, self.height-self.pmos.active_height-self.top_bottom_cell_space) self.pmos_inst=self.add_inst(name="pinv_pmos", mod=self.pmos, offset=self.pmos_pos) self.connect_inst(["Z", "A", "vdd", "vdd"]) # 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.nmos.active_height+self.top_bottom_cell_space) + self.nmos_pos = self.nmos.active_offset.scale(1,0) + vector(0,self.top_bottom_cell_space) self.nmos_inst=self.add_inst(name="pinv_nmos", mod=self.nmos, - offset=self.nmos_pos, - mirror="MX") + offset=self.nmos_pos) self.connect_inst(["Z", "A", "gnd", "gnd"]) @@ -220,50 +210,9 @@ class pinv(design.design): width=self.well_width, height=self.middle_position.y) - # def extend_active(self): - # """Extends the active area for n/p mos for the addition of the n/p well taps""" - # # calculates the new active width that includes the well_taps - # self.active_width = self.pmos.active_width \ - # + drc["active_to_body_active"] \ - # + self.pmos.active_contact.width - - # # Calculates the coordinates of the bottom left corner of active area - # # of the pmos - # offset = self.pmos_position + self.pmos.active_position - # self.add_rect(layer="active", - # offset=offset, - # width=self.active_width, - # height=self.pmos.active_height) - - # # Determines where the active of the well portion starts to add the - # # implant - # offset = offset + vector(self.pmos.active_width,0) - # implant_width = self.active_width - self.pmos.active_width - # self.add_rect(layer="nimplant", - # offset=offset, - # width=implant_width, - # height=self.pmos.active_height) - - # # Calculates the coordinates of the bottom left corner of active area - # # of the nmos - # offset = self.nmos_position + self.nmos.active_position - # self.add_rect(layer="active", - # offset=offset, - # width=self.active_width, - # height=self.nmos.active_height) - - # # Determines where the active of the well portion starts to add the - # # implant - # offset = offset + vector(self.pmos.active_width,0) - # implant_width = self.active_width - self.nmos.active_width - # self.add_rect(layer="pimplant", - # offset=offset, - # width=implant_width, - # height=self.nmos.active_height) - def route_input_gate(self): - """Routes the input gate to the left side of the cell for access""" + """ Route the input gate to the left side of the cell for access """ nmos_gate_pin = self.nmos_inst.get_pin("G") pmos_gate_pin = self.pmos_inst.get_pin("G") @@ -322,13 +271,13 @@ class pinv(design.design): def add_well_contacts(self): - """ Add n/p well taps to the layout and connect to supplies""" + """ Add n/p well taps to the layout and connect to supplies """ layer_stack = ("active", "contact", "metal1") # To the right a spacing away from the nmos right active edge nwell_contact_xoffset = self.nmos_pos.x + self.nmos.active_width + drc["active_to_body_active"] - nwell_contact_yoffset = self.nmos_pos.y - self.nmos.active_height + nwell_contact_yoffset = self.nmos_pos.y nwell_offset = vector(nwell_contact_xoffset, nwell_contact_yoffset) # Offset by half a contact in x and y nwell_offset += vector(0.5*self.nmos.active_contact.first_layer_width, @@ -337,7 +286,7 @@ class pinv(design.design): offset=nwell_offset) self.add_path("metal1",[nwell_offset,nwell_offset.scale(1,0)]) # Now add the full active and implant for the PMOS - nwell_offset = self.nmos_pos + vector(self.nmos.active_width,-self.nmos.active_height) + nwell_offset = self.nmos_pos + vector(self.nmos.active_width,0) nwell_contact_width = drc["active_to_body_active"] + self.nmos.active_contact.width self.add_rect(layer="active", offset=nwell_offset, @@ -400,7 +349,6 @@ class pinv(design.design): return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] def analytical_delay(self, slew, load=0.0): - from tech import spice r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew) diff --git a/compiler/ptx.py b/compiler/ptx.py index fb56316d..cd41993b 100644 --- a/compiler/ptx.py +++ b/compiler/ptx.py @@ -164,10 +164,14 @@ class ptx(design.design): # The width of the poly is from the left-most to right-most poly gate poly_width = poly_positions[-1].x - poly_positions[0].x + self.poly_width - # This can be limited by poly to active spacing or the poly extension - distance_below_active = self.poly_width + max(self.poly_to_active,0.5*self.poly_height) - poly_offset = poly_positions[0] - vector(0.5*self.poly_width, distance_below_active) - + if self.tx_type == "pmos": + # This can be limited by poly to active spacing or the poly extension + distance_below_active = self.poly_width + max(self.poly_to_active,0.5*self.poly_height) + poly_offset = poly_positions[0] - vector(0.5*self.poly_width, distance_below_active) + else: + # This can be limited by poly to active spacing or the poly extension + distance_above_active = max(self.poly_to_active,0.5*self.poly_height) + poly_offset = poly_positions[0] + vector(-0.5*self.poly_width, distance_above_active) # Remove the old pin and add the new one self.remove_layout_pin("G") # only keep the main pin self.add_layout_pin(text="G", @@ -189,6 +193,15 @@ class ptx(design.design): # This is the width of a contact to extend the ends of the pin end_offset = vector(self.active_contact.second_layer_width/2,0) + # drains always go to the MIDDLE of the cell, so top of NMOS, bottom of PMOS + # so reverse the directions for NMOS + if self.tx_type == "pmos": + drain_dir = 1 + source_dir = -1 + else: + drain_dir = -1 + source_dir = 1 + if len(source_positions)>1: self.remove_layout_pin("S") # remove the individual connections # Add each vertical segment @@ -282,7 +295,6 @@ class ptx(design.design): # The first one will always be a source source_positions = [self.contact_offset] drain_positions = [] - for i in range(self.mults): if i%2: # It's a source... so offset from previous drain. diff --git a/compiler/tests/04_pnand2_test.py b/compiler/tests/04_pnand2_test.py new file mode 100644 index 00000000..1c700193 --- /dev/null +++ b/compiler/tests/04_pnand2_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python2.7 +""" +Run regression tests on a parameterized nand 2. This module doesn't +generate multi_finger 2_input nand gate. It generate only the minimum +size 2_input nand gate that is nmos_width=2*tech.drc[minwidth_tx]. +""" + +import unittest +from testutils import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import verify +import sys + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 04_nand_2_test") + + +class pnand2_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import pnand2 + import tech + + debug.info(2, "Checking 2-input nand gate") + tx = pnand2.pnand2(size=1) + self.local_check(tx) + + OPTS.check_lvsdrc = True + globals.end_openram() + + + def local_check(self, tx): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + tx.sp_write(tempspice) + tx.gds_write(tempgds) + + self.assertFalse(verify.run_drc(tx.name, tempgds)) + self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 21f659f9..8d1ea431 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -67,7 +67,7 @@ layer["boundary"]= 239 #technology parameter parameter={} parameter["min_tx_size"] = 0.09 -parameter["pinv_beta"] = 3 +parameter["beta"] = 3 drclvs_home=os.environ.get("DRCLVS_HOME") drc={} diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index a33084c3..cfb52dce 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -54,7 +54,7 @@ layer["boundary"] = 83 #technology parameter parameter={} parameter["min_tx_size"] = 1.2 -parameter["pinv_beta"] = 2 #for use in pinv +parameter["beta"] = 2 drclvs_home=os.environ.get("DRCLVS_HOME")