diff --git a/compiler/pbitcell.py b/compiler/pbitcell.py deleted file mode 100644 index 871b5ef0..00000000 --- a/compiler/pbitcell.py +++ /dev/null @@ -1,688 +0,0 @@ -import contact -import pgate -import design -import debug -from tech import drc, parameter, spice -from vector import vector -from ptx import ptx -from globals import OPTS - -class pbitcell(pgate.pgate): - """ - This module implements a parametrically sized multi-port bitcell - """ - - def __init__(self, num_write=1, num_read=1): - name = "pbitcell_{0}W_{1}R".format(num_write, num_read) - pgate.pgate.__init__(self, name) - debug.info(2, "create a multi-port bitcell with {0} write ports and {1} read ports".format(num_write, num_read)) - - self.num_write = num_write - self.num_read = num_read - - self.add_pins() - self.create_layout() - self.DRC_LVS() - - def add_pins(self): - for k in range(0,self.num_write): - self.add_pin("wrow{}".format(k)) - for k in range(0,self.num_write): - self.add_pin("wbl{}".format(k)) - self.add_pin("wbl_bar{}".format(k)) - for k in range(0,self.num_read): - self.add_pin("rrow{}".format(k)) - for k in range(0,self.num_read): - self.add_pin("rbl{}".format(k)) - self.add_pin("rbl_bar{}".format(k)) - self.add_pin("vdd") - self.add_pin("gnd") - - def create_layout(self): - self.add_globals() - self.add_storage() - self.add_rails() - self.add_write_ports() - if(self.num_read > 0): - self.add_read_ports() - self.extend_well() - #self.add_fail() - - def add_globals(self): - """ Calculate transistor sizes """ - # if there are no read ports then write transistors are being used as read/write ports like in a 6T bitcell - if(self.num_read == 0): - inverter_nmos_width = 3*parameter["min_tx_size"] - inverter_pmos_width = parameter["min_tx_size"] - write_nmos_width = 1.5*parameter["min_tx_size"] - read_nmos_width = parameter["min_tx_size"] - # used for the dual port case where there are separate write and read ports - else: - inverter_nmos_width = 2*parameter["min_tx_size"] - inverter_pmos_width = parameter["min_tx_size"] - write_nmos_width = parameter["min_tx_size"] - read_nmos_width = 2*parameter["min_tx_size"] - - """ Create ptx for all transistors """ - # create ptx for inverter transistors - self.inverter_nmos = ptx(width=inverter_nmos_width, - tx_type="nmos") - self.add_mod(self.inverter_nmos) - - self.inverter_pmos = ptx(width=inverter_pmos_width, - tx_type="pmos") - self.add_mod(self.inverter_pmos) - - # create ptx for write transitors - self.write_nmos = ptx(width=write_nmos_width, - tx_type="nmos") - self.add_mod(self.write_nmos) - - # create ptx for read transistors - self.read_nmos = ptx(width=read_nmos_width, - tx_type="nmos") - self.add_mod(self.read_nmos) - - """ Define pbitcell global variables """ - # determine metal contact extensions - self.ip_ex = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) - self.w_ex = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height) - - # calculation for transistor spacing - self.write_to_write_spacing = drc["minwidth_metal2"] + self.w_ex + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] - self.read_to_read_spacing = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width - self.write_to_read_spacing = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.w_ex - - # calculations for transistor tiling (includes transistor and spacing) - self.inverter_tile_width = self.inverter_nmos.active_width + 1.5*parameter["min_tx_size"] - 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.rowline_tile_height = drc["minwidth_metal1"] + contact.m1m2.width - - # calculations related to inverter connections - self.inverter_gap = drc["poly_to_active"] + drc["poly_to_field_poly"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.ip_ex - 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_field_poly"] + 1.5*contact.poly.width - - # calculations for the edges of the cell - if(self.num_read > 0): - read_port_flag = 1; - else: - read_port_flag = 0; - - self.leftmost_xpos = -self.inverter_tile_width \ - - self.num_write*self.write_tile_width \ - - read_port_flag*(self.write_to_read_spacing - self.read_nmos.active_height - (self.num_read-1)*self.read_tile_width) \ - - drc["minwidth_poly"] - contact.m1m2.height - 0.5*drc["minwidth_metal2"] - - self.rightmost_xpos = -self.leftmost_xpos - - self.botmost_ypos = -self.rowline_tile_height \ - - self.num_write*self.rowline_tile_height \ - - read_port_flag*(self.num_read*self.rowline_tile_height) - - self.topmost_ypos = self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + drc["poly_extend_active"] + 2*drc["minwidth_metal1"] - - # calculations for the cell dimensions - self.cell_width = -2*self.leftmost_xpos - self.cell_height = self.topmost_ypos - self.botmost_ypos - - - def add_storage(self): - """ - Creates the crossed coupled inverters that act as storage for the bitcell. - The stored value of the cell is denoted as "Q" and the inverted values as "Q_bar". - """ - - # calculate transistor offsets - left_inverter_xpos = -(self.inverter_nmos.active_width + 1.5*parameter["min_tx_size"]) - right_inverter_xpos = 1.5*parameter["min_tx_size"] - inverter_pmos_ypos = self.inverter_gap + self.inverter_nmos.active_height - - # create active for nmos - self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", - mod=self.inverter_nmos, - offset=[left_inverter_xpos,0]) - self.connect_inst(["Q_bar", "Q", "gnd", "gnd"]) - - self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right", - mod=self.inverter_nmos, - offset=[right_inverter_xpos,0]) - self.connect_inst(["gnd", "Q_bar", "Q", "gnd"]) - - # create active for pmos - self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left", - mod=self.inverter_pmos, - offset=[left_inverter_xpos, inverter_pmos_ypos]) - self.connect_inst(["Q_bar", "Q", "vdd", "vdd"]) - - self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right", - mod=self.inverter_pmos, - offset=[right_inverter_xpos, inverter_pmos_ypos]) - self.connect_inst(["vdd", "Q_bar", "Q", "vdd"]) - - # connect input (gate) of inverters - self.add_path("poly", [self.inverter_nmos_left.get_pin("G").uc(), self.inverter_pmos_left.get_pin("G").bc()]) - self.add_path("poly", [self.inverter_nmos_right.get_pin("G").uc(), self.inverter_pmos_right.get_pin("G").bc()]) - - # connect output (drain/source) of inverters - self.add_path("metal1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], width=contact.well.width) - self.add_path("metal1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], width=contact.well.width) - - # add contacts to connect gate poly to drain/source metal1 (to connect Q to Q_bar) - offsetL = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.width, self.cross_couple_upper_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=offsetL, - rotate=90) - - offsetR = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.width, self.cross_couple_lower_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=offsetR, - rotate=90) - - # connect contacts to gate poly (cross couple) - gate_offsetR = vector(self.inverter_nmos_right.get_pin("G").lc().x, offsetL.y) - self.add_path("poly", [offsetL, gate_offsetR]) - - gate_offsetL = vector(self.inverter_nmos_left.get_pin("G").rc().x, offsetR.y) - self.add_path("poly", [offsetR, gate_offsetL]) - - - def add_rails(self): - """ - Adds gnd and vdd rails and connects them to the storage element - """ - - """ Add rails for vdd and gnd """ - self.gnd_position = vector(self.leftmost_xpos, -self.rowline_tile_height) - self.gnd = self.add_layout_pin(text="gnd", - layer="metal1", - offset=self.gnd_position, - width=self.cell_width, - height=contact.well.second_layer_width) - - self.vdd_position = vector(self.leftmost_xpos, self.inverter_pmos_left.get_pin("S").uc().y + drc["minwidth_metal1"]) - self.vdd = self.add_layout_pin(text="vdd", - layer="metal1", - offset=self.vdd_position, - width=self.cell_width, - height=drc["minwidth_metal1"]) - - """ Connect inverters to rails """ - # connect nmos to gnd - gnd_posL = vector(self.inverter_nmos_left.get_pin("S").bc().x, self.gnd_position.y + drc["minwidth_metal1"]) - self.add_path("metal1", [self.inverter_nmos_left.get_pin("S").bc(), gnd_posL]) - - gnd_posR = vector(self.inverter_nmos_right.get_pin("D").bc().x, self.gnd_position.y + drc["minwidth_metal1"]) - self.add_path("metal1", [self.inverter_nmos_right.get_pin("D").bc(), gnd_posR]) - - # connect pmos to vdd - vdd_posL = 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_posL]) - - vdd_posR = 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_posR]) - - - def add_write_ports(self): - """ - Adds write ports to the bit cell. A single transistor acts as the write port. - A write is enabled by setting a Write-Rowline (WROW) high, subsequently turning on the transistor. - The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q). - Driving WBL high or low sets the value of the cell. - This is a differential design, so each write port has a mirrored port that connects WBL_bar to Q_bar. - """ - - """ Define variables relevant to write transistors """ - # define offset correction due to rotation of the ptx cell - write_rotation_correct = self.write_nmos.active_height - - # define write transistor variables as empty arrays based on the number of write ports - self.write_nmos_left = [None] * self.num_write - self.write_nmos_right = [None] * self.num_write - self.wrow_positions = [None] * self.num_write - self.wbl_positions = [None] * self.num_write - self.wbl_bar_positions = [None] * self.num_write - - # iterate over the number of write ports - for k in range(0,self.num_write): - """ Add transistors """ - # calculate write transistor offsets - left_write_transistor_xpos = -self.inverter_tile_width \ - - (k+1)*self.write_tile_width + write_rotation_correct - - right_write_transistor_xpos = self.inverter_tile_width \ - + self.write_to_write_spacing + k*self.write_tile_width + write_rotation_correct - - # add write transistors - self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k), - mod=self.write_nmos, - offset=[left_write_transistor_xpos,0], - rotate=90) - self.connect_inst(["Q", "wrow{}".format(k), "wbl{}".format(k), "gnd"]) - - self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k), - mod=self.write_nmos, - offset=[right_write_transistor_xpos,0], - rotate=90) - self.connect_inst(["Q_bar", "wrow{}".format(k), "wbl_bar{}".format(k), "gnd"]) - - """ Add WROW lines """ - # calculate WROW position - wrow_ypos = self.gnd_position.y - (k+1)*self.rowline_tile_height - self.wrow_positions[k] = vector(self.leftmost_xpos, wrow_ypos) - - # add pin for WROW - self.add_layout_pin(text="wrow{}".format(k), - layer="metal1", - offset=self.wrow_positions[k], - width=self.cell_width, - height=contact.m1m2.width) - - """ Source/WBL/WBL_bar connections """ - # add metal1-to-metal2 contacts on top of write transistor source pins for connection to WBL and WBL_bar - offsetL = self.write_nmos_left[k].get_pin("S").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetL, - rotate=90) - - offsetR = self.write_nmos_right[k].get_pin("S").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetR, - rotate=90) - - # 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="wbl{}".format(k), - layer="metal2", - offset=self.wbl_positions[k], - width=drc["minwidth_metal2"], - height=self.cell_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="wbl_bar{}".format(k), - layer="metal2", - offset=self.wbl_bar_positions[k], - width=drc["minwidth_metal2"], - height=self.cell_height) - - """ Gate/WROW connections """ - # add poly-to-meltal2 contacts to connect gate of write transistors to WROW (contact next to gate) - contact_xpos = self.write_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.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) - self.add_rect_center(layer="poly", - offset=left_gate_contact, - width=contact.poly.width, - height=contact.poly.height) - - - contact_xpos = self.write_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.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) - self.add_rect_center(layer="poly", - offset=right_gate_contact, - width=contact.poly.width, - height=contact.poly.height) - - # 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]) - - 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]) - - # add metal1-to-metal2 contacts to WROW lines - left_wrow_contact = vector(left_gate_contact.x, self.wrow_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_wrow_contact, - rotate=90) - - right_wrow_contact = vector(right_gate_contact.x, self.wrow_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_wrow_contact, - rotate=90) - - # connect write transistor gate contacts to WROW contacts (metal2 path) - self.add_path("metal2", [left_gate_contact, left_wrow_contact]) - self.add_path("metal2", [right_gate_contact, right_wrow_contact]) - - """ Drain/Storage connections """ - # this path only needs to be drawn once on the last iteration of the loop - if(k == self.num_write-1): - # 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_field_poly"] - 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_field_poly"] + 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) - gate_offsetL = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_lower_ypos) - self.add_path("poly", [left_storage_contact, gate_offsetL]) - - gate_offsetR = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_lower_ypos) - self.add_path("poly", [right_storage_contact, gate_offsetR]) - - # connect contacts to drains of write transistors (metal1 path) - midL0 = vector(left_storage_contact.x - 0.5*contact.poly.height - 1.5*drc["minwidth_metal1"], left_storage_contact.y) - midL1 = vector(left_storage_contact.x - 0.5*contact.poly.height - 1.5*drc["minwidth_metal1"], self.write_nmos_left[k].get_pin("D").lc().y) - self.add_path("metal1", [left_storage_contact, midL0, midL1, self.write_nmos_left[k].get_pin("D").lc()]) - - midR0 = vector(right_storage_contact.x + 0.5*contact.poly.height + 1.5*drc["minwidth_metal1"], right_storage_contact.y) - midR1 = vector(right_storage_contact.x + 0.5*contact.poly.height + 1.5*drc["minwidth_metal1"], self.write_nmos_right[k].get_pin("D").rc().y) - self.add_path("metal1", [right_storage_contact, midR0, midR1, self.write_nmos_right[k].get_pin("D").rc()]) - - - def add_read_ports(self): - """ - Adds read ports to the bit cell. Two transistors function as a read port. - The two transistors in the port are denoted as the "read transistor" and the "read-access transistor". - The read transistor is connected to RROW (gate), RBL (drain), and the read-access transistor (source). - The read-access transistor is connected to Q_bar (gate), gnd (source), and the read transistor (drain). - A read is enabled by setting a Read-Rowline (RROW) high, subsequently turning on the read transistor. - The Read-Bitline (RBL) is precharged to high, and when the value of Q_bar is also high, the read-access transistor - is turned on, creating a connection between RBL and gnd. RBL subsequently discharges allowing for a differential read - using sense amps. This is a differential design, so each read port has a mirrored port that connects RBL_bar to Q. - """ - - """ Define variables relevant to read transistors """ - # define offset correction due to rotation of the ptx cell - 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() - - # define read transistor variables as empty arrays based on the number of read ports - self.read_nmos_left = [None] * self.num_read - self.read_nmos_right = [None] * self.num_read - self.read_access_nmos_left = [None] * self.num_read - self.read_access_nmos_right = [None] * self.num_read - self.rrow_positions = [None] * self.num_read - self.rbl_positions = [None] * self.num_read - self.rbl_bar_positions = [None] * self.num_read - - # iterate over the number of read ports - for k in range(0,self.num_read): - """ Add transistors """ - # calculate transistor offsets - left_read_transistor_xpos = -self.inverter_tile_width \ - - self.num_write*self.write_tile_width \ - - self.write_to_read_spacing - self.read_nmos.active_height - k*self.read_tile_width + read_rotation_correct - - right_read_transistor_xpos = self.inverter_tile_width \ - + self.num_write*self.write_tile_width \ - + self.write_to_read_spacing + k*self.read_tile_width + read_rotation_correct - - # add read-access transistors - self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left", - mod=self.read_nmos, - offset=[left_read_transistor_xpos,0], - rotate=90) - self.connect_inst(["RA_to_R_left{}".format(k), " Q_bar", "gnd", "gnd"]) - - self.read_access_nmos_right[k] = self.add_inst(name="read_access_nmos_right", - mod=self.read_nmos, - offset=[right_read_transistor_xpos,0], - rotate=90) - self.connect_inst(["RA_to_R_right{}".format(k), "Q", "gnd", "gnd"]) - - # add read transistors - self.read_nmos_left[k] = self.add_inst(name="read_nmos_left", - mod=self.read_nmos, - offset=[left_read_transistor_xpos,overlap_offset.x], - rotate=90) - self.connect_inst(["rbl{}".format(k), "rrow{}".format(k), "RA_to_R_left{}".format(k), "gnd"]) - - self.read_nmos_right[k] = self.add_inst(name="read_nmos_right", - mod=self.read_nmos, - offset=[right_read_transistor_xpos,overlap_offset.x], - rotate=90) - self.connect_inst(["rbl_bar{}".format(k), "rrow{}".format(k), "RA_to_R_right{}".format(k), "gnd"]) - - """ Add RROW lines """ - # calculate RROW position - rrow_ypos = self.gnd_position.y \ - - self.num_write*self.rowline_tile_height \ - - (k+1)*self.rowline_tile_height - self.rrow_positions[k] = vector(self.leftmost_xpos, rrow_ypos) - - # add pin for RROW - self.add_layout_pin(text="rrow{}".format(k), - layer="metal1", - offset=self.rrow_positions[k], - width=self.cell_width, - height=contact.m1m2.width) - - """ Source of read-access transistor / GND connection """ - # connect source of read-access transistor to GND (metal1 path) - offset = 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(), offset]) - - offset = 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(), offset]) - - """ 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 - offsetL = self.read_nmos_left[k].get_pin("D").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetL, - rotate=90) - - offsetR = self.read_nmos_right[k].get_pin("D").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetR, - rotate=90) - - # 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="rbl{}".format(k), - layer="metal2", - offset=self.rbl_positions[k], - width=drc["minwidth_metal2"], - height=self.cell_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="rbl_bar{}".format(k), - layer="metal2", - offset=self.rbl_bar_positions[k], - width=drc["minwidth_metal2"], - height=self.cell_height) - - """ Gate of read transistor / RROW connection """ - # add poly-to-meltal2 contacts to connect gate of read transistors to RROW (contact next to gate) - contact_xpos = left_read_transistor_xpos - read_rotation_correct - drc["minwidth_poly"] - 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) - - contact_xpos = right_read_transistor_xpos + drc["minwidth_poly"] + 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 RROW lines - left_rrow_contact = vector(left_gate_contact.x, self.rrow_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_rrow_contact, - rotate=90) - - right_rrow_contact = vector(right_gate_contact.x, self.rrow_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_rrow_contact, - rotate=90) - - # connect read transistor gate contacts to RROW contacts (metal2 path) - self.add_path("metal2", [left_gate_contact, left_rrow_contact]) - self.add_path("metal2", [right_gate_contact, right_rrow_contact]) - - """ 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) - contact_xpos = left_read_transistor_xpos + drc["minwidth_poly"] + 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) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_gate_contact) - - contact_xpos = right_read_transistor_xpos - read_rotation_correct - drc["minwidth_poly"] - 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) - - 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]) - - # connect contact to output of inverters (metal1 path) - # metal1 path must route over the read transistors (above drain of read transistor) - midL0 = vector(left_gate_contact.x, self.read_nmos_left[k].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) - ## continue metal1 path until inverter - midL1 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.read_nmos_left[0].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) - ## route down to be level with inverter output - midL2 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.cross_couple_upper_ypos) - ## endpoint at drain of inverter - 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(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.read_nmos_right[k].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) - midR2 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], 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]) - - - def extend_well(self): - """ 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.cell_width, - height=well_height) - - """ extend pwell over write transistors to the height of the write transistor well """ - left_write_well_xpos = -(self.inverter_tile_width + self.write_to_write_spacing - drc["well_enclosure_active"]) - right_write_well_xpos = self.inverter_tile_width + self.write_to_write_spacing - drc["well_enclosure_active"] - - 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 read transistor well """ - if(self.num_read > 0): - left_read_well_xpos = -(self.inverter_tile_width + self.num_write*self.write_tile_width + self.write_to_read_spacing - drc["well_enclosure_active"]) - right_read_well_xpos = self.inverter_tile_width + self.num_write*self.write_tile_width + self.write_to_read_spacing - drc["well_enclosure_active"] - - 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 """ - 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"] - - well_width = 2*self.inverter_pmos.active_width + 3*parameter["min_tx_size"] + 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] - self.add_rect(layer="nwell", - offset=offset, - width=well_width, - height=well_height) - - - """ add well contacts """ - # connect pimplants to gnd - offset = vector(0, self.gnd_position.y + 0.5*contact.well.second_layer_width) - self.add_contact_center(layers=("active", "contact", "metal1"), - offset=offset, - rotate=90) - - self.add_rect_center(layer="pimplant", - offset=offset, - width=drc["minwidth_tx"], - height=drc["minwidth_tx"]) - - # connect nimplants to vdd - offset = vector(0, self.vdd_position.y + 0.5*drc["minwidth_metal1"]) - self.add_contact_center(layers=("active", "contact", "metal1"), - offset=offset, - rotate=90) - - self.add_rect_center(layer="nimplant", - offset=offset, - width=drc["minwidth_tx"], - height=drc["minwidth_tx"]) - - - def add_fail(self): - # for failing drc when I want to observe the gds layout - frail_width = self.well_width = 2*drc["minwidth_metal1"] - frail_height = self.rail_height = drc["minwidth_metal1"] - - fail_position = vector(-25*drc["minwidth_tx"], - 1.5 * drc["minwidth_metal1"] - 0.5 * frail_height) # for tiling purposes - self.add_layout_pin(text="gnd", - layer="metal1", - offset=fail_position, - width=frail_width, - height=frail_height) - - fail_position2 = vector(-25*drc["minwidth_tx"], - 0.5 * drc["minwidth_metal1"]) - self.add_layout_pin(text="gnd2", - layer="metal1", - offset=fail_position2, - width=frail_width, - height=frail_height)