diff --git a/compiler/base/design.py b/compiler/base/design.py index 976b947a..211133a1 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -43,6 +43,7 @@ class design(hierarchy_design): self.poly_to_active = drc("poly_to_active") self.poly_extend_active = drc("poly_extend_active") + self.poly_to_polycontact = drc("poly_to_polycontact") self.contact_to_gate = drc("contact_to_gate") self.well_enclose_active = drc("well_enclosure_active") self.implant_enclose_active = drc("implant_enclosure_active") diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 9414ba2d..b02ceee1 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -36,7 +36,6 @@ class pbitcell(design.design): # some transistor sizes in the other netlists depend on it self.create_layout() - def create_netlist(self): self.add_pins() self.add_modules() @@ -55,26 +54,24 @@ class pbitcell(design.design): self.place_storage() self.route_storage() + self.route_rails() if(self.num_rw_ports > 0): self.place_readwrite_ports() - self.route_readwrite_wordlines() - self.route_readwrite_bitlines() - if(self.num_w_ports == 0): # routing for write to storage is the same as read/write to storage - self.route_readwrite_access() + self.route_readwrite_access() if(self.num_w_ports > 0): self.place_write_ports() - self.route_write_wordlines() - self.route_write_bitlines() self.route_write_access() if(self.num_r_ports > 0): self.place_read_ports() - self.route_read_wordlines() - self.route_read_bitlines() self.route_read_access() self.extend_well() + self.route_wordlines() + self.route_bitlines() + self.route_supply() + if self.replica_bitcell: self.route_rbc_short() @@ -82,6 +79,8 @@ class pbitcell(design.design): # this function is not needed to calculate the dimensions of pbitcell in netlist_only mode though if not OPTS.netlist_only: self.offset_all_coordinates() + gnd_overlap = vector(0, 0.5*contact.well.width) + self.translate_all(gnd_overlap) self.DRC_LVS() def add_pins(self): @@ -136,7 +135,6 @@ class pbitcell(design.design): self.Q_bar = "vdd" else: self.Q_bar = "Q_bar" - def add_modules(self): """ @@ -144,19 +142,19 @@ class pbitcell(design.design): """ # if there are any read/write ports, then the inverter nmos is sized based the number of read/write ports if(self.num_rw_ports > 0): - inverter_nmos_width = self.num_rw_ports*3*parameter["min_tx_size"] - inverter_pmos_width = parameter["min_tx_size"] - readwrite_nmos_width = 1.5*parameter["min_tx_size"] - write_nmos_width = parameter["min_tx_size"] - read_nmos_width = 2*parameter["min_tx_size"] + inverter_nmos_width = self.num_rw_ports*parameter["6T_inv_nmos_size"] + inverter_pmos_width = parameter["6T_inv_pmos_size"] + readwrite_nmos_width = parameter["6T_access_size"] + write_nmos_width = parameter["6T_access_size"] + read_nmos_width = 2*parameter["6T_inv_pmos_size"] # if there are no read/write ports, then the inverter nmos is statically sized for the dual port case else: - inverter_nmos_width = 2*parameter["min_tx_size"] - inverter_pmos_width = parameter["min_tx_size"] - readwrite_nmos_width = 1.5*parameter["min_tx_size"] - write_nmos_width = parameter["min_tx_size"] - read_nmos_width = 2*parameter["min_tx_size"] + inverter_nmos_width = 2*parameter["6T_inv_pmos_size"] + inverter_pmos_width = parameter["6T_inv_pmos_size"] + readwrite_nmos_width = parameter["6T_access_size"] + write_nmos_width = parameter["6T_access_size"] + read_nmos_width = 2*parameter["6T_inv_pmos_size"] # create ptx for inverter transistors self.inverter_nmos = ptx(width=inverter_nmos_width, @@ -184,141 +182,71 @@ class pbitcell(design.design): def calculate_spacing(self): """ Calculate transistor spacings """ - # calculate metal contact extensions over transistor active - self.inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) - self.readwrite_nmos_contact_extension = 0.5*(self.readwrite_nmos.active_contact.height - self.readwrite_nmos.active_height) - self.write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height) - self.read_nmos_contact_extension = 0.5*(self.read_nmos.active_contact.height - self.read_nmos.active_height) + readwrite_nmos_contact_extension = 0.5*(self.readwrite_nmos.active_contact.height - self.readwrite_nmos.active_height) + write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height) + read_nmos_contact_extension = 0.5*(self.read_nmos.active_contact.height - self.read_nmos.active_height) + max_contact_extension = max(readwrite_nmos_contact_extension, write_nmos_contact_extension, read_nmos_contact_extension) - # calculate the distance threshold for different gate contact spacings - self.gate_contact_thres = drc["poly_to_active"] - drc["minwidth_metal2"] + # y-offset for the access transistor's gate contact + self.gate_contact_yoffset = max_contact_extension + self.m2_space + 0.5*max(contact.poly.height, contact.m1m2.height) - #calculations for horizontal transistor to tansistor spacing - # inverter spacings - self.inverter_to_inverter_spacing = contact.poly.height + drc["minwidth_metal1"] - self.inverter_to_write_spacing = drc["pwell_to_nwell"] + 2*drc["well_enclosure_active"] + # y-position of access transistors + self.port_ypos = self.m1_space + 0.5*contact.m1m2.height + self.gate_contact_yoffset - # readwrite to readwrite transistor spacing (also acts as readwrite to write transistor spacing) - if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - self.readwrite_to_readwrite_spacing = drc["minwidth_metal2"] + self.readwrite_nmos_contact_extension + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] - else: - self.readwrite_to_readwrite_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] + # y-position of inverter nmos + self.inverter_nmos_ypos = self.port_ypos - # write to write transistor spacing - if(self.write_nmos_contact_extension > self.gate_contact_thres): - self.write_to_write_spacing = drc["minwidth_metal2"] + self.write_nmos_contact_extension + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] - else: - self.write_to_write_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] + # spacing between ports + self.bitline_offset = -self.active_width + 0.5*contact.m1m2.height + self.m2_space + self.m2_width + self.port_spacing = self.bitline_offset + self.m2_space - # read to read transistor spacing - if(self.read_nmos_contact_extension > self.gate_contact_thres): - self.read_to_read_spacing = 2*(drc["minwidth_metal2"] + self.read_nmos_contact_extension) + drc["minwidth_metal1"] + 2*contact.poly.width - else: - self.read_to_read_spacing = 2*drc["poly_to_active"] + drc["minwidth_metal1"] + 2*contact.poly.width - - # write to read transistor spacing (also acts as readwrite to read transistor spacing) - # calculation is dependent on whether the read transistor is adjacent to a write transistor or a readwrite transistor - if(self.num_w_ports > 0): - if(self.write_nmos_contact_extension > self.gate_contact_thres): - write_portion = drc["minwidth_metal2"] + self.write_nmos_contact_extension - else: - write_portion = drc["poly_to_active"] - else: - if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - write_portion = drc["minwidth_metal2"] + self.readwrite_nmos_contact_extension - else: - write_portion = drc["poly_to_active"] - - if(self.read_nmos_contact_extension > self.gate_contact_thres): - read_portion = drc["minwidth_metal2"] + self.read_nmos_contact_extension - else: - read_portion = drc["poly_to_active"] - - self.write_to_read_spacing = write_portion + read_portion + 2*contact.poly.width + drc["poly_to_polycontact"] - - # calculations for transistor tiling (transistor + spacing) - self.inverter_tile_width = self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing - self.readwrite_tile_width = self.readwrite_to_readwrite_spacing + self.readwrite_nmos.active_height - self.write_tile_width = self.write_to_write_spacing + self.write_nmos.active_height - self.read_tile_width = self.read_to_read_spacing + self.read_nmos.active_height - - # calculation for row line tiling - self.rail_tile_height = drc["active_to_body_active"] + contact.well.width - if self.inverter_pmos_contact_extension > 0: - self.vdd_tile_height = self.inverter_pmos_contact_extension + drc["minwidth_metal1"] + contact.well.width - else: - self.vdd_tile_height = self.rail_tile_height - self.rowline_tile_height = drc["minwidth_metal1"] + contact.m1m2.width + # spacing between cross coupled inverters + self.inverter_to_inverter_spacing = contact.poly.height + self.m1_space # calculations related to inverter connections - self.inverter_gap = drc["poly_to_active"] + drc["poly_to_polycontact"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.inverter_pmos_contact_extension - self.cross_couple_lower_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + 0.5*contact.poly.width - self.cross_couple_upper_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + drc["poly_to_polycontact"] + 1.5*contact.poly.width + inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) + inverter_nmos_contact_extension = 0.5*(self.inverter_nmos.active_contact.height - self.inverter_nmos.active_height) + self.inverter_gap = max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + + self.poly_to_polycontact + 2*contact.poly.width \ + + self.m1_space + inverter_pmos_contact_extension + self.cross_couple_lower_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ + + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + + 0.5*contact.poly.width + self.cross_couple_upper_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ + + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + + self.poly_to_polycontact \ + + 1.5*contact.poly.width + # spacing between wordlines (and gnd) + self.rowline_spacing = self.m1_space + contact.m1m2.width + + # spacing for vdd + vdd_offset_well_constraint = self.well_enclose_active + 0.5*contact.well.width + vdd_offset_metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space + 0.5*contact.well.width + self.vdd_offset = max(vdd_offset_well_constraint, vdd_offset_metal1_constraint) + + # read port dimensions + width_reduction = self.read_nmos.active_width - self.read_nmos.get_pin("D").cx() + self.read_port_width = 2*self.read_nmos.active_width - 2*width_reduction def calculate_postions(self): """ Calculate positions that describe the edges and dimensions of the cell """ - # create flags for excluding readwrite, write, or read port calculations if they are not included in the bitcell - if(self.num_rw_ports > 0): - self.readwrite_port_flag = True - else: - self.readwrite_port_flag = False + self.botmost_ypos = -0.5*self.m1_width - self.total_ports*self.rowline_spacing + self.topmost_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset - if(self.num_w_ports > 0): - self.write_port_flag = True - else: - self.write_port_flag = False - - if(self.num_r_ports > 0): - self.read_port_flag = True - else: - self.read_port_flag = False - - # determine the distance of the leftmost/rightmost transistor gate connection - if (self.num_r_ports > 0): - if(self.read_nmos_contact_extension > self.gate_contact_thres): - end_connection = drc["minwidth_metal2"] + self.read_nmos_contact_extension + contact.m1m2.height - else: - end_connection = drc["poly_to_active"] + contact.m1m2.height - else: - if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - end_connection = drc["minwidth_metal2"] + self.readwrite_nmos_contact_extension + contact.m1m2.height - else: - end_connection = drc["poly_to_active"] + contact.m1m2.height - - # leftmost position = storage width + read/write ports width + write ports width + read ports width + end transistor gate connections + metal spacing necessary for tiling the bitcell - self.leftmost_xpos = -self.inverter_tile_width \ - - self.inverter_to_write_spacing \ - - self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_rw_ports-1)*self.readwrite_tile_width) \ - - self.write_port_flag*self.readwrite_port_flag*self.write_to_write_spacing \ - - self.write_port_flag*(self.write_nmos.active_height + (self.num_w_ports-1)*self.write_tile_width) \ - - self.read_port_flag*self.write_to_read_spacing \ - - self.read_port_flag*(self.read_nmos.active_height + (self.num_r_ports-1)*self.read_tile_width) \ - - end_connection \ - - 0.5*drc["poly_to_polycontact"] - - self.rightmost_xpos = -self.leftmost_xpos + self.leftmost_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width \ + - self.num_rw_ports*(self.readwrite_nmos.active_width + self.port_spacing) \ + - self.num_w_ports*(self.write_nmos.active_width + self.port_spacing) \ + - self.num_r_ports*(self.read_port_width + self.port_spacing) \ + - self.bitline_offset - 0.5*self.m2_width - # bottommost position = gnd height + rwwl height + wwl height + rwl height + space needed between tiled bitcells - array_tiling_offset = 0.5*drc["minwidth_metal2"] - self.botmost_ypos = -self.rail_tile_height \ - - self.num_rw_ports*self.rowline_tile_height \ - - self.num_w_ports*self.rowline_tile_height \ - - self.num_r_ports*self.rowline_tile_height \ - - array_tiling_offset - - # topmost position = height of the inverter + height of vdd - self.topmost_ypos = self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height \ - + self.vdd_tile_height - - # calculations for the cell dimensions - array_vdd_overlap = 0.5*contact.well.width self.width = -2*self.leftmost_xpos - self.height = self.topmost_ypos - self.botmost_ypos - array_vdd_overlap - + self.height = self.topmost_ypos - self.botmost_ypos + + self.y_center = 0.5*(self.topmost_ypos + self.botmost_ypos) def create_storage(self): """ @@ -329,21 +257,20 @@ class pbitcell(design.design): # create active for nmos self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", mod=self.inverter_nmos) - self.connect_inst([self.Q_bar, "Q", "gnd", "gnd"]) + self.connect_inst(["Q", self.Q_bar, "gnd", "gnd"]) self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right", mod=self.inverter_nmos) - self.connect_inst(["gnd", self.Q_bar, "Q", "gnd"]) + self.connect_inst(["gnd", "Q", self.Q_bar, "gnd"]) # create active for pmos self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left", mod=self.inverter_pmos) - self.connect_inst([self.Q_bar, "Q", "vdd", "vdd"]) + self.connect_inst(["Q", self.Q_bar, "vdd", "vdd"]) self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right", mod=self.inverter_pmos) - self.connect_inst(["vdd", self.Q_bar, "Q", "vdd"]) - + self.connect_inst(["vdd", "Q", self.Q_bar, "vdd"]) def place_storage(self): """ @@ -353,16 +280,18 @@ class pbitcell(design.design): # calculate transistor offsets left_inverter_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width right_inverter_xpos = 0.5*self.inverter_to_inverter_spacing - inverter_pmos_ypos = self.inverter_nmos.active_height + self.inverter_gap + inverter_pmos_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap # create active for nmos - self.inverter_nmos_left.place([left_inverter_xpos,0]) - self.inverter_nmos_right.place([right_inverter_xpos,0]) + self.inverter_nmos_left.place([left_inverter_xpos, self.inverter_nmos_ypos]) + self.inverter_nmos_right.place([right_inverter_xpos, self.inverter_nmos_ypos]) # create active for pmos self.inverter_pmos_left.place([left_inverter_xpos, inverter_pmos_ypos]) self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos]) - + + self.left_building_edge = left_inverter_xpos + self.right_building_edge = right_inverter_xpos + self.inverter_nmos.active_width def route_storage(self): """ @@ -394,47 +323,26 @@ class pbitcell(design.design): gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x, contact_offset_right.y) self.add_path("poly", [contact_offset_right, gate_offset_left]) - # update furthest left and right transistor edges (this will propagate to further transistor offset calculations) - self.left_building_edge = -self.inverter_tile_width - self.right_building_edge = self.inverter_tile_width - - def route_rails(self): """ Adds gnd and vdd rails and connects them to the inverters """ # Add rails for vdd and gnd - self.gnd_position = vector(self.leftmost_xpos, -self.rail_tile_height) - self.gnd = self.add_layout_pin(text="gnd", - layer="metal1", - offset=self.gnd_position, - width=self.width, - height=contact.well.second_layer_width) + gnd_ypos = -0.5*self.m1_width - self.total_ports*self.rowline_spacing + self.gnd_position = vector(0, gnd_ypos) + self.gnd = self.add_layout_pin_rect_center(text="gnd", + layer="metal1", + offset=self.gnd_position, + width=self.width, + height=contact.well.second_layer_width) - vdd_ypos = self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height \ - + self.vdd_tile_height - contact.well.second_layer_width - self.vdd_position = vector(self.leftmost_xpos, vdd_ypos) - self.vdd = self.add_layout_pin(text="vdd", - layer="metal1", - offset=self.vdd_position, - width=self.width, - height=contact.well.second_layer_width) - - # Connect inverters to rails - # connect inverter nmos to gnd - gnd_pos_left = vector(self.inverter_nmos_left.get_pin("S").bc().x, self.gnd_position.y) - self.add_path("metal1", [self.inverter_nmos_left.get_pin("S").bc(), gnd_pos_left]) - - gnd_pos_right = vector(self.inverter_nmos_right.get_pin("D").bc().x, self.gnd_position.y) - self.add_path("metal1", [self.inverter_nmos_right.get_pin("D").bc(), gnd_pos_right]) - - # connect inverter pmos to vdd - vdd_pos_left = vector(self.inverter_nmos_left.get_pin("S").uc().x, self.vdd_position.y) - self.add_path("metal1", [self.inverter_pmos_left.get_pin("S").uc(), vdd_pos_left]) - - vdd_pos_right = vector(self.inverter_nmos_right.get_pin("D").uc().x, self.vdd_position.y) - self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) - + vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset + self.vdd_position = vector(0, vdd_ypos) + self.vdd = self.add_layout_pin_rect_center(text="vdd", + layer="metal1", + offset=self.vdd_position, + width=self.width, + height=contact.well.second_layer_width) def create_readwrite_ports(self): """ @@ -455,12 +363,11 @@ class pbitcell(design.design): # add read/write transistors self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), mod=self.readwrite_nmos) - self.connect_inst(["Q", self.rw_wl_names[k], self.rw_bl_names[k], "gnd"]) + self.connect_inst([self.rw_bl_names[k], self.rw_wl_names[k], "Q", "gnd"]) self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k), mod=self.readwrite_nmos) self.connect_inst([self.Q_bar, self.rw_wl_names[k], self.rw_br_names[k], "gnd"]) - def place_readwrite_ports(self): """ @@ -470,172 +377,57 @@ class pbitcell(design.design): # Define variables relevant to write transistors self.rwwl_positions = [None] * self.num_rw_ports self.rwbl_positions = [None] * self.num_rw_ports - self.rwbl_bar_positions = [None] * self.num_rw_ports - - # define offset correction due to rotation of the ptx module - readwrite_rotation_correct = self.readwrite_nmos.active_height + self.rwbr_positions = [None] * self.num_rw_ports # iterate over the number of read/write ports for k in range(0,self.num_rw_ports): # Add transistors # calculate read/write transistor offsets left_readwrite_transistor_xpos = self.left_building_edge \ - - self.inverter_to_write_spacing \ - - self.readwrite_nmos.active_height - k*self.readwrite_tile_width \ - + readwrite_rotation_correct + - (k+1)*self.port_spacing \ + - (k+1)*self.readwrite_nmos.active_width right_readwrite_transistor_xpos = self.right_building_edge \ - + self.inverter_to_write_spacing \ - + k*self.readwrite_tile_width \ - + readwrite_rotation_correct + + (k+1)*self.port_spacing \ + + k*self.readwrite_nmos.active_width # add read/write transistors - self.readwrite_nmos_left[k].place(offset=[left_readwrite_transistor_xpos,0], - rotate=90) + self.readwrite_nmos_left[k].place(offset=[left_readwrite_transistor_xpos, self.port_ypos]) - self.readwrite_nmos_right[k].place(offset=[right_readwrite_transistor_xpos,0], - rotate=90) + self.readwrite_nmos_right[k].place(offset=[right_readwrite_transistor_xpos, self.port_ypos]) # Add RWWL lines # calculate RWWL position - rwwl_ypos = self.gnd_position.y - (k+1)*self.rowline_tile_height - self.rwwl_positions[k] = vector(self.leftmost_xpos, rwwl_ypos) + rwwl_ypos = -0.5*self.m1_width - k*self.rowline_spacing + self.rwwl_positions[k] = vector(0, rwwl_ypos) # add pin for RWWL - self.add_layout_pin(text=self.rw_wl_names[k], - layer="metal1", - offset=self.rwwl_positions[k], - width=self.width, - height=contact.m1m2.width) + self.add_layout_pin_rect_center(text=self.rw_wl_names[k], + layer="metal1", + offset=self.rwwl_positions[k], + width=self.width, + height=self.m1_width) # add pins for RWBL and RWBL_bar, overlaid on source contacts - self.rwbl_positions[k] = vector(self.readwrite_nmos_left[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.rw_bl_names[k], - layer="metal2", - offset=self.rwbl_positions[k], - width=drc["minwidth_metal2"], - height=self.height) + rwbl_xpos = left_readwrite_transistor_xpos - self.bitline_offset + self.m2_width + self.rwbl_positions[k] = vector(rwbl_xpos, self.y_center) + self.add_layout_pin_rect_center(text=self.rw_bl_names[k], + layer="metal2", + offset=self.rwbl_positions[k], + width=drc["minwidth_metal2"], + height=self.height) - self.rwbl_bar_positions[k] = vector(self.readwrite_nmos_right[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.rw_br_names[k], - layer="metal2", - offset=self.rwbl_bar_positions[k], - width=drc["minwidth_metal2"], - height=self.height) + rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width + self.bitline_offset - self.m2_width + self.rwbr_positions[k] = vector(rwbr_xpos, self.y_center) + self.add_layout_pin_rect_center(text=self.rw_br_names[k], + layer="metal2", + offset=self.rwbr_positions[k], + width=drc["minwidth_metal2"], + height=self.height) # update furthest left and right transistor edges - self.left_building_edge = left_readwrite_transistor_xpos - self.readwrite_nmos.active_height - self.right_building_edge = right_readwrite_transistor_xpos - - def route_readwrite_wordlines(self): - """ - Routes read/write trnasistors to their respective wordlines - """ - for k in range(0,self.num_rw_ports): - # Gate/RWWL connections - # add poly-to-meltal2 contacts to connect gate of read/write transistors to RWWL (contact next to gate) - # contact must be placed a metal1 width below the source pin to avoid drc from source pin routings - if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.readwrite_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width - else: - contact_xpos = self.readwrite_nmos_left[k].offset.x - self.readwrite_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width - contact_ypos = self.readwrite_nmos_left[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height - left_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_gate_contact) - - if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.readwrite_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width - else: - contact_xpos = self.readwrite_nmos_right[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width - contact_ypos = self.readwrite_nmos_right[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height - right_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_gate_contact) - - # connect gate of read/write transistor to contact (poly path) - midL = vector(left_gate_contact.x, self.readwrite_nmos_left[k].get_pin("G").lc().y) - self.add_path("poly", [self.readwrite_nmos_left[k].get_pin("G").lc(), midL, left_gate_contact], width=contact.poly.width) - - midR = vector(right_gate_contact.x, self.readwrite_nmos_right[k].get_pin("G").rc().y) - self.add_path("poly", [self.readwrite_nmos_right[k].get_pin("G").rc(), midR, right_gate_contact], width=contact.poly.width) - - # add metal1-to-metal2 contacts to RWWL lines - left_rwwl_contact = vector(left_gate_contact.x, self.rwwl_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_rwwl_contact, - rotate=90) - - right_rwwl_contact = vector(right_gate_contact.x, self.rwwl_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_rwwl_contact, - rotate=90) - - # connect read/write transistor gate contacts to RWWL contacts (metal2 path) - self.add_path("metal2", [left_gate_contact, left_rwwl_contact]) - self.add_path("metal2", [right_gate_contact, right_rwwl_contact]) - - - def route_readwrite_bitlines(self): - """ - Routes read/write transistors to their respective bitlines - """ - for k in range(0,self.num_rw_ports): - # Source/RWBL/RWBL_bar connections - # add metal1-to-metal2 contacts on top of read/write transistor source pins for connection to WBL and WBL_bar - offset_left = self.readwrite_nmos_left[k].get_pin("S").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_left, - rotate=90) - - offset_right = self.readwrite_nmos_right[k].get_pin("S").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_right, - rotate=90) - - - def route_readwrite_access(self): - """ - Routes read/write transistors to the storage component of the bitcell - """ - last_inst = self.num_rw_ports - 1 - - # Drain/Storage connections - # this path only needs to be drawn once on the last iteration of the loop - # add contacts to connect gate of inverters to drain of read/write transistors - left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_storage_contact, - rotate=90) - - right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_storage_contact, - rotate=90) - - # connect gate of inverters to contacts (poly path) - inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_lower_ypos) - self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) - - inverter_gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_lower_ypos) - self.add_path("poly", [right_storage_contact, inverter_gate_offset_right]) - - # connect contacts to drains of read/write transistors (metal1 path) - midL0 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], left_storage_contact.y) - midL1 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.readwrite_nmos_left[last_inst].get_pin("D").lc().y) - self.add_path("metal1", [left_storage_contact, midL0], width=contact.poly.second_layer_width) # width needed to avoid drc error - self.add_path("metal1", [midL0+vector(0,0.5*contact.poly.second_layer_width), midL1, self.readwrite_nmos_left[last_inst].get_pin("D").lc()]) - - midR0 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], right_storage_contact.y) - midR1 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.readwrite_nmos_right[last_inst].get_pin("D").rc().y) - self.add_path("metal1", [right_storage_contact, midR0], width=contact.poly.second_layer_width) - self.add_path("metal1", [midR0+vector(0,0.5*contact.poly.second_layer_width), midR1, self.readwrite_nmos_right[last_inst].get_pin("D").rc()]) + self.left_building_edge = left_readwrite_transistor_xpos + self.right_building_edge = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width def create_write_ports(self): """ @@ -659,12 +451,11 @@ class pbitcell(design.design): # add write transistors self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k), mod=self.write_nmos) - self.connect_inst(["Q", self.w_wl_names[k], self.w_bl_names[k], "gnd"]) + self.connect_inst([self.w_bl_names[k], self.w_wl_names[k], "Q", "gnd"]) self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k), mod=self.write_nmos) self.connect_inst([self.Q_bar, self.w_wl_names[k], self.w_br_names[k], "gnd"]) - def place_write_ports(self): """ @@ -673,7 +464,7 @@ class pbitcell(design.design): # Define variables relevant to write transistors self.wwl_positions = [None] * self.num_w_ports self.wbl_positions = [None] * self.num_w_ports - self.wbl_bar_positions = [None] * self.num_w_ports + self.wbr_positions = [None] * self.num_w_ports # define offset correction due to rotation of the ptx module write_rotation_correct = self.write_nmos.active_height @@ -683,165 +474,50 @@ class pbitcell(design.design): # Add transistors # calculate write transistor offsets left_write_transistor_xpos = self.left_building_edge \ - - (not self.readwrite_port_flag)*self.inverter_to_write_spacing \ - - (self.readwrite_port_flag)*self.readwrite_to_readwrite_spacing \ - - self.write_nmos.active_height - k*self.write_tile_width \ - + write_rotation_correct + - (k+1)*self.port_spacing \ + - (k+1)*self.write_nmos.active_width right_write_transistor_xpos = self.right_building_edge \ - + (not self.readwrite_port_flag)*self.inverter_to_write_spacing \ - + (self.readwrite_port_flag)*self.readwrite_to_readwrite_spacing \ - + k*self.write_tile_width \ - + write_rotation_correct + + (k+1)*self.port_spacing \ + + k*self.write_nmos.active_width # add write transistors - self.write_nmos_left[k].place(offset=[left_write_transistor_xpos,0], - rotate=90) + self.write_nmos_left[k].place(offset=[left_write_transistor_xpos, self.port_ypos]) - self.write_nmos_right[k].place(offset=[right_write_transistor_xpos,0], - rotate=90) + self.write_nmos_right[k].place(offset=[right_write_transistor_xpos, self.port_ypos]) # Add WWL lines # calculate WWL position - wwl_ypos = self.gnd_position.y \ - - self.num_rw_ports*self.rowline_tile_height \ - - (k+1)*self.rowline_tile_height - self.wwl_positions[k] = vector(self.leftmost_xpos, wwl_ypos) + wwl_ypos = rwwl_ypos = -0.5*self.m1_width - self.num_rw_ports*self.rowline_spacing - k*self.rowline_spacing + self.wwl_positions[k] = vector(0, wwl_ypos) # add pin for WWL - self.add_layout_pin(text=self.w_wl_names[k], - layer="metal1", - offset=self.wwl_positions[k], - width=self.width, - height=contact.m1m2.width) + self.add_layout_pin_rect_center(text=self.w_wl_names[k], + layer="metal1", + offset=self.wwl_positions[k], + width=self.width, + height=self.m1_width) # add pins for WBL and WBL_bar, overlaid on source contacts - self.wbl_positions[k] = vector(self.write_nmos_left[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.w_bl_names[k], - layer="metal2", - offset=self.wbl_positions[k], - width=drc["minwidth_metal2"], - height=self.height) + wbl_xpos = left_write_transistor_xpos - self.bitline_offset + self.m2_width + self.wbl_positions[k] = vector(wbl_xpos, self.y_center) + self.add_layout_pin_rect_center(text=self.w_bl_names[k], + layer="metal2", + offset=self.wbl_positions[k], + width=drc["minwidth_metal2"], + height=self.height) - self.wbl_bar_positions[k] = vector(self.write_nmos_right[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.w_br_names[k], - layer="metal2", - offset=self.wbl_bar_positions[k], - width=drc["minwidth_metal2"], - height=self.height) + wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset - self.m2_width + self.wbr_positions[k] = vector(wbr_xpos, self.y_center) + self.add_layout_pin_rect_center(text=self.w_br_names[k], + layer="metal2", + offset=self.wbr_positions[k], + width=drc["minwidth_metal2"], + height=self.height) # update furthest left and right transistor edges - self.left_building_edge = left_write_transistor_xpos - self.write_nmos.active_height - self.right_building_edge = right_write_transistor_xpos - - def route_write_wordlines(self): - """ - Routes write transistors to their respective wordlines - """ - for k in range(0,self.num_w_ports): - # Gate/WWL connections - # add poly-to-meltal2 contacts to connect gate of write transistors to WWL (contact next to gate) - # contact must be placed a metal width below the source pin to avoid drc from source pin routings - if(self.write_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.write_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width - else: - contact_xpos = self.write_nmos_left[k].offset.x - self.write_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width - contact_ypos = self.write_nmos_left[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height - left_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_gate_contact) - - if(self.write_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.write_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width - else: - contact_xpos = self.write_nmos_right[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width - contact_ypos = self.write_nmos_right[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height - right_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_gate_contact) - - # connect gate of write transistor to contact (poly path) - midL = vector(left_gate_contact.x, self.write_nmos_left[k].get_pin("G").lc().y) - self.add_path("poly", [self.write_nmos_left[k].get_pin("G").lc(), midL, left_gate_contact], width=contact.poly.width) - - midR = vector(right_gate_contact.x, self.write_nmos_right[k].get_pin("G").rc().y) - self.add_path("poly", [self.write_nmos_right[k].get_pin("G").rc(), midR, right_gate_contact], width=contact.poly.width) - - # add metal1-to-metal2 contacts to WWL lines - left_wwl_contact = vector(left_gate_contact.x, self.wwl_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_wwl_contact, - rotate=90) - - right_wwl_contact = vector(right_gate_contact.x, self.wwl_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_wwl_contact, - rotate=90) - - # connect write transistor gate contacts to WWL contacts (metal2 path) - self.add_path("metal2", [left_gate_contact, left_wwl_contact]) - self.add_path("metal2", [right_gate_contact, right_wwl_contact]) - - def route_write_bitlines(self): - """ - Routes write transistors to their respective bitlines - """ - for k in range(0,self.num_w_ports): - # Source/WBL/WBL_bar connections - # add metal1-to-metal2 contacts on top of write transistor source pins for connection to WBL and WBL_bar - offset_left = self.write_nmos_left[k].get_pin("S").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_left, - rotate=90) - - offset_right = self.write_nmos_right[k].get_pin("S").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_right, - rotate=90) - - def route_write_access(self): - """ - Routes write transistors to the storage component of the bitcell - """ - last_inst = self.num_w_ports - 1 - - # Drain/Storage connections - # this path only needs to be drawn once on the last iteration of the loop - # add contacts to connect gate of inverters to drain of write transistors - left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_storage_contact, - rotate=90) - - right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_storage_contact, - rotate=90) - - # connect gate of inverters to contacts (poly path) - inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_lower_ypos) - self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) - - inverter_gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_lower_ypos) - self.add_path("poly", [right_storage_contact, inverter_gate_offset_right]) - - # connect contacts to drains of write transistors (metal1 path) - midL0 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], left_storage_contact.y) - midL1 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.write_nmos_left[last_inst].get_pin("D").lc().y) - self.add_path("metal1", [left_storage_contact, midL0], width=contact.poly.second_layer_width) # width needed to avoid drc error - self.add_path("metal1", [midL0+vector(0,0.5*contact.poly.second_layer_width), midL1, self.write_nmos_left[last_inst].get_pin("D").lc()]) - - midR0 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], right_storage_contact.y) - midR1 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.write_nmos_right[last_inst].get_pin("D").rc().y) - self.add_path("metal1", [right_storage_contact, midR0], width=contact.poly.second_layer_width) - self.add_path("metal1", [midR0+vector(0,0.5*contact.poly.second_layer_width), midR1, self.write_nmos_right[last_inst].get_pin("D").rc()]) - + self.left_building_edge = left_write_transistor_xpos + self.right_building_edge = right_write_transistor_xpos + self.write_nmos.active_width def create_read_ports(self): """ @@ -870,7 +546,7 @@ class pbitcell(design.design): self.read_access_nmos_right[k] = self.add_inst(name="read_access_nmos_right{}".format(k), mod=self.read_nmos) - self.connect_inst(["RA_to_R_right{}".format(k), "Q", "gnd", "gnd"]) + self.connect_inst(["gnd", "Q", "RA_to_R_right{}".format(k), "gnd"]) # add read transistors self.read_nmos_left[k] = self.add_inst(name="read_nmos_left{}".format(k), @@ -879,7 +555,7 @@ class pbitcell(design.design): self.read_nmos_right[k] = self.add_inst(name="read_nmos_right{}".format(k), mod=self.read_nmos) - self.connect_inst([self.r_br_names[k], self.r_wl_names[k], "RA_to_R_right{}".format(k), "gnd"]) + self.connect_inst(["RA_to_R_right{}".format(k), self.r_wl_names[k], self.r_br_names[k], "gnd"]) def place_read_ports(self): """ @@ -888,199 +564,262 @@ class pbitcell(design.design): # Define variables relevant to read transistors self.rwl_positions = [None] * self.num_r_ports self.rbl_positions = [None] * self.num_r_ports - self.rbl_bar_positions = [None] * self.num_r_ports + self.rbr_positions = [None] * self.num_r_ports # define offset correction due to rotation of the ptx module read_rotation_correct = self.read_nmos.active_height # calculate offset to overlap the drain of the read-access transistor with the source of the read transistor - overlap_offset = self.read_nmos.get_pin("D").ll() - self.read_nmos.get_pin("S").ll() + overlap_offset = self.read_nmos.get_pin("D").cx() - self.read_nmos.get_pin("S").cx() # iterate over the number of read ports for k in range(0,self.num_r_ports): # Add transistors # calculate transistor offsets left_read_transistor_xpos = self.left_building_edge \ - - self.write_to_read_spacing \ - - self.read_nmos.active_height - k*self.read_tile_width \ - + read_rotation_correct - + - (k+1)*self.port_spacing \ + - (k+1)*self.read_port_width + right_read_transistor_xpos = self.right_building_edge \ - + self.write_to_read_spacing \ - + k*self.read_tile_width \ - + read_rotation_correct + + (k+1)*self.port_spacing \ + + k*self.read_port_width # add read-access transistors - self.read_access_nmos_left[k].place(offset=[left_read_transistor_xpos,0], - rotate=90) + self.read_access_nmos_left[k].place(offset=[left_read_transistor_xpos+overlap_offset, self.port_ypos]) - self.read_access_nmos_right[k].place(offset=[right_read_transistor_xpos,0], - rotate=90) + self.read_access_nmos_right[k].place(offset=[right_read_transistor_xpos, self.port_ypos]) # add read transistors - self.read_nmos_left[k].place(offset=[left_read_transistor_xpos,overlap_offset.x], - rotate=90) + self.read_nmos_left[k].place(offset=[left_read_transistor_xpos, self.port_ypos]) - self.read_nmos_right[k].place(offset=[right_read_transistor_xpos,overlap_offset.x], - rotate=90) + self.read_nmos_right[k].place(offset=[right_read_transistor_xpos+overlap_offset, self.port_ypos]) # Add RWL lines # calculate RWL position - rwl_ypos = self.gnd_position.y \ - - self.num_rw_ports*self.rowline_tile_height \ - - self.num_w_ports*self.rowline_tile_height \ - - (k+1)*self.rowline_tile_height - self.rwl_positions[k] = vector(self.leftmost_xpos, rwl_ypos) + rwl_ypos = rwwl_ypos = -0.5*self.m1_width - self.num_rw_ports*self.rowline_spacing - self.num_w_ports*self.rowline_spacing - k*self.rowline_spacing + self.rwl_positions[k] = vector(0, rwl_ypos) # add pin for RWL - self.add_layout_pin(text=self.r_wl_names[k], - layer="metal1", - offset=self.rwl_positions[k], - width=self.width, - height=contact.m1m2.width) + self.add_layout_pin_rect_center(text=self.r_wl_names[k], + layer="metal1", + offset=self.rwl_positions[k], + width=self.width, + height=self.m1_width) # add pins for RBL and RBL_bar, overlaid on drain contacts - self.rbl_positions[k] = vector(self.read_nmos_left[k].get_pin("D").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.r_bl_names[k], - layer="metal2", - offset=self.rbl_positions[k], - width=drc["minwidth_metal2"], - height=self.height) + rbl_xpos = left_read_transistor_xpos - self.bitline_offset + self.m2_width + self.rbl_positions[k] = vector(rbl_xpos, self.y_center) + self.add_layout_pin_rect_center(text=self.r_bl_names[k], + layer="metal2", + offset=self.rbl_positions[k], + width=drc["minwidth_metal2"], + height=self.height) - self.rbl_bar_positions[k] = vector(self.read_nmos_right[k].get_pin("D").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.r_br_names[k], - layer="metal2", - offset=self.rbl_bar_positions[k], - width=drc["minwidth_metal2"], - height=self.height) + rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset - self.m2_width + self.rbr_positions[k] = vector(rbr_xpos, self.y_center) + self.add_layout_pin_rect_center(text=self.r_br_names[k], + layer="metal2", + offset=self.rbr_positions[k], + width=drc["minwidth_metal2"], + height=self.height) + + def route_wordlines(self): + """ + Routes gate of transistors to their respective wordlines + """ + port_transistors = [] + for k in range(self.num_rw_ports): + port_transistors.append(self.readwrite_nmos_left[k]) + port_transistors.append(self.readwrite_nmos_right[k]) + for k in range(self.num_w_ports): + port_transistors.append(self.write_nmos_left[k]) + port_transistors.append(self.write_nmos_right[k]) + for k in range(self.num_r_ports): + port_transistors.append(self.read_nmos_left[k]) + port_transistors.append(self.read_nmos_right[k]) + + wl_positions = [] + for k in range(self.num_rw_ports): + wl_positions.append(self.rwwl_positions[k]) + wl_positions.append(self.rwwl_positions[k]) + for k in range(self.num_w_ports): + wl_positions.append(self.wwl_positions[k]) + wl_positions.append(self.wwl_positions[k]) + for k in range(self.num_r_ports): + wl_positions.append(self.rwl_positions[k]) + wl_positions.append(self.rwl_positions[k]) - def route_read_wordlines(self): - """ - Routes read transistors to their respective worlines - """ - for k in range(0,self.num_r_ports): - # Gate of read transistor / RWL connection - # add poly-to-meltal2 contacts to connect gate of read transistors to RWL (contact next to gate) - if(self.read_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.read_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width + + for k in range(2*self.total_ports): + gate_offset = port_transistors[k].get_pin("G").bc() + port_contact_offset = gate_offset + vector(0, -self.gate_contact_yoffset + self.poly_extend_active) + wl_contact_offset = vector(gate_offset.x, wl_positions[k].y) + + if (k == 0) or (k == 1): + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=port_contact_offset) + + self.add_path("poly", [gate_offset, port_contact_offset]) + self.add_path("metal1", [port_contact_offset, wl_contact_offset]) + else: - contact_xpos = self.read_nmos_left[k].offset.x - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width - contact_ypos = self.read_nmos_left[k].get_pin("G").lc().y - left_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_gate_contact) - - if(self.read_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.read_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width - else: - contact_xpos = self.read_nmos_right[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width - contact_ypos = self.read_nmos_right[k].get_pin("G").rc().y - right_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_gate_contact) - - # connect gate of read transistor to contact (poly path) - self.add_path("poly", [self.read_nmos_left[k].get_pin("G").lc(), left_gate_contact]) - self.add_path("poly", [self.read_nmos_right[k].get_pin("G").rc(), right_gate_contact]) - - # add metal1-to-metal2 contacts to RWL lines - left_rwl_contact = vector(left_gate_contact.x, self.rwl_positions[k].y + 0.5*contact.poly.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_rwl_contact, - rotate=90) - - right_rwl_contact = vector(right_gate_contact.x, self.rwl_positions[k].y + 0.5*contact.poly.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_rwl_contact, - rotate=90) - - # connect read transistor gate contacts to RWL contacts (metal2 path) - self.add_path("metal2", [left_gate_contact, left_rwl_contact]) - self.add_path("metal2", [right_gate_contact, right_rwl_contact]) - - # Source of read-access transistor / GND connection - # connect source of read-access transistor to GND (metal1 path) - gnd_offset_left = vector(self.read_access_nmos_left[k].get_pin("S").bc().x, self.gnd_position.y) - self.add_path("metal1", [self.read_access_nmos_left[k].get_pin("S").bc(), gnd_offset_left]) - - gnd_offset_right = vector(self.read_access_nmos_right[k].get_pin("S").bc().x, self.gnd_position.y) - self.add_path("metal1", [self.read_access_nmos_right[k].get_pin("S").bc(), gnd_offset_right]) - - def route_read_bitlines(self): + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=port_contact_offset) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=port_contact_offset) + + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=wl_contact_offset, + rotate=90) + + self.add_path("poly", [gate_offset, port_contact_offset]) + self.add_path("metal2", [port_contact_offset, wl_contact_offset]) + + def route_bitlines(self): + """ + Routes read/write transistors to their respective bitlines """ - Routes read transistors to their respective bitlines - """ - for k in range(0,self.num_r_ports): - # Drain of read transistor / RBL & RBL_bar connection - # add metal1-to-metal2 contacts on top of read transistor drain pins for connection to RBL and RBL_bar - offset_left = self.read_nmos_left[k].get_pin("D").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_left, - rotate=90) + left_port_transistors = [] + right_port_transistors = [] + for k in range(self.num_rw_ports): + left_port_transistors.append(self.readwrite_nmos_left[k]) + right_port_transistors.append(self.readwrite_nmos_right[k]) + for k in range(self.num_w_ports): + left_port_transistors.append(self.write_nmos_left[k]) + right_port_transistors.append(self.write_nmos_right[k]) + for k in range(self.num_r_ports): + left_port_transistors.append(self.read_nmos_left[k]) + right_port_transistors.append(self.read_nmos_right[k]) + + bl_positions = [] + br_positions = [] + for k in range(self.num_rw_ports): + bl_positions.append(self.rwbl_positions[k]) + br_positions.append(self.rwbr_positions[k]) + for k in range(self.num_w_ports): + bl_positions.append(self.wbl_positions[k]) + br_positions.append(self.wbr_positions[k]) + for k in range(self.num_r_ports): + bl_positions.append(self.rbl_positions[k]) + br_positions.append(self.rbr_positions[k]) + + for k in range(self.total_ports): + port_contact_offest = left_port_transistors[k].get_pin("S").center() + bl_offset = vector(bl_positions[k].x, port_contact_offest.y) - offset_right = self.read_nmos_right[k].get_pin("D").center() self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_right, - rotate=90) + offset=port_contact_offest) + + self.add_path("metal2", [port_contact_offest, bl_offset], width=contact.m1m2.height) + + for k in range(self.total_ports): + port_contact_offest = right_port_transistors[k].get_pin("D").center() + br_offset = vector(br_positions[k].x, port_contact_offest.y) + + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=port_contact_offest) + + self.add_path("metal2", [port_contact_offest, br_offset], width=contact.m1m2.height) + + def route_supply(self): + # route inverter nmos and read-access transistors to gnd + nmos_contact_positions = [] + nmos_contact_positions.append(self.inverter_nmos_left.get_pin("S").center()) + nmos_contact_positions.append(self.inverter_nmos_right.get_pin("D").center()) + for k in range(self.num_r_ports): + nmos_contact_positions.append(self.read_access_nmos_left[k].get_pin("D").center()) + nmos_contact_positions.append(self.read_access_nmos_right[k].get_pin("S").center()) + + for position in nmos_contact_positions: + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=position) + + supply_offset = vector(position.x, self.gnd_position.y) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=supply_offset, + rotate=90) + + self.add_path("metal2", [position, supply_offset]) + + # route inverter pmos to vdd + vdd_pos_left = vector(self.inverter_nmos_left.get_pin("S").uc().x, self.vdd_position.y) + self.add_path("metal1", [self.inverter_pmos_left.get_pin("S").uc(), vdd_pos_left]) + + vdd_pos_right = vector(self.inverter_nmos_right.get_pin("D").uc().x, self.vdd_position.y) + self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) + + def route_readwrite_access(self): + """ + Routes read/write transistors to the storage component of the bitcell + """ + for k in range(self.num_rw_ports): + mid = vector(self.readwrite_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) + Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) + self.add_path("metal1", [self.readwrite_nmos_left[k].get_pin("D").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, Q_pos]) + + mid = vector(self.readwrite_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) + Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) + self.add_path("metal1", [self.readwrite_nmos_right[k].get_pin("S").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, Q_bar_pos]) + + def route_write_access(self): + """ + Routes read/write transistors to the storage component of the bitcell + """ + for k in range(self.num_w_ports): + mid = vector(self.write_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) + Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) + self.add_path("metal1", [self.write_nmos_left[k].get_pin("D").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, Q_pos]) + + mid = vector(self.write_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) + Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) + self.add_path("metal1", [self.write_nmos_right[k].get_pin("S").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, Q_bar_pos]) def route_read_access(self): """ Routes read access transistors to the storage component of the bitcell """ - for k in range(0,self.num_r_ports): - # Gate of read-access transistor / storage connection - # add poly-to-metal1 contacts to connect gate of read-access transistors to output of inverters (contact next to gate) - if(self.read_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.read_nmos_left[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width - else: - contact_xpos = self.read_nmos_left[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width - contact_ypos = self.read_access_nmos_left[k].get_pin("G").rc().y - left_gate_contact = vector(contact_xpos, contact_ypos) - + left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_upper_ypos) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=left_storage_contact, + rotate=90) + + right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_upper_ypos) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=right_storage_contact, + rotate=90) + + inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos) + self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) + + inverter_gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_upper_ypos) + self.add_path("poly", [right_storage_contact, inverter_gate_offset_right]) + + for k in range(self.num_r_ports): + port_contact_offset = self.read_access_nmos_left[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) + self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_gate_contact) - - if(self.read_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.read_nmos_right[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width - else: - contact_xpos = self.read_nmos_right[k].offset.x - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width - contact_ypos = self.read_access_nmos_right[k].get_pin("G").lc().y - right_gate_contact = vector(contact_xpos, contact_ypos) + offset=port_contact_offset) + + self.add_path("poly", [self.read_access_nmos_left[k].get_pin("G").uc(), port_contact_offset]) + + mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) + self.add_path("metal1", [port_contact_offset, mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, left_storage_contact]) + port_contact_offset = self.read_access_nmos_right[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) + self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_gate_contact) - - # connect gate of read-access transistor to contact (poly path) - self.add_path("poly", [self.read_access_nmos_left[k].get_pin("G").rc(), left_gate_contact]) - self.add_path("poly", [self.read_access_nmos_right[k].get_pin("G").lc(), right_gate_contact]) - - # save the positions of the first gate contacts for use in later iterations - if(k == 0): - left_gate_contact0 = left_gate_contact - right_gate_contact0 = right_gate_contact - - # connect contact to output of inverters (metal1 path) - # mid0: metal1 path must route over the read transistors (above drain of read transistor) - # mid1: continue metal1 path horizontally until at first read access gate contact - # mid2: route up or down to be level with inverter output - # endpoint at drain/source of inverter - midL0 = vector(left_gate_contact.x, self.read_nmos_left[k].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) - midL1 = vector(left_gate_contact0.x, self.read_nmos_left[0].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) - midL2 = vector(left_gate_contact0.x, self.cross_couple_upper_ypos) - left_inverter_offset = vector(self.inverter_nmos_left.get_pin("D").center().x, self.cross_couple_upper_ypos) - self.add_path("metal1", [left_gate_contact, midL0, midL1, midL2, left_inverter_offset]) - - midR0 = vector(right_gate_contact.x, self.read_nmos_right[k].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) - midR1 = vector(right_gate_contact0.x, self.read_nmos_right[k].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) - midR2 = vector(right_gate_contact0.x, self.cross_couple_upper_ypos) - right_inverter_offset = vector(self.inverter_nmos_right.get_pin("S").center().x, self.cross_couple_upper_ypos) - self.add_path("metal1", [right_gate_contact, midR0, midR1, midR2, right_inverter_offset]) + offset=port_contact_offset) + + self.add_path("poly", [self.read_access_nmos_right[k].get_pin("G").uc(), port_contact_offset]) + + mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) + self.add_path("metal1", [port_contact_offset, mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, right_storage_contact]) def extend_well(self): """ @@ -1088,73 +827,29 @@ class pbitcell(design.design): Since the pwell of the read ports rise higher than the nwell of the inverters, the well connections must be done piecewise to avoid pwell and nwell overlap. """ + + max_nmos_well_height = max(self.inverter_nmos.cell_well_height, + self.readwrite_nmos.cell_well_height, + self.write_nmos.cell_well_height, + self.read_nmos.cell_well_height) + + well_height = max_nmos_well_height + self.port_ypos - self.well_enclose_active - self.gnd_position.y # extend pwell to encompass entire nmos region of the cell up to the height of the inverter nmos well offset = vector(self.leftmost_xpos, self.botmost_ypos) - well_height = -self.botmost_ypos + self.inverter_nmos.cell_well_height - drc["well_enclosure_active"] self.add_rect(layer="pwell", offset=offset, width=self.width, height=well_height) - # extend pwell over read/write and write transistors to the - # height of the write transistor well (read/write and write - # transistors are the same height) - if(self.num_w_ports > 0): - # calculate the edge of the write transistor well closest to the center - left_write_well_xpos = self.write_nmos_left[0].offset.x + drc["well_enclosure_active"] - right_write_well_xpos = self.write_nmos_right[0].offset.x - self.write_nmos.active_height - drc["well_enclosure_active"] - else: - # calculate the edge of the read/write transistor well closest to the center - left_write_well_xpos = self.readwrite_nmos_left[0].offset.x + drc["well_enclosure_active"] - right_write_well_xpos = self.readwrite_nmos_right[0].offset.x - self.readwrite_nmos.active_height - drc["well_enclosure_active"] - - # calculate a width that will halt at the edge of the write transistors - write_well_width = -(self.leftmost_xpos - left_write_well_xpos) - write_well_height = self.write_nmos.cell_well_width - drc["well_enclosure_active"] - - offset = vector(left_write_well_xpos - write_well_width, 0) - self.add_rect(layer="pwell", - offset=offset, - width=write_well_width, - height=write_well_height) - - offset = vector(right_write_well_xpos, 0) - self.add_rect(layer="pwell", - offset=offset, - width=write_well_width, - height=write_well_height) - - # extend pwell over the read transistors to the height of the bitcell - if(self.num_r_ports > 0): - # calculate the edge of the read transistor well clostest to the center - left_read_well_xpos = self.read_nmos_left[0].offset.x + drc["well_enclosure_active"] - right_read_well_xpos = self.read_nmos_right[0].offset.x - self.read_nmos.active_height - drc["well_enclosure_active"] - - # calculate a width that will halt at the edge of the read transistors - read_well_width = -(self.leftmost_xpos - left_read_well_xpos) - read_well_height = self.topmost_ypos - - offset = vector(self.leftmost_xpos, 0) - self.add_rect(layer="pwell", - offset=offset, - width=read_well_width, - height=read_well_height) - - offset = vector(right_read_well_xpos, 0) - self.add_rect(layer="pwell", - offset=offset, - width=read_well_width, - height=read_well_height) - # extend nwell to encompass inverter_pmos # calculate offset of the left pmos well - inverter_well_xpos = -self.inverter_tile_width - drc["well_enclosure_active"] - inverter_well_ypos = self.inverter_nmos.active_height + self.inverter_gap - drc["well_enclosure_active"] + inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) - drc["well_enclosure_active"] + inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - drc["well_enclosure_active"] # calculate width of the two combined nwells # calculate height to encompass nimplant connected to vdd - well_width = 2*self.inverter_tile_width + 2*drc["well_enclosure_active"] + well_width = 2*(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) + 2*drc["well_enclosure_active"] well_height = self.vdd_position.y - inverter_well_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"] offset = [inverter_well_xpos,inverter_well_ypos] @@ -1166,7 +861,7 @@ class pbitcell(design.design): # add well contacts # connect pimplants to gnd - offset = vector(0, self.gnd_position.y + 0.5*contact.well.second_layer_width) + offset = vector(0, self.gnd_position.y) self.add_contact_center(layers=("active", "contact", "metal1"), offset=offset, rotate=90, @@ -1174,14 +869,13 @@ class pbitcell(design.design): well_type="p") # connect nimplants to vdd - offset = vector(0, self.vdd_position.y + 0.5*contact.well.second_layer_width) + offset = vector(0, self.vdd_position.y) self.add_contact_center(layers=("active", "contact", "metal1"), offset=offset, rotate=90, implant_type="n", well_type="n") - def list_bitcell_pins(self, col, row): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ bitcell_pins = [] @@ -1239,7 +933,8 @@ class pbitcell(design.design): def route_rbc_short(self): """ route the short from Q_bar to gnd necessary for the replica bitcell """ - Q_bar_pos = self.inverter_pmos_left.get_pin("D").uc() + Q_bar_pos = self.inverter_pmos_right.get_pin("S").uc() vdd_pos = vector(Q_bar_pos.x, self.vdd_position.y) self.add_path("metal1", [Q_bar_pos, vdd_pos]) + \ No newline at end of file diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 46cbc4d2..03d9961e 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -544,7 +544,7 @@ class lib: self.corner[1], self.corner[2], self.corner[0], - round_time(self.char_results["min_period"]), + round_time(self.char_sram_results["min_period"]), self.out_dir, lib_name)) diff --git a/compiler/example_config_scn4m_subm.py b/compiler/example_config_scn4m_subm.py index a0e6d490..9aa8e498 100644 --- a/compiler/example_config_scn4m_subm.py +++ b/compiler/example_config_scn4m_subm.py @@ -9,4 +9,3 @@ temperatures = [ 25 ] output_path = "temp" output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) - diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index fd4992c5..e6662617 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -95,7 +95,7 @@ class control_logic(design.design): # FIXME: These should be tuned according to the size! delay_stages = 4 # Must be non-inverting delay_fanout = 3 # This can be anything >=2 - bitcell_loads = int(math.ceil(self.num_rows / 5.0)) + bitcell_loads = int(math.ceil(self.num_rows / 2.0)) self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads, name="replica_bitline_"+self.port_type) self.add_mod(self.replica_bitline) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 3739c034..ea67eeb5 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -78,13 +78,14 @@ class precharge(pgate.pgate): self.add_path("metal1", [pmos_pin.uc(), vdd_pos]) # Add the M1->M2->M3 stack at the left edge + vdd_contact_pos = vector(0.5*self.width, vdd_position.y + 0.5*self.m1_width) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=vdd_pos.scale(0,1)) + offset=vdd_contact_pos) self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=vdd_pos.scale(0,1)) + offset=vdd_contact_pos) self.add_layout_pin_rect_center(text="vdd", layer="metal3", - offset=vdd_pos.scale(0,1)) + offset=vdd_contact_pos) def create_ptx(self): @@ -112,7 +113,7 @@ class precharge(pgate.pgate): # adds the lower pmos to layout #base = vector(self.width - 2*self.pmos.width + self.overlap_offset.x, 0) - self.lower_pmos_position = vector(self.bitcell.get_pin(self.bitcell_bl).lx(), + self.lower_pmos_position = vector(max(self.bitcell.get_pin(self.bitcell_bl).lx(), self.well_enclose_active), self.pmos.active_offset.y) self.lower_pmos_inst.place(self.lower_pmos_position) diff --git a/compiler/tests/22_ngspice_psram_func_test.py b/compiler/tests/22_ngspice_psram_func_test.py deleted file mode 100755 index 5d2af0b4..00000000 --- a/compiler/tests/22_ngspice_psram_func_test.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - -import unittest -from testutils import header,openram_test -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -from globals import OPTS -import debug - -@unittest.skip("SKIPPING 22_psram_func_test") -class psram_func_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - OPTS.spice_name="ngspice" - OPTS.analytical_delay = False - OPTS.netlist_only = True - OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell="replica_pbitcell" - - # This is a hack to reload the characterizer __init__ with the spice version - from importlib import reload - import characterizer - reload(characterizer) - from characterizer import functional - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - - from sram import sram - from sram_config import sram_config - c = sram_config(word_size=4, - num_words=32, - num_banks=1) - c.words_per_row=2 - - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 1 - OPTS.num_r_ports = 1 - - debug.info(1, "Functional test for 1bit, 16word SRAM, with 1 bank. Multiport with {}RW {}W {}R.".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)) - s = sram(c, name="sram1") - - tempspice = OPTS.openram_temp + "temp.sp" - s.sp_write(tempspice) - - corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - f = functional(s.s, tempspice, corner) - f.num_cycles = 5 - (fail,error) = f.run() - - self.assertTrue(fail,error) - - globals.end_openram() - -# instantiate a copdsay of the class to actually run the test -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() diff --git a/compiler/tests/22_ngspice_sram_func_test.py b/compiler/tests/22_ngspice_sram_func_test.py deleted file mode 100755 index a675b920..00000000 --- a/compiler/tests/22_ngspice_sram_func_test.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - -import unittest -from testutils import header,openram_test -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -from globals import OPTS -import debug - -@unittest.skip("SKIPPING 22_sram_func_test") -class sram_func_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - OPTS.spice_name="ngspice" - OPTS.analytical_delay = False - - # This is a hack to reload the characterizer __init__ with the spice version - from importlib import reload - import characterizer - reload(characterizer) - from characterizer import functional - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - - from sram import sram - from sram_config import sram_config - c = sram_config(word_size=4, - num_words=32, - num_banks=1) - c.words_per_row=2 - debug.info(1, "Functional test for 1bit, 16word SRAM, with 1 bank") - s = sram(c, name="sram1") - - tempspice = OPTS.openram_temp + "temp.sp" - s.sp_write(tempspice) - - corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - f = functional(s.s, tempspice, corner) - f.num_cycles = 10 - (fail, error) = f.run() - - self.assertTrue(fail,error) - - globals.end_openram() - -# instantiate a copdsay of the class to actually run the test -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() diff --git a/compiler/tests/22_hspice_psram_func_test.py b/compiler/tests/22_psram_func_test.py similarity index 96% rename from compiler/tests/22_hspice_psram_func_test.py rename to compiler/tests/22_psram_func_test.py index 19735b9a..ab59b8b8 100755 --- a/compiler/tests/22_hspice_psram_func_test.py +++ b/compiler/tests/22_psram_func_test.py @@ -16,7 +16,7 @@ class psram_func_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - OPTS.spice_name="hspice" + #OPTS.spice_name="hspice" OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.bitcell = "pbitcell" @@ -33,7 +33,7 @@ class psram_func_test(openram_test): from sram import sram from sram_config import sram_config c = sram_config(word_size=4, - num_words=32, + num_words=64, num_banks=1) c.words_per_row=2 diff --git a/compiler/tests/22_hspice_sram_func_test.py b/compiler/tests/22_sram_func_test.py similarity index 95% rename from compiler/tests/22_hspice_sram_func_test.py rename to compiler/tests/22_sram_func_test.py index ee33f62b..15382b1f 100755 --- a/compiler/tests/22_hspice_sram_func_test.py +++ b/compiler/tests/22_sram_func_test.py @@ -16,7 +16,7 @@ class sram_func_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - OPTS.spice_name="hspice" + #OPTS.spice_name="hspice" OPTS.analytical_delay = False # This is a hack to reload the characterizer __init__ with the spice version @@ -30,7 +30,7 @@ class sram_func_test(openram_test): from sram import sram from sram_config import sram_config c = sram_config(word_size=4, - num_words=32, + num_words=64, num_banks=1) c.words_per_row=2 debug.info(1, "Functional test for 1bit, 16word SRAM, with 1 bank") diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index f0acf8ed..7d8ee900 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -66,6 +66,10 @@ parameter={} parameter["min_tx_size"] = 0.09 parameter["beta"] = 3 +parameter["6T_inv_nmos_size"] = 0.205 +parameter["6T_inv_pmos_size"] = 0.09 +parameter["6T_access_size"] = 0.135 + drclvs_home=os.environ.get("DRCLVS_HOME") drc = design_rules("freepdk45") diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index f1a83f3d..149ccc82 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -52,6 +52,10 @@ parameter={} parameter["min_tx_size"] = 4*_lambda_ parameter["beta"] = 2 +parameter["6T_inv_nmos_size"] = 8*_lambda_ +parameter["6T_inv_pmos_size"] = 3*_lambda_ +parameter["6T_access_size"] = 4*_lambda_ + drclvs_home=os.environ.get("DRCLVS_HOME") drc = design_rules("scn3me_subm") diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 34b00ec6..b35c3943 100755 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -54,6 +54,10 @@ parameter={} parameter["min_tx_size"] = 4*_lambda_ parameter["beta"] = 2 +parameter["6T_inv_nmos_size"] = 8*_lambda_ +parameter["6T_inv_pmos_size"] = 3*_lambda_ +parameter["6T_access_size"] = 4*_lambda_ + drclvs_home=os.environ.get("DRCLVS_HOME") drc = design_rules("scn4me_sub")