diff --git a/compiler/example_configs/big_config_scn4m_subm.py b/compiler/example_configs/big_config_scn4m_subm.py index 4fa4de8d..279fa04a 100644 --- a/compiler/example_configs/big_config_scn4m_subm.py +++ b/compiler/example_configs/big_config_scn4m_subm.py @@ -3,11 +3,11 @@ num_words = 128 tech_name = "scn4m_subm" process_corners = ["TT"] -supply_voltages = [ 5.0 ] -temperatures = [ 25 ] +supply_voltages = [5.0] +temperatures = [25] output_path = "temp" -output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) +output_name = "sram_{0}_{1}_{2}".format(word_size, num_words, tech_name) drc_name = "magic" lvs_name = "netgen" diff --git a/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py b/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py index 5dad0207..ecb03395 100644 --- a/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py +++ b/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py @@ -14,7 +14,9 @@ route_supplies = True check_lvsdrc = True output_path = "temp" -output_name = "sram_1rw_1r_{0}_{1}_{2}".format(word_size,num_words,tech_name) +output_name = "sram_1rw_1r_{0}_{1}_{2}".format(word_size, + num_words, + tech_name) drc_name = "magic" lvs_name = "netgen" diff --git a/compiler/example_configs/example_config_1w_1r_scn4m_subm.py b/compiler/example_configs/example_config_1w_1r_scn4m_subm.py index c698a035..4b6584d4 100644 --- a/compiler/example_configs/example_config_1w_1r_scn4m_subm.py +++ b/compiler/example_configs/example_config_1w_1r_scn4m_subm.py @@ -14,7 +14,9 @@ route_supplies = True check_lvsdrc = True output_path = "temp" -output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size,num_words,tech_name) +output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size, + num_words, + tech_name) drc_name = "magic" lvs_name = "netgen" diff --git a/compiler/example_configs/example_config_freepdk45.py b/compiler/example_configs/example_config_freepdk45.py index 73e15b6d..eb5f6af3 100644 --- a/compiler/example_configs/example_config_freepdk45.py +++ b/compiler/example_configs/example_config_freepdk45.py @@ -10,5 +10,7 @@ route_supplies = True check_lvsdrc = True output_path = "temp" -output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) +output_name = "sram_{0}_{1}_{2}".format(word_size, + num_words, + tech_name) diff --git a/compiler/example_configs/example_config_scn4m_subm.py b/compiler/example_configs/example_config_scn4m_subm.py index cf973225..500b4beb 100644 --- a/compiler/example_configs/example_config_scn4m_subm.py +++ b/compiler/example_configs/example_config_scn4m_subm.py @@ -10,7 +10,9 @@ route_supplies = True check_lvsdrc = True output_path = "temp" -output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) +output_name = "sram_{0}_{1}_{2}".format(word_size, + num_words, + tech_name) drc_name = "magic" lvs_name = "netgen" diff --git a/compiler/example_configs/giant_config_scn4m_subm.py b/compiler/example_configs/giant_config_scn4m_subm.py index 74d52fe6..e91455da 100644 --- a/compiler/example_configs/giant_config_scn4m_subm.py +++ b/compiler/example_configs/giant_config_scn4m_subm.py @@ -7,7 +7,9 @@ supply_voltages = [ 5.0 ] temperatures = [ 25 ] output_path = "temp" -output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) +output_name = "sram_{0}_{1}_{2}".format(word_size, + num_words, + tech_name) drc_name = "magic" lvs_name = "netgen" diff --git a/compiler/example_configs/medium_config_scn4m_subm.py b/compiler/example_configs/medium_config_scn4m_subm.py index 5faebb58..7c063c97 100644 --- a/compiler/example_configs/medium_config_scn4m_subm.py +++ b/compiler/example_configs/medium_config_scn4m_subm.py @@ -3,11 +3,13 @@ num_words = 256 tech_name = "scn4m_subm" process_corners = ["TT"] -supply_voltages = [ 3.3 ] -temperatures = [ 25 ] +supply_voltages = [3.3] +temperatures = [25] output_path = "temp" -output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) +output_name = "sram_{0}_{1}_{2}".format(word_size, + num_words, + tech_name) drc_name = "magic" lvs_name = "netgen" diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index d71b1e92..d410a8c7 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -6,16 +6,14 @@ # All rights reserved. # import debug -from tech import drc -from math import log from vector import vector -from globals import OPTS import pgate from sram_factory import factory + class pand2(pgate.pgate): """ - This is a simple buffer used for driving loads. + This is a simple buffer used for driving loads. """ def __init__(self, name, size=1, height=None): debug.info(1, "Creating pnand2 {}".format(name)) @@ -23,7 +21,7 @@ class pand2(pgate.pgate): self.size = size - # Creates the netlist and layout + # Creates the netlist and layout pgate.pgate.__init__(self, name, height) def create_netlist(self): @@ -33,10 +31,13 @@ class pand2(pgate.pgate): def create_modules(self): # Shield the cap, but have at least a stage effort of 4 - self.nand = factory.create(module_type="pnand2",height=self.height) + self.nand = factory.create(module_type="pnand2", height=self.height) self.add_mod(self.nand) - self.inv = factory.create(module_type="pdriver", neg_polarity=True, fanout=3*self.size, height=self.height) + self.inv = factory.create(module_type="pdriver", + neg_polarity=True, + fanout=3*self.size, + height=self.height) self.add_mod(self.inv) def create_layout(self): @@ -54,44 +55,44 @@ class pand2(pgate.pgate): self.add_pin("gnd", "GROUND") def create_insts(self): - self.nand_inst=self.add_inst(name="pand2_nand", - mod=self.nand) - self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) + self.nand_inst = self.add_inst(name="pand2_nand", + mod=self.nand) + self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) - self.inv_inst=self.add_inst(name="pand2_inv", - mod=self.inv) - self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + self.inv_inst = self.add_inst(name="pand2_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) def place_insts(self): - # Add NAND to the right - self.nand_inst.place(offset=vector(0,0)) + # Add NAND to the right + self.nand_inst.place(offset=vector(0, 0)) # Add INV to the right - self.inv_inst.place(offset=vector(self.nand_inst.rx(),0)) + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") a2_pin = self.inv_inst.get_pin("A") - mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) + mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy()) mid2_point = vector(mid1_point, a2_pin.cy()) - self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) - + self.add_path("metal1", + [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) def add_layout_pins(self): # Continous vdd rail along with label. - vdd_pin=self.inv_inst.get_pin("vdd") + vdd_pin = self.inv_inst.get_pin("vdd") self.add_layout_pin(text="vdd", layer="metal1", - offset=vdd_pin.ll().scale(0,1), + offset=vdd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) # Continous gnd rail along with label. - gnd_pin=self.inv_inst.get_pin("gnd") + gnd_pin = self.inv_inst.get_pin("gnd") self.add_layout_pin(text="gnd", layer="metal1", - offset=gnd_pin.ll().scale(0,1), + offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) @@ -102,7 +103,7 @@ class pand2(pgate.pgate): width=pin.width(), height=pin.height()) - for pin_name in ["A","B"]: + for pin_name in ["A", "B"]: pin = self.nand_inst.get_pin(pin_name) self.add_layout_pin_rect_center(text=pin_name, layer=pin.layer, diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index 22864e5a..b02e18e4 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -6,16 +6,14 @@ # All rights reserved. # import debug -from tech import drc -from math import log from vector import vector -from globals import OPTS import pgate from sram_factory import factory + class pand3(pgate.pgate): """ - This is a simple buffer used for driving loads. + This is a simple buffer used for driving loads. """ def __init__(self, name, size=1, height=None): debug.info(1, "Creating pand3 {}".format(name)) @@ -23,7 +21,7 @@ class pand3(pgate.pgate): self.size = size - # Creates the netlist and layout + # Creates the netlist and layout pgate.pgate.__init__(self, name, height) def create_netlist(self): @@ -33,10 +31,12 @@ class pand3(pgate.pgate): def create_modules(self): # Shield the cap, but have at least a stage effort of 4 - self.nand = factory.create(module_type="pnand3",height=self.height) + self.nand = factory.create(module_type="pnand3", height=self.height) self.add_mod(self.nand) - self.inv = factory.create(module_type="pinv", size=self.size, height=self.height) + self.inv = factory.create(module_type="pinv", + size=self.size, + height=self.height) self.add_mod(self.inv) def create_layout(self): @@ -55,44 +55,44 @@ class pand3(pgate.pgate): self.add_pin("gnd", "GROUND") def create_insts(self): - self.nand_inst=self.add_inst(name="pand3_nand", - mod=self.nand) - self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"]) + self.nand_inst = self.add_inst(name="pand3_nand", + mod=self.nand) + self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"]) - self.inv_inst=self.add_inst(name="pand3_inv", - mod=self.inv) - self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + self.inv_inst = self.add_inst(name="pand3_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) def place_insts(self): - # Add NAND to the right - self.nand_inst.place(offset=vector(0,0)) + # Add NAND to the right + self.nand_inst.place(offset=vector(0, 0)) # Add INV to the right - self.inv_inst.place(offset=vector(self.nand_inst.rx(),0)) + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") a2_pin = self.inv_inst.get_pin("A") - mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) + mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) mid2_point = vector(mid1_point, a2_pin.cy()) - self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) - + self.add_path("metal1", + [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) def add_layout_pins(self): # Continous vdd rail along with label. - vdd_pin=self.inv_inst.get_pin("vdd") + vdd_pin = self.inv_inst.get_pin("vdd") self.add_layout_pin(text="vdd", layer="metal1", - offset=vdd_pin.ll().scale(0,1), + offset=vdd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) # Continous gnd rail along with label. - gnd_pin=self.inv_inst.get_pin("gnd") + gnd_pin = self.inv_inst.get_pin("gnd") self.add_layout_pin(text="gnd", layer="metal1", - offset=gnd_pin.ll().scale(0,1), + offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) @@ -103,20 +103,22 @@ class pand3(pgate.pgate): width=pin.width(), height=pin.height()) - for pin_name in ["A","B", "C"]: + for pin_name in ["A", "B", "C"]: pin = self.nand_inst.get_pin(pin_name) self.add_layout_pin_rect_center(text=pin_name, layer=pin.layer, offset=pin.center(), width=pin.width(), height=pin.height()) - - def analytical_delay(self, corner, slew, load=0.0): """ Calculate the analytical delay of DFF-> INV -> INV """ - nand_delay = self.nand.analytical_delay(corner, slew=slew, load=self.inv.input_load()) - inv_delay = self.inv.analytical_delay(corner, slew=nand_delay.slew, load=load) + nand_delay = self.nand.analytical_delay(corner, + slew=slew, + load=self.inv.input_load()) + inv_delay = self.inv.analytical_delay(corner, + slew=nand_delay.slew, + load=load) return nand_delay + inv_delay def get_stage_efforts(self, external_cout, inp_is_rise=False): diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index fcfc6586..0149f75f 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -6,20 +6,18 @@ # All rights reserved. # import debug -from tech import drc -from math import log from vector import vector -from globals import OPTS import pgate from sram_factory import factory + class pbuf(pgate.pgate): """ - This is a simple buffer used for driving loads. + This is a simple buffer used for driving loads. """ def __init__(self, name, size=4, height=None): - debug.info(1, "creating {0} with size of {1}".format(name,size)) + debug.info(1, "creating {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) self.stage_effort = 4 @@ -29,7 +27,6 @@ class pbuf(pgate.pgate): # Creates the netlist and layout pgate.pgate.__init__(self, name, height) - def create_netlist(self): self.add_pins() self.create_modules() @@ -49,53 +46,54 @@ class pbuf(pgate.pgate): def create_modules(self): # Shield the cap, but have at least a stage effort of 4 - input_size = max(1,int(self.size/self.stage_effort)) - self.inv1 = factory.create(module_type="pinv", size=input_size, height=self.height) + input_size = max(1, int(self.size / self.stage_effort)) + self.inv1 = factory.create(module_type="pinv", + size=input_size, + height=self.height) self.add_mod(self.inv1) - self.inv2 = factory.create(module_type="pinv", size=self.size, height=self.height) + self.inv2 = factory.create(module_type="pinv", + size=self.size, + height=self.height) self.add_mod(self.inv2) def create_insts(self): - self.inv1_inst=self.add_inst(name="buf_inv1", - mod=self.inv1) - self.connect_inst(["A", "zb_int", "vdd", "gnd"]) + self.inv1_inst = self.add_inst(name="buf_inv1", + mod=self.inv1) + self.connect_inst(["A", "zb_int", "vdd", "gnd"]) - - self.inv2_inst=self.add_inst(name="buf_inv2", - mod=self.inv2) - self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + self.inv2_inst = self.add_inst(name="buf_inv2", + mod=self.inv2) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) def place_insts(self): - # Add INV1 to the right - self.inv1_inst.place(vector(0,0)) + # Add INV1 to the right + self.inv1_inst.place(vector(0, 0)) # Add INV2 to the right - self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) - + self.inv2_inst.place(vector(self.inv1_inst.rx(), 0)) def add_wires(self): # inv1 Z to inv2 A z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") - mid_point = vector(z1_pin.cx(), a2_pin.cy()) + mid_point = vector(z1_pin.cx(), a2_pin.cy()) self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) - def add_layout_pins(self): # Continous vdd rail along with label. - vdd_pin=self.inv1_inst.get_pin("vdd") + vdd_pin = self.inv1_inst.get_pin("vdd") self.add_layout_pin(text="vdd", layer="metal1", - offset=vdd_pin.ll().scale(0,1), + offset=vdd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) # Continous gnd rail along with label. - gnd_pin=self.inv1_inst.get_pin("gnd") + gnd_pin = self.inv1_inst.get_pin("gnd") self.add_layout_pin(text="gnd", layer="metal1", - offset=gnd_pin.ll().scale(0,1), + offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index ec55f0c7..bb7739b7 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -7,28 +7,27 @@ # import debug import pgate -import math -from tech import drc -from math import log from vector import vector -from globals import OPTS from sram_factory import factory + class pdriver(pgate.pgate): """ - This instantiates an even or odd number of inverters sized for driving a load. + This instantiates an even or odd number of inverters + sized for driving a load. """ + 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.height = height self.neg_polarity = neg_polarity self.size_list = size_list self.fanout = fanout - if size_list == None and self.fanout == 0: + if not size_list and self.fanout == 0: debug.error("Either fanout or size list must be specified.", -1) if self.size_list and self.fanout != 0: debug.error("Cannot specify both size_list and fanout.", -1) @@ -36,34 +35,33 @@ class pdriver(pgate.pgate): debug.error("Cannot specify both size_list and neg_polarity.", -1) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height) - def compute_sizes(self): # size_list specified if self.size_list: self.num_stages = len(self.size_list) else: # Find the optimal number of stages for the given effort - self.num_stages = max(1,int(round(self.fanout**(1/self.stage_effort)))) + self.num_stages = max(1, + int(round(self.fanout ** (1 / self.stage_effort)))) # Increase the number of stages if we need to fix polarity - if self.neg_polarity and (self.num_stages%2==0): + if self.neg_polarity and (self.num_stages % 2 == 0): self.num_stages += 1 - elif not self.neg_polarity and (self.num_stages%2): + elif not self.neg_polarity and (self.num_stages % 2): self.num_stages += 1 self.size_list = [] # compute sizes backwards from the fanout fanout_prev = self.fanout for x in range(self.num_stages): - fanout_prev = max(round(fanout_prev/self.stage_effort),1) + fanout_prev = max(round(fanout_prev / self.stage_effort), 1) self.size_list.append(fanout_prev) # reverse the sizes to be from input to output self.size_list.reverse() - def create_netlist(self): self.compute_sizes() self.add_comment("sizes: {}".format(str(self.size_list))) @@ -79,29 +77,30 @@ class pdriver(pgate.pgate): self.width = self.inv_inst_list[-1].rx() self.height = self.inv_inst_list[0].height - def add_pins(self): self.add_pin("A", "INPUT") self.add_pin("Z", "OUTPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - def add_modules(self): + def add_modules(self): self.inv_list = [] for size in self.size_list: - temp_inv = factory.create(module_type="pinv", size=size, height=self.height) + temp_inv = factory.create(module_type="pinv", + size=size, + height=self.height) self.inv_list.append(temp_inv) self.add_mod(temp_inv) - def create_insts(self): self.inv_inst_list = [] - for x in range(1,self.num_stages+1): + for x in range(1, self.num_stages + 1): # Create first inverter if x == 1: - zbx_int = "Zb{}_int".format(x); - self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), - mod=self.inv_list[x-1])) + zbx_int = "Zb{}_int".format(x) + inst = self.add_inst(name="buf_inv{}".format(x), + mod=self.inv_list[x - 1]) + self.inv_inst_list.append(inst) if self.num_stages == 1: self.connect_inst(["A", "Z", "vdd", "gnd"]) else: @@ -109,70 +108,72 @@ class pdriver(pgate.pgate): # Create last inverter elif x == self.num_stages: - zbn_int = "Zb{}_int".format(x-1); - self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), - mod=self.inv_list[x-1])) + zbn_int = "Zb{}_int".format(x - 1) + inst = self.add_inst(name="buf_inv{}".format(x), + mod=self.inv_list[x - 1]) + self.inv_inst_list.append(inst) self.connect_inst([zbn_int, "Z", "vdd", "gnd"]) # Create middle inverters else: - zbx_int = "Zb{}_int".format(x-1); - zbn_int = "Zb{}_int".format(x); - self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), - mod=self.inv_list[x-1])) + zbx_int = "Zb{}_int".format(x - 1) + zbn_int = "Zb{}_int".format(x) + inst = self.add_inst(name="buf_inv{}".format(x), + mod=self.inv_list[x - 1]) + self.inv_inst_list.append(inst) self.connect_inst([zbx_int, zbn_int, "vdd", "gnd"]) - def place_modules(self): # Add the first inverter at the origin - self.inv_inst_list[0].place(vector(0,0)) + self.inv_inst_list[0].place(vector(0, 0)) # Add inverters to the right of the previous inverter - for x in range(1,len(self.inv_inst_list)): - self.inv_inst_list[x].place(vector(self.inv_inst_list[x-1].rx(),0)) + for x in range(1, len(self.inv_inst_list)): + loc = vector(self.inv_inst_list[x - 1].rx(), 0) + self.inv_inst_list[x].place(loc) - def route_wires(self): z_inst_list = [] a_inst_list = [] # inv_current Z to inv_next A - for x in range(0,len(self.inv_inst_list)-1): + for x in range(0, len(self.inv_inst_list) - 1): z_inst_list.append(self.inv_inst_list[x].get_pin("Z")) - a_inst_list.append(self.inv_inst_list[x+1].get_pin("A")) - mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy()) - self.add_path("metal1", [z_inst_list[x].center(), mid_point, a_inst_list[x].center()]) + a_inst_list.append(self.inv_inst_list[x + 1].get_pin("A")) + mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy()) + self.add_path("metal1", + [z_inst_list[x].center(), mid_point, + a_inst_list[x].center()]) - def add_layout_pins(self): # Continous vdd rail along with label. - vdd_pin=self.inv_inst_list[0].get_pin("vdd") + vdd_pin = self.inv_inst_list[0].get_pin("vdd") self.add_layout_pin(text="vdd", layer="metal1", - offset=vdd_pin.ll().scale(0,1), + offset=vdd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) # Continous gnd rail along with label. - gnd_pin=self.inv_inst_list[0].get_pin("gnd") + gnd_pin = self.inv_inst_list[0].get_pin("gnd") self.add_layout_pin(text="gnd", layer="metal1", - offset=gnd_pin.ll().scale(0,1), + offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) - z_pin = self.inv_inst_list[len(self.inv_inst_list)-1].get_pin("Z") + z_pin = self.inv_inst_list[len(self.inv_inst_list) - 1].get_pin("Z") self.add_layout_pin_rect_center(text="Z", layer=z_pin.layer, offset=z_pin.center(), - width = z_pin.width(), - height = z_pin.height()) + width=z_pin.width(), + height=z_pin.height()) a_pin = self.inv_inst_list[0].get_pin("A") self.add_layout_pin_rect_center(text="A", layer=a_pin.layer, offset=a_pin.center(), - width = a_pin.width(), - height = a_pin.height()) + width=a_pin.width(), + height=a_pin.height()) def get_sizes(self): """ Return the relative sizes of the buffers """ @@ -181,14 +182,14 @@ class pdriver(pgate.pgate): def get_stage_efforts(self, external_cout, inp_is_rise=False): """ Get the stage efforts of the A -> Z path """ cout_list = [] - for prev_inv,inv in zip(self.inv_list, self.inv_list[1:]): + for prev_inv, inv in zip(self.inv_list, self.inv_list[1:]): cout_list.append(inv.get_cin()) cout_list.append(external_cout) stage_effort_list = [] last_inp_is_rise = inp_is_rise - for inv,cout in zip(self.inv_list,cout_list): + for inv, cout in zip(self.inv_list, cout_list): stage = inv.get_stage_effort(cout, last_inp_is_rise) stage_effort_list.append(stage) last_inp_is_rise = stage.is_rise diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index f974f0c4..9113146b 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -8,14 +8,16 @@ import contact import design import debug -from tech import drc, parameter, spice +from tech import drc from vector import vector from globals import OPTS from sram_factory import factory + class pgate(design.design): """ - This is a module that implements some shared functions for parameterized gates. + This is a module that implements some shared + functions for parameterized gates. """ def __init__(self, name, height=None): @@ -29,78 +31,85 @@ class pgate(design.design): self.height = b.height self.create_netlist() - if not OPTS.netlist_only: + if not OPTS.netlist_only: self.create_layout() self.add_boundary() self.DRC_LVS() - def create_netlist(self): """ Pure virtual function """ - debug.error("Must over-ride create_netlist.",-1) + debug.error("Must over-ride create_netlist.", -1) def create_layout(self): """ Pure virtual function """ - debug.error("Must over-ride create_layout.",-1) + debug.error("Must over-ride create_layout.", -1) - def connect_pin_to_rail(self,inst,pin,supply): + def connect_pin_to_rail(self, inst, pin, supply): """ Connects a ptx pin to a supply rail. """ source_pin = inst.get_pin(pin) supply_pin = self.get_pin(supply) if supply_pin.overlaps(source_pin): return - if supply=="gnd": - height=supply_pin.by()-source_pin.by() - elif supply=="vdd": - height=supply_pin.uy()-source_pin.by() + if supply == "gnd": + height = supply_pin.by() - source_pin.by() + elif supply == "vdd": + height = supply_pin.uy() - source_pin.by() else: - debug.error("Invalid supply name.",-1) + debug.error("Invalid supply name.", -1) - if abs(height)>0: + if abs(height) > 0: self.add_rect(layer="metal1", offset=source_pin.ll(), height=height, width=source_pin.width()) def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=False): - """ Route the input gate to the left side of the cell for access. - Position specifies to place the contact the left, center, or right of gate. """ + """ + Route the input gate to the left side of the cell for access. + Position specifies to place the contact the left, center, or + right of gate. + """ nmos_gate_pin = nmos_inst.get_pin("G") pmos_gate_pin = pmos_inst.get_pin("G") # Check if the gates are aligned and give an error if they aren't! - debug.check(nmos_gate_pin.ll().x==pmos_gate_pin.ll().x, "Connecting unaligned gates not supported.") + debug.check(nmos_gate_pin.ll().x == pmos_gate_pin.ll().x, + "Connecting unaligned gates not supported.") # Pick point on the left of NMOS and connect down to PMOS - nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5*self.poly_width,0) - pmos_gate_pos = vector(nmos_gate_pos.x,pmos_gate_pin.bc().y) - self.add_path("poly",[nmos_gate_pos,pmos_gate_pos]) + nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5 * self.poly_width, 0) + pmos_gate_pos = vector(nmos_gate_pos.x, pmos_gate_pin.bc().y) + self.add_path("poly", [nmos_gate_pos, pmos_gate_pos]) # Add the via to the cell midpoint along the gate - left_gate_offset = vector(nmos_gate_pin.lx(),ypos) + left_gate_offset = vector(nmos_gate_pin.lx(), ypos) - # Center is completely symmetric. + # Center is completely symmetric. if rotate: contact_width = contact.poly.height contact_m1_width = contact.poly.second_layer_height contact_m1_height = contact.poly.second_layer_width - directions = ("H","V") + directions = ("H", "V") else: contact_width = contact.poly.width contact_m1_width = contact.poly.second_layer_width contact_m1_height = contact.poly.second_layer_height - directions = ("V","H") + directions = ("V", "H") - if position=="center": - contact_offset = left_gate_offset + vector(0.5*self.poly_width, 0) - elif position=="farleft": - contact_offset = left_gate_offset - vector(0.5*contact.poly.width, 0) - elif position=="left": - contact_offset = left_gate_offset - vector(0.5*contact_width - 0.5*self.poly_width, 0) - elif position=="right": - contact_offset = left_gate_offset + vector(0.5*contact.width + 0.5*self.poly_width, 0) + if position == "center": + contact_offset = left_gate_offset \ + + vector(0.5 * self.poly_width, 0) + elif position == "farleft": + contact_offset = left_gate_offset \ + - vector(0.5 * contact.poly.width, 0) + elif position == "left": + contact_offset = left_gate_offset \ + - vector(0.5 * contact_width - 0.5 * self.poly_width, 0) + elif position == "right": + contact_offset = left_gate_offset \ + + vector(0.5 * contact.width + 0.5 * self.poly_width, 0) else: debug.error("Invalid contact placement option.", -1) @@ -110,29 +119,26 @@ class pgate(design.design): offset=contact_offset, directions=directions) - # self.add_layout_pin_segment_center(text=name, - # layer="metal1", - # start=left_gate_offset.scale(0,1), - # end=left_gate_offset) self.add_layout_pin_rect_center(text=name, layer="metal1", offset=contact_offset, width=contact_m1_width, height=contact_m1_height) - - # This is to ensure that the contact is connected to the gate - mid_point = contact_offset.scale(0.5,1)+left_gate_offset.scale(0.5,0) + # This is to ensure that the contact is + # connected to the gate + mid_point = contact_offset.scale(0.5, 1) \ + + left_gate_offset.scale(0.5, 0) self.add_rect_center(layer="poly", offset=mid_point, height=contact.poly.first_layer_width, - width=left_gate_offset.x-contact_offset.x) + width=left_gate_offset.x - contact_offset.x) def extend_wells(self, middle_position): """ Extend the n/p wells to cover whole cell """ # Add a rail width to extend the well to the top of the rail - max_y_offset = self.height + 0.5*self.m1_width + max_y_offset = self.height + 0.5 * self.m1_width self.nwell_position = middle_position nwell_height = max_y_offset - middle_position.y if drc("has_nwell"): @@ -145,8 +151,8 @@ class pgate(design.design): width=self.well_width, height=nwell_height) - pwell_position = vector(0,-0.5*self.m1_width) - pwell_height = middle_position.y-pwell_position.y + pwell_position = vector(0, -0.5 * self.m1_width) + pwell_height = middle_position.y - pwell_position.y if drc("has_pwell"): self.add_rect(layer="pwell", offset=pwell_position, @@ -163,38 +169,45 @@ class pgate(design.design): layer_stack = ("active", "contact", "metal1") # To the right a spacing away from the pmos right active edge - contact_xoffset = pmos_pos.x + pmos.active_width + drc("active_to_body_active") - # Must be at least an well enclosure of active down from the top of the well + contact_xoffset = pmos_pos.x + pmos.active_width \ + + drc("active_to_body_active") + + # Must be at least an well enclosure of active down + # from the top of the well # OR align the active with the top of PMOS active. - max_y_offset = self.height + 0.5*self.m1_width + max_y_offset = self.height + 0.5 * self.m1_width contact_yoffset = min(pmos_pos.y + pmos.active_height - pmos.active_contact.first_layer_height, - max_y_offset - pmos.active_contact.first_layer_height/2 - self.well_enclose_active) + max_y_offset - pmos.active_contact.first_layer_height / 2 - self.well_enclose_active) contact_offset = vector(contact_xoffset, contact_yoffset) # Offset by half a contact in x and y - contact_offset += vector(0.5*pmos.active_contact.first_layer_width, - 0.5*pmos.active_contact.first_layer_height) - self.nwell_contact=self.add_via_center(layers=layer_stack, - offset=contact_offset, - directions=("H","V"), - implant_type="n", - well_type="n") + contact_offset += vector(0.5 * pmos.active_contact.first_layer_width, + 0.5 * pmos.active_contact.first_layer_height) + self.nwell_contact = self.add_via_center(layers=layer_stack, + offset=contact_offset, + directions=("H", "V"), + implant_type="n", + well_type="n") self.add_rect_center(layer="metal1", - offset=contact_offset + vector(0,0.5*(self.height-contact_offset.y)), + offset=contact_offset + vector(0, 0.5 * (self.height-contact_offset.y)), width=self.nwell_contact.mod.second_layer_width, height=self.height - contact_offset.y) # Now add the full active and implant for the PMOS - #active_offset = pmos_pos + vector(pmos.active_width,0) - # This might be needed if the spacing between the actives is not satisifed + # active_offset = pmos_pos + vector(pmos.active_width,0) + # This might be needed if the spacing between the actives + # is not satisifed # self.add_rect(layer="active", # offset=active_offset, # width=pmos.active_contact.width, # height=pmos.active_height) - # we need to ensure implants don't overlap and are spaced far enough apart + # we need to ensure implants don't overlap and are + # spaced far enough apart # implant_spacing = self.implant_space+self.implant_enclose_active - # implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active) - # implant_width = pmos.active_contact.width + 2*self.implant_enclose_active + # implant_offset = active_offset + vector(implant_spacing,0) \ + # - vector(0,self.implant_enclose_active) + # implant_width = pmos.active_contact.width \ + # + 2*self.implant_enclose_active # implant_height = pmos.active_height + 2*self.implant_enclose_active # self.add_rect(layer="nimplant", # offset=implant_offset, @@ -208,39 +221,45 @@ class pgate(design.design): layer_stack = ("active", "contact", "metal1") - pwell_position = vector(0,-0.5*self.m1_width) + pwell_position = vector(0, -0.5 * self.m1_width) # To the right a spacing away from the nmos right active edge - contact_xoffset = nmos_pos.x + nmos.active_width + drc("active_to_body_active") - # Must be at least an well enclosure of active up from the bottom of the well + contact_xoffset = nmos_pos.x + nmos.active_width \ + + drc("active_to_body_active") + # Must be at least an well enclosure of active up + # from the bottom of the well contact_yoffset = max(nmos_pos.y, - self.well_enclose_active - nmos.active_contact.first_layer_height/2) + self.well_enclose_active \ + - nmos.active_contact.first_layer_height / 2) contact_offset = vector(contact_xoffset, contact_yoffset) # Offset by half a contact - contact_offset += vector(0.5*nmos.active_contact.first_layer_width, - 0.5*nmos.active_contact.first_layer_height) - self.pwell_contact=self.add_via_center(layers=layer_stack, - offset=contact_offset, - directions=("H","V"), - implant_type="p", - well_type="p") + contact_offset += vector(0.5 * nmos.active_contact.first_layer_width, + 0.5 * nmos.active_contact.first_layer_height) + self.pwell_contact= self.add_via_center(layers=layer_stack, + offset=contact_offset, + directions=("H", "V"), + implant_type="p", + well_type="p") self.add_rect_center(layer="metal1", offset=contact_offset.scale(1,0.5), width=self.pwell_contact.mod.second_layer_width, height=contact_offset.y) # Now add the full active and implant for the NMOS - # active_offset = nmos_pos + vector(nmos.active_width,0) - # This might be needed if the spacing between the actives is not satisifed + # active_offset = nmos_pos + vector(nmos.active_width,0) + # This might be needed if the spacing between the actives + # is not satisifed # self.add_rect(layer="active", # offset=active_offset, # width=nmos.active_contact.width, # height=nmos.active_height) # implant_spacing = self.implant_space+self.implant_enclose_active - # implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active) - # implant_width = nmos.active_contact.width + 2*self.implant_enclose_active + # implant_offset = active_offset + vector(implant_spacing,0) \ + # - vector(0,self.implant_enclose_active) + # implant_width = nmos.active_contact.width \ + # + 2*self.implant_enclose_active # implant_height = nmos.active_height + 2*self.implant_enclose_active # self.add_rect(layer="pimplant", # offset=implant_offset, diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 275ccbe6..2b8ec7b7 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -16,6 +16,7 @@ from utils import round_to_grid import logical_effort from sram_factory import factory + class pinv(pgate.pgate): """ Pinv generates gds of a parametrically sized inverter. The @@ -28,12 +29,14 @@ class pinv(pgate.pgate): def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True): - debug.info(2, "creating 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.pmos_size = beta * size self.beta = beta self.route_output = False @@ -44,7 +47,7 @@ class pinv(pgate.pgate): self.add_pins() self.determine_tx_mults() self.add_ptx() - self.create_ptx() + self.create_ptx() def create_layout(self): """ Calls all functions related to the generation of the layout """ @@ -54,7 +57,11 @@ class pinv(pgate.pgate): 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", position="farleft") + self.route_input_gate(self.pmos_inst, + self.nmos_inst, + self.output_pos.y, + "A", + position="farleft") self.route_outputs() def add_pins(self): @@ -63,7 +70,6 @@ class pinv(pgate.pgate): dir_list = ["INPUT", "OUTPUT", "POWER", "GROUND"] self.add_pin_list(pin_list, dir_list) - def determine_tx_mults(self): """ Determines the number of fingers needed to achieve the size within @@ -73,58 +79,71 @@ class pinv(pgate.pgate): # 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") + 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) + # 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) # plus the tx height nmos = factory.create(module_type="ptx", tx_type="nmos") - pmos = factory.create(module_type="ptx", width=drc("minwidth_tx"), tx_type="pmos") + pmos = factory.create(module_type="ptx", + width=drc("minwidth_tx"), + tx_type="pmos") tx_height = nmos.poly_height + pmos.poly_height # rotated m1 pitch or poly to active spacing min_channel = max(contact.poly.width + self.m1_space, - contact.poly.width + 2*drc("poly_to_active")) - # This is the extra space needed to ensure DRC rules to the active contacts - extra_contact_space = max(-nmos.get_pin("D").by(),0) + contact.poly.width + 2 * drc("poly_to_active")) + + # This is the extra space needed to ensure DRC rules + # to the active contacts + extra_contact_space = max(-nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, drc("poly_extend_active"), self.poly_space) - total_height = tx_height + min_channel + 2*self.top_bottom_space - debug.check(self.height> total_height,"Cell height {0} too small for simple min height {1}.".format(self.height,total_height)) + total_height = tx_height + min_channel + 2 * self.top_bottom_space + debug.check(self.height > total_height, + "Cell height {0} too small for simple min height {1}.".format(self.height, + total_height)) - # Determine the height left to the transistors to determine the number of fingers - tx_height_available = self.height - min_channel - 2*self.top_bottom_space - # Divide the height in half. Could divide proportional to beta, but this makes - # connecting wells of multiple cells easier. + # Determine the height left to the transistors to determine + # the number of fingers + tx_height_available = self.height - min_channel - 2 * self.top_bottom_space + # Divide the height in half. Could divide proportional to beta, + # but this makes connecting wells of multiple cells easier. # Subtract the poly space under the rail of the tx - nmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly") - pmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly") + nmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly") + pmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly") - debug.info(2,"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available, - nmos_height_available, - pmos_height_available)) + debug.info(2, + "Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available, + nmos_height_available, + pmos_height_available)) - # Determine the number of mults for each to fit width into available space - self.nmos_width = self.nmos_size*drc("minwidth_tx") - self.pmos_width = self.pmos_size*drc("minwidth_tx") - nmos_required_mults = max(int(ceil(self.nmos_width/nmos_height_available)),1) - pmos_required_mults = max(int(ceil(self.pmos_width/pmos_height_available)),1) + # Determine the number of mults for each to fit width + # into available space + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") + nmos_required_mults = max(int(ceil(self.nmos_width / nmos_height_available)), 1) + pmos_required_mults = max(int(ceil(self.pmos_width / pmos_height_available)), 1) # The mults must be the same for easy connection of poly self.tx_mults = max(nmos_required_mults, pmos_required_mults) # Recompute each mult width and check it isn't too small # This could happen if the height is narrow and the size is small # User should pick a bigger size to fix it... - # We also need to round the width to the grid or we will end up with LVS property - # mismatch errors when fingers are not a grid length and get rounded in the offset geometry. + # We also need to round the width to the grid or we will end up + # with LVS property mismatch errors when fingers are not a grid + # length and get rounded in the offset geometry. self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults) - debug.check(self.nmos_width>=drc("minwidth_tx"),"Cannot finger NMOS transistors to fit cell height.") + debug.check(self.nmos_width >= drc("minwidth_tx"), + "Cannot finger NMOS transistors to fit cell height.") self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) - debug.check(self.pmos_width>=drc("minwidth_tx"),"Cannot finger PMOS transistors to fit cell height.") - + debug.check(self.pmos_width >= drc("minwidth_tx"), + "Cannot finger PMOS transistors to fit cell height.") def setup_layout_constants(self): """ @@ -136,9 +155,7 @@ class pinv(pgate.pgate): self.well_width = self.pmos.active_width + self.pmos.active_contact.width \ + drc("active_to_body_active") + 2*drc("well_enclosure_active") self.width = self.well_width - # Height is an input parameter, so it is not recomputed. - - + # Height is an input parameter, so it is not recomputed. def add_ptx(self): """ Create the PMOS and NMOS transistors. """ @@ -162,58 +179,57 @@ class pinv(pgate.pgate): """ 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), + 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), + offset=vector(0.5 * self.width, self.height), width=self.width) - - def create_ptx(self): - """ + """ Create the PMOS and NMOS netlist. """ - self.pmos_inst=self.add_inst(name="pinv_pmos", - mod=self.pmos) + 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.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_pos = self.pmos.active_offset.scale(1, 0) \ + + vector(0, + self.height - self.pmos.active_height - self.top_bottom_space) 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_pos = self.nmos.active_offset.scale(1, 0) \ + + vector(0, self.top_bottom_space) self.nmos_inst.place(self.nmos_pos) - # Output position will be in between the PMOS and NMOS drains pmos_drain_pos = self.pmos_inst.get_pin("D").ll() nmos_drain_pos = self.nmos_inst.get_pin("D").ul() - self.output_pos = vector(0,0.5*(pmos_drain_pos.y+nmos_drain_pos.y)) - - # This will help with the wells - self.well_pos = vector(0,self.nmos_inst.uy()) - + self.output_pos = vector(0, 0.5 * (pmos_drain_pos.y + nmos_drain_pos.y)) + # This will help with the wells + self.well_pos = vector(0, self.nmos_inst.uy()) def route_outputs(self): - """ Route the output (drains) together. Optionally, routes output to edge. """ + """ + Route the output (drains) together. + Optionally, routes output to edge. + """ # Get the drain pins nmos_drain_pin = self.nmos_inst.get_pin("D") @@ -222,14 +238,14 @@ class pinv(pgate.pgate): # Pick point at right most of NMOS and connect down to PMOS nmos_drain_pos = nmos_drain_pin.bc() pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y) - self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos]) + self.add_path("metal1", [nmos_drain_pos, pmos_drain_pos]) # Remember the mid for the output - mid_drain_offset = vector(nmos_drain_pos.x,self.output_pos.y) + mid_drain_offset = vector(nmos_drain_pos.x, self.output_pos.y) - if self.route_output == True: + if self.route_output: # This extends the output to the edge of the cell - output_offset = mid_drain_offset.scale(0,1) + vector(self.width,0) + output_offset = mid_drain_offset.scale(0, 1) + vector(self.width, 0) self.add_layout_pin_segment_center(text="Z", layer="metal1", start=mid_drain_offset, @@ -238,8 +254,8 @@ class pinv(pgate.pgate): # This leaves the output as an internal pin (min sized) self.add_layout_pin_rect_center(text="Z", layer="metal1", - offset=mid_drain_offset + vector(0.5*self.m1_width,0)) - + offset=mid_drain_offset \ + + vector(0.5 * self.m1_width, 0)) def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ @@ -251,9 +267,9 @@ class pinv(pgate.pgate): def connect_rails(self): """ Connect the nmos and pmos to its respective power rails """ - self.connect_pin_to_rail(self.nmos_inst,"S","gnd") + self.connect_pin_to_rail(self.nmos_inst, "S", "gnd") - self.connect_pin_to_rail(self.pmos_inst,"S","vdd") + self.connect_pin_to_rail(self.pmos_inst, "S", "vdd") def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" @@ -268,27 +284,35 @@ class pinv(pgate.pgate): def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) transition_prob = 0.5 - return transition_prob*(c_load + c_para) + return transition_prob * (c_load + c_para) def input_load(self): - """Return the capacitance of the gate connection in generic capacitive - units relative to the minimum width of a transistor""" + """ + Return the capacitance of the gate connection in generic capacitive + units relative to the minimum width of a transistor + """ return self.nmos_size + self.pmos_size def get_stage_effort(self, cout, inp_is_rise=True): - """Returns an object representing the parameters for delay in tau units. - Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. """ - parasitic_delay = 1 - return logical_effort.logical_effort(self.name, - self.size, - self.input_load(), - cout, - parasitic_delay, + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 1 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, not inp_is_rise) - def build_graph(self, graph, inst_name, port_nets): - """Adds edges based on inputs/outputs. Overrides base class function.""" - self.add_graph_edges(graph, port_nets) + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index a8d2c8b8..75b7ce6f 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -7,12 +7,10 @@ # import debug 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(pgate.pgate): """ This is a simple inverter/buffer used for driving loads. It is @@ -31,11 +29,10 @@ class pinvbuf(pgate.pgate): # The pinvbuf has a FO of 2 for the first stage, so the second stage # should be sized "half" to prevent loading of the first stage self.size = size - self.predriver_size = max(int(self.size/(self.stage_effort/2)),1) - - # Creates the netlist and layout - pgate.pgate.__init__(self, name) + self.predriver_size = max(int(self.size / (self.stage_effort / 2)), 1) + # Creates the netlist and layout + pgate.pgate.__init__(self, name) def create_netlist(self): self.add_pins() @@ -44,8 +41,8 @@ class pinvbuf(pgate.pgate): def create_layout(self): - self.width = 2*self.inv1.width + self.inv2.width - self.height = 2*self.inv1.height + self.width = 2 * self.inv1.width + self.inv2.width + self.height = 2 * self.inv1.height self.place_modules() self.route_wires() @@ -53,7 +50,6 @@ class pinvbuf(pgate.pgate): self.offset_all_coordinates() - def add_pins(self): self.add_pin("A") self.add_pin("Zb") @@ -64,96 +60,100 @@ class pinvbuf(pgate.pgate): def add_modules(self): # Shield the cap, but have at least a stage effort of 4 - input_size = max(1,int(self.predriver_size/self.stage_effort)) - self.inv = factory.create(module_type="pinv", size=input_size, height=self.row_height) + input_size = max(1, int(self.predriver_size / self.stage_effort)) + self.inv = factory.create(module_type="pinv", + size=input_size, + height=self.row_height) self.add_mod(self.inv) - self.inv1 = factory.create(module_type="pinv", size=self.predriver_size, height=self.row_height) + self.inv1 = factory.create(module_type="pinv", + size=self.predriver_size, + height=self.row_height) self.add_mod(self.inv1) - self.inv2 = factory.create(module_type="pinv", size=self.size, height=self.row_height) + self.inv2 = factory.create(module_type="pinv", + size=self.size, + height=self.row_height) self.add_mod(self.inv2) def create_insts(self): # Create INV1 (capacitance shield) - self.inv1_inst=self.add_inst(name="buf_inv1", - mod=self.inv) - self.connect_inst(["A", "zb_int", "vdd", "gnd"]) + self.inv1_inst = self.add_inst(name="buf_inv1", + mod=self.inv) + self.connect_inst(["A", "zb_int", "vdd", "gnd"]) - - self.inv2_inst=self.add_inst(name="buf_inv2", - mod=self.inv1) - self.connect_inst(["zb_int", "z_int", "vdd", "gnd"]) + self.inv2_inst = self.add_inst(name="buf_inv2", + mod=self.inv1) + self.connect_inst(["zb_int", "z_int", "vdd", "gnd"]) - self.inv3_inst=self.add_inst(name="buf_inv3", - mod=self.inv2) - self.connect_inst(["z_int", "Zb", "vdd", "gnd"]) + self.inv3_inst = self.add_inst(name="buf_inv3", + mod=self.inv2) + self.connect_inst(["z_int", "Zb", "vdd", "gnd"]) - self.inv4_inst=self.add_inst(name="buf_inv4", - mod=self.inv2) - self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + self.inv4_inst = self.add_inst(name="buf_inv4", + mod=self.inv2) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) def place_modules(self): # Add INV1 to the left (capacitance shield) - self.inv1_inst.place(vector(0,0)) + self.inv1_inst.place(vector(0, 0)) # Add INV2 to the right of INV1 - self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) + self.inv2_inst.place(vector(self.inv1_inst.rx(), 0)) # Add INV3 to the right of INV2 - self.inv3_inst.place(vector(self.inv2_inst.rx(),0)) + self.inv3_inst.place(vector(self.inv2_inst.rx(), 0)) # Add INV4 flipped to the bottom aligned with INV2 - self.inv4_inst.place(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): # inv1 Z to inv2 A z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") - mid_point = vector(z1_pin.cx(), a2_pin.cy()) + mid_point = vector(z1_pin.cx(), a2_pin.cy()) self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) # inv2 Z to inv3 A z2_pin = self.inv2_inst.get_pin("Z") a3_pin = self.inv3_inst.get_pin("A") - mid_point = vector(z2_pin.cx(), a3_pin.cy()) + mid_point = vector(z2_pin.cx(), a3_pin.cy()) self.add_path("metal1", [z2_pin.center(), mid_point, a3_pin.center()]) # inv1 Z to inv4 A (up and over) z1_pin = self.inv1_inst.get_pin("Z") a4_pin = self.inv4_inst.get_pin("A") - mid_point = vector(z1_pin.cx(), a4_pin.cy()) - self.add_wire(("metal1","via1","metal2"), [z1_pin.center(), mid_point, a4_pin.center()]) - self.add_via_center(layers=("metal1","via1","metal2"), + mid_point = vector(z1_pin.cx(), a4_pin.cy()) + self.add_wire(("metal1", "via1", "metal2"), + [z1_pin.center(), mid_point, a4_pin.center()]) + self.add_via_center(layers=("metal1", "via1", "metal2"), offset=z1_pin.center()) - - def add_layout_pins(self): # Continous vdd rail along with label. - vdd_pin=self.inv1_inst.get_pin("vdd") + vdd_pin = self.inv1_inst.get_pin("vdd") self.add_layout_pin(text="vdd", layer="metal1", - offset=vdd_pin.ll().scale(0,1), + offset=vdd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) # Continous vdd rail along with label. - gnd_pin=self.inv4_inst.get_pin("gnd") + gnd_pin = self.inv4_inst.get_pin("gnd") self.add_layout_pin(text="gnd", layer="metal1", - offset=gnd_pin.ll().scale(0,1), + offset=gnd_pin.ll().scale(0, 1), width=self.width, height=gnd_pin.height()) # Continous gnd rail along with label. - gnd_pin=self.inv1_inst.get_pin("gnd") + gnd_pin = self.inv1_inst.get_pin("gnd") self.add_layout_pin(text="gnd", layer="metal1", - offset=gnd_pin.ll().scale(0,1), + offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) @@ -161,22 +161,21 @@ class pinvbuf(pgate.pgate): self.add_layout_pin_rect_center(text="Z", layer="metal2", offset=z_pin.center()) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=("metal1", "via1", "metal2"), offset=z_pin.center()) zb_pin = self.inv3_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Zb", layer="metal2", offset=zb_pin.center()) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=("metal1", "via1", "metal2"), offset=zb_pin.center()) - a_pin = self.inv1_inst.get_pin("A") self.add_layout_pin_rect_center(text="A", layer="metal2", offset=a_pin.center()) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=("metal1", "via1", "metal2"), offset=a_pin.center()) def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False): @@ -194,7 +193,8 @@ class pinvbuf(pgate.pgate): def determine_clk_buf_bar_stage_efforts(self, external_cout, inp_is_rise=False): """Get the stage efforts of the clk -> clk_buf path""" - #After (almost) every stage, the direction of the signal inverts. + + # After (almost) every stage, the direction of the signal inverts. stage_effort_list = [] stage1_cout = self.inv1.get_cin() + self.inv2.get_cin() stage1 = self.inv.get_stage_effort(stage1_cout, inp_is_rise) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 791097e5..c5c69fd4 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -10,10 +10,10 @@ import pgate import debug from tech import drc, parameter, spice from vector import vector -from globals import OPTS import logical_effort from sram_factory import factory + class pnand2(pgate.pgate): """ This module generates gds of a parametrically sized 2-input nand. @@ -22,17 +22,19 @@ class pnand2(pgate.pgate): def __init__(self, name, size=1, height=None): """ Creates a cell for a simple 2 input nand """ - debug.info(2, "creating 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 - self.nmos_size = 2*size - self.pmos_size = parameter["beta"]*size - self.nmos_width = self.nmos_size*drc("minwidth_tx") - self.pmos_width = self.pmos_size*drc("minwidth_tx") + self.nmos_size = 2 * size + self.pmos_size = parameter["beta"] * size + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") # FIXME: Allow these to be sized - debug.check(size==1,"Size 1 pnand2 is only supported now.") + debug.check(size == 1, "Size 1 pnand2 is only supported now.") self.tx_mults = 1 # Creates the netlist and layout @@ -61,7 +63,6 @@ class pnand2(pgate.pgate): dir_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] self.add_pin_list(pin_list, dir_list) - def add_ptx(self): """ Create the PMOS and NMOS transistors. """ self.nmos = factory.create(module_type="ptx", @@ -90,113 +91,126 @@ class pnand2(pgate.pgate): self.m3_space + contact.m2m3.second_layer_width) - # Compute the other pmos2 location, but determining offset to overlap the + # 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 + contact.active.width \ - + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") + self.well_width = 2 * self.pmos.active_width + contact.active.width \ + + 2 * drc("active_to_body_active") \ + + 2 * drc("well_enclosure_active") self.width = self.well_width # Height is an input parameter, so it is not recomputed. - # This is the extra space needed to ensure DRC rules to the active contacts - extra_contact_space = max(-self.nmos.get_pin("D").by(),0) + # This is the extra space needed to ensure DRC rules + # to the active contacts + extra_contact_space = max(-self.nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell - self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, + self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space, drc("poly_extend_active"), self.poly_space) 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), + 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), + offset=vector(0.5 * self.width, self.height), width=self.width) def create_ptx(self): - """ + """ Add PMOS and NMOS to the netlist. """ - self.pmos1_inst=self.add_inst(name="pnand2_pmos1", - mod=self.pmos) + 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.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.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.height - self.pmos.active_height \ + - self.top_bottom_space) self.pmos1_inst.place(pmos1_pos) self.pmos2_pos = pmos1_pos + self.overlap_offset self.pmos2_inst.place(self.pmos2_pos) - - nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space) + nmos1_pos = vector(self.pmos.active_offset.x, + self.top_bottom_space) self.nmos1_inst.place(nmos1_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*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height)) + # 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)) - # This will help with the wells - self.well_pos = vector(0,self.nmos1_inst.uy()) + # This will help with the wells + self.well_pos = vector(0, self.nmos1_inst.uy()) def add_well_contacts(self): - """ Add n/p well taps to the layout and connect to supplies AFTER the wells are created """ + """ + Add n/p well taps to the layout and connect to supplies + AFTER the wells are created + """ self.add_nwell_contact(self.pmos, self.pmos2_pos) self.add_pwell_contact(self.nmos, self.nmos2_pos) - 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.nmos1_inst, "S", "gnd") - self.connect_pin_to_rail(self.pmos1_inst,"S","vdd") + self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd") - self.connect_pin_to_rail(self.pmos2_inst,"D","vdd") + self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd") def route_inputs(self): """ Route the A and B inputs """ - inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height + self.m2_space + 0.5*self.m2_width - self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center") + inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \ + + self.m2_space + 0.5 * self.m2_width + self.route_input_gate(self.pmos2_inst, + self.nmos2_inst, + inputB_yoffset, + "B", + position="center") # This will help with the wells and the input/output placement self.inputA_yoffset = inputB_yoffset + self.input_spacing - self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A") + self.route_input_gate(self.pmos1_inst, + self.nmos1_inst, + self.inputA_yoffset, + "A") - def route_output(self): """ Route the Z output """ - # PMOS1 drain + # PMOS1 drain pmos_pin = self.pmos1_inst.get_pin("D") top_pin_offset = pmos_pin.center() # NMOS2 drain @@ -204,24 +218,26 @@ class pnand2(pgate.pgate): bottom_pin_offset = nmos_pin.center() # Output pin - out_offset = vector(nmos_pin.center().x + self.m1_pitch,self.inputA_yoffset) + out_offset = vector(nmos_pin.center().x + self.m1_pitch, + self.inputA_yoffset) # Midpoints of the L routes go horizontal first then vertical mid1_offset = vector(out_offset.x, top_pin_offset.y) - mid2_offset = vector(out_offset.x, bottom_pin_offset.y) + mid2_offset = vector(out_offset.x, bottom_pin_offset.y) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=pmos_pin.center(), - directions=("V","H")) + directions=("V", "H")) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=nmos_pin.center(), - directions=("V","H")) + directions=("V", "H")) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=out_offset) - # PMOS1 to mid-drain to NMOS2 drain - self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset]) + self.add_path("metal2", + [top_pin_offset, mid1_offset, out_offset, + mid2_offset, bottom_pin_offset]) # This extends the output to the edge of the cell self.add_layout_pin_rect_center(text="Z", @@ -243,21 +259,32 @@ class pnand2(pgate.pgate): def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) transition_prob = 0.1875 - return transition_prob*(c_load + c_para) + return transition_prob * (c_load + c_para) def input_load(self): """Return the relative input capacitance of a single input""" - return self.nmos_size+self.pmos_size + return self.nmos_size + self.pmos_size def get_stage_effort(self, cout, inp_is_rise=True): - """Returns an object representing the parameters for delay in tau units. - Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. """ - parasitic_delay = 2 - return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise) + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 2 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) - def build_graph(self, graph, inst_name, port_nets): - """Adds edges based on inputs/outputs. Overrides base class function.""" - self.add_graph_edges(graph, port_nets) + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 508db024..621829f1 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -10,10 +10,10 @@ import pgate import debug from tech import drc, parameter, spice from vector import vector -from globals import OPTS import logical_effort from sram_factory import factory + class pnand3(pgate.pgate): """ This module generates gds of a parametrically sized 2-input nand. @@ -22,24 +22,25 @@ class pnand3(pgate.pgate): def __init__(self, name, size=1, height=None): """ Creates a cell for a simple 3 input nand """ - debug.info(2, "creating 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... # If we relax this, we could size this better. self.size = size - self.nmos_size = 2*size - self.pmos_size = parameter["beta"]*size - self.nmos_width = self.nmos_size*drc("minwidth_tx") - self.pmos_width = self.pmos_size*drc("minwidth_tx") + self.nmos_size = 2 * size + self.pmos_size = parameter["beta"] * size + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") # FIXME: Allow these to be sized - debug.check(size==1,"Size 1 pnand3 is only supported now.") + debug.check(size == 1,"Size 1 pnand3 is only supported now.") self.tx_mults = 1 # Creates the netlist and layout pgate.pgate.__init__(self, name, height) - def add_pins(self): """ Adds pins for spice netlist """ @@ -90,41 +91,44 @@ class pnand3(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. - self.well_width = 3*self.pmos.active_width + self.pmos.active_contact.width \ - + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") \ + self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \ + + 2 * drc("active_to_body_active") + 2 * drc("well_enclosure_active") \ - self.overlap_offset.x self.width = self.well_width # Height is an input parameter, so it is not recomputed. # This will help with the wells and the input/output placement - self.output_pos = vector(0,0.5*self.height) + self.output_pos = vector(0, 0.5*self.height) - # This is the extra space needed to ensure DRC rules to the active contacts + # This is the extra space needed to ensure DRC rules + # to the active contacts nmos = factory.create(module_type="ptx", tx_type="nmos") - extra_contact_space = max(-nmos.get_pin("D").by(),0) + extra_contact_space = max(-nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell - self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc("poly_extend_active"), self.poly_space) + self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space \ + + extra_contact_space, + drc("poly_extend_active"), + self.poly_space) 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), + 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), + offset=vector(0.5 * self.width, self.height), width=self.width) def create_ptx(self): - """ + """ Create the PMOS and NMOS in the netlist. """ - self.pmos1_inst=self.add_inst(name="pnand3_pmos1", - mod=self.pmos) + 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", @@ -135,27 +139,27 @@ class pnand3(pgate.pgate): mod=self.pmos) self.connect_inst(["Z", "C", "vdd", "vdd"]) - self.nmos1_inst=self.add_inst(name="pnand3_nmos1", - mod=self.nmos) + 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.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.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 + """ + 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.height - self.pmos.active_height \ + - self.top_bottom_space) self.pmos1_inst.place(pmos1_pos) pmos2_pos = pmos1_pos + self.overlap_offset @@ -165,7 +169,8 @@ class pnand3(pgate.pgate): self.pmos3_inst.place(self.pmos3_pos) - nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space) + nmos1_pos = vector(self.pmos.active_offset.x, + self.top_bottom_space) self.nmos1_inst.place(nmos1_pos) nmos2_pos = nmos1_pos + self.overlap_offset @@ -175,7 +180,7 @@ class pnand3(pgate.pgate): 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()) + self.well_pos = vector(0, self.nmos1_inst.uy()) def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ @@ -183,42 +188,53 @@ class pnand3(pgate.pgate): self.add_nwell_contact(self.pmos, self.pmos3_pos) self.add_pwell_contact(self.nmos, self.nmos3_pos) - 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.nmos1_inst, "S", "gnd") - self.connect_pin_to_rail(self.pmos1_inst,"S","vdd") + self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd") - self.connect_pin_to_rail(self.pmos2_inst,"D","vdd") + self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd") def route_inputs(self): """ Route the A and B inputs """ # wire space or wire and one contact space - metal_spacing = max(self.m1_space + self.m1_width, self.m2_space + self.m2_width, - self.m1_space + 0.5*contact.poly.width + 0.5*self.m1_width) + metal_spacing = max(self.m1_space + self.m1_width, + self.m2_space + self.m2_width, + self.m1_space + 0.5 *contact.poly.width + 0.5 * self.m1_width) - active_spacing = max(self.m1_space, 0.5*contact.poly.first_layer_width + drc("poly_to_active")) + active_spacing = max(self.m1_space, + 0.5 * contact.poly.first_layer_width + drc("poly_to_active")) inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing - self.route_input_gate(self.pmos3_inst, self.nmos3_inst, inputC_yoffset, "C", position="center") + self.route_input_gate(self.pmos3_inst, + self.nmos3_inst, + inputC_yoffset, + "C", + position="center") inputB_yoffset = inputC_yoffset + metal_spacing - self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center") + self.route_input_gate(self.pmos2_inst, + self.nmos2_inst, + inputB_yoffset, + "B", + position="center") self.inputA_yoffset = inputB_yoffset + metal_spacing - self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A", position="center") - - + self.route_input_gate(self.pmos1_inst, + self.nmos1_inst, + self.inputA_yoffset, + "A", + position="center") def route_output(self): """ Route the Z output """ - # PMOS1 drain + # PMOS1 drain pmos1_pin = self.pmos1_inst.get_pin("D") - # PMOS3 drain + # PMOS3 drain pmos3_pin = self.pmos3_inst.get_pin("D") # NMOS3 drain - nmos3_pin = self.nmos3_inst.get_pin("D") + nmos3_pin = self.nmos3_inst.get_pin("D") # Go up to metal2 for ease on all output pins self.add_via_center(layers=("metal1", "via1", "metal2"), @@ -229,10 +245,10 @@ class pnand3(pgate.pgate): offset=nmos3_pin.center()) # PMOS3 and NMOS3 are drain aligned - self.add_path("metal2",[pmos3_pin.bc(), nmos3_pin.uc()]) + self.add_path("metal2", [pmos3_pin.bc(), nmos3_pin.uc()]) # Route in the A input track (top track) - mid_offset = vector(nmos3_pin.center().x,self.inputA_yoffset) - self.add_path("metal2",[pmos1_pin.bc(), mid_offset, nmos3_pin.uc()]) + mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) + self.add_path("metal2", [pmos1_pin.bc(), mid_offset, nmos3_pin.uc()]) # This extends the output to the edge of the cell self.add_via_center(layers=("metal1", "via1", "metal2"), @@ -256,21 +272,32 @@ class pnand3(pgate.pgate): def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) transition_prob = 0.1094 - return transition_prob*(c_load + c_para) + return transition_prob *(c_load + c_para) def input_load(self): """Return the relative input capacitance of a single input""" - return self.nmos_size+self.pmos_size + return self.nmos_size + self.pmos_size def get_stage_effort(self, cout, inp_is_rise=True): - """Returns an object representing the parameters for delay in tau units. - Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. """ - parasitic_delay = 3 - return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise) + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 3 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) - def build_graph(self, graph, inst_name, port_nets): - """Adds edges based on inputs/outputs. Overrides base class function.""" - self.add_graph_edges(graph, port_nets) + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index ed7388e9..a99253cd 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -10,10 +10,9 @@ import pgate import debug from tech import drc, parameter, spice from vector import vector -from globals import OPTS -import logical_effort from sram_factory import factory + class pnor2(pgate.pgate): """ This module generates gds of a parametrically sized 2-input nor. @@ -22,22 +21,23 @@ class pnor2(pgate.pgate): def __init__(self, name, size=1, height=None): """ Creates a cell for a simple 2 input nor """ - debug.info(2, "creating 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 # We will just make this 1.5 times for now. NORs are not ideal anyhow. - self.pmos_size = 1.5*parameter["beta"]*size - self.nmos_width = self.nmos_size*drc("minwidth_tx") - self.pmos_width = self.pmos_size*drc("minwidth_tx") + self.pmos_size = 1.5 * parameter["beta"] * size + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") # FIXME: Allow these to be sized - debug.check(size==1,"Size 1 pnor2 is only supported now.") + debug.check(size==1, "Size 1 pnor2 is only supported now.") self.tx_mults = 1 # Creates the netlist and layout pgate.pgate.__init__(self, name, height) - def create_netlist(self): self.add_pins() @@ -89,44 +89,48 @@ class pnor2(pgate.pgate): self.m2_space + contact.m2m3.first_layer_width, self.m3_space + contact.m2m3.second_layer_width) - # Compute the other pmos2 location, but determining offset to overlap the - # source and drain pins + # 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 + self.pmos.active_contact.width \ - + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") + self.well_width = 2 * self.pmos.active_width \ + + self.pmos.active_contact.width \ + + 2 * drc("active_to_body_active") \ + + 2 * drc("well_enclosure_active") self.width = self.well_width # Height is an input parameter, so it is not recomputed. - # This is the extra space needed to ensure DRC rules to the active contacts - extra_contact_space = max(-self.nmos.get_pin("D").by(),0) + # This is the extra space needed to ensure DRC rules + # to the active contacts + extra_contact_space = max(-self.nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell - self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc("poly_extend_active"), self.poly_space) + self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space, + drc("poly_extend_active"), + self.poly_space) 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), + 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), + offset=vector(0.5 * self.width, self.height), width=self.width) def create_ptx(self): - """ + """ Add PMOS and NMOS to the layout at the upper-most and lowest position to provide maximum routing in channel """ - self.pmos1_inst=self.add_inst(name="pnor2_pmos1", - mod=self.pmos) + self.pmos1_inst = self.add_inst(name="pnor2_pmos1", + mod=self.pmos) self.connect_inst(["vdd", "A", "net1", "vdd"]) self.pmos2_inst = self.add_inst(name="pnor2_pmos2", @@ -134,23 +138,23 @@ class pnor2(pgate.pgate): self.connect_inst(["net1", "B", "Z", "vdd"]) - self.nmos1_inst=self.add_inst(name="pnor2_nmos1", - mod=self.nmos) + self.nmos1_inst = self.add_inst(name="pnor2_nmos1", + mod=self.nmos) self.connect_inst(["Z", "A", "gnd", "gnd"]) - self.nmos2_inst=self.add_inst(name="pnor2_nmos2", - mod=self.nmos) + self.nmos2_inst = self.add_inst(name="pnor2_nmos2", + mod=self.nmos) self.connect_inst(["Z", "B", "gnd", "gnd"]) - def place_ptx(self): - """ + """ Add 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.height - self.pmos.active_height \ + - self.top_bottom_space) self.pmos1_inst.place(pmos1_pos) self.pmos2_pos = pmos1_pos + self.overlap_offset @@ -162,42 +166,49 @@ class pnor2(pgate.pgate): 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*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height)) + # 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)) - # This will help with the wells - self.well_pos = vector(0,self.nmos1_inst.uy()) + # This will help with the wells + self.well_pos = vector(0, self.nmos1_inst.uy()) def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ self.add_nwell_contact(self.pmos, self.pmos2_pos) self.add_pwell_contact(self.nmos, self.nmos2_pos) - 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.nmos1_inst, "S", "gnd") - self.connect_pin_to_rail(self.nmos2_inst,"D","gnd") - - self.connect_pin_to_rail(self.pmos1_inst,"S","vdd") + self.connect_pin_to_rail(self.nmos2_inst, "D", "gnd") + self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd") def route_inputs(self): """ Route the A and B inputs """ # Use M2 spaces so we can drop vias on the pins later! - inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height + self.m2_space + self.m2_width - self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center") + inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \ + + self.m2_space + self.m2_width + self.route_input_gate(self.pmos2_inst, + self.nmos2_inst, + inputB_yoffset, + "B", + position="center") # This will help with the wells and the input/output placement self.inputA_yoffset = inputB_yoffset + self.input_spacing - self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A") + self.route_input_gate(self.pmos1_inst, + self.nmos1_inst, + self.inputA_yoffset, + "A") def route_output(self): """ Route the Z output """ - # PMOS2 drain + # PMOS2 drain pmos_pin = self.pmos2_inst.get_pin("D") # NMOS1 drain nmos_pin = self.nmos1_inst.get_pin("D") @@ -207,17 +218,18 @@ class pnor2(pgate.pgate): # Go up to metal2 for ease on all output pins self.add_via_center(layers=("metal1", "via1", "metal2"), offset=pmos_pin.center()) - m1m2_contact=self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=nmos_pin.center()) - + m1m2_contact = self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=nmos_pin.center()) - mid1_offset = vector(pmos_pin.center().x,nmos2_pin.center().y) - mid2_offset = vector(pmos_pin.center().x,self.inputA_yoffset) - mid3_offset = mid2_offset + vector(self.m2_width,0) + mid1_offset = vector(pmos_pin.center().x, nmos2_pin.center().y) + mid2_offset = vector(pmos_pin.center().x, self.inputA_yoffset) + mid3_offset = mid2_offset + vector(self.m2_width, 0) # PMOS1 to mid-drain to NMOS2 drain - self.add_path("metal2",[pmos_pin.bc(), mid2_offset, mid3_offset]) - self.add_path("metal2",[nmos_pin.rc(), mid1_offset, mid2_offset]) + self.add_path("metal2", + [pmos_pin.bc(), mid2_offset, mid3_offset]) + self.add_path("metal2", + [nmos_pin.rc(), mid1_offset, mid2_offset]) # This extends the output to the edge of the cell self.add_via_center(layers=("metal1", "via1", "metal2"), offset=mid3_offset) @@ -240,10 +252,11 @@ class pnor2(pgate.pgate): def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) transition_prob = 0.1875 - return transition_prob*(c_load + c_para) + return transition_prob * (c_load + c_para) - def build_graph(self, graph, inst_name, port_nets): + def build_graph(self, graph, inst_name, port_nets): """Adds edges based on inputs/outputs. Overrides base class function.""" - self.add_graph_edges(graph, port_nets) + self.add_graph_edges(graph, port_nets) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index b4423bed..bd391ecc 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -13,6 +13,7 @@ from vector import vector from globals import OPTS from sram_factory import factory + class precharge(design.design): """ Creates a single precharge cell @@ -25,16 +26,15 @@ class precharge(design.design): self.bitcell = factory.create(module_type="bitcell") self.beta = parameter["beta"] - self.ptx_width = self.beta*parameter["min_tx_size"] + self.ptx_width = self.beta * parameter["min_tx_size"] self.width = self.bitcell.width self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br - # Creates the netlist and layout # Since it has variable height, it is not a pgate. self.create_netlist() - if not OPTS.netlist_only: + if not OPTS.netlist_only: self.create_layout() self.DRC_LVS() @@ -53,7 +53,8 @@ class precharge(design.design): self.connect_to_bitlines() def add_pins(self): - self.add_pin_list(["bl", "br", "en_bar", "vdd"], ["OUTPUT", "OUTPUT", "INPUT", "POWER"]) + self.add_pin_list(["bl", "br", "en_bar", "vdd"], + ["OUTPUT", "OUTPUT", "INPUT", "POWER"]) def add_ptx(self): """ @@ -64,16 +65,13 @@ class precharge(design.design): tx_type="pmos") self.add_mod(self.pmos) - - - 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.5*self.width, self.height) + vdd_position = vector(0.5 * self.width, self.height) self.add_rect_center(layer="metal1", offset=vdd_position, width=self.width, @@ -87,44 +85,43 @@ class precharge(design.design): # Add vdd pin above the transistor self.add_power_pin("vdd", pmos_pin.center(), vertical=True) - 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.lower_pmos_inst = self.add_inst(name="lower_pmos", + mod=self.pmos) self.connect_inst(["bl", "en_bar", "br", "vdd"]) - self.upper_pmos1_inst=self.add_inst(name="upper_pmos1", - mod=self.pmos) + self.upper_pmos1_inst = self.add_inst(name="upper_pmos1", + mod=self.pmos) self.connect_inst(["bl", "en_bar", "vdd", "vdd"]) - self.upper_pmos2_inst=self.add_inst(name="upper_pmos2", - mod=self.pmos) + self.upper_pmos2_inst = self.add_inst(name="upper_pmos2", + mod=self.pmos) self.connect_inst(["br", "en_bar", "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 + # Compute the other pmos2 location, + # but determining offset to overlap the source and drain pins overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() # This is how much the contact is placed inside the ptx active contact_xdiff = self.pmos.get_pin("S").lx() # adds the lower pmos to layout bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx() - self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, self.well_enclose_active), + self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, + self.well_enclose_active), self.pmos.active_offset.y) 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 + 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.place(self.upper_pmos1_pos) @@ -146,7 +143,9 @@ class precharge(design.design): # connects the two poly for the two upper pmos(s) offset = offset + vector(0, ylength - self.poly_width) - xlength = self.upper_pmos2_inst.get_pin("G").lx() - self.upper_pmos1_inst.get_pin("G").lx() + self.poly_width + xlength = self.upper_pmos2_inst.get_pin("G").lx() \ + - self.upper_pmos1_inst.get_pin("G").lx() \ + + self.poly_width self.add_rect(layer="poly", offset=offset, width=xlength, @@ -158,16 +157,16 @@ class precharge(design.design): """ # 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) + offset = self.lower_pmos_inst.get_pin("G").ul() \ + + vector(0, 0.5 * self.poly_space) self.add_via_center(layers=("poly", "contact", "metal1"), offset=offset) # adds the en rail on metal1 self.add_layout_pin_segment_center(text="en_bar", layer="metal1", - start=offset.scale(0,1), - end=offset.scale(0,1)+vector(self.width,0)) - + start=offset.scale(0, 1), + end=offset.scale(0, 1) + vector(self.width, 0)) def place_nwell_and_contact(self): """ @@ -175,8 +174,9 @@ class precharge(design.design): """ # adds the contact from active to metal1 - well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \ - + vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active")) + well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \ + + vector(0, self.upper_pmos1_inst.uy() + contact.well.height / 2 \ + + drc("well_extend_active")) self.add_via_center(layers=("active", "contact", "metal1"), offset=well_contact_pos, implant_type="n", @@ -187,18 +187,18 @@ class precharge(design.design): # nwell should span the whole design since it is pmos only self.add_rect(layer="nwell", - offset=vector(0,0), + offset=vector(0, 0), width=self.width, height=self.height) - 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) + offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(), 0) \ + - vector(0.5 * self.m2_width, 0) self.bl_pin = self.add_layout_pin(text="bl", layer="metal2", offset=offset, @@ -206,7 +206,8 @@ class precharge(design.design): height=self.height) # adds the BR on metal 2 - offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(),0) - vector(0.5 * self.m2_width,0) + offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(), 0) \ + - vector(0.5 * self.m2_width, 0) self.br_pin = self.add_layout_pin(text="br", layer="metal2", offset=offset, @@ -218,60 +219,64 @@ class precharge(design.design): Connect the bitlines to the devices """ self.add_bitline_contacts() - self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl")) - self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl")) - self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),self.get_pin("br")) - self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br")) - + self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"), + self.get_pin("bl")) + self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"), + self.get_pin("bl")) + self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"), + self.get_pin("br")) + self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"), + self.get_pin("br")) def add_bitline_contacts(self): """ Adds contacts/via from metal1 to metal2 for bit-lines """ - stack=("metal1", "via1", "metal2") + stack = ("metal1", "via1", "metal2") upper_pin = self.upper_pmos1_inst.get_pin("S") lower_pin = self.lower_pmos_inst.get_pin("S") # BL goes up to M2 at the transistor - self.bl_contact=self.add_via_center(layers=stack, - offset=upper_pin.center(), - directions=("V","V")) + self.bl_contact =self.add_via_center(layers=stack, + offset=upper_pin.center(), + directions=("V", "V")) self.add_via_center(layers=stack, offset=lower_pin.center(), - directions=("V","V")) + directions=("V", "V")) # BR routes over on M1 first self.add_via_center(layers=stack, - offset = vector(self.br_pin.cx(), upper_pin.cy()), - directions=("V","V")) + offset=vector(self.br_pin.cx(), upper_pin.cy()), + directions=("V", "V")) self.add_via_center(layers=stack, - offset = vector(self.br_pin.cx(), lower_pin.cy()), - directions=("V","V")) + offset=vector(self.br_pin.cx(), lower_pin.cy()), + directions=("V", "V")) def connect_pmos_m1(self, pmos_pin, bit_pin): - """ - Connect a pmos pin to bitline pin + """ + Connect a pmos pin to bitline pin """ - left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) - right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) + left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) + right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) - self.add_path("metal1", [ left_pos, right_pos] ) + self.add_path("metal1", [left_pos, right_pos] ) def connect_pmos_m2(self, pmos_pin, bit_pin): - """ - Connect a pmos pin to bitline pin + """ + Connect a pmos pin to bitline pin """ - left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) - right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) + left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) + right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) - self.add_path("metal2", [ left_pos, right_pos], self.bl_contact.height) + self.add_path("metal2", [left_pos, right_pos], self.bl_contact.height) def get_en_cin(self): - """Get the relative capacitance of the enable in the precharge cell""" - #The enable connect to three pmos gates. They all use the same size pmos. + """Get the relative capacitance of the enable in the precharge cell""" + # The enable connect to three pmos gates + # They all use the same size pmos. pmos_cin = self.pmos.get_cin() - return 3*pmos_cin + return 3 * pmos_cin diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py index 0564bf86..2a972407 100644 --- a/compiler/pgates/ptristate_inv.py +++ b/compiler/pgates/ptristate_inv.py @@ -10,16 +10,12 @@ 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. - + 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. @@ -27,14 +23,16 @@ class ptristate_inv(pgate.pgate): def __init__(self, name, size=1, height=None): - debug.info(2, "creating ptristate inv {0} with size of {1}".format(name, size)) + 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.size = 2 * size self.nmos_size = size self.beta = parameter["beta"] - self.pmos_size = self.beta*size + self.pmos_size = self.beta * size self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") @@ -75,10 +73,10 @@ class ptristate_inv(pgate.pgate): # 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") + 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 + 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 @@ -104,43 +102,44 @@ class ptristate_inv(pgate.pgate): """ 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), + 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), + 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.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.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.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 + 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) @@ -154,21 +153,24 @@ class ptristate_inv(pgate.pgate): 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)) + # 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()) - + # 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_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. """ @@ -181,40 +183,41 @@ class ptristate_inv(pgate.pgate): self.add_layout_pin(text="out", layer="metal1", offset=nmos_drain_pos, - height=pmos_drain_pos.y-nmos_drain_pos.y) - + 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 """ + """ + 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") + 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") - - + 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") + self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd") + self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd") 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() + # Power in this module currently not defined. + # Returns 0 nW (leakage and dynamic). + total_power = self.return_power() return total_power def get_cin(self): - return 9*spice["min_tx_gate_c"] + return 9 * spice["min_tx_gate_c"] diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 79a29d07..3cc767d7 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -9,9 +9,9 @@ import design import debug from tech import drc, spice from vector import vector -from globals import OPTS from sram_factory import factory + class ptx(design.design): """ This module generates gds and spice of a parametrically NMOS or @@ -21,7 +21,14 @@ class ptx(design.design): you to connect the fingered gates and active for parallel devices. """ - def __init__(self, name="", width=drc("minwidth_tx"), mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None): + def __init__(self, + name="", + width=drc("minwidth_tx"), + mults=1, + tx_type="nmos", + connect_active=False, + connect_poly=False, + num_contacts=None): # 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 @@ -34,7 +41,7 @@ class ptx(design.design): if num_contacts: name += "_c{}".format(num_contacts) # replace periods with underscore for newer spice compatibility - name=name.replace('.','_') + name = name.replace('.', '_') debug.info(3, "creating ptx {0}".format(name)) design.design.__init__(self, name) @@ -51,42 +58,43 @@ class ptx(design.design): # We must always create ptx layout for pbitcell # some transistor sizes in other netlist depend on pbitcell self.create_layout() - - def create_layout(self): """Calls all functions related to the generation of the layout""" self.setup_layout_constants() self.add_active() - self.add_well_implant() + 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() + # self.DRC() def create_netlist(self): pin_list = ["D", "G", "S", "B"] - if self.tx_type=="nmos": + if self.tx_type == "nmos": body_dir = 'GROUND' - else: #Assumed that the check for either pmos or nmos is done elsewhere. + else: + # Assumed that the check for either pmos or nmos is done elsewhere. body_dir = 'POWER' dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir] self.add_pin_list(pin_list, dir_list) # self.spice.append("\n.SUBCKT {0} {1}".format(self.name, # " ".join(self.pins))) - # Just make a guess since these will actually be decided in the layout later. - area_sd = 2.5*drc("minwidth_poly")*self.tx_width - perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width - self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4:.2f}u ps={4:.2f}u as={5:.2f}p ad={5:.2f}p".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc("minwidth_poly"), - perimeter_sd, - area_sd) + # Just make a guess since these will actually + # be decided in the layout later. + area_sd = 2.5 * drc("minwidth_poly") * self.tx_width + perimeter_sd = 2 * drc("minwidth_poly") + 2 * self.tx_width + main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) + area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd, + area_sd) + self.spice_device= main_str + area_str self.spice.append("\n* ptx " + self.spice_device) # self.spice.append(".ENDS {0}".format(self.name)) @@ -116,38 +124,39 @@ class ptx(design.design): # The contacted poly pitch (or uncontacted in an odd technology) - self.poly_pitch = max(2*self.contact_to_gate + self.contact_width + self.poly_width, + self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width, self.poly_space) # The contacted poly pitch (or uncontacted in an odd technology) - self.contact_pitch = 2*self.contact_to_gate + self.contact_width + self.poly_width + self.contact_pitch = 2 * self.contact_to_gate + self.contact_width + self.poly_width # The enclosure of an active contact. Not sure about second term. active_enclose_contact = max(drc("active_enclosure_contact"), - (self.active_width - self.contact_width)/2) + (self.active_width - self.contact_width) / 2) + # This is the distance from the edge of poly to the contacted end of active self.end_to_poly = active_enclose_contact + self.contact_width + self.contact_to_gate # Active width is determined by enclosure on both ends and contacted pitch, # at least one poly and n-1 poly pitches - self.active_width = 2*self.end_to_poly + self.poly_width + (self.mults - 1)*self.poly_pitch + self.active_width = 2 * self.end_to_poly + self.poly_width + (self.mults - 1) * self.poly_pitch # Active height is just the transistor width self.active_height = self.tx_width # Poly height must include poly extension over active - self.poly_height = self.tx_width + 2*self.poly_extend_active + self.poly_height = self.tx_width + 2 * self.poly_extend_active # The active offset is due to the well extension - self.active_offset = vector([self.well_enclose_active]*2) + self.active_offset = vector([self.well_enclose_active] * 2) # Well enclosure of active, ensure minwidth as well if drc("has_{}well".format(self.well_type)): - self.cell_well_width = max(self.active_width + 2*self.well_enclose_active, - self.well_width) - self.cell_well_height = max(self.tx_width + 2*self.well_enclose_active, - self.well_width) + self.cell_well_width = max(self.active_width + 2 * self.well_enclose_active, + self.well_width) + self.cell_well_height = max(self.tx_width + 2 * self.well_enclose_active, + self.well_width) # We are going to shift the 0,0, so include that in the width and height self.height = self.cell_well_height - self.active_offset.y self.width = self.cell_well_width - self.active_offset.x @@ -157,17 +166,20 @@ class ptx(design.design): self.width = self.active_width # The active offset is due to the well extension - self.active_offset = vector([self.well_enclose_active]*2) + self.active_offset = vector([self.well_enclose_active] * 2) # This is the center of the first active contact offset (centered vertically) - self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5*self.contact_width, - 0.5*self.active_height) + self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5 * self.contact_width, + 0.5 * self.active_height) # Min area results are just flagged for now. - debug.check(self.active_width*self.active_height>=drc("minarea_active"),"Minimum active area violated.") - # We do not want to increase the poly dimensions to fix an area problem as it would cause an LVS issue. - debug.check(self.poly_width*self.poly_height>=drc("minarea_poly"),"Minimum poly area violated.") + debug.check(self.active_width * self.active_height >= drc("minarea_active"), + "Minimum active area violated.") + # We do not want to increase the poly dimensions to fix + # an area problem as it would cause an LVS issue. + debug.check(self.poly_width * self.poly_height >= drc("minarea_poly"), + "Minimum poly area violated.") def connect_fingered_poly(self, poly_positions): """ @@ -182,13 +194,19 @@ 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 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) + # 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) + # 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", @@ -205,12 +223,13 @@ class ptx(design.design): # This is the distance that we must route up or down from the center # of the contacts to avoid DRC violations to the other contacts - pin_offset = vector(0, 0.5*self.active_contact.second_layer_height \ - + self.m1_space + 0.5*self.m1_width) + pin_offset = vector(0, 0.5 * self.active_contact.second_layer_height \ + + self.m1_space + 0.5 * self.m1_width) # This is the width of a m1 extend the ends of the pin end_offset = vector(self.m1_width/2,0) - # drains always go to the MIDDLE of the cell, so top of NMOS, bottom of PMOS + # drains always go to the MIDDLE of the cell, + # so top of NMOS, bottom of PMOS # so reverse the directions for NMOS compared to PMOS. if self.tx_type == "pmos": drain_dir = -1 @@ -219,17 +238,19 @@ class ptx(design.design): drain_dir = 1 source_dir = -1 - if len(source_positions)>1: + if len(source_positions) > 1: source_offset = pin_offset.scale(source_dir,source_dir) self.remove_layout_pin("S") # remove the individual connections # Add each vertical segment for a in source_positions: - self.add_path(("metal1"), [a,a+pin_offset.scale(source_dir,source_dir)]) + self.add_path(("metal1"), + [a, a + pin_offset.scale(source_dir, + source_dir)]) # Add a single horizontal pin self.add_layout_pin_segment_center(text="S", layer="metal1", - start=source_positions[0]+source_offset-end_offset, - end=source_positions[-1]+source_offset+end_offset) + start=source_positions[0] + source_offset - end_offset, + end=source_positions[-1] + source_offset + end_offset) if len(drain_positions)>1: drain_offset = pin_offset.scale(drain_dir,drain_dir) @@ -240,24 +261,27 @@ class ptx(design.design): # Add a single horizontal pin self.add_layout_pin_segment_center(text="D", layer="metal1", - start=drain_positions[0]+drain_offset-end_offset, - end=drain_positions[-1]+drain_offset+end_offset) + start=drain_positions[0] + drain_offset - end_offset, + end=drain_positions[-1] + drain_offset + end_offset) def add_poly(self): """ Add the poly gates(s) and (optionally) connect them. """ # poly is one contacted spacing from the end and down an extension - poly_offset = self.active_offset + vector(self.poly_width,self.poly_height).scale(0.5,0.5) \ + poly_offset = self.active_offset \ + + vector(self.poly_width, self.poly_height).scale(0.5, 0.5) \ + vector(self.end_to_poly, -self.poly_extend_active) # poly_positions are the bottom center of the poly gates poly_positions = [] - # It is important that these are from left to right, so that the pins are in the right + # It is important that these are from left to right, + # so that the pins are in the right # order for the accessors for i in range(0, self.mults): - # Add this duplicate rectangle in case we remove the pin when joining fingers + # Add this duplicate rectangle in case we remove + # the pin when joining fingers self.add_rect_center(layer="poly", offset=poly_offset, height=self.poly_height, @@ -274,8 +298,8 @@ class ptx(design.design): self.connect_fingered_poly(poly_positions) def add_active(self): - """ - Adding the diffusion (active region = diffusion region) + """ + Adding the diffusion (active region = diffusion region) """ self.add_rect(layer="active", offset=self.active_offset, @@ -284,11 +308,11 @@ class ptx(design.design): # If the implant must enclose the active, shift offset # and increase width/height enclose_width = drc("implant_enclosure_active") - enclose_offset = [enclose_width]*2 + enclose_offset = [enclose_width] * 2 self.add_rect(layer="{}implant".format(self.implant_type), offset=self.active_offset - enclose_offset, - width=self.active_width + 2*enclose_width, - height=self.active_height + 2*enclose_width) + width=self.active_width + 2 * enclose_width, + height=self.active_height + 2 * enclose_width) def add_well_implant(self): """ @@ -320,15 +344,16 @@ class ptx(design.design): # The first one will always be a source source_positions = [self.contact_offset] drain_positions = [] - # It is important that these are from left to right, so that the pins are in the right + # It is important that these are from left to right, + # so that the pins are in the right # order for the accessors. for i in range(self.mults): if i%2: # It's a source... so offset from previous drain. - source_positions.append(drain_positions[-1] + vector(self.contact_pitch,0)) + source_positions.append(drain_positions[-1] + vector(self.contact_pitch, 0)) else: # It's a drain... so offset from previous source. - drain_positions.append(source_positions[-1] + vector(self.contact_pitch,0)) + drain_positions.append(source_positions[-1] + vector(self.contact_pitch, 0)) return [source_positions,drain_positions] @@ -371,9 +396,12 @@ class ptx(design.design): def get_cin(self): """Returns the relative gate cin of the tx""" - return self.tx_width/drc("minwidth_tx") + return self.tx_width / drc("minwidth_tx") - def build_graph(self, graph, inst_name, port_nets): - """Adds edges based on inputs/outputs. Overrides base class function.""" - self.add_graph_edges(graph, port_nets) - \ No newline at end of file + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) + diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 999d3ccd..ffe313c8 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -9,17 +9,17 @@ import pgate import debug from tech import drc from vector import vector -import contact -from globals import OPTS from sram_factory import factory import logical_effort + class single_level_column_mux(pgate.pgate): """ This module implements the columnmux bitline cell used in the design. Creates a single columnmux cell with the given integer size relative to minimum size. Default is 8x. Per Samira and Hodges-Jackson book: - Column-mux transistors driven by the decoder must be sized for optimal speed + 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"): @@ -38,7 +38,7 @@ class single_level_column_mux(pgate.pgate): def create_layout(self): - self.pin_height = 2*self.m2_width + self.pin_height = 2 * self.m2_width self.width = self.bitcell.width self.height = self.nmos_upper.uy() + self.pin_height self.connect_poly() @@ -50,11 +50,9 @@ class single_level_column_mux(pgate.pgate): self.bitcell = factory.create(module_type="bitcell") # Adds nmos_lower,nmos_upper to the module - self.ptx_width = self.tx_size*drc("minwidth_tx") + self.ptx_width = self.tx_size * drc("minwidth_tx") self.nmos = factory.create(module_type="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"]) @@ -68,11 +66,11 @@ class single_level_column_mux(pgate.pgate): # bl and br self.add_layout_pin(text="bl", layer="metal2", - offset=bl_pos + vector(0,self.height - self.pin_height), + offset=bl_pos + vector(0, self.height - self.pin_height), height=self.pin_height) self.add_layout_pin(text="br", layer="metal2", - offset=br_pos + vector(0,self.height - self.pin_height), + offset=br_pos + vector(0, self.height - self.pin_height), height=self.pin_height) # bl_out and br_out @@ -85,35 +83,34 @@ class single_level_column_mux(pgate.pgate): offset=br_pos, height=self.pin_height) - def add_ptx(self): """ Create the two pass gate NMOS transistors to switch the bitlines""" # Space it in the center - nmos_lower_position = self.nmos.active_offset.scale(0,1) + vector(0.5*self.bitcell.width-0.5*self.nmos.active_width,0) - self.nmos_lower=self.add_inst(name="mux_tx1", - mod=self.nmos, - offset=nmos_lower_position) + nmos_lower_position = self.nmos.active_offset.scale(0,1) \ + + vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0) + self.nmos_lower = self.add_inst(name="mux_tx1", + mod=self.nmos, + offset=nmos_lower_position) self.connect_inst(["bl", "sel", "bl_out", "gnd"]) # This aligns it directly above the other tx with gates abutting - nmos_upper_position = nmos_lower_position + vector(0,self.nmos.active_height + self.poly_space) - self.nmos_upper=self.add_inst(name="mux_tx2", - mod=self.nmos, - offset=nmos_upper_position) + nmos_upper_position = nmos_lower_position \ + + vector(0, self.nmos.active_height + self.poly_space) + self.nmos_upper = self.add_inst(name="mux_tx2", + mod=self.nmos, + offset=nmos_upper_position) self.connect_inst(["br", "sel", "br_out", "gnd"]) - def connect_poly(self): """ Connect the poly gate of the two pass transistors """ - height=self.nmos_upper.get_pin("G").uy() - self.nmos_lower.get_pin("G").by() + height = self.nmos_upper.get_pin("G").uy() - self.nmos_lower.get_pin("G").by() self.add_layout_pin(text="sel", layer="poly", offset=self.nmos_lower.get_pin("G").ll(), height=height) - def connect_bitlines(self): """ Connect the bitlines to the mux transistors """ # These are on metal2 @@ -129,52 +126,61 @@ class single_level_column_mux(pgate.pgate): nmos_upper_d_pin = self.nmos_upper.get_pin("D") # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=("metal1", "via1", "metal2"), offset=bl_pin.bc(), - directions=("V","V")) - self.add_via_center(layers=("metal1","via1","metal2"), + directions=("V", "V")) + self.add_via_center(layers=("metal1", "via1", "metal2"), offset=br_out_pin.uc(), - directions=("V","V")) - self.add_via_center(layers=("metal1","via1","metal2"), + directions=("V", "V")) + self.add_via_center(layers=("metal1", "via1", "metal2"), offset=nmos_upper_s_pin.center(), - directions=("V","V")) - self.add_via_center(layers=("metal1","via1","metal2"), + directions=("V", "V")) + self.add_via_center(layers=("metal1", "via1", "metal2"), offset=nmos_lower_d_pin.center(), - directions=("V","V")) - + directions=("V", "V")) # bl -> nmos_upper/D on metal1 # bl_out -> nmos_upper/S on metal2 - self.add_path("metal1",[bl_pin.ll(), vector(nmos_upper_d_pin.cx(),bl_pin.by()), nmos_upper_d_pin.center()]) + self.add_path("metal1", + [bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()), + nmos_upper_d_pin.center()]) # halfway up, move over - mid1 = bl_out_pin.uc().scale(1,0.4)+nmos_upper_s_pin.bc().scale(0,0.4) - mid2 = bl_out_pin.uc().scale(0,0.4)+nmos_upper_s_pin.bc().scale(1,0.4) - self.add_path("metal2",[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()]) + mid1 = bl_out_pin.uc().scale(1, 0.4) \ + + nmos_upper_s_pin.bc().scale(0, 0.4) + mid2 = bl_out_pin.uc().scale(0, 0.4) \ + + nmos_upper_s_pin.bc().scale(1, 0.4) + self.add_path("metal2", + [bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()]) # br -> nmos_lower/D on metal2 # br_out -> nmos_lower/S on metal1 - self.add_path("metal1",[br_out_pin.uc(), vector(nmos_lower_s_pin.cx(),br_out_pin.uy()), nmos_lower_s_pin.center()]) + self.add_path("metal1", + [br_out_pin.uc(), + vector(nmos_lower_s_pin.cx(), br_out_pin.uy()), + nmos_lower_s_pin.center()]) # halfway up, move over - mid1 = br_pin.bc().scale(1,0.5)+nmos_lower_d_pin.uc().scale(0,0.5) - mid2 = br_pin.bc().scale(0,0.5)+nmos_lower_d_pin.uc().scale(1,0.5) - self.add_path("metal2",[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()]) + mid1 = br_pin.bc().scale(1,0.5) \ + + nmos_lower_d_pin.uc().scale(0,0.5) + mid2 = br_pin.bc().scale(0,0.5) \ + + nmos_lower_d_pin.uc().scale(1,0.5) + self.add_path("metal2", + [br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()]) - def add_wells(self): - """ + """ Add a well and implant over the whole cell. Also, add the - pwell contact (if it exists) + pwell contact (if it exists) """ # Add it to the right, aligned in between the two tx - active_pos = vector(self.bitcell.width,self.nmos_upper.by() - 0.5*self.poly_space) - active_via = self.add_via_center(layers=("active", "contact", "metal1"), - offset=active_pos, - implant_type="p", - well_type="p") + active_pos = vector(self.bitcell.width, + self.nmos_upper.by() - 0.5 * self.poly_space) + self.add_via_center(layers=("active", "contact", "metal1"), + offset=active_pos, + implant_type="p", + well_type="p") - - # Add the M1->M2->M3 stack + # Add the M1->M2->M3 stack self.add_via_center(layers=("metal1", "via1", "metal2"), offset=active_pos) self.add_via_center(layers=("metal2", "via2", "metal3"), @@ -185,13 +191,22 @@ class single_level_column_mux(pgate.pgate): # Add well enclosure over all the tx and contact self.add_rect(layer="pwell", - offset=vector(0,0), + offset=vector(0, 0), width=self.bitcell.width, height=self.height) def get_stage_effort(self, corner, slew, load): - """Returns relative delay that the column mux. Difficult to convert to LE model.""" + """ + Returns relative delay that the column mux. + Difficult to convert to LE model. + """ parasitic_delay = 1 - cin = 2*self.tx_size #This is not CMOS, so using this may be incorrect. - return logical_effort.logical_effort('column_mux', self.tx_size, cin, load, parasitic_delay, False) + # This is not CMOS, so using this may be incorrect. + cin = 2 * self.tx_size + return logical_effort.logical_effort("column_mux", + self.tx_size, + cin, + load, + parasitic_delay, + False)