diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 39cb7b52..f4b14d79 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -9,44 +9,36 @@ class contact(hierarchy_design.hierarchy_design): """ Object for a contact shape with its conductor enclosures. Creates a contact array minimum active or poly enclosure and metal1 enclosure. - This class has enclosure on multiple sides of the contact whereas a via may - have extension on two or four sides. + This class has enclosure on two or four sides of the contact. + The direction specifies whether the first and second layer have asymmetric extension in the H or V direction. + The well/implant_type is an option to add a select/implant layer enclosing the contact. This is necessary to import layouts into Magic which requires the select to be in the same GDS hierarchy as the contact. """ - def __init__(self, layer_stack, dimensions=[1,1], implant_type=None, well_type=None, name=""): + def __init__(self, layer_stack, dimensions=(1,1), directions=("V","V"), implant_type=None, well_type=None, name=""): # This will ignore the name parameter since we can guarantee a unique name here - if implant_type or well_type: - name = "{0}_{1}_{2}_{3}x{4}_{5}{6}".format(layer_stack[0], - layer_stack[1], - layer_stack[2], - dimensions[0], - dimensions[1], - implant_type, - well_type) - - else: - name = "{0}_{1}_{2}_{3}x{4}".format(layer_stack[0], - layer_stack[1], - layer_stack[2], - dimensions[0], - dimensions[1]) - hierarchy_design.hierarchy_design.__init__(self, name) debug.info(4, "create contact object {0}".format(name)) - + self.add_comment("layers: {0}".format(layer_stack)) + self.add_comment("dimensions: {0}".format(dimensions)) + if implant_type or well_type: + self.add_comment("implant type: {0}\nwell_type: {1}".format(implant_type,well_type)) + self.layer_stack = layer_stack self.dimensions = dimensions + self.directions = directions self.offset = vector(0,0) self.implant_type = implant_type self.well_type = well_type - self.pins = [] # used for matching parm lengths + # Module does not have pins, but has empty pin list. + self.pins = [] self.create_layout() def create_layout(self): + self.setup_layers() self.setup_layout_constants() self.create_contact_array() @@ -63,6 +55,8 @@ class contact(hierarchy_design.hierarchy_design): debug.error(-1,"Must define both implant and well type or none at all.") def setup_layers(self): + """ Locally assign the layer names. """ + (first_layer, via_layer, second_layer) = self.layer_stack self.first_layer_name = first_layer self.via_layer_name = via_layer @@ -75,37 +69,58 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_name = second_layer def setup_layout_constants(self): + """ Determine the design rules for the enclosure layers """ + self.contact_width = drc("minwidth_{0}". format(self.via_layer_name)) contact_to_contact = drc("{0}_to_{0}".format(self.via_layer_name)) self.contact_pitch = self.contact_width + contact_to_contact + self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch # DRC rules + # The extend rule applies to asymmetric enclosures in one direction. + # The enclosure rule applies to symmetric enclosure component. + first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name)) - first_layer_minarea = drc("minarea_{0}".format(self.first_layer_name)) first_layer_enclosure = drc("{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)) first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)) + second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name)) - second_layer_minarea = drc("minarea_{0}".format(self.second_layer_name)) second_layer_enclosure = drc("{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)) second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)) - self.first_layer_horizontal_enclosure = max((first_layer_minwidth - self.contact_array_width) / 2, - first_layer_enclosure) - self.first_layer_vertical_enclosure = max(utils.ceil((first_layer_minarea - / (self.contact_array_width + 2*self.first_layer_horizontal_enclosure) - - self.contact_array_height)/2), - (first_layer_minwidth - self.contact_array_height)/2, - first_layer_extend) - self.second_layer_horizontal_enclosure = max((second_layer_minwidth - self.contact_array_width)/2, - second_layer_enclosure) - self.second_layer_vertical_enclosure = max(utils.ceil((second_layer_minarea - / (self.contact_array_width + 2*self.second_layer_horizontal_enclosure) - - self.contact_array_height)/2), - (second_layer_minwidth - self.contact_array_height)/2, - second_layer_extend) + # In some technologies, the minimum width may be larger than the overlap requirement around the via, so + # check this for each dimension. + if self.directions[0] == "V": + self.first_layer_horizontal_enclosure = max(first_layer_enclosure, + (first_layer_minwidth - self.contact_array_width)/2) + self.first_layer_vertical_enclosure = max(first_layer_extend, + (first_layer_minwidth - self.contact_array_height)/2) + elif self.directions[0] == "H": + self.first_layer_horizontal_enclosure = max(first_layer_extend, + (first_layer_minwidth - self.contact_array_width)/2) + self.first_layer_vertical_enclosure = max(first_layer_enclosure, + (first_layer_minwidth - self.contact_array_height)/2) + else: + debug.error("Invalid first layer direction.", -1) + + # In some technologies, the minimum width may be larger than the overlap requirement around the via, so + # check this for each dimension. + if self.directions[1] == "V": + self.second_layer_horizontal_enclosure = max(second_layer_enclosure, + (second_layer_minwidth - self.contact_array_width)/2) + self.second_layer_vertical_enclosure = max(second_layer_extend, + (second_layer_minwidth - self.contact_array_height)/2) + elif self.directions[1] == "H": + self.second_layer_horizontal_enclosure = max(second_layer_extend, + (second_layer_minwidth - self.contact_array_height)/2) + self.second_layer_vertical_enclosure = max(second_layer_enclosure, + (second_layer_minwidth - self.contact_array_width)/2) + else: + debug.error("Invalid second layer direction.", -1) + def create_contact_array(self): """ Create the contact array at the origin""" @@ -169,10 +184,10 @@ class contact(hierarchy_design.hierarchy_design): from sram_factory import factory # This is not instantiated and used for calculations only. # These are static 1x1 contacts to reuse in all the design modules. -well = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1")) -active = factory.create(module_type="contact", layer_stack=("active", "contact", "poly")) -poly = factory.create(module_type="contact", layer_stack=("poly", "contact", "metal1")) -m1m2 = factory.create(module_type="contact", layer_stack=("metal1", "via1", "metal2")) -m2m3 = factory.create(module_type="contact", layer_stack=("metal2", "via2", "metal3")) -m3m4 = factory.create(module_type="contact", layer_stack=("metal3", "via3", "metal4")) +well = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1"), directions=("H","V")) +active = factory.create(module_type="contact", layer_stack=("active", "contact", "poly"), directions=("H","V")) +poly = factory.create(module_type="contact", layer_stack=("poly", "contact", "metal1"), directions=("V","H")) +m1m2 = factory.create(module_type="contact", layer_stack=("metal1", "via1", "metal2"), directions=("H","V")) +m2m3 = factory.create(module_type="contact", layer_stack=("metal2", "via2", "metal3"), directions=("V","H")) +m3m4 = factory.create(module_type="contact", layer_stack=("metal3", "via3", "metal4"), directions=("H","V")) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 26fc6de9..cd1f2765 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -355,75 +355,59 @@ class layout(): layer_stack=layers, position_list=coordinates) - def add_contact(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None): - """ This is just an alias for a via.""" - return self.add_via(layers=layers, - offset=offset, - size=size, - mirror=mirror, - rotate=rotate, - implant_type=implant_type, - well_type=well_type) + def get_preferred_direction(self, layer): + """ Return the preferred routing directions """ + if layer in ["metal1", "metal3", "metal5"]: + return "H" + elif layer in ["active", "poly", "metal2", "metal4"]: + return "V" + else: + return "N" - def add_contact_center(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None): - """ This is just an alias for a via.""" - return self.add_via_center(layers=layers, - offset=offset, - size=size, - mirror=mirror, - rotate=rotate, - implant_type=implant_type, - well_type=well_type) - - def add_via(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None): + + def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None): """ Add a three layer via structure. """ + + if directions==None: + directions = (self.get_preferred_direction(layers[0]), self.get_preferred_direction(layers[2])) + from sram_factory import factory via = factory.create(module_type="contact", layer_stack=layers, dimensions=size, + directions=directions, implant_type=implant_type, well_type=well_type) self.add_mod(via) inst=self.add_inst(name=via.name, mod=via, - offset=offset, - mirror=mirror, - rotate=rotate) + offset=offset) # We don't model the logical connectivity of wires/paths self.connect_inst([]) return inst - def add_via_center(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None): + def add_via_center(self, layers, offset, directions=None, size=[1,1], implant_type=None, well_type=None): """ Add a three layer via structure by the center coordinate accounting for mirroring and rotation. """ + + if directions==None: + directions = (self.get_preferred_direction(layers[0]), self.get_preferred_direction(layers[2])) + from sram_factory import factory via = factory.create(module_type="contact", layer_stack=layers, dimensions=size, + directions=directions, implant_type=implant_type, well_type=well_type) height = via.height width = via.width - debug.check(mirror=="R0","Use rotate to rotate vias instead of mirror.") - if rotate==0: - corrected_offset = offset + vector(-0.5*width,-0.5*height) - elif rotate==90: - corrected_offset = offset + vector(0.5*height,-0.5*width) - elif rotate==180: - corrected_offset = offset + vector(0.5*width,0.5*height) - elif rotate==270: - corrected_offset = offset + vector(-0.5*height,0.5*width) - else: - debug.error("Invalid rotation argument.",-1) - + corrected_offset = offset + vector(-0.5*width,-0.5*height) - #print(rotate,offset,"->",corrected_offset) self.add_mod(via) inst=self.add_inst(name=via.name, mod=via, - offset=corrected_offset, - mirror=mirror, - rotate=rotate) + offset=corrected_offset) # We don't model the logical connectivity of wires/paths self.connect_inst([]) return inst @@ -748,8 +732,7 @@ class layout(): mid = vector(trunk_offset.x, pin.center().y) self.add_path(layer_stack[0], [pin.center(), mid]) self.add_via_center(layers=layer_stack, - offset=mid, - rotate=90) + offset=mid) def create_channel_route(self, netlist, @@ -926,20 +909,27 @@ class layout(): - def add_power_pin(self, name, loc, rotate=90, start_layer="metal1"): + def add_power_pin(self, name, loc, vertical=False, start_layer="metal1"): """ Add a single power pin from M3 down to M1 at the given center location. The starting layer is specified to determine which vias are needed. """ - + if vertical: + direction=("V","V") + else: + direction=("H","H") + if start_layer=="metal1": self.add_via_center(layers=("metal1", "via1", "metal2"), offset=loc, - rotate=float(rotate)) + directions=direction) + + if start_layer=="metal1" or start_layer=="metal2": via=self.add_via_center(layers=("metal2", "via2", "metal3"), offset=loc, - rotate=float(rotate)) + directions=direction) + if start_layer=="metal3": self.add_layout_pin_rect_center(text=name, layer="metal3", diff --git a/compiler/base/route.py b/compiler/base/route.py index c6ce906b..86669acd 100644 --- a/compiler/base/route.py +++ b/compiler/base/route.py @@ -65,7 +65,7 @@ class route(design): for p0,p1 in plist: if p0.z != p1.z: # via via_size = [self.num_vias]*2 - self.obj.add_via_center(self.layer_stack,vector(p0.x,p0.y),size=via_size,rotate=90) + self.obj.add_via_center(self.layer_stack,vector(p0.x,p0.y),size=via_size) elif p0.x != p1.x and p0.y != p1.y: # diagonal! debug.error("Diagonal route! {}".format(self.path),-3) else: diff --git a/compiler/base/wire.py b/compiler/base/wire.py index 8bc250fa..12b493a1 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -62,18 +62,15 @@ class wire(wire_path): continue if a[1] == c[1]: continue - via_offset = [offset[0] + 0.5*c_height, - offset[1] - 0.5*c_width] - self.obj.add_via(layers=self.layer_stack, - offset=via_offset, - rotate=90) - corner_offset = [offset[0] - 0.5*(c_height + self.vert_layer_width), - offset[1] + 0.5*(c_width - self.horiz_layer_width)] + self.obj.add_via_center(layers=self.layer_stack, + offset=offset) def create_rectangles(self): - """ Create the actual rectangles on teh appropriate layers - using the position list of the corners. """ + """ + Create the actual rectangles on the appropriate layers + using the position list of the corners. + """ pl = self.position_list # position list for index in range(len(pl) - 1): if pl[index][0] != pl[index + 1][0]: diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 94c294db..552042f5 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -198,7 +198,7 @@ class pbitcell(design.design): self.read_port_spacing = self.bitline_offset + self.m2_space # spacing between cross coupled inverters - self.inverter_to_inverter_spacing = contact.poly.height + self.m1_space + self.inverter_to_inverter_spacing = contact.poly.width + self.m1_space # calculations related to inverter connections inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) @@ -292,19 +292,20 @@ class pbitcell(design.design): self.add_path("poly", [self.inverter_nmos_right.get_pin("G").uc(), self.inverter_pmos_right.get_pin("G").bc()]) # connect output (drain/source) of inverters - self.add_path("metal1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], width=contact.well.second_layer_width) - self.add_path("metal1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], width=contact.well.second_layer_width) + self.add_path("metal1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()]) + self.add_path("metal1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()]) # add contacts to connect gate poly to drain/source metal1 (to connect Q to Q_bar) contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.height, self.cross_couple_upper_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=contact_offset_left, - rotate=90) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=contact_offset_left, + directions=("H","H")) + contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.height, self.cross_couple_lower_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=contact_offset_right, - rotate=90) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=contact_offset_right, + directions=("H","H")) # connect contacts to gate poly (cross couple connections) gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, contact_offset_left.y) @@ -395,7 +396,6 @@ class pbitcell(design.design): self.add_layout_pin_rect_center(text=self.rw_bl_names[k], layer="metal2", offset=self.rwbl_positions[k], - width=drc["minwidth_metal2"], height=self.height) rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width + self.bitline_offset - 0.5*self.m2_width @@ -403,7 +403,6 @@ class pbitcell(design.design): self.add_layout_pin_rect_center(text=self.rw_br_names[k], layer="metal2", offset=self.rwbr_positions[k], - width=drc["minwidth_metal2"], height=self.height) # update furthest left and right transistor edges @@ -472,7 +471,6 @@ class pbitcell(design.design): self.add_layout_pin_rect_center(text=self.w_bl_names[k], layer="metal2", offset=self.wbl_positions[k], - width=drc["minwidth_metal2"], height=self.height) wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset - 0.5*self.m2_width @@ -480,7 +478,6 @@ class pbitcell(design.design): self.add_layout_pin_rect_center(text=self.w_br_names[k], layer="metal2", offset=self.wbr_positions[k], - width=drc["minwidth_metal2"], height=self.height) # update furthest left and right transistor edges @@ -571,7 +568,6 @@ class pbitcell(design.design): self.add_layout_pin_rect_center(text=self.r_bl_names[k], layer="metal2", offset=self.rbl_positions[k], - width=drc["minwidth_metal2"], height=self.height) rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset - 0.5*self.m2_width @@ -579,7 +575,6 @@ class pbitcell(design.design): self.add_layout_pin_rect_center(text=self.r_br_names[k], layer="metal2", offset=self.rbr_positions[k], - width=drc["minwidth_metal2"], height=self.height) def route_wordlines(self): @@ -613,21 +608,21 @@ class pbitcell(design.design): # first transistor on either side of the cross coupled inverters does not need to route to wordline on metal2 if (k == 0) or (k == 1): - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=port_contact_offset) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=port_contact_offset) self.add_path("poly", [gate_offset, port_contact_offset]) self.add_path("metal1", [port_contact_offset, wl_contact_offset]) else: - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=port_contact_offset) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=port_contact_offset) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=port_contact_offset) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=port_contact_offset) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=wl_contact_offset, - rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=wl_contact_offset, + directions=("H","H")) self.add_path("poly", [gate_offset, port_contact_offset]) self.add_path("metal2", [port_contact_offset, wl_contact_offset]) @@ -662,8 +657,8 @@ class pbitcell(design.design): port_contact_offest = left_port_transistors[k].get_pin("S").center() bl_offset = vector(bl_positions[k].x, port_contact_offest.y) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=port_contact_offest) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=port_contact_offest) self.add_path("metal2", [port_contact_offest, bl_offset], width=contact.m1m2.height) @@ -671,8 +666,8 @@ class pbitcell(design.design): port_contact_offest = right_port_transistors[k].get_pin("D").center() br_offset = vector(br_positions[k].x, port_contact_offest.y) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=port_contact_offest) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=port_contact_offest) self.add_path("metal2", [port_contact_offest, br_offset], width=contact.m1m2.height) @@ -687,17 +682,17 @@ class pbitcell(design.design): nmos_contact_positions.append(self.read_access_nmos_right[k].get_pin("S").center()) for position in nmos_contact_positions: - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=position) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=position) if position.x > 0: contact_correct = 0.5*contact.m1m2.height else: contact_correct = -0.5*contact.m1m2.height supply_offset = vector(position.x + contact_correct, self.gnd_position.y) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=supply_offset, - rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=supply_offset, + directions=("H","H")) self.add_path("metal2", [position, supply_offset]) @@ -713,39 +708,35 @@ class pbitcell(design.design): for k in range(self.num_rw_ports): mid = vector(self.readwrite_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) - self.add_path("metal1", [self.readwrite_nmos_left[k].get_pin("D").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) - self.add_path("metal1", [mid, Q_pos]) + self.add_path("metal1", [self.readwrite_nmos_left[k].get_pin("D").uc(), mid, Q_pos]) mid = vector(self.readwrite_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) - self.add_path("metal1", [self.readwrite_nmos_right[k].get_pin("S").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) - self.add_path("metal1", [mid, Q_bar_pos]) + self.add_path("metal1", [self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos]) def route_write_access(self): """ Routes read/write transistors to the storage component of the bitcell """ for k in range(self.num_w_ports): mid = vector(self.write_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) - self.add_path("metal1", [self.write_nmos_left[k].get_pin("D").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) - self.add_path("metal1", [mid, Q_pos]) + self.add_path("metal1", [self.write_nmos_left[k].get_pin("D").uc(), mid, Q_pos]) mid = vector(self.write_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) - self.add_path("metal1", [self.write_nmos_right[k].get_pin("S").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) - self.add_path("metal1", [mid, Q_bar_pos]) + self.add_path("metal1", [self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos]) def route_read_access(self): """ Routes read access transistors to the storage component of the bitcell """ # add poly to metal1 contacts for gates of the inverters - left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_upper_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_storage_contact, - rotate=90) + left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - self.poly_to_polycontact - 0.5*contact.poly.width, self.cross_couple_upper_ypos) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=left_storage_contact, + directions=("H","H")) - right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_upper_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_storage_contact, - rotate=90) + right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + self.poly_to_polycontact + 0.5*contact.poly.width, self.cross_couple_upper_ypos) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=right_storage_contact, + directions=("H","H")) inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos) self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) @@ -758,25 +749,23 @@ class pbitcell(design.design): for k in range(self.num_r_ports): port_contact_offset = self.read_access_nmos_left[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=port_contact_offset) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=port_contact_offset) self.add_path("poly", [self.read_access_nmos_left[k].get_pin("G").uc(), port_contact_offset]) mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) - self.add_path("metal1", [port_contact_offset, mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) - self.add_path("metal1", [mid, left_storage_contact]) + self.add_path("metal1", [port_contact_offset, mid, left_storage_contact]) port_contact_offset = self.read_access_nmos_right[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=port_contact_offset) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=port_contact_offset) self.add_path("poly", [self.read_access_nmos_right[k].get_pin("G").uc(), port_contact_offset]) mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) - self.add_path("metal1", [port_contact_offset, mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) - self.add_path("metal1", [mid, right_storage_contact]) + self.add_path("metal1", [port_contact_offset, mid, right_storage_contact]) def extend_well(self): """ @@ -795,13 +784,13 @@ class pbitcell(design.design): # extend nwell to encompass inverter_pmos # calculate offset of the left pmos well - inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) - drc["well_enclosure_active"] - inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - drc["well_enclosure_active"] + inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) - self.well_enclose_active + inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - self.well_enclose_active # calculate width of the two combined nwells # calculate height to encompass nimplant connected to vdd - well_width = 2*(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) + 2*drc["well_enclosure_active"] - well_height = self.vdd_position.y - inverter_well_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"] + well_width = 2*(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) + 2*self.well_enclose_active + well_height = self.vdd_position.y - inverter_well_ypos + self.well_enclose_active + drc["minwidth_tx"] offset = [inverter_well_xpos,inverter_well_ypos] self.add_rect(layer="nwell", @@ -812,19 +801,19 @@ class pbitcell(design.design): # add well contacts # connect pimplants to gnd offset = vector(0, self.gnd_position.y) - self.add_contact_center(layers=("active", "contact", "metal1"), - offset=offset, - rotate=90, - implant_type="p", - well_type="p") + self.add_via_center(layers=("active", "contact", "metal1"), + offset=offset, + directions=("H","H"), + implant_type="p", + well_type="p") # connect nimplants to vdd offset = vector(0, self.vdd_position.y) - self.add_contact_center(layers=("active", "contact", "metal1"), - offset=offset, - rotate=90, - implant_type="n", - well_type="n") + self.add_via_center(layers=("active", "contact", "metal1"), + offset=offset, + directions=("H","H"), + implant_type="n", + well_type="n") def list_bitcell_pins(self, col, row): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 5b556660..e6ca09bc 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -800,14 +800,11 @@ class bank(design.design): bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y) self.add_path("metal3",[out_pos, bus_pos]) self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=bus_pos, - rotate=90) + offset=bus_pos) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=out_pos, - rotate=90) + offset=out_pos) self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=out_pos, - rotate=90) + offset=out_pos) def setup_routing_constraints(self): @@ -1197,8 +1194,7 @@ class bank(design.design): control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y) self.add_path("metal1", [control_pos, pin_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=control_pos, - rotate=90) + offset=control_pos) # clk to wordline_driver control_signal = self.prefix+"wl_en{}".format(port) @@ -1212,8 +1208,7 @@ class bank(design.design): control_pos = vector(control_x_offset, mid_pos.y) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=control_pos, - rotate=90) + offset=control_pos) def analytical_delay(self, corner, slew, load, port): """ return analytical delay of the bank. This will track the clock to output path""" diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 8e44dfa2..8d1d3da5 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -213,7 +213,7 @@ class bank_select(design.design): end=bank_sel_pin_end) self.add_via_center(layers=("metal2","via2","metal3"), offset=bank_sel_pin_end, - rotate=90) + directions=("H","H")) # bank_sel_bar is vertical wire bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z") @@ -252,7 +252,7 @@ class bank_select(design.design): self.add_path("metal2",[logic_pos, input_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=logic_pos, - rotate=90) + directions=("H","H")) # Connect the logic A input to the input pin @@ -260,10 +260,10 @@ class bank_select(design.design): input_pos = vector(0,logic_pos.y) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=logic_pos, - rotate=90) + directions=("H","H")) self.add_via_center(layers=("metal2", "via2", "metal3"), offset=logic_pos, - rotate=90) + directions=("H","H")) self.add_layout_pin_segment_center(text=input_name, layer="metal3", start=input_pos, @@ -295,10 +295,10 @@ class bank_select(design.design): pin_pos = vector(xoffset, supply_pin.cy()) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=pin_pos, - rotate=90) + directions=("H","H")) self.add_via_center(layers=("metal2", "via2", "metal3"), offset=pin_pos, - rotate=90) + directions=("H","H")) self.add_layout_pin_rect_center(text=n, layer="metal3", offset=pin_pos) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 79c68241..e53b72bb 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -128,7 +128,7 @@ class bitcell_array(design.design): inst = self.cell_inst[row,col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): - self.add_power_pin(pin_name, pin.center(), 0, pin.layer) + self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) def analytical_delay(self, corner, slew, load): """Returns relative delay of the bitline in the bitcell array""" diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 4f654c5f..c976e5dc 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -615,11 +615,9 @@ class control_logic(design.design): mid1 = vector(in_pos.x,out_pos.y) self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, in_pos]) self.add_via_center(layers=("metal1","via1","metal2"), - offset=out_pos, - rotate=90) + offset=out_pos) self.add_via_center(layers=("metal2","via2","metal3"), - offset=out_pos, - rotate=90) + offset=out_pos) def create_pen_row(self): if self.port_type == "rw": @@ -759,8 +757,7 @@ class control_logic(design.design): rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y) self.add_wire(("metal1","via1","metal2"),[in_pos, mid_pos, rail_pos]) self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail_pos, - rotate=90) + offset=rail_pos) self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb") if (self.port_type == "rw"): diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index bd17b37e..5fcf27f0 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -213,11 +213,9 @@ class hierarchical_decoder(design.design): """ Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=input_offset, - rotate=90) + offset=input_offset) self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=output_offset, - rotate=90) + offset=output_offset) self.add_path(("metal3"), [input_offset, output_offset]) @@ -575,8 +573,7 @@ class hierarchical_decoder(design.design): rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y) self.add_path("metal1", [rail_pos, pin.lc()]) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=rail_pos, - rotate=90) + offset=rail_pos) def route_predecode_rail_m3(self, rail_name, pin): @@ -586,12 +583,10 @@ class hierarchical_decoder(design.design): mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2) rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=pin.center(), - rotate=90) + offset=pin.center()) self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()]) self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=rail_pos, - rotate=90) + offset=rail_pos) def analytical_delay(self, corner, slew, load = 0.0): diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 147c3018..a13afd85 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -178,11 +178,9 @@ class hierarchical_predecode(design.design): a_pos = vector(self.decode_rails[a_pin].x,y_offset) self.add_path("metal1",[in_pos, a_pos]) self.add_via_center(layers = ("metal1", "via1", "metal2"), - offset=[self.input_rails[in_pin].x, y_offset], - rotate=90) + offset=[self.input_rails[in_pin].x, y_offset]) self.add_via_center(layers = ("metal1", "via1", "metal2"), - offset=[self.decode_rails[a_pin].x, y_offset], - rotate=90) + offset=[self.decode_rails[a_pin].x, y_offset]) def route_output_inverters(self): """ @@ -223,8 +221,7 @@ class hierarchical_predecode(design.design): rail_pos = vector(self.decode_rails[out_pin].x,y_offset) self.add_path("metal1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) self.add_via_center(layers = ("metal1", "via1", "metal2"), - offset=rail_pos, - rotate=90) + offset=rail_pos) #route input @@ -232,8 +229,7 @@ class hierarchical_predecode(design.design): in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y) self.add_path("metal1", [in_pos, inv_in_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=in_pos, - rotate=90) + offset=in_pos) def route_nand_to_rails(self): @@ -254,8 +250,8 @@ class hierarchical_predecode(design.design): rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) self.add_path("metal1", [rail_pos, pin_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=rail_pos, - rotate=90) + offset=rail_pos) + diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index 8d93b257..c68ebc8f 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -39,8 +39,8 @@ class replica_bitline(design.design): #self.add_lvs_correspondence_points() # Extra pitch on top and right - self.width = self.rbl_inst.rx() - self.dc_inst.lx() + self.m2_pitch - self.height = max(self.rbl_inst.uy(), self.dc_inst.uy()) + self.m3_pitch + self.width = self.replica_column_inst.rx() - self.delay_chain_inst.lx() + self.m2_pitch + self.height = max(self.replica_column_inst.uy(), self.delay_chain_inst.uy()) + self.m3_pitch self.DRC_LVS() @@ -111,12 +111,12 @@ class replica_bitline(design.design): self.connect_inst(["vdd", "delayed_en", "bl0_0", "vdd"]) # add the well and poly contact - self.dc_inst=self.add_inst(name="delay_chain", - mod=self.delay_chain) + self.delay_chain_inst=self.add_inst(name="delay_chain", + mod=self.delay_chain) self.connect_inst(["en", "delayed_en", "vdd", "gnd"]) - self.rbc_inst=self.add_inst(name="bitcell", - mod=self.replica_bitcell) + self.replica_cell_inst=self.add_inst(name="bitcell", + mod=self.replica_bitcell) temp = [] for port in self.all_ports: temp.append("bl{}_0".format(port)) @@ -127,8 +127,8 @@ class replica_bitline(design.design): temp.append("gnd") self.connect_inst(temp) - self.rbl_inst=self.add_inst(name="load", - mod=self.rbl) + self.replica_column_inst=self.add_inst(name="load", + mod=self.rbl) temp = [] for port in self.all_ports: @@ -153,12 +153,12 @@ class replica_bitline(design.design): self.tx_inst.place(self.access_tx_offset) - self.dc_inst.place(self.delay_chain_offset) + self.delay_chain_inst.place(self.delay_chain_offset) - self.rbc_inst.place(offset=self.bitcell_offset, + self.replica_cell_inst.place(offset=self.bitcell_offset, mirror="MX") - self.rbl_inst.place(self.rbl_offset) + self.replica_column_inst.place(self.rbl_offset) def route(self): @@ -172,7 +172,7 @@ class replica_bitline(design.design): # Connect the WL and gnd pins directly to the center and right gnd rails for row in range(self.bitcell_loads): wl = self.wl_list[0]+"_{}".format(row) - pin = self.rbl_inst.get_pin(wl) + pin = self.replica_column_inst.get_pin(wl) # Route the connection to the right so that it doesn't interfere with the cells # Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions @@ -188,7 +188,7 @@ class replica_bitline(design.design): # for multiport, need to short wordlines to each other so they all connect to gnd. wl_last = self.wl_list[-1]+"_{}".format(row) - pin_last = self.rbl_inst.get_pin(wl_last) + pin_last = self.replica_column_inst.get_pin(wl_last) self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0)) def short_wordlines(self, wl_pin_a, wl_pin_b, pin_side, is_replica_cell, cell_row=0, offset_x_vec=None): @@ -229,10 +229,10 @@ class replica_bitline(design.design): for port in self.all_ports: if is_replica_cell: wl = self.wl_list[port] - pin = self.rbc_inst.get_pin(wl) + pin = self.replica_cell_inst.get_pin(wl) else: wl = self.wl_list[port]+"_{}".format(cell_row) - pin = self.rbl_inst.get_pin(wl) + pin = self.replica_column_inst.get_pin(wl) if pin_side == "left": self.add_path("metal1", [pin.lc()-correct_x, pin.lc()]) @@ -245,8 +245,8 @@ class replica_bitline(design.design): """ Propagate all vdd/gnd pins up to this level for all modules """ # These are the instances that every bank has - top_instances = [self.rbl_inst, - self.dc_inst] + top_instances = [self.replica_column_inst, + self.delay_chain_inst] for inst in top_instances: self.copy_layout_pin(inst, "vdd") self.copy_layout_pin(inst, "gnd") @@ -257,11 +257,11 @@ class replica_bitline(design.design): pin = self.rbl_inv_inst.get_pin("vdd") self.add_power_pin("vdd", pin.lc()) - pin=self.rbc_inst.get_pin("vdd") - self.add_power_pin("vdd", pin.center(), 0, pin.layer) + for pin in self.replica_cell_inst.get_pins("vdd"): + self.add_power_pin(name="vdd", loc=pin.center(), vertical=True, start_layer=pin.layer) - for pin in self.rbc_inst.get_pins("gnd"): - self.add_power_pin("gnd", pin.center()) + for pin in self.replica_cell_inst.get_pins("gnd"): + self.add_power_pin("gnd", pin.center(), vertical=True, start_layer=pin.layer) @@ -275,10 +275,10 @@ class replica_bitline(design.design): poly_offset = poly_pin.uc() # This centers the contact above the poly by one pitch contact_offset = poly_offset + vector(0,self.m2_pitch) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=contact_offset) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=contact_offset) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=contact_offset) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=contact_offset) self.add_segment_center(layer="poly", start=poly_offset, end=contact_offset) @@ -289,12 +289,12 @@ class replica_bitline(design.design): # height=self.delay_chain_offset.y-nwell_offset.y) # 2. Route delay chain output to access tx gate - delay_en_offset = self.dc_inst.get_pin("out").bc() + delay_en_offset = self.delay_chain_inst.get_pin("out").bc() self.add_path("metal2", [delay_en_offset,contact_offset]) # 3. Route the contact of previous route to the bitcell WL # route bend of previous net to bitcell WL - wl_offset = self.rbc_inst.get_pin(self.wl_list[0]).lc() + wl_offset = self.replica_cell_inst.get_pin(self.wl_list[0]).lc() wl_mid1 = wl_offset - vector(1.5*drc("minwidth_metal1"), 0) wl_mid2 = vector(wl_mid1.x, contact_offset.y) #xmid_point= 0.5*(wl_offset.x+contact_offset.x) @@ -305,8 +305,8 @@ class replica_bitline(design.design): # 4. Short wodlines if multiport wl = self.wl_list[0] wl_last = self.wl_list[-1] - pin = self.rbc_inst.get_pin(wl) - pin_last = self.rbc_inst.get_pin(wl_last) + pin = self.replica_cell_inst.get_pin(wl) + pin_last = self.replica_cell_inst.get_pin(wl_last) x_offset = self.short_wordlines(pin, pin_last, "left", True) #correct = vector(0.5*drc("minwidth_metal1"), 0) @@ -315,7 +315,7 @@ class replica_bitline(design.design): # DRAIN ROUTE # Route the drain to the vdd rail drain_offset = self.tx_inst.get_pin("D").center() - self.add_power_pin("vdd", drain_offset, rotate=0) + self.add_power_pin("vdd", drain_offset, vertical=True) # SOURCE ROUTE # Route the drain to the RBL inverter input @@ -325,7 +325,7 @@ class replica_bitline(design.design): # Route the connection of the source route to the RBL bitline (left) # Via will go halfway down from the bitcell - bl_offset = self.rbc_inst.get_pin(self.bl_list[0]).bc() + bl_offset = self.replica_cell_inst.get_pin(self.bl_list[0]).bc() # Route down a pitch so we can use M2 routing bl_down_offset = bl_offset - vector(0, self.m2_pitch) self.add_path("metal2",[source_offset, bl_down_offset, bl_offset]) @@ -344,12 +344,12 @@ class replica_bitline(design.design): def route_vdd(self): """ Route all signals connected to vdd """ - self.copy_layout_pin(self.dc_inst,"vdd") - self.copy_layout_pin(self.rbc_inst,"vdd") + self.copy_layout_pin(self.delay_chain_inst,"vdd") + self.copy_layout_pin(self.replica_cell_inst,"vdd") # Connect the WL and vdd pins directly to the center and right vdd rails # Connect RBL vdd pins to center and right rails - rbl_vdd_pins = self.rbl_inst.get_pins("vdd") + rbl_vdd_pins = self.replica_column_inst.get_pins("vdd") for pin in rbl_vdd_pins: if pin.layer != "metal1": continue @@ -360,11 +360,9 @@ class replica_bitline(design.design): start=start, end=end) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=start, - rotate=90) + offset=start) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end, - rotate=90) + offset=end) # Add via for the inverter pin = self.rbl_inv_inst.get_pin("vdd") @@ -374,38 +372,34 @@ class replica_bitline(design.design): start=start, end=end) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=start, - rotate=90) + offset=start) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end, - rotate=90) + offset=end) # Add via for the RBC - pin = self.rbc_inst.get_pin("vdd") + pin = self.replica_cell_inst.get_pin("vdd") start = pin.lc() end = vector(self.right_vdd_pin.cx(),pin.cy()) self.add_segment_center(layer="metal1", start=start, end=end) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end, - rotate=90) + offset=end) # Create the RBL rails too - rbl_pins = self.rbl_inst.get_pins("vdd") + rbl_pins = self.replica_column_inst.get_pins("vdd") for pin in rbl_pins: if pin.layer != "metal1": continue # If above the delay line, route the full width left = vector(self.left_vdd_pin.cx(),pin.cy()) center = vector(self.center_vdd_pin.cx(),pin.cy()) - if pin.cy() > self.dc_inst.uy() + self.m1_pitch: + if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch: start = left self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left, - rotate=90) + offset=left) else: start = center end = vector(self.right_vdd_pin.cx()+0.5*self.m1_width,pin.cy()) @@ -425,22 +419,22 @@ class replica_bitline(design.design): # Route the gnd lines from left to right # Add via for the delay chain - left_gnd_start = self.dc_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0) - left_gnd_end = vector(left_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch) + left_gnd_start = self.delay_chain_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0) + left_gnd_end = vector(left_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch) self.left_gnd_pin=self.add_segment_center(layer="metal2", start=left_gnd_start, end=left_gnd_end) # Gnd line to the left of the replica bitline - center_gnd_start = self.rbc_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0) - center_gnd_end = vector(center_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch) + center_gnd_start = self.replica_cell_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0) + center_gnd_end = vector(center_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch) self.center_gnd_pin=self.add_segment_center(layer="metal2", start=center_gnd_start, end=center_gnd_end) # Gnd line to the right of the replica bitline - right_gnd_start = self.rbc_inst.lr().scale(1,0) + vector(self.m2_pitch,0) - right_gnd_end = vector(right_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch) + right_gnd_start = self.replica_cell_inst.lr().scale(1,0) + vector(self.m2_pitch,0) + right_gnd_end = vector(right_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch) self.right_gnd_pin=self.add_segment_center(layer="metal2", start=right_gnd_start, end=right_gnd_end) @@ -450,13 +444,13 @@ class replica_bitline(design.design): # Connect the WL and gnd pins directly to the center and right gnd rails for row in range(self.bitcell_loads): wl = self.wl_list[0]+"_{}".format(row) - pin = self.rbl_inst.get_pin(wl) + pin = self.replica_column_inst.get_pin(wl) if pin.layer != "metal1": continue # If above the delay line, route the full width left = vector(self.left_gnd_pin.cx(),pin.cy()) center = vector(self.center_gnd_pin.cx(),pin.cy()) - if pin.cy() > self.dc_inst.uy() + self.m1_pitch: + if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch: start = left else: start = center @@ -467,47 +461,41 @@ class replica_bitline(design.design): end=end) if start == left: self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left, - rotate=90) + offset=left) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=center, - rotate=90) + offset=center) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end, - rotate=90) + offset=end) - rbl_gnd_pins = self.rbl_inst.get_pins("gnd") - # Add L shapes to each vertical gnd rail - for pin in rbl_gnd_pins: - if pin.layer != "metal1": - continue - # If above the delay line, route the full width - left = vector(self.left_gnd_pin.cx(),pin.cy()) - center = vector(self.center_gnd_pin.cx(),pin.cy()) - if pin.cy() > self.dc_inst.uy() + self.m1_pitch: - start = left - else: - start = center - end = vector(self.right_gnd_pin.cx(),pin.cy()) - self.add_segment_center(layer="metal1", - start=start, - end=end) - if start == left: - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=center, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end, - rotate=90) + # rbl_gnd_pins = self.replica_column_inst.get_pins("gnd") + # # Add L shapes to each vertical gnd rail + # for pin in rbl_gnd_pins: + # if pin.layer != "metal1": + # continue + # # If above the delay line, route the full width + # left = vector(self.left_gnd_pin.cx(),pin.cy()) + # center = vector(self.center_gnd_pin.cx(),pin.cy()) + # if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch: + # start = left + # else: + # start = center + # end = vector(self.right_gnd_pin.cx(),pin.cy()) + # self.add_segment_center(layer="metal1", + # start=start, + # end=end) + # if start == left: + # self.add_via_center(layers=("metal1", "via1", "metal2"), + # offset=left) + # self.add_via_center(layers=("metal1", "via1", "metal2"), + # offset=center) + # self.add_via_center(layers=("metal1", "via1", "metal2"), + # offset=end) # Connect the gnd pins of the delay chain to the left rails - dc_gnd_pins = self.dc_inst.get_pins("gnd") + dc_gnd_pins = self.delay_chain_inst.get_pins("gnd") for pin in dc_gnd_pins: if pin.layer != "metal1": continue @@ -520,12 +508,10 @@ class replica_bitline(design.design): start=start, end=end) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=start, - rotate=90) + offset=start) # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=end, - #rotate=90) + # offset=end) # Add via for the inverter @@ -536,16 +522,14 @@ class replica_bitline(design.design): # start=start, # end=end) # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=start, - #rotate=90) + # offset=start) # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=end, - #rotate=90) + # offset=end) # Create RBL rails too - rbl_pins = self.rbl_inst.get_pins("gnd") + rbl_pins = self.replica_column_inst.get_pins("gnd") for pin in rbl_pins: if pin.layer != "metal2": continue @@ -560,7 +544,7 @@ class replica_bitline(design.design): def add_layout_pins(self): """ Route the input and output signal """ - en_offset = self.dc_inst.get_pin("in").bc() + en_offset = self.delay_chain_inst.get_pin("in").bc() self.add_layout_pin_segment_center(text="en", layer="metal2", start=en_offset, @@ -587,7 +571,7 @@ class replica_bitline(design.design): height=pin.height(), width=pin.width()) - pin = self.dc_inst.get_pin("out") + pin = self.delay_chain_inst.get_pin("out") self.add_label_pin(text="delayed_en", layer=pin.layer, offset=pin.ll(), diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index d7829900..576d6ad7 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -131,8 +131,7 @@ class single_level_column_mux_array(design.design): self.add_layout_pin(text="sel_{}".format(j), layer="metal1", offset=offset, - width=self.mux.width * self.columns, - height=contact.m1m2.width) + width=self.mux.width * self.columns) def add_vertical_poly_rail(self): """ Connect the poly to the address rails """ @@ -149,73 +148,60 @@ class single_level_column_mux_array(design.design): offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy()) # Add the poly contact with a shift to account for the rotation self.add_via_center(layers=("metal1", "contact", "poly"), - offset=offset, - rotate=90) + offset=offset) self.add_path("poly", [offset, gate_offset]) def route_bitlines(self): """ Connect the output bit-lines to form the appropriate width mux """ for j in range(self.columns): - bl_offset = self.mux_inst[j].get_pin("bl_out").ll() - br_offset = self.mux_inst[j].get_pin("br_out").ll() + bl_offset = self.mux_inst[j].get_pin("bl_out").bc() + br_offset = self.mux_inst[j].get_pin("br_out").bc() bl_out_offset = bl_offset - vector(0,(self.words_per_row+1)*self.m1_pitch) br_out_offset = br_offset - vector(0,(self.words_per_row+2)*self.m1_pitch) + bl_out_offset_end = bl_out_offset + vector(0,self.route_height) + br_out_offset_end = br_out_offset + vector(0,self.route_height) + if (j % self.words_per_row) == 0: # Create the metal1 to connect the n-way mux output from the pass gate # These will be located below the select lines. Yes, these are M2 width # to ensure vias are enclosed and M1 min width rules. - width = contact.m1m2.width + self.mux.width * (self.words_per_row - 1) - self.add_rect(layer="metal1", - offset=bl_out_offset, - width=width, - height=drc("minwidth_metal2")) - self.add_rect(layer="metal1", - offset=br_out_offset, - width=width, - height=drc("minwidth_metal2")) - + width = self.m2_width + self.mux.width * (self.words_per_row - 1) + self.add_path("metal1", [bl_out_offset, bl_out_offset+vector(width,0)]) + self.add_path("metal1", [br_out_offset, br_out_offset+vector(width,0)]) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux - self.add_layout_pin(text="bl_out_{}".format(int(j/self.words_per_row)), - layer="metal2", - offset=bl_out_offset.scale(1,0), - width=drc('minwidth_metal2'), - height=self.route_height) - self.add_layout_pin(text="br_out_{}".format(int(j/self.words_per_row)), - layer="metal2", - offset=br_out_offset.scale(1,0), - width=drc('minwidth_metal2'), - height=self.route_height) + self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j/self.words_per_row)), + layer="metal2", + start=bl_out_offset, + end=bl_out_offset_end) + self.add_layout_pin_segment_center(text="br_out_{}".format(int(j/self.words_per_row)), + layer="metal2", + start=br_out_offset, + end=br_out_offset_end) + # This via is on the right of the wire - self.add_via(layers=("metal1", "via1", "metal2"), - offset=bl_out_offset + vector(contact.m1m2.height,0), - rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=bl_out_offset) + # This via is on the left of the wire - self.add_via(layers=("metal1", "via1", "metal2"), - offset= br_out_offset, - rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=br_out_offset) else: - self.add_rect(layer="metal2", - offset=bl_out_offset, - width=drc('minwidth_metal2'), - height=self.route_height-bl_out_offset.y) + self.add_path("metal2", [ bl_out_offset, bl_out_offset_end]) + self.add_path("metal2", [ br_out_offset, br_out_offset_end]) + # This via is on the right of the wire - self.add_via(layers=("metal1", "via1", "metal2"), - offset=bl_out_offset + vector(contact.m1m2.height,0), - rotate=90) - self.add_rect(layer="metal2", - offset=br_out_offset, - width=drc('minwidth_metal2'), - height=self.route_height-br_out_offset.y) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=bl_out_offset) # This via is on the left of the wire - self.add_via(layers=("metal1", "via1", "metal2"), - offset= br_out_offset, - rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=br_out_offset) + def analytical_delay(self, corner, slew, load): from tech import parameter @@ -230,4 +216,4 @@ class single_level_column_mux_array(design.design): from tech import parameter #Bitcell drain load being used to estimate mux NMOS drain load drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - return drain_load \ No newline at end of file + return drain_load diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 6ddba9de..977c8789 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -81,21 +81,13 @@ class wordline_driver(design.design): (gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num) # Route both supplies - for n in ["vdd", "gnd"]: - supply_pin = self.inv2_inst[num].get_pin(n) + for name in ["vdd", "gnd"]: + supply_pin = self.inv2_inst[num].get_pin(name) # Add pins in two locations for xoffset in [a_xoffset, b_xoffset]: pin_pos = vector(xoffset, supply_pin.cy()) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=pin_pos, - rotate=90) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=pin_pos, - rotate=90) - self.add_layout_pin_rect_center(text=n, - layer="metal3", - offset=pin_pos) + self.add_power_pin(name, pin_pos) @@ -193,13 +185,14 @@ class wordline_driver(design.design): start=input_offset, end=mid_via_offset) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=mid_via_offset) + offset=mid_via_offset, + directions=("V","V")) # now connect to the nand2 B self.add_path("metal2", [mid_via_offset, b_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=b_pos - vector(0.5*contact.m1m2.height,0), - rotate=90) + directions=("H","H")) # output each WL on the right diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 2f641da9..25ea591f 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -42,7 +42,7 @@ class pgate(design.design): height=height, width=source_pin.width()) - def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=90): + 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. """ @@ -61,14 +61,16 @@ class pgate(design.design): left_gate_offset = vector(nmos_gate_pin.lx(),ypos) # Center is completely symmetric. - if rotate==90: + 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") 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") if position=="center": contact_offset = left_gate_offset + vector(0.5*self.poly_width, 0) @@ -79,9 +81,12 @@ class pgate(design.design): else: debug.error("Invalid contact placement option.", -1) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=contact_offset, - rotate=rotate) + # Non-preferred direction via + + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=contact_offset, + directions=directions) + # self.add_layout_pin_segment_center(text=name, # layer="metal1", # start=left_gate_offset.scale(0,1), @@ -145,10 +150,11 @@ class pgate(design.design): # 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_contact_center(layers=layer_stack, - offset=contact_offset, - implant_type="n", - well_type="n") + 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)), width=self.nwell_contact.mod.second_layer_width, @@ -191,10 +197,11 @@ class pgate(design.design): # 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_contact_center(layers=layer_stack, - offset=contact_offset, - implant_type="p", - well_type="p") + 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, diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index f00e9af7..5f61d200 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -58,7 +58,7 @@ 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", rotate=0) + self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A") self.route_outputs() def add_pins(self): @@ -222,8 +222,8 @@ class pinv(pgate.pgate): pmos_drain_pin = self.pmos_inst.get_pin("D") # Pick point at right most of NMOS and connect down to PMOS - nmos_drain_pos = nmos_drain_pin.lr() - vector(0.5*self.m1_width,0) - pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.bc().y) + nmos_drain_pos = nmos_drain_pin.lr() + pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y) self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos]) # Remember the mid for the output diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 95a9bc28..168bfaf2 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -203,13 +203,15 @@ class pnand2(pgate.pgate): mid1_offset = vector(out_offset.x, top_pin_offset.y) mid2_offset = vector(out_offset.x, bottom_pin_offset.y) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=pmos_pin.center()) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=nmos_pin.center()) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=out_offset, - rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=pmos_pin.center(), + directions=("V","H")) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=nmos_pin.center(), + 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]) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index d0c37b55..95d02f29 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -213,12 +213,12 @@ class pnand3(pgate.pgate): nmos3_pin = self.nmos3_inst.get_pin("D") # Go up to metal2 for ease on all output pins - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=pmos1_pin.center()) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=pmos3_pin.center()) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=nmos3_pin.center()) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=pmos1_pin.center()) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=pmos3_pin.center()) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=nmos3_pin.center()) # PMOS3 and NMOS3 are drain aligned self.add_path("metal2",[pmos3_pin.bc(), nmos3_pin.uc()]) @@ -227,8 +227,8 @@ class pnand3(pgate.pgate): self.add_path("metal2",[pmos1_pin.bc(), mid_offset, nmos3_pin.uc()]) # This extends the output to the edge of the cell - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=mid_offset) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=mid_offset) self.add_layout_pin_rect_center(text="Z", layer="metal1", offset=mid_offset, diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 8a5c80d4..125261d1 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -184,11 +184,11 @@ class pnor2(pgate.pgate): nmos2_pin = self.nmos2_inst.get_pin("D") # Go up to metal2 for ease on all output pins - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=pmos_pin.center()) - m1m2_contact=self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=nmos_pin.center(), - rotate=90) + 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()) + mid1_offset = vector(pmos_pin.center().x,nmos2_pin.center().y) mid2_offset = vector(pmos_pin.center().x,self.inputA_yoffset) @@ -198,9 +198,8 @@ class pnor2(pgate.pgate): 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_contact_center(layers=("metal1", "via1", "metal2"), - offset=mid3_offset, - rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=mid3_offset) self.add_layout_pin_rect_center(text="Z", layer="metal1", offset=mid3_offset, diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index cea9c845..5a7672d6 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -75,7 +75,7 @@ class precharge(pgate.pgate): self.add_path("metal1", [pmos_pin.uc(), pmos_vdd_pos]) # Add vdd pin above the transistor - self.add_power_pin("vdd", pmos_pin.center(), rotate=0) + self.add_power_pin("vdd", pmos_pin.center(), vertical=True) def create_ptx(self): @@ -149,9 +149,9 @@ class precharge(pgate.pgate): # 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) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=offset, - rotate=90) + 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", @@ -168,10 +168,10 @@ class precharge(pgate.pgate): # 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")) - self.add_contact_center(layers=("active", "contact", "metal1"), - offset=well_contact_pos, - implant_type="n", - well_type="n") + self.add_via_center(layers=("active", "contact", "metal1"), + offset=well_contact_pos, + implant_type="n", + well_type="n") # leave an extra pitch for the height self.height = well_contact_pos.y + contact.well.height + self.m1_pitch @@ -225,16 +225,20 @@ class precharge(pgate.pgate): lower_pin = self.lower_pmos_inst.get_pin("S") # BL goes up to M2 at the transistor - self.bl_contact=self.add_contact_center(layers=stack, - offset=upper_pin.center()) - self.add_contact_center(layers=stack, - offset=lower_pin.center()) + 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")) # BR routes over on M1 first - self.add_contact_center(layers=stack, - offset = vector(self.br_pin.cx(), upper_pin.cy())) - self.add_contact_center(layers=stack, - offset = vector(self.br_pin.cx(), lower_pin.cy())) + self.add_via_center(layers=stack, + 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")) def connect_pmos_m1(self, pmos_pin, bit_pin): """ diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 3a127454..36fda7a0 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -326,11 +326,12 @@ class ptx(design.design): [source_positions,drain_positions] = self.get_contact_positions() for pos in source_positions: - contact=self.add_contact_center(layers=("active", "contact", "metal1"), - offset=pos, - size=(1, self.num_contacts), - implant_type=self.implant_type, - well_type=self.well_type) + contact=self.add_via_center(layers=("active", "contact", "metal1"), + offset=pos, + size=(1, self.num_contacts), + directions=("H","V"), + implant_type=self.implant_type, + well_type=self.well_type) self.add_layout_pin_rect_center(text="S", layer="metal1", offset=pos, @@ -339,11 +340,12 @@ class ptx(design.design): for pos in drain_positions: - contact=self.add_contact_center(layers=("active", "contact", "metal1"), - offset=pos, - size=(1, self.num_contacts), - implant_type=self.implant_type, - well_type=self.well_type) + contact=self.add_via_center(layers=("active", "contact", "metal1"), + offset=pos, + size=(1, self.num_contacts), + directions=("H","V"), + implant_type=self.implant_type, + well_type=self.well_type) self.add_layout_pin_rect_center(text="D", layer="metal1", offset=pos, diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 2bdf5bef..d652fa9f 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -127,13 +127,18 @@ class single_level_column_mux(design.design): # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D self.add_via_center(layers=("metal1","via1","metal2"), - offset=bl_pin.bc()) + offset=bl_pin.bc(), + directions=("V","V")) self.add_via_center(layers=("metal1","via1","metal2"), - offset=br_out_pin.uc()) + offset=br_out_pin.uc(), + directions=("V","V")) self.add_via_center(layers=("metal1","via1","metal2"), - offset=nmos_upper_s_pin.center()) + offset=nmos_upper_s_pin.center(), + directions=("V","V")) self.add_via_center(layers=("metal1","via1","metal2"), - offset=nmos_lower_d_pin.center()) + offset=nmos_lower_d_pin.center(), + directions=("V","V")) + # bl -> nmos_upper/D on metal1 # bl_out -> nmos_upper/S on metal2 diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index cbfec653..5f334cc7 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -200,8 +200,7 @@ class sram_1bank(sram_base): clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y) self.add_path("metal1", [control_clk_buf_pos, clk_steiner_pos]) self.add_via_center(layers=("metal1","via1","metal2"), - offset=clk_steiner_pos, - rotate=90) + offset=clk_steiner_pos) # Note, the via to the control logic is taken care of above self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos]) @@ -233,8 +232,7 @@ class sram_1bank(sram_base): dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) self.connect_rail_from_left_m2m3(src_pin, dest_pin) self.add_via_center(layers=("metal1","via1","metal2"), - offset=src_pin.rc(), - rotate=90) + offset=src_pin.rc()) def route_row_addr_dff(self): @@ -250,8 +248,7 @@ class sram_1bank(sram_base): mid_pos = vector(bank_pos.x,flop_pos.y) self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos]) self.add_via_center(layers=("metal2","via2","metal3"), - offset=flop_pos, - rotate=90) + offset=flop_pos) def route_col_addr_dff(self): """ Connect the output of the row flops to the bank pins """ diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 177f34c6..90d9771a 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -469,8 +469,7 @@ class sram_base(design, verilog, lef): out_pos = dest_pin.center() self.add_wire(("metal3","via2","metal2"),[in_pos, vector(out_pos.x,in_pos.y),out_pos]) self.add_via_center(layers=("metal2","via2","metal3"), - offset=src_pin.rc(), - rotate=90) + offset=src_pin.rc()) def connect_rail_from_left_m2m1(self, src_pin, dest_pin): diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 1b4041d4..fcb985ff 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -54,12 +54,15 @@ class sram_factory: self.objects[module_type] = [] # Either retreive a previous object or create a new one + #print("new",kwargs) for obj in self.objects[module_type]: (obj_kwargs, obj_item) = obj # Must have the same dictionary exactly (conservative) if obj_kwargs == kwargs: - debug.info(3, "Existing module: type={0} name={1} kwargs={2}".format(module_type, obj_item.name, str(kwargs))) + #debug.info(0, "Existing module: type={0} name={1} kwargs={2}".format(module_type, obj_item.name, str(kwargs))) return obj_item + #else: + # print("obj",obj_kwargs) # Use the default name if there are default arguments # This is especially for library cells so that the spice and gds files can be found. @@ -67,7 +70,8 @@ class sram_factory: # Create a unique name and increment the index module_name = "{0}_{1}".format(module_name, self.module_indices[module_type]) self.module_indices[module_type] += 1 - #debug.info(1, "New module: type={0} name={1} kwargs={2}".format(module_type,module_name,str(kwargs))) + + #debug.info(0, "New module: type={0} name={1} kwargs={2}".format(module_type,module_name,str(kwargs))) obj = mod(name=module_name,**kwargs) self.objects[module_type].append((kwargs,obj)) return obj diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py index 29d7ca0e..cbd309dc 100755 --- a/compiler/tests/03_contact_test.py +++ b/compiler/tests/03_contact_test.py @@ -7,36 +7,50 @@ import sys,os sys.path.append(os.path.join(sys.path[0],"..")) import globals from globals import OPTS +from sram_factory import factory import debug class contact_test(openram_test): def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name)) - import contact - - for layer_stack in [("poly", "contact", "metal1"), ("metal1", "via1", "metal2")]: + for layer_stack in [("metal1", "via1", "metal2"), ("poly", "contact", "metal1")]: stack_name = ":".join(map(str, layer_stack)) # Check single 1 x 1 contact" debug.info(2, "1 x 1 {} test".format(stack_name)) - c = contact.contact(layer_stack, (1, 1)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1)) + self.local_drc_check(c) + + # Check single 1 x 1 contact" + debug.info(2, "1 x 1 {} test".format(stack_name)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1), directions=("H","V")) self.local_drc_check(c) + # Check single x 1 contact" + debug.info(2, "1 x 1 {} test".format(stack_name)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1), directions=("H","H")) + self.local_drc_check(c) + + # Check single 1 x 1 contact" + debug.info(2, "1 x 1 {} test".format(stack_name)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1), directions=("V","V")) + self.local_drc_check(c) + # check vertical array with one in the middle and two ends debug.info(2, "1 x 3 {} test".format(stack_name)) - c = contact.contact(layer_stack, (1, 3)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 3)) self.local_drc_check(c) # check horizontal array with one in the middle and two ends debug.info(2, "3 x 1 {} test".format(stack_name)) - c = contact.contact(layer_stack, (3, 1)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(3, 1)) self.local_drc_check(c) # check 3x3 array for all possible neighbors debug.info(2, "3 x 3 {} test".format(stack_name)) - c = contact.contact(layer_stack, (3, 3)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(3, 3)) self.local_drc_check(c) globals.end_openram()