From c415b2adab22355fe4922a73417f803da756251a Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Sat, 10 Apr 2021 05:11:18 +0900 Subject: [PATCH 01/33] Several changes to match the requirements of tsmc18. 90% done --- compiler/base/hierarchy_layout.py | 20 +++++++ compiler/pgates/pgate.py | 91 +++++++++++++++++++++++++++++-- compiler/pgates/precharge.py | 23 ++++++++ compiler/pgates/ptx.py | 5 +- 4 files changed, 133 insertions(+), 6 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index fad5c8aea..3337e56eb 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1240,6 +1240,16 @@ def add_power_pin(self, name, loc, directions=None, start_layer="m1"): min_area = drc["minarea_{}".format(self.pwr_grid_layer)] width = round_to_grid(sqrt(min_area)) height = round_to_grid(min_area / width) + elif OPTS.tech_name == "tsmc18": + min_area = drc["minarea_{}".format(self.pwr_grid_layer)] + min_width = drc["minwidth_{}".format(self.pwr_grid_layer)] + dir = preferred_directions[self.pwr_grid_layer] + if dir == "V": + width = round_to_grid(min_width) + height = round_to_grid(min_area / width) + else: + height = round_to_grid(min_width) + width = round_to_grid(min_area / height) else: width = None height = None @@ -1281,6 +1291,16 @@ def copy_power_pin(self, pin, loc=None, directions=None): min_area = drc["minarea_{}".format(self.pwr_grid_layer)] width = round_to_grid(sqrt(min_area)) height = round_to_grid(min_area / width) + elif OPTS.tech_name == "tsmc18": + min_area = drc["minarea_{}".format(self.pwr_grid_layer)] + min_width = drc["minwidth_{}".format(self.pwr_grid_layer)] + dir = preferred_directions[self.pwr_grid_layer] + if dir == "V": + width = round_to_grid(min_width) + height = round_to_grid(min_area / width) + else: + height = round_to_grid(min_width) + width = round_to_grid(min_area / height) else: width = None height = None diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 19a027e9b..f6570fbae 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -11,9 +11,12 @@ import math from bisect import bisect_left from tech import layer, drc +from tech import layer_indices +from tech import layer_stacks from vector import vector from globals import OPTS from tech import cell_properties as cell_props +from utils import round_to_grid if cell_props.ptx.bin_spice_models: from tech import nmos_bins, pmos_bins @@ -135,11 +138,39 @@ def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", di offset=contact_offset, directions=directions) + # TSMC18 gate port hack + width = via.mod.second_layer_width + height = via.mod.second_layer_width + if OPTS.tech_name == "tsmc18": + cur_layer = "poly" + while cur_layer != self.route_layer: + from_id = layer_indices[cur_layer] + to_id = layer_indices[self.route_layer] + + if from_id < to_id: # grow the stack up + search_id = 0 + next_id = 2 + else: # grow the stack down + search_id = 2 + next_id = 0 + + curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None) + cur_layer = curr_stack[next_id] + # print("Putting {}".format(cur_layer)) + if cur_layer != "poly": + min_area = drc["minarea_{}".format(cur_layer)] + width = round_to_grid(math.sqrt(min_area)) + height = round_to_grid(min_area / width) + self.add_rect_center(layer=self.route_layer, + offset=contact_offset, + width=width, + height=height) + self.add_layout_pin_rect_center(text=name, layer=self.route_layer, offset=contact_offset, - width=via.mod.second_layer_width, - height=via.mod.second_layer_height) + width=width, + height=height) # This is to ensure that the contact is # connected to the gate mid_point = contact_offset.scale(0.5, 1) \ @@ -201,8 +232,11 @@ def add_nwell_contact(self, pmos, pmos_pos): layer_stack = self.active_stack # To the right a spacing away from the pmos right active edge + # Also, avoid to do intersection of nimp and pimp + # TODO: The pimplant_to_nimplant is new contact_xoffset = pmos_pos.x + pmos.active_width \ - + self.active_space + + max(self.active_space, 2*drc("implant_enclose_active") + drc("pimplant_to_nimplant"), + drc("implant_to_active") + drc("implant_enclose_active")) # Must be at least an well enclosure of active down # from the top of the well @@ -247,6 +281,28 @@ def add_nwell_contact(self, pmos, pmos_pos): # width=implant_width, # height=implant_height) + # TSMC18 gate port hack + if OPTS.tech_name == "tsmc18": + min_area = drc["minarea_{}".format(self.active_stack[0])] + width = round_to_grid(self.nwell_contact.mod.first_layer_width) + height = round_to_grid(min_area / width) + width_impl = width + 2 * drc("implant_enclose_active") + height_impl = height + 2 * drc("implant_enclose_active") # contact.py:250 + width_well = width + 2 * self.nwell_contact.mod.well_enclose_active + height_well = height + 2 * self.nwell_contact.mod.well_enclose_active # contact.py:264 + self.add_rect_center(layer=self.active_stack[0], + offset=contact_offset, + width=width, + height=height) + self.add_rect_center(layer="nimplant", + offset=contact_offset, + width=width_impl, + height=height_impl) + self.add_rect_center(layer="nwell", + offset=contact_offset, + width=width_well, + height=height_well) + # Return the top of the well def extend_implants(self): @@ -301,8 +357,13 @@ def add_pwell_contact(self, nmos, nmos_pos): layer_stack = self.active_stack # To the right a spacing away from the nmos right active edge + # Also, avoid to do intersection of nimp and pimp + # Also, there should be a distance between channel and implant + # TODO: The pimplant_to_nimplant is new. Probably not in other techs contact_xoffset = nmos_pos.x + nmos.active_width \ - + self.active_space + + max(self.active_space, + 2*drc("implant_enclose_active") + drc("pimplant_to_nimplant"), + drc("implant_to_active") + drc("implant_enclose_active")) # Must be at least an well enclosure of active up # from the bottom of the well contact_yoffset = max(nmos_pos.y, @@ -344,6 +405,28 @@ def add_pwell_contact(self, nmos, nmos_pos): # width=implant_width, # height=implant_height) + # TSMC18 gate port hack + if OPTS.tech_name == "tsmc18": + min_area = drc["minarea_{}".format(self.active_stack[0])] + width = round_to_grid(self.pwell_contact.mod.first_layer_width) + height = round_to_grid(min_area / width) + width_impl = width + 2 * drc("implant_enclose_active") + height_impl = height + 2 * drc("implant_enclose_active") # contact.py:250 + width_well = width + 2 * self.pwell_contact.mod.well_enclose_active + height_well = height + 2 * self.pwell_contact.mod.well_enclose_active # contact.py:264 + self.add_rect_center(layer=self.active_stack[0], + offset=contact_offset, + width=width, + height=height) + self.add_rect_center(layer="pimplant", + offset=contact_offset, + width=width_impl, + height=height_impl) + self.add_rect_center(layer="pwell", + offset=contact_offset, + width=width_well, + height=height_well) + def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index d19993847..8d86ac281 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -14,6 +14,7 @@ from globals import OPTS from sram_factory import factory from tech import cell_properties as cell_props +from utils import round_to_grid class precharge(design.design): @@ -230,6 +231,28 @@ def place_nwell_and_contact(self): width=self.width, height=self.height) + # TSMC18 gate port hack + if OPTS.tech_name == "tsmc18": + min_area = drc["minarea_{}".format(self.active_stack[0])] + height = round_to_grid(self.well_contact.mod.first_layer_width) + width = round_to_grid(min_area / height) + width_impl = width + 2 * drc("implant_enclose_active") + height_impl = height + 2 * drc("implant_enclose_active") # contact.py:250 + width_well = width + 2 * self.well_contact.mod.well_enclose_active + height_well = height + 2 * self.well_contact.mod.well_enclose_active # contact.py:264 + self.add_rect_center(layer=self.active_stack[0], + offset=self.well_contact_pos, + width=width, + height=height) + self.add_rect_center(layer="nimplant", + offset=self.well_contact_pos, + width=width_impl, + height=height_impl) + self.add_rect_center(layer="nwell", + offset=self.well_contact_pos, + width=width_well, + height=height_well) + def route_bitlines(self): """ Adds both bit-line and bit-line-bar to the module diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index a2928ce18..8ab694579 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -370,11 +370,12 @@ def add_active(self): # If the implant must enclose the active, shift offset # and increase width/height enclose_width = self.implant_enclose_active - enclose_offset = [enclose_width] * 2 + enclose_height = max(self.implant_enclose_active, drc("implant_to_channel")) + enclose_offset = vector(enclose_width, enclose_height) self.implant = 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) + height=self.active_height + 2 * enclose_height) def add_well_implant(self): """ From c5ffad3c34d271892a4fae6bccd25cb94dadfedd Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Sat, 10 Apr 2021 16:12:45 +0900 Subject: [PATCH 02/33] Final touches to all pgates --- compiler/pgates/column_mux.py | 27 ++++++++++++++++- compiler/pgates/pand2.py | 27 +++++++++++++++++ compiler/pgates/pand3.py | 27 +++++++++++++++++ compiler/pgates/pand4.py | 27 +++++++++++++++++ compiler/pgates/pgate.py | 21 +++++++++---- compiler/pgates/pnand3.py | 5 ++++ compiler/pgates/pnand4.py | 5 ++++ compiler/pgates/precharge.py | 55 +++++++++++++++++++++++++++++++++-- 8 files changed, 185 insertions(+), 9 deletions(-) diff --git a/compiler/pgates/column_mux.py b/compiler/pgates/column_mux.py index 1e8c5bf8e..110025356 100644 --- a/compiler/pgates/column_mux.py +++ b/compiler/pgates/column_mux.py @@ -12,6 +12,7 @@ from sram_factory import factory from tech import cell_properties as cell_props from globals import OPTS +from utils import round_to_grid class column_mux(pgate.pgate): @@ -221,7 +222,7 @@ def add_pn_wells(self): # 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) - self.add_via_center(layers=self.active_stack, + self.well_contact = self.add_via_center(layers=self.active_stack, offset=active_pos, implant_type="p", well_type="p") @@ -241,3 +242,27 @@ def add_pn_wells(self): offset=vector(0, 0), width=self.bitcell.width, height=self.height) + + # TSMC18 gate port hack + if OPTS.tech_name == "tsmc18": + # Body connection + min_area = drc["minarea_{}".format(self.active_stack[0])] + width = round_to_grid(self.well_contact.mod.first_layer_width) + height = round_to_grid(min_area / width) + width_impl = width + 2 * drc("implant_enclose_active") + height_impl = height + 2 * drc("implant_enclose_active") # contact.py:250 + self.add_rect_center(layer=self.active_stack[0], + offset=active_pos, + width=width, + height=height) + self.add_rect_center(layer="pimplant", + offset=active_pos, + width=width_impl, + height=height_impl) + if "pwell" in layer: + width_well = width + 2 * self.well_contact.mod.well_enclose_active + height_well = height + 2 * self.well_contact.mod.well_enclose_active # contact.py:264 + self.add_rect_center(layer="pwell", + offset=active_pos, + width=width_well, + height=height_well) diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 48b5d3a71..903005724 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -9,6 +9,8 @@ from vector import vector import pgate from sram_factory import factory +from globals import OPTS +from tech import drc class pand2(pgate.pgate): @@ -85,6 +87,31 @@ def place_insts(self): # Add INV to the right self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + # Extension of the imp in both n and p for tsmc18 + if OPTS.tech_name == "tsmc18": + pmos_rightmost_nand = self.nand_inst.mod.pmos2_pos + pmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.pmos_pos + vector(self.nand_inst.rx(), 0) + nmos_rightmost_nand = self.nand_inst.mod.nmos2_pos + nmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.nmos_pos + vector(self.nand_inst.rx(), 0) + pleft = pmos_rightmost_nand.x + self.nand_inst.mod.pmos_right.active_width + drc("implant_enclose_active") + pright = pmos_leftmost_inv.x - drc("implant_enclose_active") + ptop = pmos_rightmost_nand.y + self.nand_inst.mod.pmos_right.active_height + \ + max(drc("implant_enclose_active"), drc("implant_to_channel")) + pbottom = pmos_rightmost_nand.y - max(drc("implant_enclose_active"), drc("implant_to_channel")) + self.add_rect(layer="pimplant", + offset=vector(pleft, pbottom), + width=pright - pleft, + height=ptop - pbottom) + nleft = nmos_rightmost_nand.x + self.nand_inst.mod.nmos_right.active_width + drc("implant_enclose_active") + nright = nmos_leftmost_inv.x - drc("implant_enclose_active") + ntop = nmos_rightmost_nand.y + self.nand_inst.mod.nmos_right.active_height + \ + max(drc("implant_enclose_active"), drc("implant_to_channel")) + nbottom = nmos_rightmost_nand.y - max(drc("implant_enclose_active"), drc("implant_to_channel")) + self.add_rect(layer="nimplant", + offset=vector(nleft, nbottom), + width=nright - nleft, + height=ntop - nbottom) + def route_supply_rails(self): """ Add vdd/gnd rails to the top, (middle), and bottom. """ self.add_layout_pin_rect_center(text="gnd", diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index 713178cce..9919f7d57 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -9,6 +9,8 @@ from vector import vector import pgate from sram_factory import factory +from globals import OPTS +from tech import drc class pand3(pgate.pgate): @@ -90,6 +92,31 @@ def place_insts(self): # Add INV to the right self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + # Extension of the imp in both n and p for tsmc18 + if OPTS.tech_name == "tsmc18": + pmos_rightmost_nand = self.nand_inst.mod.pmos3_pos + pmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.pmos_pos + vector(self.nand_inst.rx(), 0) + nmos_rightmost_nand = self.nand_inst.mod.nmos3_pos + nmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.nmos_pos + vector(self.nand_inst.rx(), 0) + pleft = pmos_rightmost_nand.x + self.nand_inst.mod.pmos_right.active_width + drc("implant_enclose_active") + pright = pmos_leftmost_inv.x - drc("implant_enclose_active") + ptop = pmos_rightmost_nand.y + self.nand_inst.mod.pmos_right.active_height + \ + max(drc("implant_enclose_active"), drc("implant_to_channel")) + pbottom = pmos_rightmost_nand.y - max(drc("implant_enclose_active"), drc("implant_to_channel")) + self.add_rect(layer="pimplant", + offset=vector(pleft, pbottom), + width=pright - pleft, + height=ptop - pbottom) + nleft = nmos_rightmost_nand.x + self.nand_inst.mod.nmos_right.active_width + drc("implant_enclose_active") + nright = nmos_leftmost_inv.x - drc("implant_enclose_active") + ntop = nmos_rightmost_nand.y + self.nand_inst.mod.nmos_right.active_height + \ + max(drc("implant_enclose_active"), drc("implant_to_channel")) + nbottom = nmos_rightmost_nand.y - max(drc("implant_enclose_active"), drc("implant_to_channel")) + self.add_rect(layer="nimplant", + offset=vector(nleft, nbottom), + width=nright - nleft, + height=ntop - nbottom) + def route_supply_rails(self): """ Add vdd/gnd rails to the top, (middle), and bottom. """ self.add_layout_pin_rect_center(text="gnd", diff --git a/compiler/pgates/pand4.py b/compiler/pgates/pand4.py index 63eb11335..71f43f554 100644 --- a/compiler/pgates/pand4.py +++ b/compiler/pgates/pand4.py @@ -9,6 +9,8 @@ from vector import vector import pgate from sram_factory import factory +from globals import OPTS +from tech import drc class pand4(pgate.pgate): @@ -91,6 +93,31 @@ def place_insts(self): # Add INV to the right self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + # Extension of the imp in both n and p for tsmc18 + if OPTS.tech_name == "tsmc18": + pmos_rightmost_nand = self.nand_inst.mod.pmos4_pos + pmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.pmos_pos + vector(self.nand_inst.rx(), 0) + nmos_rightmost_nand = self.nand_inst.mod.nmos4_pos + nmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.nmos_pos + vector(self.nand_inst.rx(), 0) + pleft = pmos_rightmost_nand.x + self.nand_inst.mod.pmos_right.active_width + drc("implant_enclose_active") + pright = pmos_leftmost_inv.x - drc("implant_enclose_active") + ptop = pmos_rightmost_nand.y + self.nand_inst.mod.pmos_right.active_height + \ + max(drc("implant_enclose_active"), drc("implant_to_channel")) + pbottom = pmos_rightmost_nand.y - max(drc("implant_enclose_active"), drc("implant_to_channel")) + self.add_rect(layer="pimplant", + offset=vector(pleft, pbottom), + width=pright - pleft, + height=ptop - pbottom) + nleft = nmos_rightmost_nand.x + self.nand_inst.mod.nmos_right.active_width + drc("implant_enclose_active") + nright = nmos_leftmost_inv.x - drc("implant_enclose_active") + ntop = nmos_rightmost_nand.y + self.nand_inst.mod.nmos_right.active_height + \ + max(drc("implant_enclose_active"), drc("implant_to_channel")) + nbottom = nmos_rightmost_nand.y - max(drc("implant_enclose_active"), drc("implant_to_channel")) + self.add_rect(layer="nimplant", + offset=vector(nleft, nbottom), + width=nright - nleft, + height=ntop - nbottom) + def route_supply_rails(self): """ Add vdd/gnd rails to the top, (middle), and bottom. """ self.add_layout_pin_rect_center(text="gnd", diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index f6570fbae..e3c973ddd 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -12,7 +12,7 @@ from bisect import bisect_left from tech import layer, drc from tech import layer_indices -from tech import layer_stacks +from tech import layer_stacks, preferred_directions from vector import vector from globals import OPTS from tech import cell_properties as cell_props @@ -140,7 +140,9 @@ def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", di # TSMC18 gate port hack width = via.mod.second_layer_width - height = via.mod.second_layer_width + height = via.mod.second_layer_height + offset = contact_offset + # TODO: Put this in a function? if OPTS.tech_name == "tsmc18": cur_layer = "poly" while cur_layer != self.route_layer: @@ -159,18 +161,25 @@ def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", di # print("Putting {}".format(cur_layer)) if cur_layer != "poly": min_area = drc["minarea_{}".format(cur_layer)] + min_width = drc["minwidth_{}".format(cur_layer)] width = round_to_grid(math.sqrt(min_area)) height = round_to_grid(min_area / width) - self.add_rect_center(layer=self.route_layer, - offset=contact_offset, + dir = preferred_directions[cur_layer] + # TODO: This is very hackish. We literally just put the rect a little up + # This is caused by the nand3 and 4, whose pitches are a mess + offset = contact_offset + if(dir == "H"): + offset = contact_offset + vector(0.0, -(height - min_width)/2) + self.add_rect_center(layer=cur_layer, + offset=offset, width=width, height=height) self.add_layout_pin_rect_center(text=name, layer=self.route_layer, offset=contact_offset, - width=width, - height=height) + width=via.mod.second_layer_width, + height=via.mod.second_layer_height) # This is to ensure that the contact is # connected to the gate mid_point = contact_offset.scale(0.5, 1) \ diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 1c14092b9..09d86aaf7 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -13,6 +13,7 @@ from sram_factory import factory import contact from tech import cell_properties as cell_props +from globals import OPTS class pnand3(pgate.pgate): @@ -226,6 +227,10 @@ def route_inputs(self): active_to_poly_contact, active_to_poly_contact2) + # TODO: There has to be a better way for this + if OPTS.tech_name == "tsmc18": + self.inputA_yoffset += self.m1_pitch + apin = self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, diff --git a/compiler/pgates/pnand4.py b/compiler/pgates/pnand4.py index 2a88f2d12..218f71d30 100644 --- a/compiler/pgates/pnand4.py +++ b/compiler/pgates/pnand4.py @@ -13,6 +13,7 @@ from sram_factory import factory import contact from tech import cell_properties as cell_props +from globals import OPTS class pnand4(pgate.pgate): @@ -241,6 +242,10 @@ def route_inputs(self): active_to_poly_contact, active_to_poly_contact2) + # TODO: There has to be a better way for this + if OPTS.tech_name == "tsmc18": + self.inputA_yoffset += self.m1_pitch + apin = self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 8d86ac281..b524ca2b3 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -8,8 +8,10 @@ import contact import design import debug +import math from pgate import pgate from tech import parameter, drc +from tech import layer_indices, layer_stacks from vector import vector from globals import OPTS from sram_factory import factory @@ -156,8 +158,8 @@ def place_ptx(self): self.upper_pmos1_inst.place(self.upper_pmos1_pos) # Second pmos to the right of the first - upper_pmos2_pos = self.upper_pmos1_pos + overlap_offset - self.upper_pmos2_inst.place(upper_pmos2_pos) + self.upper_pmos2_pos = self.upper_pmos1_pos + overlap_offset + self.upper_pmos2_inst.place(self.upper_pmos2_pos) def connect_poly(self): """ @@ -233,6 +235,7 @@ def place_nwell_and_contact(self): # TSMC18 gate port hack if OPTS.tech_name == "tsmc18": + # Body connection min_area = drc["minarea_{}".format(self.active_stack[0])] height = round_to_grid(self.well_contact.mod.first_layer_width) width = round_to_grid(min_area / height) @@ -253,6 +256,24 @@ def place_nwell_and_contact(self): width=width_well, height=height_well) + # Span of the whole pimp + left = min(self.lower_pmos_position.x, + self.upper_pmos1_pos.x , + self.upper_pmos2_pos.x) - drc("implant_enclose_active") + right = max(self.lower_pmos_position.x + self.pmos.active_width, + self.upper_pmos1_pos.x + self.pmos.active_width, + self.upper_pmos2_pos.x + self.pmos.active_width) + drc("implant_enclose_active") + top = max(self.lower_pmos_position.y + self.pmos.active_height, + self.upper_pmos1_pos.y + self.pmos.active_height, + self.upper_pmos2_pos.y + self.pmos.active_height) + drc("implant_enclose_active") + bottom = min(self.lower_pmos_position.y, + self.upper_pmos1_pos.y, + self.upper_pmos2_pos.y) - drc("implant_enclose_active") + self.add_rect(layer="pimplant", + offset=vector(left, bottom), + width=right - left, + height=top - bottom) + def route_bitlines(self): """ Adds both bit-line and bit-line-bar to the module @@ -294,6 +315,32 @@ def connect_to_bitlines(self): self.connect_pmos(self.upper_pmos2_inst.get_pin("D"), self.br_xoffset) + # Helper function for adding minarea rectangles when via (only for tsmc18 for now) + def helper_areas(self, from_layer, to_layer, contact_offset): + cur_layer = from_layer + while cur_layer != to_layer: + from_id = layer_indices[cur_layer] + to_id = layer_indices[to_layer] + + if from_id < to_id: # grow the stack up + search_id = 0 + next_id = 2 + else: # grow the stack down + search_id = 2 + next_id = 0 + + #vprint("Putting {}".format(cur_layer)) + min_area = drc["minarea_{}".format(cur_layer)] + width = round_to_grid(math.sqrt(min_area)) + height = round_to_grid(min_area / width) + self.add_rect_center(layer=cur_layer, + offset=contact_offset, + width=width, + height=height) + + curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None) + cur_layer = curr_stack[next_id] + def add_bitline_contacts(self): """ Adds contacts/via from metal1 to metal2 for bit-lines @@ -305,6 +352,8 @@ def add_bitline_contacts(self): to_layer=self.bitline_layer, offset=lower_pin.center(), directions=("V", "V")) + if OPTS.tech_name == "tsmc18": + self.helper_areas(lower_pin.layer, self.bitline_layer, lower_pin.center()) # BR for upper_pin in [self.upper_pmos1_inst.get_pin("S"), self.upper_pmos2_inst.get_pin("D")]: @@ -312,6 +361,8 @@ def add_bitline_contacts(self): to_layer=self.bitline_layer, offset=upper_pin.center(), directions=("V", "V")) + if OPTS.tech_name == "tsmc18": + self.helper_areas(lower_pin.layer, self.bitline_layer, upper_pin.center()) def connect_pmos(self, pmos_pin, bit_xoffset): """ From cb0f2715a0bf41af60bfbee3e6574ec51024a42c Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Tue, 13 Apr 2021 16:06:51 +0900 Subject: [PATCH 03/33] LVS confirmation --- compiler/base/pin_layout.py | 32 +++++++++++++++++++++++++++----- compiler/base/utils.py | 13 +++++++------ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index eb4cb2adf..ebfc3a61e 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -8,7 +8,7 @@ import debug from tech import GDS, drc from vector import vector -from tech import layer, layer_indices +from tech import layer, layer_indices, pin_layer import math @@ -46,8 +46,20 @@ def __init__(self, name, rect, layer_name_pp): self._layer = layer_name break else: - debug.error("Layer {} is not a valid routing layer in the tech file.".format(layer_name_pp), -1) + # Iterate also the pin_layer + for (layer_name, lpp) in pin_layer.items(): + if not lpp: + continue + if self.same_lpp(layer_name_pp, lpp): + self._layer = layer_name + break + else: + debug.error("Layer {} is not a valid routing layer in the tech file.".format(layer_name_pp), -1) + try: + self.lpp_pin = pin_layer[self.layer] + except: + self.lpp_pin = layer[self.layer] self.lpp = layer[self.layer] self._recompute_hash() @@ -369,7 +381,8 @@ def gds_write_file(self, newLayout): debug.info(4, "writing pin (" + str(self.layer) + "):" + str(self.width()) + "x" + str(self.height()) + " @ " + str(self.ll())) - (layer_num, purpose) = layer[self.layer] + (true_layer_num, purpose) = layer[self.layer] + layer_num = true_layer_num try: from tech import pin_purpose except ImportError: @@ -379,14 +392,23 @@ def gds_write_file(self, newLayout): except ImportError: label_purpose = purpose - newLayout.addBox(layerNumber=layer_num, + # Here, try to extract the full layer and purpose if it is inside pin_layer + try: + from tech import pin_layer + (layer_num, label_purpose) = pin_layer[self.layer] + (layer_num, pin_purpose) = pin_layer[self.layer] + except: + pass + + + newLayout.addBox(layerNumber=true_layer_num, purposeNumber=purpose, offsetInMicrons=self.ll(), width=self.width(), height=self.height(), center=False) # Draw a second pin shape too - if pin_purpose != purpose: + if pin_purpose != purpose or layer_num != true_layer_num: newLayout.addBox(layerNumber=layer_num, purposeNumber=pin_purpose, offsetInMicrons=self.ll(), diff --git a/compiler/base/utils.py b/compiler/base/utils.py index 2b6ef8881..ff1c91ef4 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -148,12 +148,13 @@ def get_gds_pins(pin_names, name, gds_filename, units): cell[str(pin_name)] = [] pin_list = cell_vlsi.getPinShape(str(pin_name)) for pin_shape in pin_list: - (lpp, boundary) = pin_shape - rect = [vector(boundary[0], boundary[1]), - vector(boundary[2], boundary[3])] - # this is a list because other cells/designs - # may have must-connect pins - cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp)) + if pin_shape is not None: + (lpp, boundary) = pin_shape + rect = [vector(boundary[0], boundary[1]), + vector(boundary[2], boundary[3])] + # this is a list because other cells/designs + # may have must-connect pins + cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp)) _GDS_PINS_CACHE[k] = cell return dict(cell) From 9e0711bb6ad5f941b9102747ae8f49ee3836a6f1 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Tue, 13 Apr 2021 23:46:25 +0900 Subject: [PATCH 04/33] Changed some rules to pass scmos tests --- compiler/base/pin_layout.py | 6 +++++- compiler/pgates/pgate.py | 26 ++++++++++++++++++++------ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index ebfc3a61e..a182d910e 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -8,7 +8,7 @@ import debug from tech import GDS, drc from vector import vector -from tech import layer, layer_indices, pin_layer +from tech import layer, layer_indices import math @@ -47,6 +47,10 @@ def __init__(self, name, rect, layer_name_pp): break else: # Iterate also the pin_layer + try: + from tech import pin_layer + except: + pin_layer = {} for (layer_name, lpp) in pin_layer.items(): if not lpp: continue diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index e3c973ddd..0a0e7eda3 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -242,10 +242,17 @@ def add_nwell_contact(self, pmos, pmos_pos): # To the right a spacing away from the pmos right active edge # Also, avoid to do intersection of nimp and pimp - # TODO: The pimplant_to_nimplant is new + # The pimplant_to_nimplant is new + pimp_to_nimp = 0 + imp_to_active = 0 + try: + pimp_to_nimp = drc("pimplant_to_nimplant") + imp_to_active = drc("implant_to_active") + except: + pass contact_xoffset = pmos_pos.x + pmos.active_width \ - + max(self.active_space, 2*drc("implant_enclose_active") + drc("pimplant_to_nimplant"), - drc("implant_to_active") + drc("implant_enclose_active")) + + max(self.active_space, 2*drc("implant_enclose_active") + pimp_to_nimp, + imp_to_active + drc("implant_enclose_active")) # Must be at least an well enclosure of active down # from the top of the well @@ -368,11 +375,18 @@ def add_pwell_contact(self, nmos, nmos_pos): # To the right a spacing away from the nmos right active edge # Also, avoid to do intersection of nimp and pimp # Also, there should be a distance between channel and implant - # TODO: The pimplant_to_nimplant is new. Probably not in other techs + # The pimplant_to_nimplant is new. + pimp_to_nimp = 0 + imp_to_active = 0 + try: + pimp_to_nimp = drc("pimplant_to_nimplant") + imp_to_active = drc("implant_to_active") + except: + pass contact_xoffset = nmos_pos.x + nmos.active_width \ + max(self.active_space, - 2*drc("implant_enclose_active") + drc("pimplant_to_nimplant"), - drc("implant_to_active") + drc("implant_enclose_active")) + 2*drc("implant_enclose_active") + pimp_to_nimp, + imp_to_active + drc("implant_enclose_active")) # Must be at least an well enclosure of active up # from the bottom of the well contact_yoffset = max(nmos_pos.y, From 50872014a4cc7b5c602866fdd449e6aa8e5de5f5 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Wed, 14 Apr 2021 02:18:01 +0900 Subject: [PATCH 05/33] Modifications for passing LVS. Added some configurations for lvs_spice binding --- compiler/modules/bank.py | 12 ++++++++++-- compiler/pgates/ptx.py | 10 ++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 9c2b2add0..cb0069c93 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -235,10 +235,14 @@ def compute_instance_port0_offsets(self): # control logic to allow control signals to easily pass over in M3 # by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs # may be routed in M3 or M4 + # TODO: In tsmc180, is not 1.25 (dff is 6.72. crosses with the first gnd) + mult = 1.25 + if OPTS.tech_name == "tsmc18": + mult = 1.5 x_offset = self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width if self.col_addr_size > 0: x_offset += self.column_decoder.width + self.col_addr_bus_width - y_offset = 1.25 * self.dff.height + self.column_decoder.height + y_offset = mult * self.dff.height + self.column_decoder.height else: y_offset = 0 self.column_decoder_offsets[port] = vector(-x_offset, -y_offset) @@ -279,10 +283,14 @@ def compute_instance_port1_offsets(self): # control logic to allow control signals to easily pass over in M3 # by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs # may be routed in M3 or M4 + # TODO: In tsmc180, is not 1.25 (dff is 6.72. crosses with the first gnd) + mult = 1.25 + if OPTS.tech_name == "tsmc18": + mult = 1.5 x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width if self.col_addr_size > 0: x_offset += self.column_decoder.width + self.col_addr_bus_width - y_offset = self.bitcell_array_top + 1.25 * self.dff.height + self.column_decoder.height + y_offset = self.bitcell_array_top + mult * self.dff.height + self.column_decoder.height else: y_offset = self.bitcell_array_top self.column_decoder_offsets[port] = vector(x_offset, y_offset) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 8ab694579..8051dc490 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -149,6 +149,12 @@ def create_netlist(self): self.spice_device = main_str + area_str self.spice.append("\n* spice ptx " + self.spice_device) + # For LVS devices, the name may change + try: + from tech import lvs_spice + except: + lvs_spice = spice + if cell_props.ptx.model_is_subckt and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre": # sky130 requires mult parameter too. It is not the same as m, but I don't understand it. # self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2} l={3} mult=1".format(spice[self.tx_type], @@ -163,12 +169,12 @@ def create_netlist(self): drc("minwidth_poly")) elif cell_props.ptx.model_is_subckt: # sky130 requires mult parameter too - self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2}u l={3}u".format(spice[self.tx_type], + self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2}u l={3}u".format(lvs_spice[self.tx_type], self.mults, self.tx_width, drc("minwidth_poly")) else: - self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], + self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(lvs_spice[self.tx_type], self.mults, self.tx_width, drc("minwidth_poly")) From 852dea1a8b9073c343555b187db6b14b321def43 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Thu, 22 Apr 2021 09:54:47 +0900 Subject: [PATCH 06/33] Fixes to pin_layout.py for supporting +p in layer names --- compiler/base/pin_layout.py | 45 +++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 767b97938..63a6d5f90 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -33,6 +33,12 @@ def __init__(self, name, rect, layer_name_pp): # These are the valid pin layers valid_layers = {x: layer[x] for x in layer_indices.keys()} + # search for the "+p" pins also + valid_pin_layers = {} + for x in layer_indices.keys(): + y = x + "p" + if y in layer: + valid_pin_layers[x] = layer[y] # if it's a string, use the name if type(layer_name_pp) == str: @@ -46,12 +52,7 @@ def __init__(self, name, rect, layer_name_pp): self._layer = layer_name break else: - # Iterate also the pin_layer - try: - from tech import pin_layer - except: - pin_layer = {} - for (layer_name, lpp) in pin_layer.items(): + for (layer_name, lpp) in valid_pin_layers.items(): if not lpp: continue if self.same_lpp(layer_name_pp, lpp): @@ -60,10 +61,6 @@ def __init__(self, name, rect, layer_name_pp): else: debug.error("Layer {} is not a valid routing layer in the tech file.".format(layer_name_pp), -1) - try: - self.lpp_pin = pin_layer[self.layer] - except: - self.lpp_pin = layer[self.layer] self.lpp = layer[self.layer] self._recompute_hash() @@ -415,16 +412,8 @@ def gds_write_file(self, newLayout): except ImportError: label_purpose = purpose - # Here, try to extract the full layer and purpose if it is inside pin_layer - try: - from tech import pin_layer - (layer_num, label_purpose) = pin_layer[self.layer] - (layer_num, pin_purpose) = pin_layer[self.layer] - except: - pass - - newLayout.addBox(layerNumber=true_layer_num, + newLayout.addBox(layerNumber=layer_num, purposeNumber=purpose, offsetInMicrons=self.ll(), width=self.width(), @@ -445,11 +434,19 @@ def gds_write_file(self, newLayout): zoom = GDS["zoom"] except KeyError: zoom = None - newLayout.addText(text=self.name, - layerNumber=layer_num, - purposeNumber=label_purpose, - magnification=zoom, - offsetInMicrons=self.center()) + # Draw a second pin text too if it is different + if pin_layer_num != layer_num: + newLayout.addText(text=self.name, + layerNumber=pin_layer_num, + purposeNumber=pin_purpose, + magnification=zoom, + offsetInMicrons=self.center()) + else: + newLayout.addText(text=self.name, + layerNumber=layer_num, + purposeNumber=label_purpose, + magnification=zoom, + offsetInMicrons=self.center()) def compute_overlap(self, other): """ Calculate the rectangular overlap of two rectangles. """ From 8414912d383c4b8f487f03df75efa79f4a258e1b Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Thu, 22 Apr 2021 15:56:08 +0900 Subject: [PATCH 07/33] Adding to the delay test the tsmc18 --- compiler/tests/21_model_delay_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/tests/21_model_delay_test.py b/compiler/tests/21_model_delay_test.py index e5c4b96df..1e1fd8738 100755 --- a/compiler/tests/21_model_delay_test.py +++ b/compiler/tests/21_model_delay_test.py @@ -76,6 +76,8 @@ def runTest(self): error_tolerance = 0.25 elif OPTS.tech_name == "scn4m_subm": error_tolerance = 0.25 + elif OPTS.tech_name == "tsmc18": + error_tolerance = 0.25 else: self.assertTrue(False) # other techs fail From 96bf7d2d7b8746ffa63dec469f964b56d78deff9 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Sat, 1 May 2021 22:45:16 +0900 Subject: [PATCH 08/33] Added support for the grid power routing. LEF generation now supports multiple pins --- compiler/base/lef.py | 26 +++++++++++++------------- compiler/router/supply_grid_router.py | 2 +- compiler/sram/sram_base.py | 5 ++++- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/compiler/base/lef.py b/compiler/base/lef.py index f0d6dda08..69fb3b249 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -110,19 +110,19 @@ def compute_abstract_blockages(self): # For each pin, remove the blockage and add the pin for pin_name in self.pins: - pin = self.get_pin(pin_name) - inflated_pin = pin.inflated_pin(multiple=1) - for blockage in self.blockages[pin.layer]: - if blockage.overlaps(inflated_pin): - intersection_shape = blockage.intersection(inflated_pin) - # If it is zero area, don't add the pin - if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]: - continue - # Remove the old blockage and add the new ones - self.blockages[pin.layer].remove(blockage) - intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer) - new_blockages = blockage.cut(intersection_pin) - self.blockages[pin.layer].extend(new_blockages) + for pin in self.get_pins(pin_name): + inflated_pin = pin.inflated_pin(multiple=1) + for blockage in self.blockages[pin.layer]: + if blockage.overlaps(inflated_pin): + intersection_shape = blockage.intersection(inflated_pin) + # If it is zero area, don't add the pin + if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]: + continue + # Remove the old blockage and add the new ones + self.blockages[pin.layer].remove(blockage) + intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer) + new_blockages = blockage.cut(intersection_pin) + self.blockages[pin.layer].extend(new_blockages) def lef_write_header(self): diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 8a2014749..83dfa7b1c 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -357,7 +357,7 @@ def route_pins_to_rails(self, pin_name): # This is inefficient since it is non-incremental, but it was # easier to debug. - self.prepare_blockages(pin_name) + self.prepare_blockages() # Add the single component of the pin as the source # which unmarks it as a blockage too diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 7621f67bc..f7d26d814 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -24,7 +24,7 @@ class sram_base(design, verilog, lef): """ def __init__(self, name, sram_config): design.__init__(self, name) - lef.__init__(self, ["m1", "m2", "m3", "m4"]) + lef.__init__(self, ["m1", "m2", "m3", "m4", "m5"]) # TODO: Generation of lef layers should be dependent of tech verilog.__init__(self) self.sram_config = sram_config @@ -247,6 +247,9 @@ def route_supplies(self): rtr.route() # Find the lowest leftest pin for vdd and gnd + # NOTE: Not necessary if the route_supplies is "grid" + if OPTS.route_supplies == "grid": + return for pin_name in ["vdd", "gnd"]: # Copy the pin shape(s) to rectangles for pin in self.get_pins(pin_name): From cb0cccce3b6812a9d683ee0b7ee0edbf42783ac5 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Mon, 10 May 2021 22:18:16 +0900 Subject: [PATCH 09/33] Added a lambda rect intersection in pin_group. This helps for segment detection that is not 2, but still intersects (3 or 4) --- compiler/base/pin_layout.py | 15 ++++++++++++++- compiler/router/pin_group.py | 2 +- compiler/router/supply_grid_router.py | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 63a6d5f90..13f0940b6 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -504,6 +504,7 @@ def overlap_length(self, other): Calculate the intersection segment and determine its length """ + inter=lambda a,b: a[0].x < b[1].x and a[1].x > b[0].x and a[0].y < b[1].y and a[1].y > b[0].y if self.contains(other): return math.inf elif other.contains(self): @@ -516,7 +517,19 @@ def overlap_length(self, other): (p1, p2) = intersections return math.sqrt(pow(p1[0]-p2[0], 2) + pow(p1[1]-p2[1], 2)) else: - # This is where we had a corner intersection or none + # TODO: Check this version + # The case is: If there is a full intersection in X, but not in Y, + # compute_overlap_segment does not detect it, as the number + # of points here is 4. Our lambda will take care of that. + # This is the last resort + if inter(self.rect, other.rect): + debug.warning("Detected lambda-intersection. The length is {0}. The rects are: {1} : {2}".format( + len(intersections), + self.rect, + other.rect + )) + return math.inf + # This is where we had a corner intersection or none (1 or 0) return 0 diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index 7a5d88173..d57237f01 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -620,7 +620,7 @@ def convert_pin(self): # for pin in self.pins: # lx = pin.lx() # ly = pin.by() - # if lx > 87.9 and lx < 87.99 and ly > 18.56 and ly < 18.6: + # if lx > 122.1 and lx < 123.5 and ly > 61.1 and ly < 61.7: # breakpoint() for pin in self.pins: debug.info(4, " Converting {0}".format(pin)) diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 83dfa7b1c..8a9312b44 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -369,6 +369,7 @@ def route_pins_to_rails(self, pin_name): # Actually run the A* router if not self.run_router(detour_scale=5): + debug.warning("Component not routed. Location: {0}. Writting a debug gds.".format(str(pg.grids))) self.write_debug_gds("debug_route.gds", False) # if index==3 and pin_name=="vdd": From 5bb1c8196d57ce6d47508613d64ff9c1e063be45 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Tue, 11 May 2021 03:43:19 +0900 Subject: [PATCH 10/33] Miscellaneous fixes on the overlap. Removing the (V, V) creation of vias in write_driver and precharger for supplies. Working almost dlawless for TSMC180 with exception of some spacings --- compiler/base/pin_layout.py | 48 +++++--------------------- compiler/modules/write_driver_array.py | 2 +- compiler/pgates/precharge.py | 6 ++-- compiler/router/pin_group.py | 8 ++++- compiler/router/router.py | 37 ++++++++++---------- compiler/router/supply_grid_router.py | 10 +++--- 6 files changed, 44 insertions(+), 67 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 13f0940b6..38f5c6f0e 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -189,32 +189,15 @@ def intersection(self, other): def xoverlaps(self, other): """ Check if shape has x overlap """ - (ll, ur) = self.rect - (oll, our) = other.rect - x_overlaps = False - # check if self is within other x range - if (ll.x >= oll.x and ll.x <= our.x) or (ur.x >= oll.x and ur.x <= our.x): - x_overlaps = True - # check if other is within self x range - if (oll.x >= ll.x and oll.x <= ur.x) or (our.x >= ll.x and our.x <= ur.x): - x_overlaps = True - - return x_overlaps + a = self.rect + b = other.rect + return a[0].x < b[1].x and a[1].x > b[0].x def yoverlaps(self, other): """ Check if shape has x overlap """ - (ll, ur) = self.rect - (oll, our) = other.rect - y_overlaps = False - - # check if self is within other y range - if (ll.y >= oll.y and ll.y <= our.y) or (ur.y >= oll.y and ur.y <= our.y): - y_overlaps = True - # check if other is within self y range - if (oll.y >= ll.y and oll.y <= ur.y) or (our.y >= ll.y and our.y <= ur.y): - y_overlaps = True - - return y_overlaps + a = self.rect + b = other.rect + return a[0].y < b[1].y and a[1].y > b[0].y def xcontains(self, other): """ Check if shape contains the x overlap """ @@ -504,7 +487,6 @@ def overlap_length(self, other): Calculate the intersection segment and determine its length """ - inter=lambda a,b: a[0].x < b[1].x and a[1].x > b[0].x and a[0].y < b[1].y and a[1].y > b[0].y if self.contains(other): return math.inf elif other.contains(self): @@ -517,18 +499,6 @@ def overlap_length(self, other): (p1, p2) = intersections return math.sqrt(pow(p1[0]-p2[0], 2) + pow(p1[1]-p2[1], 2)) else: - # TODO: Check this version - # The case is: If there is a full intersection in X, but not in Y, - # compute_overlap_segment does not detect it, as the number - # of points here is 4. Our lambda will take care of that. - # This is the last resort - if inter(self.rect, other.rect): - debug.warning("Detected lambda-intersection. The length is {0}. The rects are: {1} : {2}".format( - len(intersections), - self.rect, - other.rect - )) - return math.inf # This is where we had a corner intersection or none (1 or 0) return 0 @@ -584,9 +554,9 @@ def on_segment(self, p, q, r): Given three co-linear points, determine if q lies on segment pr """ if q.x <= max(p.x, r.x) and \ - q.x >= min(p.x, r.x) and \ - q.y <= max(p.y, r.y) and \ - q.y >= min(p.y, r.y): + q.x >= min(p.x, r.x) and \ + q.y <= max(p.y, r.y) and \ + q.y >= min(p.y, r.y): return True return False diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 07c2ce608..768d8cf80 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -208,7 +208,7 @@ def add_layout_pins(self): for n in ["vdd", "gnd"]: pin_list = self.driver_insts[i].get_pins(n) for pin in pin_list: - self.copy_power_pin(pin, directions=("V", "V")) + self.copy_power_pin(pin) if self.write_size: for bit in range(self.num_wmasks): diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index b524ca2b3..336e8aacd 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -111,13 +111,11 @@ def route_vdd_rail(self): self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos]) self.add_power_pin("vdd", - self.well_contact_pos, - directions=("V", "V")) + self.well_contact_pos) self.add_via_stack_center(from_layer=pmos_pin.layer, to_layer=self.en_layer, - offset=pmos_pin.center(), - directions=("V", "V")) + offset=pmos_pin.center()) def create_ptx(self): """ diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index d57237f01..ebd9b27a8 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -461,6 +461,12 @@ def enclose_pin(self): # Compute the enclosure pin_layout list of the set of tracks self.enclosures = self.compute_enclosures() + # for pin in self.pins: + # lx = pin.lx() + # ly = pin.by() + # if lx > 61.1 and lx < 62.0 and ly > 56.2 and ly < 56.7: + # breakpoint() + # Find a connector to every pin and add it to the enclosures for pin in self.pins: @@ -620,7 +626,7 @@ def convert_pin(self): # for pin in self.pins: # lx = pin.lx() # ly = pin.by() - # if lx > 122.1 and lx < 123.5 and ly > 61.1 and ly < 61.7: + # if lx > 61.1 and lx < 62.0 and ly > 56.2 and ly < 56.7: # breakpoint() for pin in self.pins: debug.info(4, " Converting {0}".format(pin)) diff --git a/compiler/router/router.py b/compiler/router/router.py index 5213af4a8..923aac31c 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -34,7 +34,7 @@ def __init__(self, layers, design, gds_filename=None, bbox=None, margin=0, route route on top of this. The blockages from the gds/module will be considered. """ - + router_tech.__init__(self, layers, route_track_width) self.cell = design @@ -105,10 +105,10 @@ def init_bbox(self, bbox=None, margin=0): self.bbox = (self.ll - margin_offset, self.ur + margin_offset) size = self.ur - self.ll debug.info(1, "Size: {0} x {1} with perimeter margin {2}".format(size.x, size.y, margin)) - + def get_bbox(self): return self.bbox - + def create_routing_grid(self, router_type): """ Create a sprase routing grid with A* expansion functions. @@ -373,7 +373,7 @@ def remove_adjacent_grid(self, pg1, pg2, adj_grids): def set_supply_rail_blocked(self, value): # This is just a virtual function pass - + def prepare_blockages(self): """ Reset and add all of the blockages in the design. @@ -382,7 +382,7 @@ def prepare_blockages(self): # Start fresh. Not the best for run-time, but simpler. self.clear_all_blockages() - + # This adds the initial blockges of the design # which includes all blockages due to non-pin shapes # print("BLOCKING:", self.blocked_grids) @@ -449,7 +449,7 @@ def clear_blockages(self, pin_name): """ blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.blockages} self.set_blockages(blockage_grids, False) - + def clear_all_blockages(self): """ Clear all blockages on the grid. @@ -510,7 +510,7 @@ def retrieve_blockages(self, lpp): new_shape = pin_layout("blockage{}".format(len(self.blockages)), rect, lpp) - + # If there is a rectangle that is the same in the pins, # it isn't a blockage! if new_shape not in self.all_pins and not self.pin_contains(new_shape): @@ -521,7 +521,7 @@ def pin_contains(self, shape): if pin.contains(shape): return True return False - + def convert_point_to_units(self, p): """ Convert a path set of tracks to center line path. @@ -535,7 +535,7 @@ def convert_wave_to_units(self, wave): Convert a wave to a set of center points """ return [self.convert_point_to_units(i) for i in wave] - + def convert_shape_to_tracks(self, shape): """ Convert a rectangular shape into track units. @@ -676,12 +676,13 @@ def convert_pin_coord_to_tracks(self, pin, coord): pin.inflate(0.5 * self.track_space), pin.layer) - overlap_length = pin.overlap_length(track_pin) + inter=lambda a,b: math.inf if a[0].x < b[1].x and a[1].x > b[0].x and a[0].y < b[1].y and a[1].y > b[0].y else 0 + overlap_length = inter(pin.rect, track_pin.rect) #pin.overlap_length(track_pin) debug.info(4,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length)) - inflated_overlap_length = inflated_pin.overlap_length(track_pin) + inflated_overlap_length = inter(inflated_pin.rect, track_pin.rect) #inflated_pin.overlap_length(track_pin) debug.info(4,"Check overlap: {0} {1} . {2} = {3}".format(coord, inflated_pin.rect, track_pin, @@ -886,7 +887,7 @@ def add_perimeter_target(self, side="all"): This will mark all the cells on the perimeter of the original layout as a target. """ self.rg.add_perimeter_target(side=side) - + def num_pin_components(self, pin_name): """ This returns how many disconnected pin components there are. @@ -1151,7 +1152,7 @@ def annotate_grid(self, g): self.cell.add_label(text="{0},{1}".format(g[0], g[1]), layer="text", offset=shape[0]) - + def del_router_info(self): """ Erase all of the comments on the current level. @@ -1212,9 +1213,9 @@ def get_perimeter_pin(self): for v in self.paths[-1]: if self.rg.is_target(v): return self.convert_track_to_pin(v) - + return None - + def get_ll_pin(self, pin_name): """ Return the lowest, leftest pin group """ @@ -1226,7 +1227,7 @@ def get_ll_pin(self, pin_name): else: if pin.lx() <= keep_pin.lx() and pin.by() <= keep_pin.by(): keep_pin = pin - + return keep_pin def check_all_routed(self, pin_name): @@ -1236,8 +1237,8 @@ def check_all_routed(self, pin_name): for pg in self.pin_groups[pin_name]: if not pg.is_routed(): return False - - + + # FIXME: This should be replaced with vector.snap_to_grid at some point def snap_to_grid(offset): """ diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 8a9312b44..527af6de7 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -66,14 +66,15 @@ def route(self, vdd_name="vdd", gnd_name="gnd"): # Block everything self.prepare_blockages() self.clear_blockages(self.gnd_name) - - + self.write_debug_gds("post_blockages.gds", False) + + # Determine the rail locations self.route_supply_rails(self.gnd_name, 0) # Block everything self.prepare_blockages() - self.clear_blockages(self.vdd_name) + self.clear_blockages(self.vdd_name) # Determine the rail locations self.route_supply_rails(self.vdd_name, 1) print_time("Routing supply rails", datetime.now(), start_time, 3) @@ -82,6 +83,7 @@ def route(self, vdd_name="vdd", gnd_name="gnd"): self.route_simple_overlaps(vdd_name) self.route_simple_overlaps(gnd_name) print_time("Simple overlap routing", datetime.now(), start_time, 3) + self.write_debug_gds("post_simple_overlap.gds", False) # Route the supply pins to the supply rails # Route vdd first since we want it to be shorter @@ -89,7 +91,7 @@ def route(self, vdd_name="vdd", gnd_name="gnd"): self.route_pins_to_rails(vdd_name) self.route_pins_to_rails(gnd_name) print_time("Maze routing supplies", datetime.now(), start_time, 3) - # self.write_debug_gds("final.gds", False) + self.write_debug_gds("final.gds", False) # Did we route everything?? if not self.check_all_routed(vdd_name): From bfdf65bcf7a09881c182abeb55f377a24fcf2ade Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Tue, 11 May 2021 14:19:12 +0900 Subject: [PATCH 11/33] Delete some of the generated files for debug --- compiler/router/supply_grid_router.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 527af6de7..a2500ee98 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -66,7 +66,6 @@ def route(self, vdd_name="vdd", gnd_name="gnd"): # Block everything self.prepare_blockages() self.clear_blockages(self.gnd_name) - self.write_debug_gds("post_blockages.gds", False) # Determine the rail locations @@ -83,7 +82,6 @@ def route(self, vdd_name="vdd", gnd_name="gnd"): self.route_simple_overlaps(vdd_name) self.route_simple_overlaps(gnd_name) print_time("Simple overlap routing", datetime.now(), start_time, 3) - self.write_debug_gds("post_simple_overlap.gds", False) # Route the supply pins to the supply rails # Route vdd first since we want it to be shorter @@ -91,7 +89,7 @@ def route(self, vdd_name="vdd", gnd_name="gnd"): self.route_pins_to_rails(vdd_name) self.route_pins_to_rails(gnd_name) print_time("Maze routing supplies", datetime.now(), start_time, 3) - self.write_debug_gds("final.gds", False) + # self.write_debug_gds("final.gds", False) # Did we route everything?? if not self.check_all_routed(vdd_name): From 471d76335c4b8e79e62151ca78c534ddd5a08853 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Sat, 15 May 2021 01:43:57 +0900 Subject: [PATCH 12/33] Supply pins only on corners when grid routing --- compiler/router/supply_grid_router.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index a2500ee98..a29c5342a 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -196,17 +196,25 @@ def add_supply_rails(self, name): This is after the paths have been pruned and only include rails that are connected with vias. """ + + max_yoffset = self.rg.ur.y + max_xoffset = self.rg.ur.x + min_yoffset = self.rg.ll.y + min_xoffset = self.rg.ll.x + for rail in self.supply_rails[name]: ll = grid_utils.get_lower_left(rail) ur = grid_utils.get_upper_right(rail) - z = ll.z - pin = self.compute_pin_enclosure(ll, ur, z, name) - debug.info(3, "Adding supply rail {0} {1}->{2} {3}".format(name, ll, ur, pin)) - self.cell.add_layout_pin(text=name, - layer=pin.layer, - offset=pin.ll(), - width=pin.width(), - height=pin.height()) + # Add the ones only in the perimeter + if ll.x <= min_xoffset or ll.y <= min_yoffset or ur.x >= max_xoffset or ur.y >= max_yoffset: + z = ll.z + pin = self.compute_pin_enclosure(ll, ur, z, name) + debug.info(3, "Adding supply rail {0} {1}->{2} {3}".format(name, ll, ur, pin)) + self.cell.add_layout_pin(text=name, + layer=pin.layer, + offset=pin.ll(), + width=pin.width(), + height=pin.height()) def compute_supply_rails(self, name, supply_number): """ From fe374f91fa1a5d3f47e7e7f3030cee9ae11a7a86 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Sat, 15 May 2021 02:59:59 +0900 Subject: [PATCH 13/33] Supply rails now only get exposed in the edges --- compiler/router/supply_grid_router.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index a29c5342a..e7bf2a92b 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -202,19 +202,28 @@ def add_supply_rails(self, name): min_yoffset = self.rg.ll.y min_xoffset = self.rg.ll.x + # Remove the current pins in the layout. Only leave the new ones + self.cell.remove_layout_pin(name) + for rail in self.supply_rails[name]: ll = grid_utils.get_lower_left(rail) ur = grid_utils.get_upper_right(rail) + z = ll.z + pin = self.compute_pin_enclosure(ll, ur, z, name) # Add the ones only in the perimeter - if ll.x <= min_xoffset or ll.y <= min_yoffset or ur.x >= max_xoffset or ur.y >= max_yoffset: - z = ll.z - pin = self.compute_pin_enclosure(ll, ur, z, name) - debug.info(3, "Adding supply rail {0} {1}->{2} {3}".format(name, ll, ur, pin)) + if ll.x <= min_xoffset or ll.y <= min_yoffset or ur.x >= (max_xoffset-1) or ur.y >= (max_yoffset-1): + debug.info(3, "Adding supply pin rail {0} {1}->{2} {3}".format(name, ll, ur, pin)) self.cell.add_layout_pin(text=name, layer=pin.layer, offset=pin.ll(), width=pin.width(), height=pin.height()) + else: + debug.info(3, "Adding supply norm rail {0} {1}->{2} {3}".format(name, ll, ur, pin)) + self.cell.add_rect(layer=pin.layer, + offset=pin.ll(), + width=pin.width(), + height=pin.height()) def compute_supply_rails(self, name, supply_number): """ From f879b252167478714019cbc7cf9b34b90e5d38a1 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Sat, 15 May 2021 21:33:35 +0900 Subject: [PATCH 14/33] Supply grid faster. Supply grid and escape routing both are in the perimeter both --- compiler/router/supply_grid_router.py | 67 +++++++++++++++++++++++---- compiler/sram/sram_base.py | 6 ++- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index e7bf2a92b..8204257fc 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -31,7 +31,10 @@ def __init__(self, layers, design, gds_filename=None, bbox=None): # Power rail width in minimum wire widths self.route_track_width = 2 - router.__init__(self, layers, design, gds_filename, bbox, self.route_track_width) + if not bbox: + router.__init__(self, layers, design, gds_filename, bbox, self.route_track_width) + else: + router.__init__(self, layers, design, gds_filename, bbox) # The bbox should be already aligned. # The list of supply rails (grid sets) that may be routed self.supply_rails = {} @@ -83,6 +86,11 @@ def route(self, vdd_name="vdd", gnd_name="gnd"): self.route_simple_overlaps(gnd_name) print_time("Simple overlap routing", datetime.now(), start_time, 3) + start_time = datetime.now() + self.route_1step(vdd_name) + self.route_1step(gnd_name) + print_time("1 step routing", datetime.now(), start_time, 3) + # Route the supply pins to the supply rails # Route vdd first since we want it to be shorter start_time = datetime.now() @@ -126,6 +134,43 @@ def route_simple_overlaps(self, pin_name): debug.info(1, "Routed {} simple overlap pins".format(routed_count)) + def route_1step(self, pin_name): + """ + This will check 1-step easy routes. This is mainly created for faster routes and avoid the full-fledged + router. + """ + debug.info(1, "Routing 1 step pins for {0}".format(pin_name)) + + dirs = [vector3d(1, 0, 0), vector3d(-1, 0, 0), vector3d(0, 1, 0), vector3d(0, -1, 0), vector3d(0, 0, 1), vector3d(0, 0, -1)] + # These are the wire tracks + wire_tracks = self.supply_rail_tracks[pin_name] + routed_count=0 + for pg in self.pin_groups[pin_name]: + if pg.is_routed(): + continue + + # Explore all grids + for grid in pg.grids: + # Explore all directions + for dir in dirs: + pt = grid + dir + if pt in wire_tracks: + # Contruct the path + path = [grid, pt] + abs_path = [self.convert_point_to_units(x) for x in path] + debug.info(3, "Adding easy route {0} {1}->{2}, {3}".format(pin_name, grid, pt, abs_path)) + routed_count += 1 + pg.set_routed() + self.cell.add_route(layers=self.layers, + coordinates=abs_path, + layer_widths=self.layer_widths) + break + else: + continue + break + + debug.info(1, "Routed {} simple overlap pins".format(routed_count)) + def finalize_supply_rails(self, name): """ Determine which supply rails overlap and can accomodate a via. @@ -211,7 +256,7 @@ def add_supply_rails(self, name): z = ll.z pin = self.compute_pin_enclosure(ll, ur, z, name) # Add the ones only in the perimeter - if ll.x <= min_xoffset or ll.y <= min_yoffset or ur.x >= (max_xoffset-1) or ur.y >= (max_yoffset-1): + if ll.x <= min_xoffset or ll.y <= min_yoffset or ur.x >= max_xoffset or ur.y >= max_yoffset: debug.info(3, "Adding supply pin rail {0} {1}->{2} {3}".format(name, ll, ur, pin)) self.cell.add_layout_pin(text=name, layer=pin.layer, @@ -245,7 +290,7 @@ def compute_supply_rails(self, name, supply_number): # Seed the function at the location with the given width wave = [vector3d(min_xoffset, offset, 0)] # While we can keep expanding east in this horizontal track - while wave and wave[0].x < max_xoffset: + while wave and wave[0].x <= max_xoffset: added_rail = self.find_supply_rail(name, wave, direction.EAST) if not added_rail: # Just seed with the next one @@ -260,7 +305,7 @@ def compute_supply_rails(self, name, supply_number): # Seed the function at the location with the given width wave = [vector3d(offset, min_yoffset, 1)] # While we can keep expanding north in this vertical track - while wave and wave[0].y < max_yoffset: + while wave and wave[0].y <= max_yoffset: added_rail = self.find_supply_rail(name, wave, direction.NORTH) if not added_rail: # Just seed with the next one @@ -303,15 +348,19 @@ def probe_supply_rail(self, name, start_wave, direct): if not wave_path: return None + # NOTE: Why? The trimmings are kinda useless for escape routing + # The escape routing is already done at this point + # this will be ignored for now. + # drop the first and last steps to leave escape routing room # around the blockage that stopped the probe # except, don't drop the first if it is the first in a row/column - if (direct==direction.NORTH and start_wave[0].y>0): - wave_path.trim_first() - elif (direct == direction.EAST and start_wave[0].x>0): - wave_path.trim_first() + #if (direct==direction.NORTH and start_wave[0].y>0): + # wave_path.trim_first() + #elif (direct == direction.EAST and start_wave[0].x>0): + # wave_path.trim_first() - wave_path.trim_last() + #wave_path.trim_last() return wave_path diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index f7d26d814..63018ca9b 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -40,6 +40,9 @@ def __init__(self, name, sram_config): if not self.num_spare_cols: self.num_spare_cols = 0 + # For assigning only once the bbox + self.bbox = None + def add_pins(self): """ Add pins for entire SRAM. """ @@ -243,7 +246,7 @@ def route_supplies(self): elif OPTS.route_supplies: from supply_tree_router import supply_tree_router as router - rtr=router(grid_stack, self) + rtr=router(grid_stack, self, None, self.bbox) # Use a possible bbox here rtr.route() # Find the lowest leftest pin for vdd and gnd @@ -324,6 +327,7 @@ def route_escape_pins(self): design=self, margin=4 * self.m3_pitch) rtr.escape_route(pins_to_route) + self.bbox = (rtr.ll, rtr.ur) # Capture the bbox after done with the escape routes, as can increase def compute_bus_sizes(self): """ Compute the independent bus widths shared between two and four bank SRAMs """ From 70143a8dc97e38cae075964631569aca87a33435 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Sun, 16 May 2021 04:33:22 +0900 Subject: [PATCH 15/33] Fixed errors with the blockage removal in the router (reflected only in congested routing). nand4 transistors halved --- compiler/pgates/pnand4.py | 4 ++-- compiler/router/router.py | 8 ++++++++ compiler/router/supply_grid_router.py | 10 ++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/compiler/pgates/pnand4.py b/compiler/pgates/pnand4.py index 218f71d30..5c4d21566 100644 --- a/compiler/pgates/pnand4.py +++ b/compiler/pgates/pnand4.py @@ -32,8 +32,8 @@ def __init__(self, name, size=1, height=None, add_wells=True): # 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_size = size + self.pmos_size = parameter["beta"] * size / 2.0 self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") diff --git a/compiler/router/router.py b/compiler/router/router.py index 923aac31c..3dfb89eab 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -351,12 +351,16 @@ def remove_adjacent_grid(self, pg1, pg2, adj_grids): bigger.grids.remove(adj) bigger.secondary_grids.remove(adj) self.blocked_grids.add(adj) + if adj in bigger.blockages: + bigger.blockages.remove(adj) elif adj in smaller.secondary_grids: debug.info(3,"Removing {} from smaller secondary {}".format(adj, smaller)) smaller.grids.remove(adj) smaller.secondary_grids.remove(adj) self.blocked_grids.add(adj) + if adj in smaller.blockages: + smaller.blockages.remove(adj) else: # If we couldn't remove from a secondary grid, # we must remove from the primary @@ -365,10 +369,14 @@ def remove_adjacent_grid(self, pg1, pg2, adj_grids): debug.info(3,"Removing {} from bigger primary {}".format(adj, bigger)) bigger.grids.remove(adj) + if adj in bigger.blockages: + bigger.blockages.remove(adj) elif adj in smaller.grids: debug.info(3,"Removing {} from smaller primary {}".format(adj, smaller)) smaller.grids.remove(adj) + if adj in smaller.blockages: + smaller.blockages.remove(adj) def set_supply_rail_blocked(self, value): # This is just a virtual function diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 8204257fc..72a8d0da5 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -94,8 +94,8 @@ def route(self, vdd_name="vdd", gnd_name="gnd"): # Route the supply pins to the supply rails # Route vdd first since we want it to be shorter start_time = datetime.now() - self.route_pins_to_rails(vdd_name) - self.route_pins_to_rails(gnd_name) + #self.route_pins_to_rails(vdd_name) + #self.route_pins_to_rails(gnd_name) print_time("Maze routing supplies", datetime.now(), start_time, 3) # self.write_debug_gds("final.gds", False) @@ -145,7 +145,7 @@ def route_1step(self, pin_name): # These are the wire tracks wire_tracks = self.supply_rail_tracks[pin_name] routed_count=0 - for pg in self.pin_groups[pin_name]: + for i, pg in enumerate(self.pin_groups[pin_name]): if pg.is_routed(): continue @@ -154,7 +154,7 @@ def route_1step(self, pin_name): # Explore all directions for dir in dirs: pt = grid + dir - if pt in wire_tracks: + if pt in wire_tracks and pt not in self.blocked_grids: # Contruct the path path = [grid, pt] abs_path = [self.convert_point_to_units(x) for x in path] @@ -164,6 +164,8 @@ def route_1step(self, pin_name): self.cell.add_route(layers=self.layers, coordinates=abs_path, layer_widths=self.layer_widths) + # Add just the grid here in the pingroup + self.pin_groups[pin_name][i].grids.add(pt) break else: continue From d9c8a2f9e53315f2d3321d395e64b55ca8ade71b Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Mon, 17 May 2021 18:23:41 +0900 Subject: [PATCH 16/33] Avoid large border intersections in VDD or GND --- compiler/router/supply_grid_router.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 72a8d0da5..dc2d4dece 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -257,8 +257,13 @@ def add_supply_rails(self, name): ur = grid_utils.get_upper_right(rail) z = ll.z pin = self.compute_pin_enclosure(ll, ur, z, name) + # Determinate if this rail is actually part of the very corner. This is to avoid + # large intersections, but only keeping the short ones + # TODO: The way to know that is horizontal or vertical, is just looking at z + avoid = (z == 0 and (ll.y <= min_yoffset or ur.y >= max_yoffset)) or \ + (z == 1 and (ll.x <= min_xoffset or ur.x >= max_xoffset)) # Add the ones only in the perimeter - if ll.x <= min_xoffset or ll.y <= min_yoffset or ur.x >= max_xoffset or ur.y >= max_yoffset: + if (ll.x <= min_xoffset or ll.y <= min_yoffset or ur.x >= max_xoffset or ur.y >= max_yoffset) and not avoid: debug.info(3, "Adding supply pin rail {0} {1}->{2} {3}".format(name, ll, ur, pin)) self.cell.add_layout_pin(text=name, layer=pin.layer, From 3097599a7698c3a6fadb946c2c4b157f50489b5a Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Tue, 18 May 2021 19:47:47 +0900 Subject: [PATCH 17/33] Quitting the 1-step margin of the LEF obs --- compiler/base/lef.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/base/lef.py b/compiler/base/lef.py index 69fb3b249..308316919 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -97,7 +97,8 @@ def compute_abstract_blockages(self): # Start with blockages on all layers the size of the block # minus the pin escape margin (hard coded to 4 x m3 pitch) # These are a pin_layout to use their geometric functions - perimeter_margin = self.m3_pitch + # TODO: The escape is already set to 0. Is not anymore 4xm3. Perimeter is now 0 + perimeter_margin = 0 # self.m3_pitch self.blockages = {} for layer_name in self.lef_layers: self.blockages[layer_name]=[] From a4cece463595043b7064189a142f6232af808320 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Tue, 18 May 2021 20:02:07 +0900 Subject: [PATCH 18/33] Remove the LEF m5 cover --- compiler/sram/sram_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 63018ca9b..918bb9177 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -24,7 +24,7 @@ class sram_base(design, verilog, lef): """ def __init__(self, name, sram_config): design.__init__(self, name) - lef.__init__(self, ["m1", "m2", "m3", "m4", "m5"]) # TODO: Generation of lef layers should be dependent of tech + lef.__init__(self, ["m1", "m2", "m3", "m4"]) verilog.__init__(self) self.sram_config = sram_config From c5f28a5251ea1e2a73369cfb7dc71013a3bce410 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Tue, 2 Nov 2021 22:39:00 +0900 Subject: [PATCH 19/33] Fixes for working with the latest version --- compiler/base/pin_layout.py | 27 +++++++++++++++++---------- compiler/modules/bank.py | 2 +- compiler/router/supply_grid_router.py | 2 +- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 1833a476c..e1b73bda3 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -53,16 +53,23 @@ def __init__(self, name, rect, layer_name_pp): break else: - try: - from tech import layer_override - from tech import layer_override_name - if layer_override[name]: - self.lpp = layer_override[name] - self.layer = "pwellp" - self._recompute_hash() - return - except: - debug.error("Layer {} is not a valid routing layer in the tech file.".format(layer_name_pp), -1) + for (layer_name, lpp) in valid_pin_layers.items(): + if not lpp: + continue + if self.same_lpp(layer_name_pp, lpp): + self._layer = layer_name + break + else: + try: + from tech import layer_override + from tech import layer_override_name + if layer_override[name]: + self.lpp = layer_override[name] + self.layer = "pwellp" + self._recompute_hash() + return + except: + debug.error("Layer {} is not a valid routing layer in the tech file.".format(layer_name_pp), -1) self.lpp = layer[self.layer] self._recompute_hash() diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 3e094b9a9..e9d51be52 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -242,7 +242,7 @@ def compute_instance_port0_offsets(self): # TODO: In tsmc180, is not 1.25 (dff is 6.72. crosses with the first gnd) mult = 1.25 if OPTS.tech_name == "tsmc18": - mult = 1.5 + mult = 1.35 x_offset = self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width if self.col_addr_size > 0: x_offset += self.column_decoder.width + self.col_addr_bus_width diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 72f448b73..041a795c2 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -34,7 +34,7 @@ def __init__(self, layers, design, bbox=None, pin_type=None, margin=0): # The pin escape router already made the bounding box big enough, # so we can use the regular bbox here. if pin_type: - debug.check(pin_type in ["left", "right", "top", "bottom", "single", "ring"], + debug.check(pin_type in ["left", "right", "top", "bottom", "single", "ring", "multiple"], "Invalid pin type {}".format(pin_type)) self.pin_type = pin_type if not bbox: From e4d3940941d9a6a1ebcbd3ebfbffa39310a30a9c Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Thu, 4 Nov 2021 18:49:27 +0900 Subject: [PATCH 20/33] LAPIS20 addition whenever the tsmc18 is used --- compiler/base/hierarchy_layout.py | 4 ++-- compiler/modules/bank.py | 4 ++-- compiler/pgates/column_mux.py | 2 +- compiler/pgates/pand2.py | 2 +- compiler/pgates/pand3.py | 2 +- compiler/pgates/pand4.py | 2 +- compiler/pgates/pgate.py | 6 +++--- compiler/pgates/pnand3.py | 2 +- compiler/pgates/pnand4.py | 2 +- compiler/pgates/precharge.py | 6 +++--- compiler/tests/21_model_delay_test.py | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 072b5b09f..76436cd49 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1298,7 +1298,7 @@ def add_power_pin(self, name, loc, directions=None, start_layer="m1"): min_area = drc["minarea_{}".format(self.pwr_grid_layer)] width = round_to_grid(sqrt(min_area)) height = round_to_grid(min_area / width) - elif OPTS.tech_name == "tsmc18": + elif OPTS.tech_name in ["tsmc18", "lapis20"]: min_area = drc["minarea_{}".format(self.pwr_grid_layer)] min_width = drc["minwidth_{}".format(self.pwr_grid_layer)] dir = preferred_directions[self.pwr_grid_layer] @@ -1351,7 +1351,7 @@ def copy_power_pin(self, pin, loc=None, directions=None, new_name=""): min_area = drc["minarea_{}".format(self.pwr_grid_layer)] width = round_to_grid(sqrt(min_area)) height = round_to_grid(min_area / width) - elif OPTS.tech_name == "tsmc18": + elif OPTS.tech_name in ["tsmc18", "lapis20"]: min_area = drc["minarea_{}".format(self.pwr_grid_layer)] min_width = drc["minwidth_{}".format(self.pwr_grid_layer)] dir = preferred_directions[self.pwr_grid_layer] diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index e9d51be52..cbd4aba05 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -241,7 +241,7 @@ def compute_instance_port0_offsets(self): # may be routed in M3 or M4 # TODO: In tsmc180, is not 1.25 (dff is 6.72. crosses with the first gnd) mult = 1.25 - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: mult = 1.35 x_offset = self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width if self.col_addr_size > 0: @@ -289,7 +289,7 @@ def compute_instance_port1_offsets(self): # may be routed in M3 or M4 # TODO: In tsmc180, is not 1.25 (dff is 6.72. crosses with the first gnd) mult = 1.25 - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: mult = 1.5 x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width if self.col_addr_size > 0: diff --git a/compiler/pgates/column_mux.py b/compiler/pgates/column_mux.py index ee774a907..aecdc3941 100644 --- a/compiler/pgates/column_mux.py +++ b/compiler/pgates/column_mux.py @@ -255,7 +255,7 @@ def add_pn_wells(self): height=self.height) # TSMC18 gate port hack - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: # Body connection min_area = drc["minarea_{}".format(self.active_stack[0])] width = round_to_grid(self.well_contact.mod.first_layer_width) diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 903005724..45766128f 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -88,7 +88,7 @@ def place_insts(self): self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) # Extension of the imp in both n and p for tsmc18 - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: pmos_rightmost_nand = self.nand_inst.mod.pmos2_pos pmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.pmos_pos + vector(self.nand_inst.rx(), 0) nmos_rightmost_nand = self.nand_inst.mod.nmos2_pos diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index 9919f7d57..a7347c9e9 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -93,7 +93,7 @@ def place_insts(self): self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) # Extension of the imp in both n and p for tsmc18 - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: pmos_rightmost_nand = self.nand_inst.mod.pmos3_pos pmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.pmos_pos + vector(self.nand_inst.rx(), 0) nmos_rightmost_nand = self.nand_inst.mod.nmos3_pos diff --git a/compiler/pgates/pand4.py b/compiler/pgates/pand4.py index 71f43f554..eae3982a1 100644 --- a/compiler/pgates/pand4.py +++ b/compiler/pgates/pand4.py @@ -94,7 +94,7 @@ def place_insts(self): self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) # Extension of the imp in both n and p for tsmc18 - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: pmos_rightmost_nand = self.nand_inst.mod.pmos4_pos pmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.pmos_pos + vector(self.nand_inst.rx(), 0) nmos_rightmost_nand = self.nand_inst.mod.nmos4_pos diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 0a0e7eda3..08a63cfd2 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -143,7 +143,7 @@ def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", di height = via.mod.second_layer_height offset = contact_offset # TODO: Put this in a function? - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: cur_layer = "poly" while cur_layer != self.route_layer: from_id = layer_indices[cur_layer] @@ -298,7 +298,7 @@ def add_nwell_contact(self, pmos, pmos_pos): # height=implant_height) # TSMC18 gate port hack - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: min_area = drc["minarea_{}".format(self.active_stack[0])] width = round_to_grid(self.nwell_contact.mod.first_layer_width) height = round_to_grid(min_area / width) @@ -429,7 +429,7 @@ def add_pwell_contact(self, nmos, nmos_pos): # height=implant_height) # TSMC18 gate port hack - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: min_area = drc["minarea_{}".format(self.active_stack[0])] width = round_to_grid(self.pwell_contact.mod.first_layer_width) height = round_to_grid(min_area / width) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index ad4c245d8..21bc8670f 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -228,7 +228,7 @@ def route_inputs(self): active_to_poly_contact2) # TODO: There has to be a better way for this - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: self.inputA_yoffset += self.m1_pitch apin = self.route_input_gate(self.pmos1_inst, diff --git a/compiler/pgates/pnand4.py b/compiler/pgates/pnand4.py index 9835f2b3e..2e385d9e4 100644 --- a/compiler/pgates/pnand4.py +++ b/compiler/pgates/pnand4.py @@ -243,7 +243,7 @@ def route_inputs(self): active_to_poly_contact2) # TODO: There has to be a better way for this - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: self.inputA_yoffset += self.m1_pitch apin = self.route_input_gate(self.pmos1_inst, diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 96b2e7fa0..4a115bb71 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -236,7 +236,7 @@ def place_nwell_and_contact(self): height=self.height) # TSMC18 gate port hack - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: # Body connection min_area = drc["minarea_{}".format(self.active_stack[0])] height = round_to_grid(self.well_contact.mod.first_layer_width) @@ -354,7 +354,7 @@ def add_bitline_contacts(self): to_layer=self.bitline_layer, offset=lower_pin.center(), directions=("V", "V")) - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: self.helper_areas(lower_pin.layer, self.bitline_layer, lower_pin.center()) # BR @@ -363,7 +363,7 @@ def add_bitline_contacts(self): to_layer=self.bitline_layer, offset=upper_pin.center(), directions=("V", "V")) - if OPTS.tech_name == "tsmc18": + if OPTS.tech_name in ["tsmc18", "lapis20"]: self.helper_areas(lower_pin.layer, self.bitline_layer, upper_pin.center()) def connect_pmos(self, pmos_pin, bit_xoffset): diff --git a/compiler/tests/21_model_delay_test.py b/compiler/tests/21_model_delay_test.py index 56d3a5a9c..286ce9ac9 100755 --- a/compiler/tests/21_model_delay_test.py +++ b/compiler/tests/21_model_delay_test.py @@ -80,7 +80,7 @@ def runTest(self): error_tolerance = 0.30 elif OPTS.tech_name == "scn4m_subm": error_tolerance = 0.30 - elif OPTS.tech_name == "tsmc18": + elif OPTS.tech_name in ["tsmc18", "lapis20"]: error_tolerance = 0.25 else: self.assertTrue(False) # other techs fail From fae2b8ddb9e6e511e0b0f60982749e18c31340a9 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Thu, 11 Nov 2021 16:26:09 +0900 Subject: [PATCH 21/33] Avoid the 'multiple' additional ring boundary when generating the grid supply route --- compiler/sram/sram_1bank.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index a6abeba64..64c0d23e2 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -350,17 +350,23 @@ def route_layout(self): big_margin = 6 * rt.track_width little_margin = 0 - pre_bbox = self.get_bbox(side="ring", - big_margin=rt.track_width) + if OPTS.supply_pin_type == "multiple": + pre_bbox = self.get_bbox(side="multiple") # NOTE: This will not add anything + bbox = pre_bbox + else: + pre_bbox = self.get_bbox(side="ring", + big_margin=rt.track_width) + bbox = self.get_bbox(side=OPTS.supply_pin_type, + big_margin=big_margin, + little_margin=little_margin) - bbox = self.get_bbox(side=OPTS.supply_pin_type, - big_margin=big_margin, - little_margin=little_margin) self.route_escape_pins(bbox) # Route the supplies first since the MST is not blockage aware # and signals can route to anywhere on sides (it is flexible) self.route_supplies(pre_bbox) + print(bbox) + print(pre_bbox) def route_dffs(self, add_routes=True): From 06e08f75f2b63fe920557011df009da4dc5d90e0 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Thu, 11 Nov 2021 23:41:23 +0900 Subject: [PATCH 22/33] Fixed the connected _set for detecting the enclosures when the pin is exactly equal to the enclosure --- compiler/router/pin_group.py | 6 +++++- compiler/router/supply_grid_router.py | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index b79aa1b4a..c4e5e46aa 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -552,7 +552,11 @@ def transitive_overlap(self, shape, shape_list): connected_set.add(cur_shape) # Remove the original shape - connected_set.remove(shape) + # NOTE: Normally the shape_list has only 1 shape, and "shape" is the same + # That means we do not need to remove the original shape + # Is highly improbable, but has happened in tsmc18 and lapis20 + if shape in connected_set and len(connected_set) > 1: + connected_set.remove(shape) # if len(connected_set){2}, {3}".format(pin_name, grid, pt, abs_path)) + debug.info(3, " Adding easy route {0} {1}->{2}, {3}".format(pin_name, grid, pt, abs_path)) routed_count += 1 pg.set_routed() self.cell.add_route(layers=self.layers, From 3be37482693c962731107177086d5288070d6be3 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Fri, 12 Nov 2021 00:26:54 +0900 Subject: [PATCH 23/33] Prints removed --- compiler/sram/sram_1bank.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 64c0d23e2..e79d6bd47 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -365,8 +365,6 @@ def route_layout(self): # Route the supplies first since the MST is not blockage aware # and signals can route to anywhere on sides (it is flexible) self.route_supplies(pre_bbox) - print(bbox) - print(pre_bbox) def route_dffs(self, add_routes=True): From 1b21d49ffdebe3119079dc84a2234cd551a8fdb5 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Tue, 28 Dec 2021 17:17:24 +0900 Subject: [PATCH 24/33] Added rohm180 --- compiler/base/hierarchy_layout.py | 4 ++-- compiler/modules/bank.py | 4 ++-- compiler/pgates/column_mux.py | 2 +- compiler/pgates/pand2.py | 2 +- compiler/pgates/pand3.py | 2 +- compiler/pgates/pand4.py | 2 +- compiler/pgates/pgate.py | 6 +++--- compiler/pgates/pnand3.py | 2 +- compiler/pgates/pnand4.py | 2 +- compiler/pgates/precharge.py | 6 +++--- compiler/tests/21_model_delay_test.py | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 76436cd49..176f16def 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1298,7 +1298,7 @@ def add_power_pin(self, name, loc, directions=None, start_layer="m1"): min_area = drc["minarea_{}".format(self.pwr_grid_layer)] width = round_to_grid(sqrt(min_area)) height = round_to_grid(min_area / width) - elif OPTS.tech_name in ["tsmc18", "lapis20"]: + elif OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: min_area = drc["minarea_{}".format(self.pwr_grid_layer)] min_width = drc["minwidth_{}".format(self.pwr_grid_layer)] dir = preferred_directions[self.pwr_grid_layer] @@ -1351,7 +1351,7 @@ def copy_power_pin(self, pin, loc=None, directions=None, new_name=""): min_area = drc["minarea_{}".format(self.pwr_grid_layer)] width = round_to_grid(sqrt(min_area)) height = round_to_grid(min_area / width) - elif OPTS.tech_name in ["tsmc18", "lapis20"]: + elif OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: min_area = drc["minarea_{}".format(self.pwr_grid_layer)] min_width = drc["minwidth_{}".format(self.pwr_grid_layer)] dir = preferred_directions[self.pwr_grid_layer] diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index cbd4aba05..1e3f1d625 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -241,7 +241,7 @@ def compute_instance_port0_offsets(self): # may be routed in M3 or M4 # TODO: In tsmc180, is not 1.25 (dff is 6.72. crosses with the first gnd) mult = 1.25 - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: mult = 1.35 x_offset = self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width if self.col_addr_size > 0: @@ -289,7 +289,7 @@ def compute_instance_port1_offsets(self): # may be routed in M3 or M4 # TODO: In tsmc180, is not 1.25 (dff is 6.72. crosses with the first gnd) mult = 1.25 - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: mult = 1.5 x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width if self.col_addr_size > 0: diff --git a/compiler/pgates/column_mux.py b/compiler/pgates/column_mux.py index aecdc3941..3bb599305 100644 --- a/compiler/pgates/column_mux.py +++ b/compiler/pgates/column_mux.py @@ -255,7 +255,7 @@ def add_pn_wells(self): height=self.height) # TSMC18 gate port hack - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: # Body connection min_area = drc["minarea_{}".format(self.active_stack[0])] width = round_to_grid(self.well_contact.mod.first_layer_width) diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 45766128f..79234cf0b 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -88,7 +88,7 @@ def place_insts(self): self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) # Extension of the imp in both n and p for tsmc18 - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: pmos_rightmost_nand = self.nand_inst.mod.pmos2_pos pmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.pmos_pos + vector(self.nand_inst.rx(), 0) nmos_rightmost_nand = self.nand_inst.mod.nmos2_pos diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index a7347c9e9..f91f518fc 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -93,7 +93,7 @@ def place_insts(self): self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) # Extension of the imp in both n and p for tsmc18 - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: pmos_rightmost_nand = self.nand_inst.mod.pmos3_pos pmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.pmos_pos + vector(self.nand_inst.rx(), 0) nmos_rightmost_nand = self.nand_inst.mod.nmos3_pos diff --git a/compiler/pgates/pand4.py b/compiler/pgates/pand4.py index eae3982a1..66b8daaaa 100644 --- a/compiler/pgates/pand4.py +++ b/compiler/pgates/pand4.py @@ -94,7 +94,7 @@ def place_insts(self): self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) # Extension of the imp in both n and p for tsmc18 - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: pmos_rightmost_nand = self.nand_inst.mod.pmos4_pos pmos_leftmost_inv = self.inv_inst.mod.inv_inst_list[0].mod.pmos_pos + vector(self.nand_inst.rx(), 0) nmos_rightmost_nand = self.nand_inst.mod.nmos4_pos diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 08a63cfd2..9ef71338e 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -143,7 +143,7 @@ def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", di height = via.mod.second_layer_height offset = contact_offset # TODO: Put this in a function? - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: cur_layer = "poly" while cur_layer != self.route_layer: from_id = layer_indices[cur_layer] @@ -298,7 +298,7 @@ def add_nwell_contact(self, pmos, pmos_pos): # height=implant_height) # TSMC18 gate port hack - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: min_area = drc["minarea_{}".format(self.active_stack[0])] width = round_to_grid(self.nwell_contact.mod.first_layer_width) height = round_to_grid(min_area / width) @@ -429,7 +429,7 @@ def add_pwell_contact(self, nmos, nmos_pos): # height=implant_height) # TSMC18 gate port hack - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: min_area = drc["minarea_{}".format(self.active_stack[0])] width = round_to_grid(self.pwell_contact.mod.first_layer_width) height = round_to_grid(min_area / width) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 21bc8670f..76fcb0ebe 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -228,7 +228,7 @@ def route_inputs(self): active_to_poly_contact2) # TODO: There has to be a better way for this - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: self.inputA_yoffset += self.m1_pitch apin = self.route_input_gate(self.pmos1_inst, diff --git a/compiler/pgates/pnand4.py b/compiler/pgates/pnand4.py index 2e385d9e4..628e4ddbf 100644 --- a/compiler/pgates/pnand4.py +++ b/compiler/pgates/pnand4.py @@ -243,7 +243,7 @@ def route_inputs(self): active_to_poly_contact2) # TODO: There has to be a better way for this - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: self.inputA_yoffset += self.m1_pitch apin = self.route_input_gate(self.pmos1_inst, diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 4a115bb71..d4d2514e5 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -236,7 +236,7 @@ def place_nwell_and_contact(self): height=self.height) # TSMC18 gate port hack - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: # Body connection min_area = drc["minarea_{}".format(self.active_stack[0])] height = round_to_grid(self.well_contact.mod.first_layer_width) @@ -354,7 +354,7 @@ def add_bitline_contacts(self): to_layer=self.bitline_layer, offset=lower_pin.center(), directions=("V", "V")) - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: self.helper_areas(lower_pin.layer, self.bitline_layer, lower_pin.center()) # BR @@ -363,7 +363,7 @@ def add_bitline_contacts(self): to_layer=self.bitline_layer, offset=upper_pin.center(), directions=("V", "V")) - if OPTS.tech_name in ["tsmc18", "lapis20"]: + if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: self.helper_areas(lower_pin.layer, self.bitline_layer, upper_pin.center()) def connect_pmos(self, pmos_pin, bit_xoffset): diff --git a/compiler/tests/21_model_delay_test.py b/compiler/tests/21_model_delay_test.py index 286ce9ac9..b913c3466 100755 --- a/compiler/tests/21_model_delay_test.py +++ b/compiler/tests/21_model_delay_test.py @@ -80,7 +80,7 @@ def runTest(self): error_tolerance = 0.30 elif OPTS.tech_name == "scn4m_subm": error_tolerance = 0.30 - elif OPTS.tech_name in ["tsmc18", "lapis20"]: + elif OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: error_tolerance = 0.25 else: self.assertTrue(False) # other techs fail From ea588ab6f83207c8b467a8d8e1330d0837c0f0ec Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Thu, 13 Jan 2022 18:19:29 +0900 Subject: [PATCH 25/33] all pinv/nand/nor have extensions for rohm180 --- compiler/opennvram.py | 32 ++++++++++++++++++++++++++++++ compiler/pgates/pinv.py | 27 ++++++++++++++++++++++++++ compiler/pgates/pnand2.py | 29 +++++++++++++++++++++++++++ compiler/pgates/pnand3.py | 29 +++++++++++++++++++++++++++ compiler/pgates/pnand4.py | 30 ++++++++++++++++++++++++++++ compiler/pgates/pnor2.py | 41 +++++++++++++++++++++++++++++++++------ 6 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 compiler/opennvram.py diff --git a/compiler/opennvram.py b/compiler/opennvram.py new file mode 100644 index 000000000..83ec5e49c --- /dev/null +++ b/compiler/opennvram.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +""" +NVRAM Compiler + +Based on the SRAM compiler + +The output files append the given suffixes to the output name: +a spice (.sp) file for circuit simulation +a GDS2 (.gds) file containing the layout +a LEF (.lef) file for preliminary P&R (real one should be from layout) +""" + +import sys +import datetime +import globals as g + +(OPTS, args) = g.parse_args() + +# Check that we are left with a single configuration file as argument. +if len(args) != 1: + print(g.USAGE) + sys.exit(2) + + + diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index a3e467b78..ab7758215 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -254,6 +254,33 @@ def place_ptx(self): 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)) + # Special requirement for rohm180 (Possibly for another ones is ok to put this) + # We need to extend the implants to the same point as the well + # Now, this function is called before the extend_wells + # So we need to do the same as the extends well would do + if OPTS.tech_name == "rohm180": + mos_list = [(self.pmos, self.pmos_pos, self.nmos, self.nmos_pos)] + for pmos,pmos_pos,nmos,nmos_pos in mos_list: + nwell_yoffset = 0.48 * self.height + # PMOS phase + r1 = pmos.implant + pimp_width = r1.width + pimp_position = vector(r1.offset.x + pmos_pos.x, nwell_yoffset) + pimp_height = pmos_pos.y + r1.offset.y - nwell_yoffset + self.add_rect(layer="pimplant", + offset=pimp_position, + width=pimp_width, + height=pimp_height) + # NMOS phase + r1 = nmos.implant + nimp_width = r1.width + nimp_position = nmos_pos + r1.offset + vector(0, r1.height) + nimp_height = nwell_yoffset - nimp_position.y + self.add_rect(layer="nimplant", + offset=nimp_position, + width=nimp_width, + height=nimp_height) + def route_outputs(self): """ Route the output (drains) together. diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 1fafc2100..fc7eb2183 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -13,6 +13,7 @@ from sram_factory import factory import contact from tech import cell_properties as cell_props +from globals import OPTS class pnand2(pgate.pgate): @@ -155,6 +156,34 @@ def place_ptx(self): self.nmos2_pos = nmos1_pos + self.overlap_offset self.nmos2_inst.place(self.nmos2_pos) + # Special requirement for rohm180 (Possibly for another ones is ok to put this) + # We need to extend the implants to the same point as the well + # Now, this function is called before the extend_wells + # So we need to do the same as the extends well would do + if OPTS.tech_name == "rohm180": + mos_list = [(self.pmos_left, self.pmos1_pos, self.nmos_left, self.nmos1_pos), + (self.pmos_right, self.pmos2_pos, self.nmos_right, self.nmos2_pos)] + for pmos,pmos_pos,nmos,nmos_pos in mos_list: + nwell_yoffset = 0.48 * self.height + # PMOS phase + r1 = pmos.implant + pimp_width = r1.width + pimp_position = vector(r1.offset.x + pmos_pos.x, nwell_yoffset) + pimp_height = pmos_pos.y + r1.offset.y - nwell_yoffset + self.add_rect(layer="pimplant", + offset=pimp_position, + width=pimp_width, + height=pimp_height) + # NMOS phase + r1 = nmos.implant + nimp_width = r1.width + nimp_position = nmos_pos + r1.offset + vector(0, r1.height) + nimp_height = nwell_yoffset - nimp_position.y + self.add_rect(layer="nimplant", + offset=nimp_position, + width=nimp_width, + height=nimp_height) + def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 76fcb0ebe..274822332 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -190,6 +190,35 @@ def place_ptx(self): self.nmos3_pos = nmos2_pos + self.ptx_offset self.nmos3_inst.place(self.nmos3_pos) + # Special requirement for rohm180 (Possibly for another ones is ok to put this) + # We need to extend the implants to the same point as the well + # Now, this function is called before the extend_wells + # So we need to do the same as the extends well would do + if OPTS.tech_name == "rohm180": + mos_list = [(self.pmos_left, self.pmos1_pos, self.nmos_left, self.nmos1_pos), + (self.pmos_center, self.pmos2_pos, self.nmos_center, self.nmos2_pos), + (self.pmos_right, self.pmos3_pos, self.nmos_right, self.nmos3_pos)] + for pmos,pmos_pos,nmos,nmos_pos in mos_list: + nwell_yoffset = 0.48 * self.height + # PMOS phase + r1 = pmos.implant + pimp_width = r1.width + pimp_position = vector(r1.offset.x + pmos_pos.x, nwell_yoffset) + pimp_height = pmos_pos.y + r1.offset.y - nwell_yoffset + self.add_rect(layer="pimplant", + offset=pimp_position, + width=pimp_width, + height=pimp_height) + # NMOS phase + r1 = nmos.implant + nimp_width = r1.width + nimp_position = nmos_pos + r1.offset + vector(0, r1.height) + nimp_height = nwell_yoffset - nimp_position.y + self.add_rect(layer="nimplant", + offset=nimp_position, + width=nimp_width, + height=nimp_height) + def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ diff --git a/compiler/pgates/pnand4.py b/compiler/pgates/pnand4.py index 628e4ddbf..20d8f7224 100644 --- a/compiler/pgates/pnand4.py +++ b/compiler/pgates/pnand4.py @@ -204,6 +204,36 @@ def place_ptx(self): self.nmos4_pos = nmos3_pos + self.ptx_offset self.nmos4_inst.place(self.nmos4_pos) + # Special requirement for rohm180 (Possibly for another ones is ok to put this) + # We need to extend the implants to the same point as the well + # Now, this function is called before the extend_wells + # So we need to do the same as the extends well would do + if OPTS.tech_name == "rohm180": + mos_list = [(self.pmos_left, self.pmos1_pos, self.nmos_left, self.nmos1_pos), + (self.pmos_center, self.pmos2_pos, self.nmos_center, self.nmos2_pos), + (self.pmos_center, self.pmos3_pos, self.nmos_center, self.nmos3_pos), + (self.pmos_right, self.pmos4_pos, self.nmos_right, self.nmos4_pos)] + for pmos,pmos_pos,nmos,nmos_pos in mos_list: + nwell_yoffset = 0.48 * self.height + # PMOS phase + r1 = pmos.implant + pimp_width = r1.width + pimp_position = vector(r1.offset.x + pmos_pos.x, nwell_yoffset) + pimp_height = pmos_pos.y + r1.offset.y - nwell_yoffset + self.add_rect(layer="pimplant", + offset=pimp_position, + width=pimp_width, + height=pimp_height) + # NMOS phase + r1 = nmos.implant + nimp_width = r1.width + nimp_position = nmos_pos + r1.offset + vector(0, r1.height) + nimp_height = nwell_yoffset - nimp_position.y + self.add_rect(layer="nimplant", + offset=nimp_position, + width=nimp_width, + height=nimp_height) + def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index d59f28fea..5842dbc13 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -11,6 +11,7 @@ from vector import vector from sram_factory import factory from tech import cell_properties as cell_props +from globals import OPTS class pnor2(pgate.pgate): @@ -157,19 +158,47 @@ def place_ptx(self): self.top_bottom_space = max(contact_to_vdd_rail_space, poly_to_poly_gate_space) - pmos1_pos = vector(self.pmos_right.active_offset.x, + self.pmos1_pos = vector(self.pmos_right.active_offset.x, self.height - self.pmos_right.active_height - self.top_bottom_space) - self.pmos1_inst.place(pmos1_pos) + self.pmos1_inst.place(self.pmos1_pos) - self.pmos2_pos = pmos1_pos + self.overlap_offset + self.pmos2_pos = self.pmos1_pos + self.overlap_offset self.pmos2_inst.place(self.pmos2_pos) - nmos1_pos = vector(self.pmos_right.active_offset.x, self.top_bottom_space) - self.nmos1_inst.place(nmos1_pos) + self.nmos1_pos = vector(self.pmos_right.active_offset.x, self.top_bottom_space) + self.nmos1_inst.place(self.nmos1_pos) - self.nmos2_pos = nmos1_pos + self.overlap_offset + self.nmos2_pos = self.nmos1_pos + self.overlap_offset self.nmos2_inst.place(self.nmos2_pos) + # Special requirement for rohm180 (Possibly for another ones is ok to put this) + # We need to extend the implants to the same point as the well + # Now, this function is called before the extend_wells + # So we need to do the same as the extends well would do + if OPTS.tech_name == "rohm180": + mos_list = [(self.pmos_left, self.pmos1_pos, self.nmos_left, self.nmos1_pos), + (self.pmos_right, self.pmos2_pos, self.nmos_right, self.nmos2_pos)] + for pmos,pmos_pos,nmos,nmos_pos in mos_list: + nwell_yoffset = 0.48 * self.height + # PMOS phase + r1 = pmos.implant + pimp_width = r1.width + pimp_position = vector(r1.offset.x + pmos_pos.x, nwell_yoffset) + pimp_height = pmos_pos.y + r1.offset.y - nwell_yoffset + self.add_rect(layer="pimplant", + offset=pimp_position, + width=pimp_width, + height=pimp_height) + # NMOS phase + r1 = nmos.implant + nimp_width = r1.width + nimp_position = nmos_pos + r1.offset + vector(0, r1.height) + nimp_height = nwell_yoffset - nimp_position.y + self.add_rect(layer="nimplant", + offset=nimp_position, + width=nimp_width, + height=nimp_height) + def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ From c75c3618f0a12cf24541afeb15529c85041c2505 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Thu, 13 Jan 2022 18:22:13 +0900 Subject: [PATCH 26/33] all pinv/nand/nor have extensions for rohm180 --- compiler/pgates/pnand2.py | 2 +- compiler/pgates/pnand3.py | 4 ++-- compiler/pgates/pnand4.py | 6 +++--- compiler/pgates/pnor2.py | 14 +++++++------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index fc7eb2183..a316aee20 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -161,7 +161,7 @@ def place_ptx(self): # Now, this function is called before the extend_wells # So we need to do the same as the extends well would do if OPTS.tech_name == "rohm180": - mos_list = [(self.pmos_left, self.pmos1_pos, self.nmos_left, self.nmos1_pos), + mos_list = [(self.pmos_left, pmos1_pos, self.nmos_left, nmos1_pos), (self.pmos_right, self.pmos2_pos, self.nmos_right, self.nmos2_pos)] for pmos,pmos_pos,nmos,nmos_pos in mos_list: nwell_yoffset = 0.48 * self.height diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 274822332..03a636e13 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -195,8 +195,8 @@ def place_ptx(self): # Now, this function is called before the extend_wells # So we need to do the same as the extends well would do if OPTS.tech_name == "rohm180": - mos_list = [(self.pmos_left, self.pmos1_pos, self.nmos_left, self.nmos1_pos), - (self.pmos_center, self.pmos2_pos, self.nmos_center, self.nmos2_pos), + mos_list = [(self.pmos_left, pmos1_pos, self.nmos_left, nmos1_pos), + (self.pmos_center, pmos2_pos, self.nmos_center, nmos2_pos), (self.pmos_right, self.pmos3_pos, self.nmos_right, self.nmos3_pos)] for pmos,pmos_pos,nmos,nmos_pos in mos_list: nwell_yoffset = 0.48 * self.height diff --git a/compiler/pgates/pnand4.py b/compiler/pgates/pnand4.py index 20d8f7224..84cd116ca 100644 --- a/compiler/pgates/pnand4.py +++ b/compiler/pgates/pnand4.py @@ -209,9 +209,9 @@ def place_ptx(self): # Now, this function is called before the extend_wells # So we need to do the same as the extends well would do if OPTS.tech_name == "rohm180": - mos_list = [(self.pmos_left, self.pmos1_pos, self.nmos_left, self.nmos1_pos), - (self.pmos_center, self.pmos2_pos, self.nmos_center, self.nmos2_pos), - (self.pmos_center, self.pmos3_pos, self.nmos_center, self.nmos3_pos), + mos_list = [(self.pmos_left, pmos1_pos, self.nmos_left, nmos1_pos), + (self.pmos_center, pmos2_pos, self.nmos_center, nmos2_pos), + (self.pmos_center, pmos3_pos, self.nmos_center, nmos3_pos), (self.pmos_right, self.pmos4_pos, self.nmos_right, self.nmos4_pos)] for pmos,pmos_pos,nmos,nmos_pos in mos_list: nwell_yoffset = 0.48 * self.height diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 5842dbc13..ca1ef5199 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -158,17 +158,17 @@ def place_ptx(self): self.top_bottom_space = max(contact_to_vdd_rail_space, poly_to_poly_gate_space) - self.pmos1_pos = vector(self.pmos_right.active_offset.x, + pmos1_pos = vector(self.pmos_right.active_offset.x, self.height - self.pmos_right.active_height - self.top_bottom_space) - self.pmos1_inst.place(self.pmos1_pos) + self.pmos1_inst.place(pmos1_pos) - self.pmos2_pos = self.pmos1_pos + self.overlap_offset + self.pmos2_pos = pmos1_pos + self.overlap_offset self.pmos2_inst.place(self.pmos2_pos) - self.nmos1_pos = vector(self.pmos_right.active_offset.x, self.top_bottom_space) - self.nmos1_inst.place(self.nmos1_pos) + nmos1_pos = vector(self.pmos_right.active_offset.x, self.top_bottom_space) + self.nmos1_inst.place(nmos1_pos) - self.nmos2_pos = self.nmos1_pos + self.overlap_offset + self.nmos2_pos = nmos1_pos + self.overlap_offset self.nmos2_inst.place(self.nmos2_pos) # Special requirement for rohm180 (Possibly for another ones is ok to put this) @@ -176,7 +176,7 @@ def place_ptx(self): # Now, this function is called before the extend_wells # So we need to do the same as the extends well would do if OPTS.tech_name == "rohm180": - mos_list = [(self.pmos_left, self.pmos1_pos, self.nmos_left, self.nmos1_pos), + mos_list = [(self.pmos_left, pmos1_pos, self.nmos_left, nmos1_pos), (self.pmos_right, self.pmos2_pos, self.nmos_right, self.nmos2_pos)] for pmos,pmos_pos,nmos,nmos_pos in mos_list: nwell_yoffset = 0.48 * self.height From 1c99624b561ac7316907b82cb727c86e77587396 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Fri, 14 Jan 2022 15:27:59 +0900 Subject: [PATCH 27/33] Test for rounding the wirepath with the unit instead of the grid --- compiler/base/utils.py | 19 +++++++++++++++++++ compiler/base/wire_path.py | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/compiler/base/utils.py b/compiler/base/utils.py index fe27c9c0a..c394fbaae 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -47,6 +47,25 @@ def snap_to_grid(offset): return [round_to_grid(offset[0]), round_to_grid(offset[1])] +# NOTE: To snap to the unit instead of the grid. Grid is for geometries, unit is for routes or wires. +def round_to_unit(number): + """ + Rounds an arbitrary number to the unit. + """ + grid = tech.GDS["unit"][0] + # this gets the nearest integer value + number_grid = int(round(round((number / grid), 2), 0)) + number_off = number_grid * grid + return number_off + + +def snap_to_unit(offset): + """ + Changes the coodrinate to match the unit settings + """ + return [round_to_unit(offset[0]), + round_to_unit(offset[1])] + def pin_center(boundary): """ diff --git a/compiler/base/wire_path.py b/compiler/base/wire_path.py index 411b9ede7..352180992 100644 --- a/compiler/base/wire_path.py +++ b/compiler/base/wire_path.py @@ -9,12 +9,12 @@ from tech import layer as techlayer import debug from vector import vector -from utils import snap_to_grid +from utils import snap_to_unit def create_rectilinear_route(my_list): """ Add intermediate nodes if it isn't rectilinear. Also skip repeated nodes. Also, convert to vector if the aren't.""" - pl = [snap_to_grid(x) for x in my_list] + pl = [snap_to_unit(x) for x in my_list] my_list = [] for index in range(len(pl) - 1): From 6a723fafa160ca15f44668f081c3aebdb4a06180 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Fri, 14 Jan 2022 15:39:12 +0900 Subject: [PATCH 28/33] snap_to_grid already do this --- compiler/base/geometry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index d2f4e98e0..d45dd59af 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -501,8 +501,8 @@ def __init__(self, lpp, offset, width, height): self.name = "rect" self.offset = vector(offset).snap_to_grid() self.size = vector(width, height).snap_to_grid() - self.width = round_to_grid(self.size.x) - self.height = round_to_grid(self.size.y) + self.width = self.size.x # round_to_grid(self.size.x) # NOTE: snap_to_grid already do this! + self.height = self.size.y # round_to_grid(self.size.y) self.compute_boundary(offset, "", 0) debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): " From 0faa8e62491412f6557a9438520623b550a6d7ca Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Fri, 14 Jan 2022 15:45:17 +0900 Subject: [PATCH 29/33] Round to grid to the nwell offset for the implant --- compiler/pgates/pinv.py | 2 +- compiler/pgates/pnand2.py | 3 ++- compiler/pgates/pnand3.py | 3 ++- compiler/pgates/pnand4.py | 3 ++- compiler/pgates/pnor2.py | 3 ++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index ab7758215..3ba187e99 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -261,7 +261,7 @@ def place_ptx(self): if OPTS.tech_name == "rohm180": mos_list = [(self.pmos, self.pmos_pos, self.nmos, self.nmos_pos)] for pmos,pmos_pos,nmos,nmos_pos in mos_list: - nwell_yoffset = 0.48 * self.height + nwell_yoffset = round_to_grid(0.48 * self.height) # PMOS phase r1 = pmos.implant pimp_width = r1.width diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index a316aee20..cc1e1022f 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -14,6 +14,7 @@ import contact from tech import cell_properties as cell_props from globals import OPTS +from utils import round_to_grid class pnand2(pgate.pgate): @@ -164,7 +165,7 @@ def place_ptx(self): mos_list = [(self.pmos_left, pmos1_pos, self.nmos_left, nmos1_pos), (self.pmos_right, self.pmos2_pos, self.nmos_right, self.nmos2_pos)] for pmos,pmos_pos,nmos,nmos_pos in mos_list: - nwell_yoffset = 0.48 * self.height + nwell_yoffset = round_to_grid(0.48 * self.height) # PMOS phase r1 = pmos.implant pimp_width = r1.width diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 03a636e13..a1f24513d 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -14,6 +14,7 @@ import contact from tech import cell_properties as cell_props from globals import OPTS +from utils import round_to_grid class pnand3(pgate.pgate): @@ -199,7 +200,7 @@ def place_ptx(self): (self.pmos_center, pmos2_pos, self.nmos_center, nmos2_pos), (self.pmos_right, self.pmos3_pos, self.nmos_right, self.nmos3_pos)] for pmos,pmos_pos,nmos,nmos_pos in mos_list: - nwell_yoffset = 0.48 * self.height + nwell_yoffset = round_to_grid(0.48 * self.height) # PMOS phase r1 = pmos.implant pimp_width = r1.width diff --git a/compiler/pgates/pnand4.py b/compiler/pgates/pnand4.py index 84cd116ca..cb81a04af 100644 --- a/compiler/pgates/pnand4.py +++ b/compiler/pgates/pnand4.py @@ -14,6 +14,7 @@ import contact from tech import cell_properties as cell_props from globals import OPTS +from utils import round_to_grid class pnand4(pgate.pgate): @@ -214,7 +215,7 @@ def place_ptx(self): (self.pmos_center, pmos3_pos, self.nmos_center, nmos3_pos), (self.pmos_right, self.pmos4_pos, self.nmos_right, self.nmos4_pos)] for pmos,pmos_pos,nmos,nmos_pos in mos_list: - nwell_yoffset = 0.48 * self.height + nwell_yoffset = round_to_grid(0.48 * self.height) # PMOS phase r1 = pmos.implant pimp_width = r1.width diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index ca1ef5199..c28f3f7b6 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -12,6 +12,7 @@ from sram_factory import factory from tech import cell_properties as cell_props from globals import OPTS +from utils import round_to_grid class pnor2(pgate.pgate): @@ -179,7 +180,7 @@ def place_ptx(self): mos_list = [(self.pmos_left, pmos1_pos, self.nmos_left, nmos1_pos), (self.pmos_right, self.pmos2_pos, self.nmos_right, self.nmos2_pos)] for pmos,pmos_pos,nmos,nmos_pos in mos_list: - nwell_yoffset = 0.48 * self.height + nwell_yoffset = round_to_grid(0.48 * self.height) # PMOS phase r1 = pmos.implant pimp_width = r1.width From 79b5c023c5decc70e71dcf9f6bb7d3b29cecb360 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Fri, 14 Jan 2022 16:15:05 +0900 Subject: [PATCH 30/33] Adding snap_to_grid for position in mos for the logic pgates only --- compiler/pgates/pinv.py | 2 +- compiler/pgates/pnand2.py | 4 ++-- compiler/pgates/pnand3.py | 6 +++--- compiler/pgates/pnand4.py | 8 ++++---- compiler/pgates/pnor2.py | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 3ba187e99..0d67b2443 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -259,7 +259,7 @@ def place_ptx(self): # Now, this function is called before the extend_wells # So we need to do the same as the extends well would do if OPTS.tech_name == "rohm180": - mos_list = [(self.pmos, self.pmos_pos, self.nmos, self.nmos_pos)] + mos_list = [(self.pmos, self.pmos_pos.snap_to_grid(), self.nmos, self.nmos_pos.snap_to_grid())] for pmos,pmos_pos,nmos,nmos_pos in mos_list: nwell_yoffset = round_to_grid(0.48 * self.height) # PMOS phase diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index cc1e1022f..775a8a755 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -162,8 +162,8 @@ def place_ptx(self): # Now, this function is called before the extend_wells # So we need to do the same as the extends well would do if OPTS.tech_name == "rohm180": - mos_list = [(self.pmos_left, pmos1_pos, self.nmos_left, nmos1_pos), - (self.pmos_right, self.pmos2_pos, self.nmos_right, self.nmos2_pos)] + mos_list = [(self.pmos_left, pmos1_pos.snap_to_grid(), self.nmos_left, nmos1_pos.snap_to_grid()), + (self.pmos_right, self.pmos2_pos.snap_to_grid(), self.nmos_right, self.nmos2_pos.snap_to_grid())] for pmos,pmos_pos,nmos,nmos_pos in mos_list: nwell_yoffset = round_to_grid(0.48 * self.height) # PMOS phase diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index a1f24513d..87dd90c32 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -196,9 +196,9 @@ def place_ptx(self): # Now, this function is called before the extend_wells # So we need to do the same as the extends well would do if OPTS.tech_name == "rohm180": - mos_list = [(self.pmos_left, pmos1_pos, self.nmos_left, nmos1_pos), - (self.pmos_center, pmos2_pos, self.nmos_center, nmos2_pos), - (self.pmos_right, self.pmos3_pos, self.nmos_right, self.nmos3_pos)] + mos_list = [(self.pmos_left, pmos1_pos.snap_to_grid(), self.nmos_left, nmos1_pos.snap_to_grid()), + (self.pmos_center, pmos2_pos.snap_to_grid(), self.nmos_center, nmos2_pos.snap_to_grid()), + (self.pmos_right, self.pmos3_pos.snap_to_grid(), self.nmos_right, self.nmos3_pos.snap_to_grid())] for pmos,pmos_pos,nmos,nmos_pos in mos_list: nwell_yoffset = round_to_grid(0.48 * self.height) # PMOS phase diff --git a/compiler/pgates/pnand4.py b/compiler/pgates/pnand4.py index cb81a04af..bf7fb23dd 100644 --- a/compiler/pgates/pnand4.py +++ b/compiler/pgates/pnand4.py @@ -210,10 +210,10 @@ def place_ptx(self): # Now, this function is called before the extend_wells # So we need to do the same as the extends well would do if OPTS.tech_name == "rohm180": - mos_list = [(self.pmos_left, pmos1_pos, self.nmos_left, nmos1_pos), - (self.pmos_center, pmos2_pos, self.nmos_center, nmos2_pos), - (self.pmos_center, pmos3_pos, self.nmos_center, nmos3_pos), - (self.pmos_right, self.pmos4_pos, self.nmos_right, self.nmos4_pos)] + mos_list = [(self.pmos_left, pmos1_pos.snap_to_grid(), self.nmos_left, nmos1_pos.snap_to_grid()), + (self.pmos_center, pmos2_pos.snap_to_grid(), self.nmos_center, nmos2_pos.snap_to_grid()), + (self.pmos_center, pmos3_pos.snap_to_grid(), self.nmos_center, nmos3_pos.snap_to_grid()), + (self.pmos_right, self.pmos4_pos.snap_to_grid(), self.nmos_right, self.nmos4_pos.snap_to_grid())] for pmos,pmos_pos,nmos,nmos_pos in mos_list: nwell_yoffset = round_to_grid(0.48 * self.height) # PMOS phase diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index c28f3f7b6..330eda529 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -177,8 +177,8 @@ def place_ptx(self): # Now, this function is called before the extend_wells # So we need to do the same as the extends well would do if OPTS.tech_name == "rohm180": - mos_list = [(self.pmos_left, pmos1_pos, self.nmos_left, nmos1_pos), - (self.pmos_right, self.pmos2_pos, self.nmos_right, self.nmos2_pos)] + mos_list = [(self.pmos_left, pmos1_pos.snap_to_grid(), self.nmos_left, nmos1_pos.snap_to_grid()), + (self.pmos_right, self.pmos2_pos.snap_to_grid(), self.nmos_right, self.nmos2_pos.snap_to_grid())] for pmos,pmos_pos,nmos,nmos_pos in mos_list: nwell_yoffset = round_to_grid(0.48 * self.height) # PMOS phase From 959200313e58d84996db6cfa0e2f9748f068c2e0 Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Fri, 14 Jan 2022 17:31:09 +0900 Subject: [PATCH 31/33] Previous area fix in the input pins with a ratio of 1.2 --- compiler/pgates/pgate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 9ef71338e..453aaa6e0 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -162,7 +162,7 @@ def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", di if cur_layer != "poly": min_area = drc["minarea_{}".format(cur_layer)] min_width = drc["minwidth_{}".format(cur_layer)] - width = round_to_grid(math.sqrt(min_area)) + width = round_to_grid(math.sqrt(min_area) * 1.2) height = round_to_grid(min_area / width) dir = preferred_directions[cur_layer] # TODO: This is very hackish. We literally just put the rect a little up From adae3c519b26ae7759f356386e6a06c274deae9e Mon Sep 17 00:00:00 2001 From: Ckristian Duran Date: Fri, 21 Jan 2022 17:25:19 +0900 Subject: [PATCH 32/33] pnand3 fixed DRC for ROHM180 --- compiler/pgates/pnand3.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 87dd90c32..163f89bb5 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -258,8 +258,10 @@ def route_inputs(self): active_to_poly_contact2) # TODO: There has to be a better way for this - if OPTS.tech_name in ["tsmc18", "lapis20", "rohm180"]: + if OPTS.tech_name in ["tsmc18", "lapis20"]: self.inputA_yoffset += self.m1_pitch + elif OPTS.tech_name in ["rohm180"]: + self.inputA_yoffset += self.m1_width apin = self.route_input_gate(self.pmos1_inst, self.nmos1_inst, From 5ad1db95a80e04c541bf16d59678375a60734807 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 22 Nov 2022 10:41:18 -0800 Subject: [PATCH 33/33] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..b080fd3e5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,27 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Version** +Which commit are you using? + +**To Reproduce** +What did you do to demonstrate the bug? +Please include your configuration file used. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Logs** +If applicable, add logs or output to help explain your problem. + +**Additional context** +Add any other context about the problem here.