From 64e7ed5b5ea81d695f59c36abbc3dcbb598a17ab Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Tue, 9 Jan 2018 13:39:42 -0800 Subject: [PATCH 01/46] Adding pbitcell.py: a multiport bitcell with a variable number of write ports and read ports Adding 04_pbitcell_test.py: The benchtest for pbitcell Mostly done. Layout nearly complete with the exception of the well contacts and a connection between the gates of the read transistors and their corresponding vias. Then several drc corrections need to be made. --- compiler/pbitcell.py | 546 +++++++++++++++++++++++++++++ compiler/tests/04_pbitcell_test.py | 60 ++++ 2 files changed, 606 insertions(+) create mode 100644 compiler/pbitcell.py create mode 100644 compiler/tests/04_pbitcell_test.py diff --git a/compiler/pbitcell.py b/compiler/pbitcell.py new file mode 100644 index 00000000..40c82343 --- /dev/null +++ b/compiler/pbitcell.py @@ -0,0 +1,546 @@ +import design +import debug +from tech import drc, parameter, spice +from vector import vector +import contact +from math import ceil +from ptx import ptx +from globals import OPTS + +class pbitcell(design.design): + """ + This module implements a parametrically sized multi-port bitcell. + """ + + def __init__(self): + name = "pbitcell" + design.design.__init__(self, name) + debug.info(2, "create a multi-port bitcell") + + self.num_write = 2 + self.num_read = 2 + + self.create_layout() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("WROW") + self.add_pin("WBL") + self.add_pin("WBL_bar") + for k in range(0,self.num_read): + self.add_pin("RROW{}".format(k)) + 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.create_ptx() + self.add_storage() + self.add_rails() + self.add_write_transistors() + self.add_read_transistors() + self.extend_well() + #self.add_fail() + + def create_ptx(self): + """ + Create ptx all transistors. Also define measurements to be used throughout bitcell. + """ + self.i_nmos = ptx(width=2.3*parameter["min_tx_size"], + tx_type="nmos", + connect_active=True, + connect_poly=True) + self.add_mod(self.i_nmos) + + self.i_pmos = ptx(width=parameter["min_tx_size"], + tx_type="pmos", + connect_active=True, + connect_poly=True) + self.add_mod(self.i_pmos) + + # create ptx for write transitors + self.w_nmos = ptx(width=1.5*parameter["min_tx_size"], + tx_type="nmos", + connect_active=True, + connect_poly=True) + self.add_mod(self.w_nmos) + + # create ptx for read transistors + self.r_nmos = ptx(width=parameter["min_tx_size"], + tx_type="nmos", + connect_active=True, + connect_poly=True) + self.add_mod(self.r_nmos) + + # determine metal contact extentions + self.in_ex = 0.5*(self.i_nmos.active_contact.height - self.i_nmos.active_height) + self.ip_ex = 0.5*(self.i_pmos.active_contact.height - self.i_pmos.active_height) + self.w_ex = 0.5*(self.w_nmos.active_contact.height - self.w_nmos.active_height) + self.r_ex = 0.5*(self.r_nmos.active_contact.height - self.r_nmos.active_height) + + # determine global measurements + spacer = 5*drc["minwidth_metal2"] + self.w_ex + self.r_ex + + self.w_tile_width = 3*parameter["min_tx_size"] + self.w_nmos.active_height + self.r_tile_width = 3*parameter["min_tx_size"] + self.r_nmos.active_height + self.inv_gap = 5*contact.poly.width + self.leftmost_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"] + drc["well_enclosure_active"]) \ + - self.num_write*(3*parameter["min_tx_size"] + self.w_nmos.active_height) \ + - (self.num_read-1)*(3*parameter["min_tx_size"] + self.r_nmos.active_height) - spacer - self.r_nmos.active_height + self.botmost_ypos = -2*drc["minwidth_metal1"] - self.num_write*2*drc["minwidth_metal2"] - self.num_read*2*drc["minwidth_metal2"] + self.topmost_ypos = self.inv_gap + self.i_nmos.active_height + self.i_pmos.active_height + drc["poly_extend_active"] + 2*drc["minwidth_metal1"] + self.cell_width = -2*self.leftmost_xpos + self.cell_height = self.topmost_ypos - self.botmost_ypos + + + def add_rails(self): + """ + Adds rails for vdd and gnd. + """ + self.gnd_position = vector(self.leftmost_xpos, -2*drc["minwidth_metal1"]) + self.gnd = self.add_layout_pin(text="gnd", + layer="metal1", + offset=self.gnd_position, + width=self.cell_width, + height=drc["minwidth_metal1"]) + + self.vdd_position = vector(self.leftmost_xpos, self.i_pmosL.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 sources to rails + # connect nmos to gnd + i_nmosL_source = self.i_nmosL.get_pin("S").bc() + gnd_posL = vector(self.i_nmosL.get_pin("S").bc().x, self.gnd_position.y + drc["minwidth_metal1"]) + self.add_path("metal1", [i_nmosL_source, gnd_posL]) + + i_nmosR_drain = self.i_nmosR.get_pin("D").bc() + gnd_posR = vector(self.i_nmosR.get_pin("D").bc().x, self.gnd_position.y + drc["minwidth_metal1"]) + self.add_path("metal1", [i_nmosR_drain, gnd_posR]) + + # connect pmos to vdd + i_pmosL_source = self.i_pmosL.get_pin("S").uc() + vdd_posL = vector(self.i_nmosL.get_pin("S").uc().x, self.vdd_position.y) + self.add_path("metal1", [i_pmosL_source, vdd_posL]) + + i_pmosR_drain = self.i_pmosR.get_pin("D").uc() + vdd_posR = vector(self.i_nmosR.get_pin("D").uc().x, self.vdd_position.y) + self.add_path("metal1", [i_pmosR_drain, vdd_posR]) + + + def add_storage(self): + """ + Creates the crossed coupled inverters that act as storage for the bitcell. + """ + # calculate offsets + lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) + rit_xpos = 1.5*parameter["min_tx_size"] + it_ypos = self.inv_gap + self.i_nmos.active_height + + + # create active + self.i_nmosL = self.add_inst(name="i_nmosL", + mod=self.i_nmos, + offset=[lit_xpos,0]) + self.connect_inst(["IR", "IL", "gnd", "gnd"]) + + self.i_nmosR = self.add_inst(name="i_nmosR", + mod=self.i_nmos, + offset=[rit_xpos,0]) + self.connect_inst(["IL", "IR", "gnd", "gnd"]) + + self.i_pmosL = self.add_inst(name="i_pmosL", + mod=self.i_pmos, + offset=[lit_xpos, it_ypos]) + self.connect_inst(["IL", "IR", "vdd", "vdd"]) + + self.i_pmosR = self.add_inst(name="i_pmosR", + mod=self.i_pmos, + offset=[rit_xpos, it_ypos]) + self.connect_inst(["IL", "IR", "vdd", "vdd"]) + + + # connect poly for inverters + i_nmosL_poly = self.i_nmosL.get_pin("G").uc() + i_pmosL_poly = self.i_pmosL.get_pin("G").bc() + self.add_path("poly", [i_nmosL_poly, i_pmosL_poly]) + + i_nmosR_poly = self.i_nmosR.get_pin("G").uc() + i_pmosR_poly = self.i_pmosR.get_pin("G").bc() + self.add_path("poly", [i_nmosR_poly, i_pmosR_poly]) + + + # connect drains for inverters + i_nmosL_drain = self.i_nmosL.get_pin("D").uc() + i_pmosL_drain = self.i_pmosL.get_pin("D").bc() + self.add_path("metal1", [i_nmosL_drain, i_pmosL_drain]) + + i_nmosR_source = self.i_nmosR.get_pin("S").uc() + i_pmosR_source = self.i_pmosR.get_pin("S").bc() + self.add_path("metal1", [i_nmosR_source, i_pmosR_source]) + + # add cross couple vias + offset = vector(self.i_nmosL.get_pin("D").ur().x + contact.poly.height, self.i_nmos.active_height + 0.6*self.inv_gap) + self.icl = self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset, + rotate=90) + + offset = vector(self.i_nmosR.get_pin("S").ul().x, self.i_nmos.active_height + 0.2*self.inv_gap) + self.icr = self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset, + rotate=90) + + + # connect vias to poly (cross couple) + correct = 0.5*(self.i_nmos.active_contact.width - drc["minwidth_metal1"]) + cross_width = 0.5*self.i_nmos.active_width - 0.5*drc["minwidth_poly"] + 3*parameter["min_tx_size"] + correct + offset = vector(self.i_nmosL.get_pin("G").ur().x, self.i_nmos.active_height + 0.2*self.inv_gap) + self.add_rect(layer="poly", + offset=offset, + width=cross_width, + height=contact.poly.width) + + offset = vector(self.i_nmosL.get_pin("D").ur().x, self.i_nmos.active_height + 0.6*self.inv_gap) + self.add_rect(layer="poly", + offset=offset, + width=cross_width, + height=contact.poly.width) + + + def add_write_transistors(self): + """ + Define variables relevant to write transistors + """ + lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) + rit_xpos = 1.5*parameter["min_tx_size"] + rot_correct = self.w_nmos.active_height + + self.w_nmosL = [None] * self.num_write + self.w_nmosR = [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 + + for k in range(0,self.num_write): + """ + Add transistors and WROW lines + """ + wrow_ypos = -2*drc["minwidth_metal1"] - (k+1)*2*drc["minwidth_metal2"] + lwt_xpos = lit_xpos + rot_correct - (k+1)*self.w_tile_width + rwt_xpos = rit_xpos + self.i_nmos.active_width + 3*parameter["min_tx_size"] + rot_correct + k*self.w_tile_width + + self.w_nmosL[k] = self.add_inst(name="w_nmosL{}".format(k), + mod=self.w_nmos, + offset=[lwt_xpos,0], + rotate=90) + self.connect_inst(["IR", "WROW{}".format(k), "WBL{}".format(k), "gnd"]) + + self.w_nmosR[k] = self.add_inst(name="w_nmosR{}".format(k), + mod=self.w_nmos, + offset=[rwt_xpos,0], + rotate=90) + self.connect_inst(["IL", "WROW{}".format(k), "WBL_bar{}".format(k), "gnd"]) + + + # add WROW lines + self.wrow_positions[k] = vector(self.leftmost_xpos, wrow_ypos) + self.add_layout_pin(text="WROW{}".format(k), + layer="metal1", + offset=self.wrow_positions[k], + width=self.cell_width, + height=drc["minwidth_metal1"]) + + """ + Source/WBL/WBL_bar connections + """ + # connect sources to WBL and WBL_bar + self.wbl_positions[k] = vector(lwt_xpos - 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(rwt_xpos - rot_correct,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) + + # add vias to connect source of wt to WBL and WBL_bar + offset = self.w_nmosL[k].get_pin("S").ll() + vector(self.w_nmos.active_height, 0) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) + + offset = self.w_nmosR[k].get_pin("S").ll() + vector(self.w_nmos.active_height, 0) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) + + """ + Gate/WROW connections + """ + # add vias to connect wt gate to WROW + correct = vector(0.5*(drc["minwidth_metal2"] - drc["minwidth_metal1"]),0) + offset = self.w_nmosL[k].get_pin("S").ll() - vector(drc["minwidth_metal2"] + contact.poly.width, 0) + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset) + + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset+correct) + + via_offsetL = offset + vector(0.5*contact.poly.width, 0) + gate_offsetL = vector(offset.x + 0.5*contact.poly.width, self.w_nmosL[k].get_pin("G").ul().y) + self.add_path("poly", [via_offsetL, gate_offsetL], width=contact.poly.width) + self.add_path("poly", [gate_offsetL, self.w_nmosL[k].get_pin("G").lc()]) + + offset = self.w_nmosR[k].get_pin("S").lr() + vector(drc["minwidth_metal2"], 0) + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset) + + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset+correct) + + via_offsetR = offset + vector(0.5*contact.poly.width, 0) + gate_offsetR = vector(offset.x + 0.5*contact.poly.width, self.w_nmosR[k].get_pin("G").ur().y) + self.add_path("poly", [via_offsetR, gate_offsetR], width=contact.poly.width) + self.add_path("poly", [gate_offsetR, self.w_nmosR[k].get_pin("G").rc()]) + + # add vias to WROW lines + offset = vector(via_offsetL.x + contact.m1m2.height - 0.5*drc["minwidth_metal2"],self.wrow_positions[k].y) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) + + wrow_offset = vector(via_offsetL.x, self.wrow_positions[k].y) + self.add_path("metal2", [via_offsetL, wrow_offset]) + + offset = vector(via_offsetR.x + 0.5*drc["minwidth_metal2"], self.wrow_positions[k].y) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) + + wrow_offset = vector(via_offsetR.x, self.wrow_positions[k].y) + self.add_path("metal2", [via_offsetR, wrow_offset]) + + + """ + Drain/Storage connections + """ + if(k == self.num_write-1): + # add vias to connect input of inverters to drain of wt + offset = vector(lit_xpos - drc["minwidth_metal1"], self.i_nmos.active_height + 0.2*self.inv_gap) + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset, + rotate=90) + + offset = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap) + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset, + rotate=90) + + # connect drains of wt to input of inverters + correct = 0.5*(contact.poly.width - drc["minwidth_metal1"]) + w_nmosL_drain = self.w_nmosL[k].get_pin("D").rc() + via_offsetLB = vector(lit_xpos - drc["minwidth_metal1"] - contact.poly.height, self.w_nmosL[k].get_pin("D").rc().y) + via_offsetLT = vector(lit_xpos - drc["minwidth_metal1"] - contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + contact.poly.width - correct) + self.add_path("metal1", [w_nmosL_drain, via_offsetLB, via_offsetLT]) + + w_nmosR_drain = self.w_nmosR[k].get_pin("D").lc() + via_offsetRB = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.w_nmosR[k].get_pin("D").lc().y) + via_offsetRT = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + contact.poly.width - correct) + self.add_path("metal1", [w_nmosR_drain, via_offsetRB, via_offsetRT]) + + via_offsetL = vector(lit_xpos - drc["minwidth_metal1"] - contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width) + gate_offsetL = vector(self.i_nmosL.get_pin("G").ll().x, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width) + self.add_path("poly", [via_offsetL, gate_offsetL], width=contact.poly.width) + + via_offsetR = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width) + gate_offsetR = vector(self.i_nmosR.get_pin("G").lr().x, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width) + self.add_path("poly", [via_offsetR, gate_offsetR], width=contact.poly.width) + + + def add_read_transistors(self): + """ + Define variables relevant to read transistors + """ + lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) + rit_xpos = 1.5*parameter["min_tx_size"] + wrow_ypos = -2*drc["minwidth_metal1"] - self.num_write*2*drc["minwidth_metal2"] + lwt_xpos = lit_xpos - self.num_write*self.w_tile_width + rwt_xpos = rit_xpos + self.i_nmos.active_width + self.num_write*self.w_tile_width + + rot_correct = self.r_nmos.active_height + spacer = 5*drc["minwidth_metal2"] + + self.r_nmosL = [None] * self.num_read + self.r_nmosR = [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 + + + for k in range(0,self.num_read): + """ + Add transistors and RROW lines + """ + lrt_xpos = lwt_xpos - spacer - self.r_nmos.active_height + rot_correct - k*self.r_tile_width + rrt_xpos = rwt_xpos + spacer + rot_correct + k*self.r_tile_width + + self.r_nmosL[k] = self.add_inst(name="r_nmosL", + mod=self.r_nmos, + offset=[lrt_xpos,0], + rotate=90) + self.connect_inst(["RROW{}".format(k), "IL", "RBL{}".format(k), "gnd"]) + + self.r_nmosR[k] = self.add_inst(name="r_nmosR", + mod=self.r_nmos, + offset=[rrt_xpos,0], + rotate=90) + self.connect_inst(["RROW{}".format(k), "IR", "RBL_bar{}".format(k), "gnd"]) + + + # add RROW lines + rrow_ypos = wrow_ypos - (k+1)*2*drc["minwidth_metal2"] + self.rrow_positions[k] = vector(self.leftmost_xpos, rrow_ypos) + self.add_layout_pin(text="RROW{}".format(k), + layer="metal1", + offset=self.rrow_positions[k], + width=self.cell_width, + height=drc["minwidth_metal1"]) + + """ + Drain/RBL/RBL_bar connections + """ + # connect drains to RBL and RBL_bar + self.rbl_positions[k] = vector(self.r_nmosL[k].get_pin("D").rc().x + 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.r_nmosR[k].get_pin("D").lc().x - 2*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) + + # add vias to connect drain of rt to RBL and RBL_bar + offset = self.r_nmosL[k].get_pin("D").ll() + vector(contact.m1m2.height, 0) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) + + offset = self.r_nmosR[k].get_pin("D").ll() + vector(contact.m1m2.height, 0) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) + + # connect drain of rt to RBL and RBL_bar + rbl_offset = vector(self.rbl_positions[k].x, self.r_nmosL[k].get_pin("D").lc().y) + self.add_path("metal2", [self.r_nmosL[k].get_pin("D").lc(), rbl_offset]) + + rbl_bar_offset = vector(self.rbl_bar_positions[k].x, self.r_nmosR[k].get_pin("D").rc().y) + self.add_path("metal2", [self.r_nmosR[k].get_pin("D").rc(), rbl_bar_offset]) + + """ + Source/RROW connections + """ + # add vias on rt sources + offset = self.r_nmosL[k].get_pin("S").ll() + vector(contact.m1m2.height, 0) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) + + offset = self.r_nmosR[k].get_pin("S").ll() + vector(contact.m1m2.height, 0) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) + + # add vias on rrows + offset = vector(self.r_nmosL[k].get_pin("S").ll().x + contact.m1m2.height, self.rrow_positions[k].y) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) + + offset = vector(self.r_nmosR[k].get_pin("S").ll().x + contact.m1m2.height, self.rrow_positions[k].y) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) + + # connect sources of rt to rrows + rrow_offset = vector(self.r_nmosL[k].get_pin("S").bc().x, self.rrow_positions[k].y) + self.add_path("metal2", [self.r_nmosL[k].get_pin("S").bc(), rrow_offset]) + + rrow_offset = vector(self.r_nmosR[k].get_pin("S").bc().x, self.rrow_positions[k].y) + self.add_path("metal2", [self.r_nmosR[k].get_pin("S").bc(), rrow_offset]) + + """ + Gate/inverter connections + """ + # add vias to connect to gate of rt + offsetL = vector(self.r_nmosL[k].get_pin("D").lr().x + drc["minwidth_metal1"], self.r_nmosL[k].get_pin("G").lr().y) + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offsetL) + + offsetR = vector(self.r_nmosR[k].get_pin("D").ll().x - drc["minwidth_metal1"] - contact.poly.width, self.r_nmosR[k].get_pin("G").ll().y) + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offsetR) + + # add metal1 path between gate vias and inverter inputs + midL = vector(offsetL.x + 0.5*contact.poly.width, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"]) + gate_offsetL = vector(self.i_nmosL.get_pin("D").ll().x, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"]) + self.add_path("metal1", [offsetL+vector(0.5*contact.poly.width,0), midL, gate_offsetL]) + + midR = vector(offsetR.x + 0.5*contact.poly.width, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"]) + gate_offsetR = vector(self.i_nmosR.get_pin("S").lr().x, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"]) + self.add_path("metal1", [offsetR+vector(0.5*contact.poly.width,0), midR, gate_offsetR]) + + + + def extend_well(self): + offset = vector(self.leftmost_xpos, -2*drc["minwidth_metal1"]) + well_height = self.w_nmos.well_width + 2*drc["minwidth_metal1"] + self.add_rect(layer="pwell", + offset=offset, + width=self.cell_width, + height=well_height) + + lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"] + drc["well_enclosure_active"]) + it_ypos = self.inv_gap + self.i_nmos.active_height - drc["well_enclosure_active"] + offset = [lit_xpos,it_ypos] + well_width = 2*self.i_pmos.active_width + 3*parameter["min_tx_size"] + 2*drc["well_enclosure_active"] + well_height = self.i_pmos.well_height + 2*drc["minwidth_metal1"] + self.add_rect(layer="nwell", + offset=offset, + width=well_width, + height=well_height) + + + + def add_fail(self): + # for failing drc + frail_width = self.well_width = 10*drc["minwidth_tx"] + frail_height = self.rail_height = drc["minwidth_metal1"] + + self.gnd_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=self.gnd_position, + width=frail_width, + height=frail_height) + + self.gnd_position2 = vector(-25*drc["minwidth_tx"], - 0.5 * drc["minwidth_metal1"]) + self.add_layout_pin(text="gnd2", + layer="metal1", + offset=self.gnd_position2, + width=frail_width, + height=frail_height) diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py new file mode 100644 index 00000000..e5512a1c --- /dev/null +++ b/compiler/tests/04_pbitcell_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python2.7 +""" +Run regresion tests on a parameterized inverter +""" + +import unittest +from testutils import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import verify + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 04_pbitcell_test") + + +class pbitcell_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.check_lvsdrc = False + + import pbitcell + import tech + + debug.info(2, "Test for pbitcell") + tx = pbitcell.pbitcell() + self.local_check(tx) + + OPTS.check_lvsdrc = True + globals.end_openram() + + def local_check(self, tx): + #tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + #tx.sp_write(tempspice) + tx.gds_write(tempgds) + + self.assertFalse(verify.run_drc(tx.name, tempgds)) + #self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice)) + + #os.remove(tempspice) + os.remove(tempgds) + + # reset the static duplicate name checker for unit tests + import design + design.design.name_map=[] + + + + +# instantiate a copy 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() From b90f5c9a59e6c1611600400b286fbff555282b8b Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Thu, 8 Feb 2018 14:21:15 -0800 Subject: [PATCH 02/46] pbitcell is now a multiport cell with a read transistor that connects to RBL and RROW and a read access transistor that connects to Q and gnd current commit works without drc errors on freepdk45 but has drc rules not included in scn3me_subm. Does have lvs errors adding several unit tests: the basic one that tests the full functionality of the pbitcell, one with no write ports, and one with no read ports --- compiler/pbitcell.py | 685 ++++++++++++++------------ compiler/tests/04_pbitcell_0R_test.py | 60 +++ compiler/tests/04_pbitcell_0W_test.py | 60 +++ compiler/tests/04_pbitcell_test.py | 4 +- 4 files changed, 486 insertions(+), 323 deletions(-) create mode 100644 compiler/tests/04_pbitcell_0R_test.py create mode 100644 compiler/tests/04_pbitcell_0W_test.py diff --git a/compiler/pbitcell.py b/compiler/pbitcell.py index 40c82343..86bc5e9d 100644 --- a/compiler/pbitcell.py +++ b/compiler/pbitcell.py @@ -9,26 +9,29 @@ from globals import OPTS class pbitcell(design.design): """ - This module implements a parametrically sized multi-port bitcell. + This module implements a parametrically sized multi-port bitcell """ - def __init__(self): + def __init__(self, num_write=1, num_read=1): name = "pbitcell" design.design.__init__(self, name) - debug.info(2, "create a multi-port bitcell") + debug.info(2, "create a multi-port bitcell with {0} write ports and {1} read ports".format(num_write, num_read)) - self.num_write = 2 - self.num_read = 2 + self.num_write = num_write + self.num_read = num_read self.create_layout() self.DRC_LVS() def add_pins(self): - self.add_pin("WROW") - self.add_pin("WBL") - self.add_pin("WBL_bar") + 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") @@ -37,67 +40,127 @@ class pbitcell(design.design): def create_layout(self): self.create_ptx() self.add_storage() - self.add_rails() - self.add_write_transistors() - self.add_read_transistors() + self.add_rails() + if(self.num_write > 0): + self.add_write_transistors() + if(self.num_read > 0): + self.add_read_transistors() self.extend_well() #self.add_fail() def create_ptx(self): - """ - Create ptx all transistors. Also define measurements to be used throughout bitcell. - """ - self.i_nmos = ptx(width=2.3*parameter["min_tx_size"], - tx_type="nmos", - connect_active=True, - connect_poly=True) + """ Create ptx for all transistors. Also define measurements to be used throughout bitcell """ + # create ptx for inverter transistors + self.i_nmos = ptx(width=2*parameter["min_tx_size"], + tx_type="nmos", + connect_active=True, + connect_poly=True) self.add_mod(self.i_nmos) self.i_pmos = ptx(width=parameter["min_tx_size"], - tx_type="pmos", - connect_active=True, - connect_poly=True) + tx_type="pmos", + connect_active=True, + connect_poly=True) self.add_mod(self.i_pmos) # create ptx for write transitors - self.w_nmos = ptx(width=1.5*parameter["min_tx_size"], - tx_type="nmos", - connect_active=True, - connect_poly=True) + self.w_nmos = ptx(width=parameter["min_tx_size"], + tx_type="nmos", + connect_active=True, + connect_poly=True) self.add_mod(self.w_nmos) # create ptx for read transistors - self.r_nmos = ptx(width=parameter["min_tx_size"], - tx_type="nmos", - connect_active=True, - connect_poly=True) + self.r_nmos = ptx(width=2*parameter["min_tx_size"], + mults=2, + tx_type="nmos", + connect_active=False, + connect_poly=False) self.add_mod(self.r_nmos) - # determine metal contact extentions - self.in_ex = 0.5*(self.i_nmos.active_contact.height - self.i_nmos.active_height) + # determine metal contact extensions self.ip_ex = 0.5*(self.i_pmos.active_contact.height - self.i_pmos.active_height) - self.w_ex = 0.5*(self.w_nmos.active_contact.height - self.w_nmos.active_height) - self.r_ex = 0.5*(self.r_nmos.active_contact.height - self.r_nmos.active_height) + self.w_ex = 0.5*(self.w_nmos.active_contact.height - self.w_nmos.active_height) # determine global measurements - spacer = 5*drc["minwidth_metal2"] + self.w_ex + self.r_ex + w_spacer = drc["minwidth_metal2"] + self.w_ex + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + r_spacer = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width + rw_spacer = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.w_ex - self.w_tile_width = 3*parameter["min_tx_size"] + self.w_nmos.active_height - self.r_tile_width = 3*parameter["min_tx_size"] + self.r_nmos.active_height - self.inv_gap = 5*contact.poly.width - self.leftmost_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"] + drc["well_enclosure_active"]) \ - - self.num_write*(3*parameter["min_tx_size"] + self.w_nmos.active_height) \ - - (self.num_read-1)*(3*parameter["min_tx_size"] + self.r_nmos.active_height) - spacer - self.r_nmos.active_height + self.w_tile_width = w_spacer + self.w_nmos.active_height + self.r_tile_width = r_spacer + self.r_nmos.active_height + self.inv_gap = drc["poly_to_active"] + drc["poly_to_field_poly"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.ip_ex + self.cross_heightL = self.i_nmos.active_height + drc["poly_to_active"] + 0.5*contact.poly.width + self.cross_heightU = self.i_nmos.active_height + drc["poly_to_active"] + drc["poly_to_field_poly"] + 1.5*contact.poly.width + + self.leftmost_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) \ + - self.num_write*(w_spacer + self.w_nmos.active_height) \ + - rw_spacer - self.r_nmos.active_height - (self.num_read-1)*self.r_tile_width \ + - 3*drc["minwidth_metal1"] self.botmost_ypos = -2*drc["minwidth_metal1"] - self.num_write*2*drc["minwidth_metal2"] - self.num_read*2*drc["minwidth_metal2"] self.topmost_ypos = self.inv_gap + self.i_nmos.active_height + self.i_pmos.active_height + drc["poly_extend_active"] + 2*drc["minwidth_metal1"] 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. + """ + lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) + rit_xpos = 1.5*parameter["min_tx_size"] + it_ypos = self.inv_gap + self.i_nmos.active_height + + # create active + self.i_nmosL = self.add_inst(name="i_nmosL", + mod=self.i_nmos, + offset=[lit_xpos,0]) + self.connect_inst(["Q_bar", "Q", "gnd", "gnd"]) + + self.i_nmosR = self.add_inst(name="i_nmosR", + mod=self.i_nmos, + offset=[rit_xpos,0]) + self.connect_inst(["gnd", "Q_bar", "Q", "gnd"]) + + self.i_pmosL = self.add_inst(name="i_pmosL", + mod=self.i_pmos, + offset=[lit_xpos, it_ypos]) + self.connect_inst(["Q_bar", "Q", "vdd", "vdd"]) + + self.i_pmosR = self.add_inst(name="i_pmosR", + mod=self.i_pmos, + offset=[rit_xpos, it_ypos]) + self.connect_inst(["vdd", "Q_bar", "Q", "vdd"]) + + # connect poly for inverters + self.add_path("poly", [self.i_nmosL.get_pin("G").uc(), self.i_pmosL.get_pin("G").bc()]) + self.add_path("poly", [self.i_nmosR.get_pin("G").uc(), self.i_pmosR.get_pin("G").bc()]) + + # connect drains for inverters + self.add_path("metal1", [self.i_nmosL.get_pin("D").uc(), self.i_pmosL.get_pin("D").bc()]) + self.add_path("metal1", [self.i_nmosR.get_pin("S").uc(), self.i_pmosR.get_pin("S").bc()]) + + # add contacts to connect gate poly to drain/source metal1 (to connect Q to Q_bar) + offsetL = vector(self.i_nmosL.get_pin("G").rc().x + drc["poly_to_field_poly"] + 0.5*contact.poly.width, self.cross_heightU) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=offsetL, + rotate=90) + + offsetR = vector(self.i_nmosR.get_pin("G").lc().x - drc["poly_to_field_poly"] - 0.5*contact.poly.width, self.cross_heightL) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=offsetR, + rotate=90) + + # connect contacts to gate poly (cross couple) + gate_offsetR = vector(self.i_nmosR.get_pin("G").lc().x, offsetL.y) + self.add_path("poly", [offsetL, gate_offsetR]) + + gate_offsetL = vector(self.i_nmosL.get_pin("G").rc().x, offsetR.y) + self.add_path("poly", [offsetR, gate_offsetL]) + + def add_rails(self): - """ - Adds rails for vdd and gnd. - """ + """ Add rails for vdd and gnd """ self.gnd_position = vector(self.leftmost_xpos, -2*drc["minwidth_metal1"]) self.gnd = self.add_layout_pin(text="gnd", layer="metal1", @@ -111,112 +174,27 @@ class pbitcell(design.design): offset=self.vdd_position, width=self.cell_width, height=drc["minwidth_metal1"]) - - # connect sources to rails - # connect nmos to gnd - i_nmosL_source = self.i_nmosL.get_pin("S").bc() - gnd_posL = vector(self.i_nmosL.get_pin("S").bc().x, self.gnd_position.y + drc["minwidth_metal1"]) - self.add_path("metal1", [i_nmosL_source, gnd_posL]) - i_nmosR_drain = self.i_nmosR.get_pin("D").bc() + """ Connect inverters to rails """ + # connect nmos to gnd + gnd_posL = vector(self.i_nmosL.get_pin("S").bc().x, self.gnd_position.y + drc["minwidth_metal1"]) + self.add_path("metal1", [self.i_nmosL.get_pin("S").bc(), gnd_posL]) + gnd_posR = vector(self.i_nmosR.get_pin("D").bc().x, self.gnd_position.y + drc["minwidth_metal1"]) - self.add_path("metal1", [i_nmosR_drain, gnd_posR]) + self.add_path("metal1", [self.i_nmosR.get_pin("D").bc(), gnd_posR]) # connect pmos to vdd - i_pmosL_source = self.i_pmosL.get_pin("S").uc() vdd_posL = vector(self.i_nmosL.get_pin("S").uc().x, self.vdd_position.y) - self.add_path("metal1", [i_pmosL_source, vdd_posL]) + self.add_path("metal1", [self.i_pmosL.get_pin("S").uc(), vdd_posL]) - i_pmosR_drain = self.i_pmosR.get_pin("D").uc() vdd_posR = vector(self.i_nmosR.get_pin("D").uc().x, self.vdd_position.y) - self.add_path("metal1", [i_pmosR_drain, vdd_posR]) + self.add_path("metal1", [self.i_pmosR.get_pin("D").uc(), vdd_posR]) - - def add_storage(self): - """ - Creates the crossed coupled inverters that act as storage for the bitcell. - """ - # calculate offsets - lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) - rit_xpos = 1.5*parameter["min_tx_size"] - it_ypos = self.inv_gap + self.i_nmos.active_height - - - # create active - self.i_nmosL = self.add_inst(name="i_nmosL", - mod=self.i_nmos, - offset=[lit_xpos,0]) - self.connect_inst(["IR", "IL", "gnd", "gnd"]) - - self.i_nmosR = self.add_inst(name="i_nmosR", - mod=self.i_nmos, - offset=[rit_xpos,0]) - self.connect_inst(["IL", "IR", "gnd", "gnd"]) - self.i_pmosL = self.add_inst(name="i_pmosL", - mod=self.i_pmos, - offset=[lit_xpos, it_ypos]) - self.connect_inst(["IL", "IR", "vdd", "vdd"]) - - self.i_pmosR = self.add_inst(name="i_pmosR", - mod=self.i_pmos, - offset=[rit_xpos, it_ypos]) - self.connect_inst(["IL", "IR", "vdd", "vdd"]) - - - # connect poly for inverters - i_nmosL_poly = self.i_nmosL.get_pin("G").uc() - i_pmosL_poly = self.i_pmosL.get_pin("G").bc() - self.add_path("poly", [i_nmosL_poly, i_pmosL_poly]) - - i_nmosR_poly = self.i_nmosR.get_pin("G").uc() - i_pmosR_poly = self.i_pmosR.get_pin("G").bc() - self.add_path("poly", [i_nmosR_poly, i_pmosR_poly]) - - - # connect drains for inverters - i_nmosL_drain = self.i_nmosL.get_pin("D").uc() - i_pmosL_drain = self.i_pmosL.get_pin("D").bc() - self.add_path("metal1", [i_nmosL_drain, i_pmosL_drain]) - - i_nmosR_source = self.i_nmosR.get_pin("S").uc() - i_pmosR_source = self.i_pmosR.get_pin("S").bc() - self.add_path("metal1", [i_nmosR_source, i_pmosR_source]) - - # add cross couple vias - offset = vector(self.i_nmosL.get_pin("D").ur().x + contact.poly.height, self.i_nmos.active_height + 0.6*self.inv_gap) - self.icl = self.add_contact(layers=("poly", "contact", "metal1"), - offset=offset, - rotate=90) - - offset = vector(self.i_nmosR.get_pin("S").ul().x, self.i_nmos.active_height + 0.2*self.inv_gap) - self.icr = self.add_contact(layers=("poly", "contact", "metal1"), - offset=offset, - rotate=90) - - - # connect vias to poly (cross couple) - correct = 0.5*(self.i_nmos.active_contact.width - drc["minwidth_metal1"]) - cross_width = 0.5*self.i_nmos.active_width - 0.5*drc["minwidth_poly"] + 3*parameter["min_tx_size"] + correct - offset = vector(self.i_nmosL.get_pin("G").ur().x, self.i_nmos.active_height + 0.2*self.inv_gap) - self.add_rect(layer="poly", - offset=offset, - width=cross_width, - height=contact.poly.width) - - offset = vector(self.i_nmosL.get_pin("D").ur().x, self.i_nmos.active_height + 0.6*self.inv_gap) - self.add_rect(layer="poly", - offset=offset, - width=cross_width, - height=contact.poly.width) - - def add_write_transistors(self): - """ - Define variables relevant to write transistors - """ + """ Define variables relevant to write transistors """ lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) - rit_xpos = 1.5*parameter["min_tx_size"] + rit_xpos = 1.5*parameter["min_tx_size"] + self.i_nmos.active_width rot_correct = self.w_nmos.active_height self.w_nmosL = [None] * self.num_write @@ -225,26 +203,27 @@ class pbitcell(design.design): self.wbl_positions = [None] * self.num_write self.wbl_bar_positions = [None] * self.num_write + for k in range(0,self.num_write): - """ - Add transistors and WROW lines - """ - wrow_ypos = -2*drc["minwidth_metal1"] - (k+1)*2*drc["minwidth_metal2"] - lwt_xpos = lit_xpos + rot_correct - (k+1)*self.w_tile_width - rwt_xpos = rit_xpos + self.i_nmos.active_width + 3*parameter["min_tx_size"] + rot_correct + k*self.w_tile_width + """ Add transistors and WROW lines """ + # calculate transistor offsets + w_spacer = drc["minwidth_metal2"] + self.w_ex + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + wrow_ypos = self.gnd_position.y - (k+1)*2*drc["minwidth_metal2"] + lwt_xpos = lit_xpos - (k+1)*self.w_tile_width + rot_correct + rwt_xpos = rit_xpos + w_spacer + k*self.w_tile_width + rot_correct + # add write transistors self.w_nmosL[k] = self.add_inst(name="w_nmosL{}".format(k), mod=self.w_nmos, offset=[lwt_xpos,0], rotate=90) - self.connect_inst(["IR", "WROW{}".format(k), "WBL{}".format(k), "gnd"]) + self.connect_inst(["Q", "WROW{}".format(k), "WBL{}".format(k), "gnd"]) self.w_nmosR[k] = self.add_inst(name="w_nmosR{}".format(k), mod=self.w_nmos, offset=[rwt_xpos,0], rotate=90) - self.connect_inst(["IL", "WROW{}".format(k), "WBL_bar{}".format(k), "gnd"]) - + self.connect_inst(["Q_bar", "WROW{}".format(k), "WBL_bar{}".format(k), "gnd"]) # add WROW lines self.wrow_positions[k] = vector(self.leftmost_xpos, wrow_ypos) @@ -254,130 +233,128 @@ class pbitcell(design.design): width=self.cell_width, height=drc["minwidth_metal1"]) - """ - Source/WBL/WBL_bar connections - """ - # connect sources to WBL and WBL_bar - self.wbl_positions[k] = vector(lwt_xpos - drc["minwidth_metal2"],self.botmost_ypos) + + """ Source/WBL/WBL_bar connections """ + # add contacts to connect source of wt to WBL or WBL_bar + offsetL = self.w_nmosL[k].get_pin("S").center() + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=offsetL, + rotate=90) + + offsetR = self.w_nmosR[k].get_pin("S").center() + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=offsetR, + rotate=90) + + # add WBL and WBL_bar (simultaneously connects to source of wt) + self.wbl_positions[k] = vector(self.w_nmosL[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(rwt_xpos - rot_correct,self.botmost_ypos) + self.wbl_bar_positions[k] = vector(self.w_nmosR[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) - # add vias to connect source of wt to WBL and WBL_bar - offset = self.w_nmosL[k].get_pin("S").ll() + vector(self.w_nmos.active_height, 0) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset, - rotate=90) - offset = self.w_nmosR[k].get_pin("S").ll() + vector(self.w_nmos.active_height, 0) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset, - rotate=90) + """ Gate/WROW connections """ + # add contacts to connect gate of wt to WROW (poly to metal2) + offsetL = vector(self.w_nmosL[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width, self.w_nmosL[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=offsetL) - """ - Gate/WROW connections - """ - # add vias to connect wt gate to WROW - correct = vector(0.5*(drc["minwidth_metal2"] - drc["minwidth_metal1"]),0) - offset = self.w_nmosL[k].get_pin("S").ll() - vector(drc["minwidth_metal2"] + contact.poly.width, 0) - self.add_contact(layers=("poly", "contact", "metal1"), - offset=offset) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=offsetL) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset+correct) + self.add_rect_center(layer="poly", + offset=offsetL, + width=contact.poly.width, + height=contact.poly.height) - via_offsetL = offset + vector(0.5*contact.poly.width, 0) - gate_offsetL = vector(offset.x + 0.5*contact.poly.width, self.w_nmosL[k].get_pin("G").ul().y) - self.add_path("poly", [via_offsetL, gate_offsetL], width=contact.poly.width) - self.add_path("poly", [gate_offsetL, self.w_nmosL[k].get_pin("G").lc()]) + offsetR = vector(self.w_nmosR[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width, self.w_nmosR[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=offsetR) - offset = self.w_nmosR[k].get_pin("S").lr() + vector(drc["minwidth_metal2"], 0) - self.add_contact(layers=("poly", "contact", "metal1"), - offset=offset) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=offsetR) + + self.add_rect_center(layer="poly", + offset=offsetR, + width=contact.poly.width, + height=contact.poly.height) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset+correct) + # connect gate of wt to contact + midL = vector(offsetL.x, self.w_nmosL[k].get_pin("G").lc().y) + self.add_path("poly", [self.w_nmosL[k].get_pin("G").lc(), midL, offsetL]) - via_offsetR = offset + vector(0.5*contact.poly.width, 0) - gate_offsetR = vector(offset.x + 0.5*contact.poly.width, self.w_nmosR[k].get_pin("G").ur().y) - self.add_path("poly", [via_offsetR, gate_offsetR], width=contact.poly.width) - self.add_path("poly", [gate_offsetR, self.w_nmosR[k].get_pin("G").rc()]) + midR = vector(offsetR.x, self.w_nmosR[k].get_pin("G").rc().y) + self.add_path("poly", [self.w_nmosR[k].get_pin("G").rc(), midR, offsetR]) - # add vias to WROW lines - offset = vector(via_offsetL.x + contact.m1m2.height - 0.5*drc["minwidth_metal2"],self.wrow_positions[k].y) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset, - rotate=90) - - wrow_offset = vector(via_offsetL.x, self.wrow_positions[k].y) - self.add_path("metal2", [via_offsetL, wrow_offset]) + # add contacts to WROW lines + offset = vector(offsetL.x, self.wrow_positions[k].y + 0.5*drc["minwidth_metal1"]) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) - offset = vector(via_offsetR.x + 0.5*drc["minwidth_metal2"], self.wrow_positions[k].y) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset, - rotate=90) + offset = vector(offsetR.x, self.wrow_positions[k].y + 0.5*drc["minwidth_metal1"]) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) - wrow_offset = vector(via_offsetR.x, self.wrow_positions[k].y) - self.add_path("metal2", [via_offsetR, wrow_offset]) + # connect wt gate contacts to WROW contacts + wrow_offset = vector(offsetL.x, self.wrow_positions[k].y) + self.add_path("metal2", [offsetL, wrow_offset]) + + wrow_offset = vector(offsetR.x, self.wrow_positions[k].y) + self.add_path("metal2", [offsetR, wrow_offset]) - """ - Drain/Storage connections - """ + """ Drain/Storage connections """ if(k == self.num_write-1): - # add vias to connect input of inverters to drain of wt - offset = vector(lit_xpos - drc["minwidth_metal1"], self.i_nmos.active_height + 0.2*self.inv_gap) - self.add_contact(layers=("poly", "contact", "metal1"), - offset=offset, - rotate=90) + # add contacts to connect gate of inverters to drain of wt + offsetL = vector(self.i_nmosL.get_pin("G").lc().x - drc["poly_to_field_poly"] - 0.5*contact.poly.width, self.cross_heightL) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=offsetL, + rotate=90) - offset = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap) - self.add_contact(layers=("poly", "contact", "metal1"), - offset=offset, - rotate=90) + offsetR = vector(self.i_nmosR.get_pin("G").rc().x + drc["poly_to_field_poly"] + 0.5*contact.poly.width, self.cross_heightL) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=offsetR, + rotate=90) - # connect drains of wt to input of inverters - correct = 0.5*(contact.poly.width - drc["minwidth_metal1"]) - w_nmosL_drain = self.w_nmosL[k].get_pin("D").rc() - via_offsetLB = vector(lit_xpos - drc["minwidth_metal1"] - contact.poly.height, self.w_nmosL[k].get_pin("D").rc().y) - via_offsetLT = vector(lit_xpos - drc["minwidth_metal1"] - contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + contact.poly.width - correct) - self.add_path("metal1", [w_nmosL_drain, via_offsetLB, via_offsetLT]) + # connect gate of inverters to contacts + gate_offsetL = vector(self.i_nmosL.get_pin("G").lc().x, offsetL.y) + self.add_path("poly", [offsetL, gate_offsetL]) - w_nmosR_drain = self.w_nmosR[k].get_pin("D").lc() - via_offsetRB = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.w_nmosR[k].get_pin("D").lc().y) - via_offsetRT = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + contact.poly.width - correct) - self.add_path("metal1", [w_nmosR_drain, via_offsetRB, via_offsetRT]) + gate_offsetR = vector(self.i_nmosR.get_pin("G").rc().x, offsetR.y) + self.add_path("poly", [offsetR, gate_offsetR]) - via_offsetL = vector(lit_xpos - drc["minwidth_metal1"] - contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width) - gate_offsetL = vector(self.i_nmosL.get_pin("G").ll().x, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width) - self.add_path("poly", [via_offsetL, gate_offsetL], width=contact.poly.width) + # connect contacts to drains of wt + midL0 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], offsetL.y) + midL1 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.w_nmosL[k].get_pin("D").lc().y) + self.add_path("metal1", [offsetL, midL0, midL1, self.w_nmosL[k].get_pin("D").lc()]) - via_offsetR = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width) - gate_offsetR = vector(self.i_nmosR.get_pin("G").lr().x, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width) - self.add_path("poly", [via_offsetR, gate_offsetR], width=contact.poly.width) + midR0 = vector(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], offsetR.y) + midR1 = vector(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.w_nmosR[k].get_pin("D").rc().y) + self.add_path("metal1", [offsetR, midR0, midR1, self.w_nmosR[k].get_pin("D").rc()]) def add_read_transistors(self): - """ - Define variables relevant to read transistors - """ + """ Define variables relevant to read transistors """ lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) - rit_xpos = 1.5*parameter["min_tx_size"] - wrow_ypos = -2*drc["minwidth_metal1"] - self.num_write*2*drc["minwidth_metal2"] + rit_xpos = 1.5*parameter["min_tx_size"] + self.i_nmos.active_width lwt_xpos = lit_xpos - self.num_write*self.w_tile_width - rwt_xpos = rit_xpos + self.i_nmos.active_width + self.num_write*self.w_tile_width + rwt_xpos = rit_xpos + self.num_write*self.w_tile_width + wrow_ypos = self.gnd_position.y - self.num_write*2*drc["minwidth_metal2"] rot_correct = self.r_nmos.active_height - spacer = 5*drc["minwidth_metal2"] + r_spacer = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width + rw_spacer = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.w_ex self.r_nmosL = [None] * self.num_read self.r_nmosR = [None] * self.num_read @@ -387,24 +364,23 @@ class pbitcell(design.design): for k in range(0,self.num_read): - """ - Add transistors and RROW lines - """ - lrt_xpos = lwt_xpos - spacer - self.r_nmos.active_height + rot_correct - k*self.r_tile_width - rrt_xpos = rwt_xpos + spacer + rot_correct + k*self.r_tile_width + """ Add transistors and RROW lines """ + # calculate transistor offsets + lrt_xpos = lwt_xpos - rw_spacer - self.r_nmos.active_height - k*self.r_tile_width + rot_correct + rrt_xpos = rwt_xpos + rw_spacer + k*self.r_tile_width + rot_correct + # add read transistors self.r_nmosL[k] = self.add_inst(name="r_nmosL", mod=self.r_nmos, offset=[lrt_xpos,0], rotate=90) - self.connect_inst(["RROW{}".format(k), "IL", "RBL{}".format(k), "gnd"]) + self.connect_inst(["RROW{}".format(k), "Q", "RBL{}".format(k), "gnd"]) self.r_nmosR[k] = self.add_inst(name="r_nmosR", mod=self.r_nmos, offset=[rrt_xpos,0], rotate=90) - self.connect_inst(["RROW{}".format(k), "IR", "RBL_bar{}".format(k), "gnd"]) - + self.connect_inst(["RROW{}".format(k), "Q_bar", "RBL_bar{}".format(k), "gnd"]) # add RROW lines rrow_ypos = wrow_ypos - (k+1)*2*drc["minwidth_metal2"] @@ -414,133 +390,200 @@ class pbitcell(design.design): offset=self.rrow_positions[k], width=self.cell_width, height=drc["minwidth_metal1"]) - - """ - Drain/RBL/RBL_bar connections - """ - # connect drains to RBL and RBL_bar - self.rbl_positions[k] = vector(self.r_nmosL[k].get_pin("D").rc().x + drc["minwidth_metal2"],self.botmost_ypos) + + + """ Source of RA transistor / GND connection """ + offset = vector(self.r_nmosL[k].get_pins("S")[0].bc().x, self.gnd_position.y) + self.add_path("metal1", [self.r_nmosL[k].get_pins("S")[0].bc(), offset]) + + offset = vector(self.r_nmosR[k].get_pins("S")[0].bc().x, self.gnd_position.y) + self.add_path("metal1", [self.r_nmosR[k].get_pins("S")[0].bc(), offset]) + + + """ Source of R transistor / RBL & RBL_bar connection """ + # add contacts to connect source of rt to RBL or RBL_bar + offsetL = self.r_nmosL[k].get_pins("S")[1].center() + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=offsetL, + rotate=90) + + offsetR = self.r_nmosR[k].get_pins("S")[1].center() + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=offsetR, + rotate=90) + + # add RBL and RBL_bar (simultaneously connects to source of rt) + self.rbl_positions[k] = vector(self.r_nmosL[k].get_pins("S")[1].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.r_nmosR[k].get_pin("D").lc().x - 2*drc["minwidth_metal2"],self.botmost_ypos) + self.rbl_bar_positions[k] = vector(self.r_nmosR[k].get_pins("S")[1].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) - # add vias to connect drain of rt to RBL and RBL_bar - offset = self.r_nmosL[k].get_pin("D").ll() + vector(contact.m1m2.height, 0) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset, - rotate=90) - offset = self.r_nmosR[k].get_pin("D").ll() + vector(contact.m1m2.height, 0) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset, - rotate=90) - - # connect drain of rt to RBL and RBL_bar - rbl_offset = vector(self.rbl_positions[k].x, self.r_nmosL[k].get_pin("D").lc().y) - self.add_path("metal2", [self.r_nmosL[k].get_pin("D").lc(), rbl_offset]) + """ Gate of R transistor / RROW connection """ + # add contact to connect gate of rt to RROW (poly to metal2) + offsetL = vector(lrt_xpos - rot_correct - drc["minwidth_poly"] - 0.5*contact.poly.width, self.r_nmosL[k].get_pins("G")[1].lc().y) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=offsetL) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=offsetL) + + offsetR = vector(rrt_xpos + drc["minwidth_poly"] + 0.5*contact.poly.width, self.r_nmosR[k].get_pins("G")[1].rc().y) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=offsetR) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=offsetR) - rbl_bar_offset = vector(self.rbl_bar_positions[k].x, self.r_nmosR[k].get_pin("D").rc().y) - self.add_path("metal2", [self.r_nmosR[k].get_pin("D").rc(), rbl_bar_offset]) - - """ - Source/RROW connections - """ - # add vias on rt sources - offset = self.r_nmosL[k].get_pin("S").ll() + vector(contact.m1m2.height, 0) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset, - rotate=90) + # connect gate of rt to contact + self.add_path("poly", [self.r_nmosL[k].get_pins("G")[1].lc(), offsetL]) + self.add_path("poly", [self.r_nmosR[k].get_pins("G")[1].rc(), offsetR]) - offset = self.r_nmosR[k].get_pin("S").ll() + vector(contact.m1m2.height, 0) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset, - rotate=90) + # add contacts to RROW lines + row_offsetL = vector(self.r_nmosL[k].get_pins("S")[1].lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width, self.rrow_positions[k].y + 0.5*drc["minwidth_metal1"]) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=row_offsetL, + rotate=90) + + row_offsetR = vector(self.r_nmosR[k].get_pins("S")[1].rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width, self.rrow_positions[k].y + 0.5*drc["minwidth_metal1"]) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=row_offsetR, + rotate=90) - # add vias on rrows - offset = vector(self.r_nmosL[k].get_pin("S").ll().x + contact.m1m2.height, self.rrow_positions[k].y) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset, - rotate=90) + # connect rt gate contacts to RROW contacts + self.add_path("metal2", [offsetL, row_offsetL]) + self.add_path("metal2", [offsetR, row_offsetR]) - offset = vector(self.r_nmosR[k].get_pin("S").ll().x + contact.m1m2.height, self.rrow_positions[k].y) - self.add_contact(layers=("metal1", "via1", "metal2"), - offset=offset, - rotate=90) - # connect sources of rt to rrows - rrow_offset = vector(self.r_nmosL[k].get_pin("S").bc().x, self.rrow_positions[k].y) - self.add_path("metal2", [self.r_nmosL[k].get_pin("S").bc(), rrow_offset]) + """ Gate of RA transistor / storage connection """ + # add contact to connect gate of rat to output of inverters + offsetL = vector(lrt_xpos + drc["minwidth_poly"] + 0.5*contact.poly.width, self.r_nmosL[k].get_pins("G")[0].rc().y) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=offsetL) + + offsetR = vector(rrt_xpos - rot_correct - drc["minwidth_poly"] - 0.5*contact.poly.width, self.r_nmosR[k].get_pins("G")[0].lc().y) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=offsetR) - rrow_offset = vector(self.r_nmosR[k].get_pin("S").bc().x, self.rrow_positions[k].y) - self.add_path("metal2", [self.r_nmosR[k].get_pin("S").bc(), rrow_offset]) + # connect gate of rat to contact + self.add_path("poly", [self.r_nmosL[k].get_pins("G")[0].rc(), offsetL]) + self.add_path("poly", [self.r_nmosR[k].get_pins("G")[0].lc(), offsetR]) - """ - Gate/inverter connections - """ - # add vias to connect to gate of rt - offsetL = vector(self.r_nmosL[k].get_pin("D").lr().x + drc["minwidth_metal1"], self.r_nmosL[k].get_pin("G").lr().y) - self.add_contact(layers=("poly", "contact", "metal1"), - offset=offsetL) - - offsetR = vector(self.r_nmosR[k].get_pin("D").ll().x - drc["minwidth_metal1"] - contact.poly.width, self.r_nmosR[k].get_pin("G").ll().y) - self.add_contact(layers=("poly", "contact", "metal1"), - offset=offsetR) - - # add metal1 path between gate vias and inverter inputs - midL = vector(offsetL.x + 0.5*contact.poly.width, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"]) - gate_offsetL = vector(self.i_nmosL.get_pin("D").ll().x, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"]) - self.add_path("metal1", [offsetL+vector(0.5*contact.poly.width,0), midL, gate_offsetL]) - - midR = vector(offsetR.x + 0.5*contact.poly.width, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"]) - gate_offsetR = vector(self.i_nmosR.get_pin("S").lr().x, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"]) - self.add_path("metal1", [offsetR+vector(0.5*contact.poly.width,0), midR, gate_offsetR]) + # connect contact to output of inverters + midL0 = vector(offsetL.x, self.r_nmosL[k].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"]) + midL1 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.r_nmosL[0].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"]) + midL2 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.cross_heightU) + gate_offsetL = vector(self.i_nmosL.get_pin("D").center().x, self.cross_heightU) + self.add_path("metal1", [offsetL, midL0, midL1, midL2, gate_offsetL]) + midR0 = vector(offsetR.x, self.r_nmosR[k].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"]) + midR1 = vector(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.r_nmosR[k].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"]) + midR2 = vector(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.cross_heightU) + gate_offsetR = vector(self.i_nmosR.get_pin("S").center().x, self.cross_heightU) + self.add_path("metal1", [offsetR, midR0, midR1, midR2, gate_offsetR]) def extend_well(self): - offset = vector(self.leftmost_xpos, -2*drc["minwidth_metal1"]) - well_height = self.w_nmos.well_width + 2*drc["minwidth_metal1"] + """ extend nwell and pwell """ + # extend pwell to encompass i_nmos + offset = vector(self.leftmost_xpos, self.botmost_ypos) + well_height = -self.botmost_ypos + self.i_nmos.well_height - drc["well_enclosure_active"] self.add_rect(layer="pwell", offset=offset, width=self.cell_width, height=well_height) + + # extend pwell to encompass r_nmos + r_well_width = self.num_read*self.r_tile_width + r_well_height = self.r_nmos.well_width - drc["well_enclosure_active"] + offset = vector(self.leftmost_xpos, 0) + self.add_rect(layer="pwell", + offset=offset, + width=r_well_width, + height=r_well_height) + + offset = vector(-self.leftmost_xpos - r_well_width, 0) + self.add_rect(layer="pwell", + offset=offset, + width=r_well_width, + height=r_well_height) + + # extend pwell to encompass w_nmos + lwt_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"] + self.w_tile_width - self.w_nmos.active_height - drc["well_enclosure_active"]) + rwt_xpos = 1.5*parameter["min_tx_size"] + self.i_nmos.active_width + self.w_tile_width - self.w_nmos.active_height - drc["well_enclosure_active"] + w_well_width = -(self.leftmost_xpos - lwt_xpos) + w_well_height = self.w_nmos.well_width - drc["well_enclosure_active"] + offset = vector(lwt_xpos - w_well_width, 0) + self.add_rect(layer="pwell", + offset=offset, + width=w_well_width, + height=w_well_height) + + offset = vector(rwt_xpos, 0) + self.add_rect(layer="pwell", + offset=offset, + width=w_well_width, + height=w_well_height) + + # extend nwell to encompass i_pmos lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"] + drc["well_enclosure_active"]) it_ypos = self.inv_gap + self.i_nmos.active_height - drc["well_enclosure_active"] offset = [lit_xpos,it_ypos] well_width = 2*self.i_pmos.active_width + 3*parameter["min_tx_size"] + 2*drc["well_enclosure_active"] - well_height = self.i_pmos.well_height + 2*drc["minwidth_metal1"] + well_height = self.vdd_position.y - it_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"] 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*drc["minwidth_metal1"]) + 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 - frail_width = self.well_width = 10*drc["minwidth_tx"] + frail_width = self.well_width = 2*drc["minwidth_metal1"] frail_height = self.rail_height = drc["minwidth_metal1"] - self.gnd_position = vector(-25*drc["minwidth_tx"], - 1.5 * drc["minwidth_metal1"] - 0.5 * frail_height) # for tiling purposes + 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=self.gnd_position, + offset=fail_position, width=frail_width, height=frail_height) - self.gnd_position2 = vector(-25*drc["minwidth_tx"], - 0.5 * drc["minwidth_metal1"]) + fail_position2 = vector(-25*drc["minwidth_tx"], - 0.5 * drc["minwidth_metal1"]) self.add_layout_pin(text="gnd2", layer="metal1", - offset=self.gnd_position2, + offset=fail_position2, width=frail_width, height=frail_height) diff --git a/compiler/tests/04_pbitcell_0R_test.py b/compiler/tests/04_pbitcell_0R_test.py new file mode 100644 index 00000000..9766db8a --- /dev/null +++ b/compiler/tests/04_pbitcell_0R_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python2.7 +""" +Run regresion tests on a parameterized inverter +""" + +import unittest +from testutils import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import verify + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 04_pbitcell_test") + + +class pbitcell_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.check_lvsdrc = False + + import pbitcell + import tech + + debug.info(2, "Test for pbitcell with 2 write ports and 0 read ports") + tx = pbitcell.pbitcell(num_write=2,num_read=0) + self.local_check(tx) + + OPTS.check_lvsdrc = True + globals.end_openram() + + def local_check(self, tx): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + tx.sp_write(tempspice) + tx.gds_write(tempgds) + + self.assertFalse(verify.run_drc(tx.name, tempgds)) + self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + # reset the static duplicate name checker for unit tests + import design + design.design.name_map=[] + + + + +# instantiate a copy 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/04_pbitcell_0W_test.py b/compiler/tests/04_pbitcell_0W_test.py new file mode 100644 index 00000000..38b1dd25 --- /dev/null +++ b/compiler/tests/04_pbitcell_0W_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python2.7 +""" +Run regresion tests on a parameterized inverter +""" + +import unittest +from testutils import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import verify + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 04_pbitcell_test") + + +class pbitcell_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.check_lvsdrc = False + + import pbitcell + import tech + + debug.info(2, "Test for pbitcell with 0 write ports and 2 read ports") + tx = pbitcell.pbitcell(num_write=0,num_read=2) + self.local_check(tx) + + OPTS.check_lvsdrc = True + globals.end_openram() + + def local_check(self, tx): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + tx.sp_write(tempspice) + tx.gds_write(tempgds) + + self.assertFalse(verify.run_drc(tx.name, tempgds)) + self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + # reset the static duplicate name checker for unit tests + import design + design.design.name_map=[] + + + + +# instantiate a copy 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/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index e5512a1c..3ba42b76 100644 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -25,8 +25,8 @@ class pbitcell_test(unittest.TestCase): import pbitcell import tech - debug.info(2, "Test for pbitcell") - tx = pbitcell.pbitcell() + debug.info(2, "Test for pbitcell with 2 write ports and 2 read ports") + tx = pbitcell.pbitcell(num_write=2,num_read=2) self.local_check(tx) OPTS.check_lvsdrc = True From 4ea2a70a1ba8a143757ff45ebf5e83befd8c244a Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Mon, 19 Feb 2018 10:58:08 -0800 Subject: [PATCH 03/46] removing unnecessary unit test for pbitcell --- compiler/tests/04_pbitcell_0R_test.py | 60 --------------------------- 1 file changed, 60 deletions(-) delete mode 100644 compiler/tests/04_pbitcell_0R_test.py diff --git a/compiler/tests/04_pbitcell_0R_test.py b/compiler/tests/04_pbitcell_0R_test.py deleted file mode 100644 index 9766db8a..00000000 --- a/compiler/tests/04_pbitcell_0R_test.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python2.7 -""" -Run regresion tests on a parameterized inverter -""" - -import unittest -from testutils import header -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -import debug -import verify - -OPTS = globals.OPTS - -#@unittest.skip("SKIPPING 04_pbitcell_test") - - -class pbitcell_test(unittest.TestCase): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - OPTS.check_lvsdrc = False - - import pbitcell - import tech - - debug.info(2, "Test for pbitcell with 2 write ports and 0 read ports") - tx = pbitcell.pbitcell(num_write=2,num_read=0) - self.local_check(tx) - - OPTS.check_lvsdrc = True - globals.end_openram() - - def local_check(self, tx): - tempspice = OPTS.openram_temp + "temp.sp" - tempgds = OPTS.openram_temp + "temp.gds" - - tx.sp_write(tempspice) - tx.gds_write(tempgds) - - self.assertFalse(verify.run_drc(tx.name, tempgds)) - self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice)) - - os.remove(tempspice) - os.remove(tempgds) - - # reset the static duplicate name checker for unit tests - import design - design.design.name_map=[] - - - - -# instantiate a copy 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() From 4d3f01ff2f9ae48ff943f594dfdf4d8659ef92e3 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 28 Feb 2018 11:14:53 -0800 Subject: [PATCH 04/46] The bitcell currently passes DRC and LVS for FreePDK45 and SCMOS There are 2 benchtests for the bitcell: 1) one with 2 write ports and 2 read ports 2) one with 2 write ports and 0 read ports The second test is meant to show how the bitcell functions when read/write ports are used instead of separate ports for read and write The bitcell currently passes both tests in both technologies Certain sizing optimizations still need to be done on the bitcell --- compiler/pbitcell.py | 735 +++++++++++++++----------- compiler/tests/04_pbitcell_0R_test.py | 43 ++ compiler/tests/04_pbitcell_0W_test.py | 60 --- compiler/tests/04_pbitcell_test.py | 33 +- 4 files changed, 468 insertions(+), 403 deletions(-) create mode 100644 compiler/tests/04_pbitcell_0R_test.py delete mode 100644 compiler/tests/04_pbitcell_0W_test.py diff --git a/compiler/pbitcell.py b/compiler/pbitcell.py index 86bc5e9d..871b5ef0 100644 --- a/compiler/pbitcell.py +++ b/compiler/pbitcell.py @@ -1,104 +1,131 @@ +import contact +import pgate import design import debug from tech import drc, parameter, spice from vector import vector -import contact -from math import ceil from ptx import ptx from globals import OPTS -class pbitcell(design.design): +class pbitcell(pgate.pgate): """ This module implements a parametrically sized multi-port bitcell """ def __init__(self, num_write=1, num_read=1): - name = "pbitcell" - design.design.__init__(self, name) + 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)) + 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)) + 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)) + 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("rbl{}".format(k)) + self.add_pin("rbl_bar{}".format(k)) self.add_pin("vdd") self.add_pin("gnd") def create_layout(self): - self.create_ptx() + self.add_globals() self.add_storage() self.add_rails() - if(self.num_write > 0): - self.add_write_transistors() + self.add_write_ports() if(self.num_read > 0): - self.add_read_transistors() + self.add_read_ports() self.extend_well() #self.add_fail() - def create_ptx(self): - """ Create ptx for all transistors. Also define measurements to be used throughout bitcell """ + 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.i_nmos = ptx(width=2*parameter["min_tx_size"], - tx_type="nmos", - connect_active=True, - connect_poly=True) - self.add_mod(self.i_nmos) + self.inverter_nmos = ptx(width=inverter_nmos_width, + tx_type="nmos") + self.add_mod(self.inverter_nmos) - self.i_pmos = ptx(width=parameter["min_tx_size"], - tx_type="pmos", - connect_active=True, - connect_poly=True) - self.add_mod(self.i_pmos) + self.inverter_pmos = ptx(width=inverter_pmos_width, + tx_type="pmos") + self.add_mod(self.inverter_pmos) # create ptx for write transitors - self.w_nmos = ptx(width=parameter["min_tx_size"], - tx_type="nmos", - connect_active=True, - connect_poly=True) - self.add_mod(self.w_nmos) + self.write_nmos = ptx(width=write_nmos_width, + tx_type="nmos") + self.add_mod(self.write_nmos) # create ptx for read transistors - self.r_nmos = ptx(width=2*parameter["min_tx_size"], - mults=2, - tx_type="nmos", - connect_active=False, - connect_poly=False) - self.add_mod(self.r_nmos) + 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.i_pmos.active_contact.height - self.i_pmos.active_height) - self.w_ex = 0.5*(self.w_nmos.active_contact.height - self.w_nmos.active_height) + 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) - # determine global measurements - w_spacer = drc["minwidth_metal2"] + self.w_ex + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] - r_spacer = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width - rw_spacer = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.w_ex + # 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 - self.w_tile_width = w_spacer + self.w_nmos.active_height - self.r_tile_width = r_spacer + self.r_nmos.active_height - self.inv_gap = drc["poly_to_active"] + drc["poly_to_field_poly"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.ip_ex - self.cross_heightL = self.i_nmos.active_height + drc["poly_to_active"] + 0.5*contact.poly.width - self.cross_heightU = self.i_nmos.active_height + drc["poly_to_active"] + drc["poly_to_field_poly"] + 1.5*contact.poly.width + # 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 - self.leftmost_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) \ - - self.num_write*(w_spacer + self.w_nmos.active_height) \ - - rw_spacer - self.r_nmos.active_height - (self.num_read-1)*self.r_tile_width \ - - 3*drc["minwidth_metal1"] - self.botmost_ypos = -2*drc["minwidth_metal1"] - self.num_write*2*drc["minwidth_metal2"] - self.num_read*2*drc["minwidth_metal2"] - self.topmost_ypos = self.inv_gap + self.i_nmos.active_height + self.i_pmos.active_height + drc["poly_extend_active"] + 2*drc["minwidth_metal1"] + # 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 @@ -106,439 +133,511 @@ class pbitcell(design.design): 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". """ - lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) - rit_xpos = 1.5*parameter["min_tx_size"] - it_ypos = self.inv_gap + self.i_nmos.active_height + + # 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 - self.i_nmosL = self.add_inst(name="i_nmosL", - mod=self.i_nmos, - offset=[lit_xpos,0]) + # 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.i_nmosR = self.add_inst(name="i_nmosR", - mod=self.i_nmos, - offset=[rit_xpos,0]) + 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"]) - - self.i_pmosL = self.add_inst(name="i_pmosL", - mod=self.i_pmos, - offset=[lit_xpos, it_ypos]) + + # 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.i_pmosR = self.add_inst(name="i_pmosR", - mod=self.i_pmos, - offset=[rit_xpos, it_ypos]) + 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 poly for inverters - self.add_path("poly", [self.i_nmosL.get_pin("G").uc(), self.i_pmosL.get_pin("G").bc()]) - self.add_path("poly", [self.i_nmosR.get_pin("G").uc(), self.i_pmosR.get_pin("G").bc()]) + # 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 drains for inverters - self.add_path("metal1", [self.i_nmosL.get_pin("D").uc(), self.i_pmosL.get_pin("D").bc()]) - self.add_path("metal1", [self.i_nmosR.get_pin("S").uc(), self.i_pmosR.get_pin("S").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.i_nmosL.get_pin("G").rc().x + drc["poly_to_field_poly"] + 0.5*contact.poly.width, self.cross_heightU) + 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.i_nmosR.get_pin("G").lc().x - drc["poly_to_field_poly"] - 0.5*contact.poly.width, self.cross_heightL) + 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.i_nmosR.get_pin("G").lc().x, offsetL.y) + 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.i_nmosL.get_pin("G").rc().x, offsetR.y) + 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, -2*drc["minwidth_metal1"]) + 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=drc["minwidth_metal1"]) + height=contact.well.second_layer_width) - self.vdd_position = vector(self.leftmost_xpos, self.i_pmosL.get_pin("S").uc().y + drc["minwidth_metal1"]) + 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.i_nmosL.get_pin("S").bc().x, self.gnd_position.y + drc["minwidth_metal1"]) - self.add_path("metal1", [self.i_nmosL.get_pin("S").bc(), gnd_posL]) + 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.i_nmosR.get_pin("D").bc().x, self.gnd_position.y + drc["minwidth_metal1"]) - self.add_path("metal1", [self.i_nmosR.get_pin("D").bc(), gnd_posR]) + 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.i_nmosL.get_pin("S").uc().x, self.vdd_position.y) - self.add_path("metal1", [self.i_pmosL.get_pin("S").uc(), vdd_posL]) + 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.i_nmosR.get_pin("D").uc().x, self.vdd_position.y) - self.add_path("metal1", [self.i_pmosR.get_pin("D").uc(), vdd_posR]) + 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_transistors(self): + 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 """ - lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) - rit_xpos = 1.5*parameter["min_tx_size"] + self.i_nmos.active_width - rot_correct = self.w_nmos.active_height + # define offset correction due to rotation of the ptx cell + write_rotation_correct = self.write_nmos.active_height - self.w_nmosL = [None] * self.num_write - self.w_nmosR = [None] * self.num_write + # 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 - + 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 and WROW lines """ - # calculate transistor offsets - w_spacer = drc["minwidth_metal2"] + self.w_ex + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] - wrow_ypos = self.gnd_position.y - (k+1)*2*drc["minwidth_metal2"] - lwt_xpos = lit_xpos - (k+1)*self.w_tile_width + rot_correct - rwt_xpos = rit_xpos + w_spacer + k*self.w_tile_width + rot_correct + """ 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.w_nmosL[k] = self.add_inst(name="w_nmosL{}".format(k), - mod=self.w_nmos, - offset=[lwt_xpos,0], - rotate=90) - self.connect_inst(["Q", "WROW{}".format(k), "WBL{}".format(k), "gnd"]) + 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.w_nmosR[k] = self.add_inst(name="w_nmosR{}".format(k), - mod=self.w_nmos, - offset=[rwt_xpos,0], - rotate=90) - self.connect_inst(["Q_bar", "WROW{}".format(k), "WBL_bar{}".format(k), "gnd"]) - - # add WROW lines + 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) - self.add_layout_pin(text="WROW{}".format(k), + + # add pin for WROW + self.add_layout_pin(text="wrow{}".format(k), layer="metal1", offset=self.wrow_positions[k], width=self.cell_width, - height=drc["minwidth_metal1"]) - - + height=contact.m1m2.width) + """ Source/WBL/WBL_bar connections """ - # add contacts to connect source of wt to WBL or WBL_bar - offsetL = self.w_nmosL[k].get_pin("S").center() + # 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.w_nmosR[k].get_pin("S").center() + offsetR = self.write_nmos_right[k].get_pin("S").center() self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=offsetR, rotate=90) - # add WBL and WBL_bar (simultaneously connects to source of wt) - self.wbl_positions[k] = vector(self.w_nmosL[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text="WBL{}".format(k), + # 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.w_nmosR[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text="WBL_bar{}".format(k), + 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 contacts to connect gate of wt to WROW (poly to metal2) - offsetL = vector(self.w_nmosL[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width, self.w_nmosL[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height) + # 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=offsetL) - + offset=left_gate_contact) self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetL) - + offset=left_gate_contact) self.add_rect_center(layer="poly", - offset=offsetL, + offset=left_gate_contact, width=contact.poly.width, height=contact.poly.height) - offsetR = vector(self.w_nmosR[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width, self.w_nmosR[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.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=offsetR) - + offset=right_gate_contact) self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetR) - + offset=right_gate_contact) self.add_rect_center(layer="poly", - offset=offsetR, + offset=right_gate_contact, width=contact.poly.width, - height=contact.poly.height) + height=contact.poly.height) - # connect gate of wt to contact - midL = vector(offsetL.x, self.w_nmosL[k].get_pin("G").lc().y) - self.add_path("poly", [self.w_nmosL[k].get_pin("G").lc(), midL, offsetL]) + # 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(offsetR.x, self.w_nmosR[k].get_pin("G").rc().y) - self.add_path("poly", [self.w_nmosR[k].get_pin("G").rc(), midR, offsetR]) + 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 contacts to WROW lines - offset = vector(offsetL.x, self.wrow_positions[k].y + 0.5*drc["minwidth_metal1"]) + # 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=offset, + offset=left_wrow_contact, rotate=90) - offset = vector(offsetR.x, self.wrow_positions[k].y + 0.5*drc["minwidth_metal1"]) + 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=offset, + offset=right_wrow_contact, rotate=90) - # connect wt gate contacts to WROW contacts - wrow_offset = vector(offsetL.x, self.wrow_positions[k].y) - self.add_path("metal2", [offsetL, wrow_offset]) - - wrow_offset = vector(offsetR.x, self.wrow_positions[k].y) - self.add_path("metal2", [offsetR, wrow_offset]) - - + # 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 wt - offsetL = vector(self.i_nmosL.get_pin("G").lc().x - drc["poly_to_field_poly"] - 0.5*contact.poly.width, self.cross_heightL) + # 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=offsetL, + offset=left_storage_contact, rotate=90) - offsetR = vector(self.i_nmosR.get_pin("G").rc().x + drc["poly_to_field_poly"] + 0.5*contact.poly.width, self.cross_heightL) + 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=offsetR, + offset=right_storage_contact, rotate=90) - # connect gate of inverters to contacts - gate_offsetL = vector(self.i_nmosL.get_pin("G").lc().x, offsetL.y) - self.add_path("poly", [offsetL, gate_offsetL]) + # 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.i_nmosR.get_pin("G").rc().x, offsetR.y) - self.add_path("poly", [offsetR, gate_offsetR]) + 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 wt - midL0 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], offsetL.y) - midL1 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.w_nmosL[k].get_pin("D").lc().y) - self.add_path("metal1", [offsetL, midL0, midL1, self.w_nmosL[k].get_pin("D").lc()]) + # 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(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], offsetR.y) - midR1 = vector(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.w_nmosR[k].get_pin("D").rc().y) - self.add_path("metal1", [offsetR, midR0, midR1, self.w_nmosR[k].get_pin("D").rc()]) + 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_transistors(self): - """ Define variables relevant to read transistors """ - lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) - rit_xpos = 1.5*parameter["min_tx_size"] + self.i_nmos.active_width - lwt_xpos = lit_xpos - self.num_write*self.w_tile_width - rwt_xpos = rit_xpos + self.num_write*self.w_tile_width - wrow_ypos = self.gnd_position.y - self.num_write*2*drc["minwidth_metal2"] + 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. + """ - rot_correct = self.r_nmos.active_height - r_spacer = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width - rw_spacer = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.w_ex + """ Define variables relevant to read transistors """ + # define offset correction due to rotation of the ptx cell + read_rotation_correct = self.read_nmos.active_height - self.r_nmosL = [None] * self.num_read - self.r_nmosR = [None] * self.num_read + # 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 and RROW lines """ + """ Add transistors """ # calculate transistor offsets - lrt_xpos = lwt_xpos - rw_spacer - self.r_nmos.active_height - k*self.r_tile_width + rot_correct - rrt_xpos = rwt_xpos + rw_spacer + k*self.r_tile_width + rot_correct + 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.r_nmosL[k] = self.add_inst(name="r_nmosL", - mod=self.r_nmos, - offset=[lrt_xpos,0], - rotate=90) - self.connect_inst(["RROW{}".format(k), "Q", "RBL{}".format(k), "gnd"]) + 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.r_nmosR[k] = self.add_inst(name="r_nmosR", - mod=self.r_nmos, - offset=[rrt_xpos,0], - rotate=90) - self.connect_inst(["RROW{}".format(k), "Q_bar", "RBL_bar{}".format(k), "gnd"]) - - # add RROW lines - rrow_ypos = wrow_ypos - (k+1)*2*drc["minwidth_metal2"] + 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) - self.add_layout_pin(text="RROW{}".format(k), + + # add pin for RROW + self.add_layout_pin(text="rrow{}".format(k), layer="metal1", offset=self.rrow_positions[k], width=self.cell_width, - height=drc["minwidth_metal1"]) + 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]) - - """ Source of RA transistor / GND connection """ - offset = vector(self.r_nmosL[k].get_pins("S")[0].bc().x, self.gnd_position.y) - self.add_path("metal1", [self.r_nmosL[k].get_pins("S")[0].bc(), offset]) - - offset = vector(self.r_nmosR[k].get_pins("S")[0].bc().x, self.gnd_position.y) - self.add_path("metal1", [self.r_nmosR[k].get_pins("S")[0].bc(), offset]) - - - """ Source of R transistor / RBL & RBL_bar connection """ - # add contacts to connect source of rt to RBL or RBL_bar - offsetL = self.r_nmosL[k].get_pins("S")[1].center() + 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.r_nmosR[k].get_pins("S")[1].center() + offsetR = self.read_nmos_right[k].get_pin("D").center() self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=offsetR, rotate=90) - # add RBL and RBL_bar (simultaneously connects to source of rt) - self.rbl_positions[k] = vector(self.r_nmosL[k].get_pins("S")[1].center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text="RBL{}".format(k), + # 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.r_nmosR[k].get_pins("S")[1].center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text="RBL_bar{}".format(k), + 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) - - """ Gate of R transistor / RROW connection """ - # add contact to connect gate of rt to RROW (poly to metal2) - offsetL = vector(lrt_xpos - rot_correct - drc["minwidth_poly"] - 0.5*contact.poly.width, self.r_nmosL[k].get_pins("G")[1].lc().y) self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=offsetL) + offset=left_gate_contact) self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetL) - - offsetR = vector(rrt_xpos + drc["minwidth_poly"] + 0.5*contact.poly.width, self.r_nmosR[k].get_pins("G")[1].rc().y) + 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=offsetR) + offset=right_gate_contact) self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetR) + offset=right_gate_contact) - # connect gate of rt to contact - self.add_path("poly", [self.r_nmosL[k].get_pins("G")[1].lc(), offsetL]) - self.add_path("poly", [self.r_nmosR[k].get_pins("G")[1].rc(), offsetR]) + # 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 contacts to RROW lines - row_offsetL = vector(self.r_nmosL[k].get_pins("S")[1].lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width, self.rrow_positions[k].y + 0.5*drc["minwidth_metal1"]) + # 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=row_offsetL, + offset=left_rrow_contact, rotate=90) - row_offsetR = vector(self.r_nmosR[k].get_pins("S")[1].rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width, self.rrow_positions[k].y + 0.5*drc["minwidth_metal1"]) + 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=row_offsetR, + offset=right_rrow_contact, rotate=90) - # connect rt gate contacts to RROW contacts - self.add_path("metal2", [offsetL, row_offsetL]) - self.add_path("metal2", [offsetR, row_offsetR]) - - - """ Gate of RA transistor / storage connection """ - # add contact to connect gate of rat to output of inverters - offsetL = vector(lrt_xpos + drc["minwidth_poly"] + 0.5*contact.poly.width, self.r_nmosL[k].get_pins("G")[0].rc().y) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=offsetL) + # 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]) - offsetR = vector(rrt_xpos - rot_correct - drc["minwidth_poly"] - 0.5*contact.poly.width, self.r_nmosR[k].get_pins("G")[0].lc().y) + """ 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=offsetR) + offset=left_gate_contact) - # connect gate of rat to contact - self.add_path("poly", [self.r_nmosL[k].get_pins("G")[0].rc(), offsetL]) - self.add_path("poly", [self.r_nmosR[k].get_pins("G")[0].lc(), offsetR]) + 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) - # connect contact to output of inverters - midL0 = vector(offsetL.x, self.r_nmosL[k].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"]) - midL1 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.r_nmosL[0].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"]) - midL2 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.cross_heightU) - gate_offsetL = vector(self.i_nmosL.get_pin("D").center().x, self.cross_heightU) - self.add_path("metal1", [offsetL, midL0, midL1, midL2, gate_offsetL]) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=right_gate_contact) - midR0 = vector(offsetR.x, self.r_nmosR[k].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"]) - midR1 = vector(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.r_nmosR[k].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"]) - midR2 = vector(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.cross_heightU) - gate_offsetR = vector(self.i_nmosR.get_pin("S").center().x, self.cross_heightU) - self.add_path("metal1", [offsetR, midR0, midR1, midR2, gate_offsetR]) + # 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 nwell and pwell """ - # extend pwell to encompass i_nmos + """ 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.i_nmos.well_height - drc["well_enclosure_active"] + 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 to encompass r_nmos - r_well_width = self.num_read*self.r_tile_width - r_well_height = self.r_nmos.well_width - drc["well_enclosure_active"] - offset = vector(self.leftmost_xpos, 0) - self.add_rect(layer="pwell", - offset=offset, - width=r_well_width, - height=r_well_height) - - offset = vector(-self.leftmost_xpos - r_well_width, 0) - self.add_rect(layer="pwell", - offset=offset, - width=r_well_width, - height=r_well_height) - - # extend pwell to encompass w_nmos - lwt_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"] + self.w_tile_width - self.w_nmos.active_height - drc["well_enclosure_active"]) - rwt_xpos = 1.5*parameter["min_tx_size"] + self.i_nmos.active_width + self.w_tile_width - self.w_nmos.active_height - drc["well_enclosure_active"] + """ 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"] - w_well_width = -(self.leftmost_xpos - lwt_xpos) - w_well_height = self.w_nmos.well_width - drc["well_enclosure_active"] - offset = vector(lwt_xpos - w_well_width, 0) - self.add_rect(layer="pwell", - offset=offset, - width=w_well_width, - height=w_well_height) - - offset = vector(rwt_xpos, 0) - self.add_rect(layer="pwell", - offset=offset, - width=w_well_width, - height=w_well_height) + write_well_width = -(self.leftmost_xpos - left_write_well_xpos) + write_well_height = self.write_nmos.cell_well_width - drc["well_enclosure_active"] - # extend nwell to encompass i_pmos - lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"] + drc["well_enclosure_active"]) - it_ypos = self.inv_gap + self.i_nmos.active_height - drc["well_enclosure_active"] - offset = [lit_xpos,it_ypos] - well_width = 2*self.i_pmos.active_width + 3*parameter["min_tx_size"] + 2*drc["well_enclosure_active"] - well_height = self.vdd_position.y - it_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"] + 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, @@ -547,7 +646,7 @@ class pbitcell(design.design): """ add well contacts """ # connect pimplants to gnd - offset = vector(0, self.gnd_position.y + 0.5*drc["minwidth_metal1"]) + 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) @@ -570,7 +669,7 @@ class pbitcell(design.design): def add_fail(self): - # for failing drc + # 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"] diff --git a/compiler/tests/04_pbitcell_0R_test.py b/compiler/tests/04_pbitcell_0R_test.py new file mode 100644 index 00000000..cb274548 --- /dev/null +++ b/compiler/tests/04_pbitcell_0R_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python2.7 +""" +Run regresion tests on a parameterized bitcell +""" + +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 + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 04_pbitcell_test") + + +class pbitcell_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + global verify + import verify + OPTS.check_lvsdrc = False + + import pbitcell + import tech + + debug.info(2, "Bitcell with 2 write ports and 0 read ports") + tx = pbitcell.pbitcell(num_write=2,num_read=0) + self.local_check(tx) + + OPTS.check_lvsdrc = True + globals.end_openram() + + +# instantiate a copy 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/04_pbitcell_0W_test.py b/compiler/tests/04_pbitcell_0W_test.py deleted file mode 100644 index 38b1dd25..00000000 --- a/compiler/tests/04_pbitcell_0W_test.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python2.7 -""" -Run regresion tests on a parameterized inverter -""" - -import unittest -from testutils import header -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -import debug -import verify - -OPTS = globals.OPTS - -#@unittest.skip("SKIPPING 04_pbitcell_test") - - -class pbitcell_test(unittest.TestCase): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - OPTS.check_lvsdrc = False - - import pbitcell - import tech - - debug.info(2, "Test for pbitcell with 0 write ports and 2 read ports") - tx = pbitcell.pbitcell(num_write=0,num_read=2) - self.local_check(tx) - - OPTS.check_lvsdrc = True - globals.end_openram() - - def local_check(self, tx): - tempspice = OPTS.openram_temp + "temp.sp" - tempgds = OPTS.openram_temp + "temp.gds" - - tx.sp_write(tempspice) - tx.gds_write(tempgds) - - self.assertFalse(verify.run_drc(tx.name, tempgds)) - self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice)) - - os.remove(tempspice) - os.remove(tempgds) - - # reset the static duplicate name checker for unit tests - import design - design.design.name_map=[] - - - - -# instantiate a copy 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/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 3ba42b76..36c251b5 100644 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -1,55 +1,38 @@ #!/usr/bin/env python2.7 """ -Run regresion tests on a parameterized inverter +Run regresion tests on a parameterized bitcell """ import unittest -from testutils import header +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 -import verify OPTS = globals.OPTS #@unittest.skip("SKIPPING 04_pbitcell_test") -class pbitcell_test(unittest.TestCase): +class pbitcell_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + global verify + import verify OPTS.check_lvsdrc = False import pbitcell import tech - debug.info(2, "Test for pbitcell with 2 write ports and 2 read ports") + debug.info(2, "Bitcell with 2 write ports and 2 read ports") tx = pbitcell.pbitcell(num_write=2,num_read=2) self.local_check(tx) OPTS.check_lvsdrc = True - globals.end_openram() - - def local_check(self, tx): - #tempspice = OPTS.openram_temp + "temp.sp" - tempgds = OPTS.openram_temp + "temp.gds" - - #tx.sp_write(tempspice) - tx.gds_write(tempgds) - - self.assertFalse(verify.run_drc(tx.name, tempgds)) - #self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice)) - - #os.remove(tempspice) - os.remove(tempgds) - - # reset the static duplicate name checker for unit tests - import design - design.design.name_map=[] - - + globals.end_openram() # instantiate a copy of the class to actually run the test From d41abb3074b07d46fc348b6e2106528ca4b50fca Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 28 Feb 2018 11:25:22 -0800 Subject: [PATCH 05/46] moved pbitcell to new folder for parametrically sized cells --- compiler/pgates/pbitcell.py | 688 ++++++++++++++++++++++++++++++++++++ 1 file changed, 688 insertions(+) create mode 100644 compiler/pgates/pbitcell.py diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py new file mode 100644 index 00000000..871b5ef0 --- /dev/null +++ b/compiler/pgates/pbitcell.py @@ -0,0 +1,688 @@ +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) From 1ba626fce12f9152c7e08832dd6ffa7e98705e94 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 28 Feb 2018 11:28:04 -0800 Subject: [PATCH 06/46] removed pbitcell for compiler folder --- compiler/pbitcell.py | 688 ------------------------------------------- 1 file changed, 688 deletions(-) delete mode 100644 compiler/pbitcell.py 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) From d6ef91786b46275434bf26676aa795645ff01aab Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 28 Feb 2018 17:56:13 -0800 Subject: [PATCH 07/46] updating pbitcell with latest layout optimizations --- compiler/pgates/pbitcell.py | 229 +++++++++++++++++++++--------------- 1 file changed, 131 insertions(+), 98 deletions(-) diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index 871b5ef0..00d75a21 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -15,7 +15,7 @@ class pbitcell(pgate.pgate): 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)) + 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 @@ -24,20 +24,23 @@ class pbitcell(pgate.pgate): 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)) + if(self.num_read > 0): + 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() @@ -46,16 +49,19 @@ class pbitcell(pgate.pgate): if(self.num_read > 0): self.add_read_ports() self.extend_well() + self.offset_all_coordinates() #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 there are no read ports then write transistors are being used as read/write ports, like in a 6T cell 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"] + read_nmos_width = parameter["min_tx_size"] # read transistor not necessary but included as to not generate errors in the code when referenced + # used for the dual port case where there are separate write and read ports else: inverter_nmos_width = 2*parameter["min_tx_size"] @@ -84,17 +90,29 @@ class pbitcell(pgate.pgate): 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) + # 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.write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height) + + # calculation for transistor spacing (exact solutions) + #self.inverter_to_inverter_spacing = 3*parameter["min_tx_size"] + #self.inverter_to_write_spacing = need to calculate + #self.write_to_write_spacing = drc["minwidth_metal2"] + self.write_nmos_contact_extension + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + #self.write_to_read_spacing = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.write_nmos_contact_extension + #self.read_to_read_spacing = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width + + # calculation for transistor spacing (symmetric solutions) + self.inverter_to_inverter_spacing = 3*parameter["min_tx_size"] + #self.inverter_to_write_spacing = need to calculate + spacing_option1 = contact.poly.width + 2*drc["poly_to_field_poly"] + 2*drc["poly_extend_active"] + spacing_option2 = contact.poly.width + 2*drc["minwidth_metal2"] + 2*self.write_nmos_contact_extension + self.write_to_write_spacing = max(spacing_option1, spacing_option2) + self.write_to_read_spacing = drc["poly_to_field_poly"] + 2*contact.poly.width + 2*drc["minwidth_metal2"] + 2*self.write_nmos_contact_extension + self.read_to_read_spacing = drc["minwidth_metal1"] + 2*contact.poly.width + 2*drc["minwidth_poly"] - # 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.inverter_tile_width = self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing 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 @@ -102,28 +120,34 @@ class pbitcell(pgate.pgate): 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.inverter_gap = drc["poly_to_active"] + drc["poly_to_field_poly"] + 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_field_poly"] + 1.5*contact.poly.width - # calculations for the edges of the cell + """ Calculations for the edges of the cell """ + # create a flag for excluding read port calculations if they are not included in the bitcell if(self.num_read > 0): - read_port_flag = 1; + read_port_flag = 1 else: - read_port_flag = 0; - + read_port_flag = 0 + + # leftmost position = storage width + write ports width + read ports width + read transistor gate connections + metal spacing necessary for tiling the bitcell 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"] + - 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 + # bottommost position = gnd height + wrow height + rrow height 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"] + # 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.inverter_pmos_contact_extension + 2*drc["minwidth_metal1"] # calculations for the cell dimensions self.cell_width = -2*self.leftmost_xpos @@ -133,13 +157,13 @@ class pbitcell(pgate.pgate): 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". + The stored value of the cell is denoted as "Q", and the inverted value 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 + 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 # create active for nmos self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", @@ -168,31 +192,31 @@ class pbitcell(pgate.pgate): 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) + self.add_path("metal1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], width=contact.well.second_layer_width) + self.add_path("metal1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], width=contact.well.second_layer_width) # 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) + contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.height, self.cross_couple_upper_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=offsetL, + offset=contact_offset_left, rotate=90) - offsetR = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.width, self.cross_couple_lower_ypos) + contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.height, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=offsetR, + offset=contact_offset_right, 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_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, contact_offset_left.y) + self.add_path("poly", [contact_offset_left, gate_offset_right]) - gate_offsetL = vector(self.inverter_nmos_left.get_pin("G").rc().x, offsetR.y) - self.add_path("poly", [offsetR, gate_offsetL]) - + 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]) + def add_rails(self): """ - Adds gnd and vdd rails and connects them to the storage element + Add gnd and vdd rails and connects them to the inverters """ """ Add rails for vdd and gnd """ @@ -211,19 +235,19 @@ class pbitcell(pgate.pgate): 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]) + # 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_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]) + 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 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]) + # 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_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]) + 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 add_write_ports(self): @@ -251,10 +275,12 @@ class pbitcell(pgate.pgate): """ Add transistors """ # calculate write transistor offsets left_write_transistor_xpos = -self.inverter_tile_width \ - - (k+1)*self.write_tile_width + write_rotation_correct + - (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 + + 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), @@ -283,14 +309,14 @@ class pbitcell(pgate.pgate): """ 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() + offset_left = self.write_nmos_left[k].get_pin("S").center() self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetL, + offset=offset_left, rotate=90) - offsetR = self.write_nmos_right[k].get_pin("S").center() + offset_right = self.write_nmos_right[k].get_pin("S").center() self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetR, + offset=offset_right, rotate=90) # add pins for WBL and WBL_bar, overlaid on source contacts @@ -310,6 +336,7 @@ class pbitcell(pgate.pgate): """ Gate/WROW connections """ # add poly-to-meltal2 contacts to connect gate of write transistors to WROW (contact next to gate) + # contact must be placed a metal width below the source pin to avoid drc from routing to the source pins 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) @@ -317,12 +344,7 @@ class pbitcell(pgate.pgate): 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) - + offset=left_gate_contact) 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 @@ -331,18 +353,14 @@ class pbitcell(pgate.pgate): 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) + 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]) + 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]) + 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 WROW lines left_wrow_contact = vector(left_gate_contact.x, self.wrow_positions[k].y + 0.5*contact.m1m2.width) @@ -374,11 +392,11 @@ class pbitcell(pgate.pgate): 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]) + 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]) - 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]) + 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(left_storage_contact.x - 0.5*contact.poly.height - 1.5*drc["minwidth_metal1"], left_storage_contact.y) @@ -397,7 +415,7 @@ class pbitcell(pgate.pgate): 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 + The Read-Bitline (RBL) is precharged to high, and when the value of Q_bar is 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. """ @@ -424,11 +442,13 @@ class pbitcell(pgate.pgate): # 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 + - 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 + + 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", @@ -460,7 +480,7 @@ class pbitcell(pgate.pgate): # calculate RROW position rrow_ypos = self.gnd_position.y \ - self.num_write*self.rowline_tile_height \ - - (k+1)*self.rowline_tile_height + - (k+1)*self.rowline_tile_height self.rrow_positions[k] = vector(self.leftmost_xpos, rrow_ypos) # add pin for RROW @@ -469,25 +489,17 @@ class pbitcell(pgate.pgate): 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() + offset_left = self.read_nmos_left[k].get_pin("D").center() self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetL, + offset=offset_left, rotate=90) - offsetR = self.read_nmos_right[k].get_pin("D").center() + offset_right = self.read_nmos_right[k].get_pin("D").center() self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offsetR, + offset=offset_right, rotate=90) # add pins for RBL and RBL_bar, overlaid on drain contacts @@ -507,7 +519,7 @@ class pbitcell(pgate.pgate): """ 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_xpos = left_read_transistor_xpos - self.read_nmos.active_height - 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) @@ -543,7 +555,15 @@ class pbitcell(pgate.pgate): # 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]) - + + """ 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]) + """ 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 @@ -553,7 +573,7 @@ class pbitcell(pgate.pgate): 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_xpos = right_read_transistor_xpos - self.read_nmos.active_height - 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) @@ -565,13 +585,13 @@ class pbitcell(pgate.pgate): 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) + # mid0: metal1 path must route over the read transistors (above drain of read transistor) + # mid1: continue metal1 path horizontally until at inverter + # mid2: route 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"]) - ## 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]) @@ -583,6 +603,12 @@ class pbitcell(pgate.pgate): def extend_well(self): + """ + Connects wells between ptx cells to avoid drc spacing issues. + Since the pwell of the read ports rise higher than the pmos of the inverters, + the well connections must be done piecewise to avoid pwell and nwell overlap. + """ + """ 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"] @@ -592,9 +618,11 @@ class pbitcell(pgate.pgate): height=well_height) """ extend pwell over write transistors to the height of the write transistor well """ + # calculate the edge of the write transistor well closest to the center 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"] + # 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"] @@ -610,11 +638,13 @@ class pbitcell(pgate.pgate): width=write_well_width, height=write_well_height) - """ extend pwell over the read transistors to the height of the read transistor well """ + """ extend pwell over the read transistors to the height of the bitcell """ if(self.num_read > 0): + # calculate the edge of the read transistor well clostest to the center 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"] + # 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 @@ -631,10 +661,13 @@ class pbitcell(pgate.pgate): 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"] - well_width = 2*self.inverter_pmos.active_width + 3*parameter["min_tx_size"] + 2*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_height = self.vdd_position.y - inverter_well_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"] offset = [inverter_well_xpos,inverter_well_ypos] From d33dec4e9ea6653f40fdbb12b776ad863cae4b7b Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Fri, 2 Mar 2018 10:49:26 -0800 Subject: [PATCH 08/46] Separated add_globals function into add_ptx and add_globals --- compiler/pgates/pbitcell.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index 00d75a21..0afaa3c7 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -24,7 +24,7 @@ class pbitcell(pgate.pgate): self.create_layout() self.DRC_LVS() - + def add_pins(self): for k in range(0,self.num_write): self.add_pin("wrow{}".format(k)) @@ -40,8 +40,9 @@ class pbitcell(pgate.pgate): self.add_pin("vdd") self.add_pin("gnd") - + def create_layout(self): + self.create_ptx() self.add_globals() self.add_storage() self.add_rails() @@ -52,8 +53,7 @@ class pbitcell(pgate.pgate): self.offset_all_coordinates() #self.add_fail() - - def add_globals(self): + def create_ptx(self): """ Calculate transistor sizes """ # if there are no read ports then write transistors are being used as read/write ports, like in a 6T cell if(self.num_read == 0): @@ -88,7 +88,9 @@ class pbitcell(pgate.pgate): self.read_nmos = ptx(width=read_nmos_width, tx_type="nmos") self.add_mod(self.read_nmos) - + + + def add_globals(self): """ Define pbitcell global variables """ # 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) @@ -110,7 +112,6 @@ class pbitcell(pgate.pgate): self.write_to_read_spacing = drc["poly_to_field_poly"] + 2*contact.poly.width + 2*drc["minwidth_metal2"] + 2*self.write_nmos_contact_extension self.read_to_read_spacing = drc["minwidth_metal1"] + 2*contact.poly.width + 2*drc["minwidth_poly"] - # calculations for transistor tiling (includes transistor and spacing) self.inverter_tile_width = self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing self.write_tile_width = self.write_to_write_spacing + self.write_nmos.active_height @@ -150,8 +151,8 @@ class pbitcell(pgate.pgate): + self.inverter_pmos_contact_extension + 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 + self.width = -2*self.leftmost_xpos + self.height = self.topmost_ypos - self.botmost_ypos def add_storage(self): @@ -212,7 +213,7 @@ class pbitcell(pgate.pgate): 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]) - + def add_rails(self): """ From fc294cb282b85e756ef1587e6ffd9c2e3ae8691c Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Fri, 2 Mar 2018 10:53:29 -0800 Subject: [PATCH 09/46] Fixed cell height and width --- compiler/pgates/pbitcell.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index 0afaa3c7..f3befc66 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -225,14 +225,14 @@ class pbitcell(pgate.pgate): self.gnd = self.add_layout_pin(text="gnd", layer="metal1", offset=self.gnd_position, - width=self.cell_width, + width=self.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, + width=self.width, height=drc["minwidth_metal1"]) """ Connect inverters to rails """ @@ -305,7 +305,7 @@ class pbitcell(pgate.pgate): self.add_layout_pin(text="wrow{}".format(k), layer="metal1", offset=self.wrow_positions[k], - width=self.cell_width, + width=self.width, height=contact.m1m2.width) """ Source/WBL/WBL_bar connections """ @@ -326,14 +326,14 @@ class pbitcell(pgate.pgate): layer="metal2", offset=self.wbl_positions[k], width=drc["minwidth_metal2"], - height=self.cell_height) + 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="wbl_bar{}".format(k), layer="metal2", offset=self.wbl_bar_positions[k], width=drc["minwidth_metal2"], - height=self.cell_height) + height=self.height) """ Gate/WROW connections """ # add poly-to-meltal2 contacts to connect gate of write transistors to WROW (contact next to gate) @@ -488,7 +488,7 @@ class pbitcell(pgate.pgate): self.add_layout_pin(text="rrow{}".format(k), layer="metal1", offset=self.rrow_positions[k], - width=self.cell_width, + width=self.width, height=contact.m1m2.width) """ Drain of read transistor / RBL & RBL_bar connection """ @@ -509,14 +509,14 @@ class pbitcell(pgate.pgate): layer="metal2", offset=self.rbl_positions[k], width=drc["minwidth_metal2"], - height=self.cell_height) + 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="rbl_bar{}".format(k), layer="metal2", offset=self.rbl_bar_positions[k], width=drc["minwidth_metal2"], - height=self.cell_height) + height=self.height) """ Gate of read transistor / RROW connection """ # add poly-to-meltal2 contacts to connect gate of read transistors to RROW (contact next to gate) @@ -615,7 +615,7 @@ class pbitcell(pgate.pgate): 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, + width=self.width, height=well_height) """ extend pwell over write transistors to the height of the write transistor well """ From 820a8440c904622554fdb0e2bf2f15dadfeeb078 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Tue, 6 Mar 2018 16:36:11 -0800 Subject: [PATCH 10/46] adding unit test for bitcell array using pbitcell --- compiler/pgates/pbitcell.py | 29 +++++++++++-------- compiler/tests/04_pbitcell_test.py | 4 +++ ...l_0R_test.py => 05_pbitcell_array_test.py} | 20 +++++-------- 3 files changed, 29 insertions(+), 24 deletions(-) rename compiler/tests/{04_pbitcell_0R_test.py => 05_pbitcell_array_test.py} (62%) diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index f3befc66..65e005e8 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -26,17 +26,19 @@ class pbitcell(pgate.pgate): 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): + + for k in range(self.num_write): self.add_pin("wbl{}".format(k)) self.add_pin("wbl_bar{}".format(k)) - if(self.num_read > 0): - 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)) + for k in range(self.num_read): + self.add_pin("rbl{}".format(k)) + self.add_pin("rbl_bar{}".format(k)) + + for k in range(self.num_write): + self.add_pin("wrow{}".format(k)) + for k in range(self.num_read): + self.add_pin("rrow{}".format(k)) + self.add_pin("vdd") self.add_pin("gnd") @@ -51,6 +53,8 @@ class pbitcell(pgate.pgate): self.add_read_ports() self.extend_well() self.offset_all_coordinates() + #offset = vector(0, -0.5*drc["minwidth_metal2"]) + #self.translate_all(offset) #self.add_fail() def create_ptx(self): @@ -152,7 +156,7 @@ class pbitcell(pgate.pgate): # calculations for the cell dimensions self.width = -2*self.leftmost_xpos - self.height = self.topmost_ypos - self.botmost_ypos + self.height = self.topmost_ypos - self.botmost_ypos + 0.5*drc["minwidth_metal2"] - 0.5*drc["minwidth_metal1"] def add_storage(self): @@ -610,9 +614,10 @@ class pbitcell(pgate.pgate): the well connections must be done piecewise to avoid pwell and nwell overlap. """ + cell_well_tiling_offset = 0.5*drc["minwidth_metal2"] """ 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"] + offset = vector(self.leftmost_xpos, self.botmost_ypos - cell_well_tiling_offset) + well_height = -self.botmost_ypos + self.inverter_nmos.cell_well_height - drc["well_enclosure_active"] + cell_well_tiling_offset self.add_rect(layer="pwell", offset=offset, width=self.width, diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 36c251b5..86e3fb1f 100644 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -30,6 +30,10 @@ class pbitcell_test(openram_test): debug.info(2, "Bitcell with 2 write ports and 2 read ports") tx = pbitcell.pbitcell(num_write=2,num_read=2) self.local_check(tx) + + debug.info(2, "Bitcell with 2 write ports and 0 read ports") + tx = pbitcell.pbitcell(num_write=2,num_read=0) + self.local_check(tx) OPTS.check_lvsdrc = True globals.end_openram() diff --git a/compiler/tests/04_pbitcell_0R_test.py b/compiler/tests/05_pbitcell_array_test.py similarity index 62% rename from compiler/tests/04_pbitcell_0R_test.py rename to compiler/tests/05_pbitcell_array_test.py index cb274548..60b14ac8 100644 --- a/compiler/tests/04_pbitcell_0R_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python2.7 """ -Run regresion tests on a parameterized bitcell +Run a regresion test on a basic array """ import unittest @@ -11,12 +11,9 @@ import globals from globals import OPTS import debug -OPTS = globals.OPTS +#@unittest.skip("SKIPPING 05_array_test") -#@unittest.skip("SKIPPING 04_pbitcell_test") - - -class pbitcell_test(openram_test): +class array_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) @@ -24,17 +21,16 @@ class pbitcell_test(openram_test): import verify OPTS.check_lvsdrc = False - import pbitcell - import tech + import bitcell_array - debug.info(2, "Bitcell with 2 write ports and 0 read ports") - tx = pbitcell.pbitcell(num_write=2,num_read=0) - self.local_check(tx) + OPTS.bitcell = "pbitcell" + debug.info(2, "Testing 4x4 array for multiport bitcell") + a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) + self.local_check(a) OPTS.check_lvsdrc = True globals.end_openram() - # instantiate a copy of the class to actually run the test if __name__ == "__main__": (OPTS, args) = globals.parse_args() From 0ea5d0b6a7f0aca6144e508a689af6909d24170d Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Tue, 6 Mar 2018 17:03:21 -0800 Subject: [PATCH 11/46] making changes to bitcell_array to account for the addition nets from the multiported bitcells --- compiler/modules/bitcell_array.py | 180 ++++++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 12 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 44318206..03b787f4 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -30,9 +30,18 @@ class bitcell_array(design.design): self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] self.width = self.column_size*self.cell.width - self.add_pins() + if(OPTS.bitcell == "pbitcell"): + self.add_multiport_pins() + else: + self.add_pins() + self.create_layout() - self.add_layout_pins() + + if(OPTS.bitcell == "pbitcell"): + self.add_multiport_layout_pins() + else: + self.add_layout_pins() + self.DRC_LVS() def add_pins(self): @@ -43,6 +52,25 @@ class bitcell_array(design.design): self.add_pin("wl[{0}]".format(row)) self.add_pin("vdd") self.add_pin("gnd") + + def add_multiport_pins(self): + self.num_write = self.cell.num_write + self.num_read = self.cell.num_read + + for col in range(self.column_size): + for k in range(self.num_write): + self.add_pin("wbl{0}[{1}]".format(k,col)) + self.add_pin("wbl_bar{0}[{1}]".format(k,col)) + for k in range(self.num_read): + self.add_pin("rbl{0}[{1}]".format(k,col)) + self.add_pin("rbl_bar{0}[{1}]".format(k,col)) + for row in range(self.row_size): + for k in range(self.num_write): + self.add_pin("wrow{0}[{1}]".format(k,row)) + for k in range(self.num_read): + self.add_pin("rrow{0}[{1}]".format(k,row)) + self.add_pin("vdd") + self.add_pin("gnd") def create_layout(self): xoffset = 0.0 @@ -58,16 +86,31 @@ class bitcell_array(design.design): else: tempy = yoffset dir_key = "" - - self.cell_inst[row,col]=self.add_inst(name=name, - mod=self.cell, - offset=[xoffset, tempy], - mirror=dir_key) - self.connect_inst(["bl[{0}]".format(col), - "br[{0}]".format(col), - "wl[{0}]".format(row), - "vdd", - "gnd"]) + + if(OPTS.bitcell == "pbitcell"): + self.cell_inst[row,col]=self.add_inst(name=name, + mod=self.cell, + offset=[xoffset, tempy], + mirror=dir_key) + self.connect_inst(["wbl0[{0}]".format(col), + "wbl_bar0[{0}]".format(col), + "rbl0[{0}]".format(col), + "rbl_bar0[{0}]".format(col), + "wrow0[{0}]".format(row), + "rrow0[{0}]".format(row), + "vdd", + "gnd"]) + else: + self.cell_inst[row,col]=self.add_inst(name=name, + mod=self.cell, + offset=[xoffset, tempy], + mirror=dir_key) + self.connect_inst(["bl[{0}]".format(col), + "br[{0}]".format(col), + "wl[{0}]".format(row), + "vdd", + "gnd"]) + yoffset += self.cell.height xoffset += self.cell.width @@ -161,6 +204,119 @@ class bitcell_array(design.design): # increments to the next row height offset.y += self.cell.height + + def add_multiport_layout_pins(self): + + # Our cells have multiple gnd pins for now. + # FIXME: fix for multiple vdd too + vdd_pin = self.cell.get_pin("vdd") + + # shift it up by the overlap amount (gnd_pin) too + # must find the lower gnd pin to determine this overlap + lower_y = self.cell.height + gnd_pins = self.cell.get_pins("gnd") + for gnd_pin in gnd_pins: + if gnd_pin.layer=="metal2" and gnd_pin.by() Date: Thu, 8 Mar 2018 16:39:26 -0800 Subject: [PATCH 12/46] fixed bitcell_array to work with different sized pbitcells, changed sizing in pbitcell to minimize space between inverters --- compiler/modules/bitcell_array.py | 24 ++++++--- compiler/pgates/pbitcell.py | 65 ++++++++++++------------ compiler/tests/05_pbitcell_array_test.py | 4 +- 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 03b787f4..5b41ccff 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -88,18 +88,26 @@ class bitcell_array(design.design): dir_key = "" if(OPTS.bitcell == "pbitcell"): + bitcell_nets = [] + for k in range(self.num_write): + bitcell_nets.append("wbl{0}[{1}]".format(k,col)) + bitcell_nets.append("wbl_bar{0}[{1}]".format(k,col)) + for k in range(self.num_read): + bitcell_nets.append("rbl{0}[{1}]".format(k,col)) + bitcell_nets.append("rbl_bar{0}[{1}]".format(k,col)) + for k in range(self.num_write): + bitcell_nets.append("wrow{0}[{1}]".format(k,row)) + for k in range(self.num_read): + bitcell_nets.append("rrow{0}[{1}]".format(k,row)) + bitcell_nets.append("vdd") + bitcell_nets.append("gnd") + self.cell_inst[row,col]=self.add_inst(name=name, mod=self.cell, offset=[xoffset, tempy], mirror=dir_key) - self.connect_inst(["wbl0[{0}]".format(col), - "wbl_bar0[{0}]".format(col), - "rbl0[{0}]".format(col), - "rbl_bar0[{0}]".format(col), - "wrow0[{0}]".format(row), - "rrow0[{0}]".format(row), - "vdd", - "gnd"]) + self.connect_inst(bitcell_nets) + else: self.cell_inst[row,col]=self.add_inst(name=name, mod=self.cell, diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index 65e005e8..e76dd933 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -101,20 +101,20 @@ class pbitcell(pgate.pgate): self.write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height) # calculation for transistor spacing (exact solutions) - #self.inverter_to_inverter_spacing = 3*parameter["min_tx_size"] - #self.inverter_to_write_spacing = need to calculate - #self.write_to_write_spacing = drc["minwidth_metal2"] + self.write_nmos_contact_extension + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] - #self.write_to_read_spacing = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.write_nmos_contact_extension - #self.read_to_read_spacing = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width + 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"] + self.write_to_write_spacing = drc["minwidth_metal2"] + self.write_nmos_contact_extension + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + self.write_to_read_spacing = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.write_nmos_contact_extension + self.read_to_read_spacing = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width # calculation for transistor spacing (symmetric solutions) - self.inverter_to_inverter_spacing = 3*parameter["min_tx_size"] + #self.inverter_to_inverter_spacing = 3*parameter["min_tx_size"] #self.inverter_to_write_spacing = need to calculate - spacing_option1 = contact.poly.width + 2*drc["poly_to_field_poly"] + 2*drc["poly_extend_active"] - spacing_option2 = contact.poly.width + 2*drc["minwidth_metal2"] + 2*self.write_nmos_contact_extension - self.write_to_write_spacing = max(spacing_option1, spacing_option2) - self.write_to_read_spacing = drc["poly_to_field_poly"] + 2*contact.poly.width + 2*drc["minwidth_metal2"] + 2*self.write_nmos_contact_extension - self.read_to_read_spacing = drc["minwidth_metal1"] + 2*contact.poly.width + 2*drc["minwidth_poly"] + #spacing_option1 = contact.poly.width + 2*drc["poly_to_field_poly"] + 2*drc["poly_extend_active"] + #spacing_option2 = contact.poly.width + 2*drc["minwidth_metal2"] + 2*self.write_nmos_contact_extension + #self.write_to_write_spacing = max(spacing_option1, spacing_option2) + #self.write_to_read_spacing = drc["poly_to_field_poly"] + 2*contact.poly.width + 2*drc["minwidth_metal2"] + 2*self.write_nmos_contact_extension + #self.read_to_read_spacing = drc["minwidth_metal1"] + 2*contact.poly.width + 2*drc["minwidth_poly"] # calculations for transistor tiling (includes transistor and spacing) self.inverter_tile_width = self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing @@ -122,6 +122,7 @@ class pbitcell(pgate.pgate): 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"] + 0.5*(drc["minwidth_tx"] - drc["minwidth_metal1"]) + drc["minwidth_metal1"] self.rowline_tile_height = drc["minwidth_metal1"] + contact.m1m2.width # calculations related to inverter connections @@ -138,7 +139,7 @@ class pbitcell(pgate.pgate): # leftmost position = storage width + write ports width + read ports width + read transistor gate connections + metal spacing necessary for tiling the bitcell self.leftmost_xpos = -self.inverter_tile_width \ - - self.num_write*self.write_tile_width \ + - self.inverter_to_write_spacing - self.write_nmos.active_height - (self.num_write-1)*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"] @@ -146,13 +147,13 @@ class pbitcell(pgate.pgate): self.rightmost_xpos = -self.leftmost_xpos # bottommost position = gnd height + wrow height + rrow height - self.botmost_ypos = -self.rowline_tile_height \ + self.botmost_ypos = -self.rail_tile_height \ - self.num_write*self.rowline_tile_height \ - read_port_flag*(self.num_read*self.rowline_tile_height) # 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.inverter_pmos_contact_extension + 2*drc["minwidth_metal1"] + + self.rail_tile_height # calculations for the cell dimensions self.width = -2*self.leftmost_xpos @@ -225,14 +226,17 @@ class pbitcell(pgate.pgate): """ """ Add rails for vdd and gnd """ - self.gnd_position = vector(self.leftmost_xpos, -self.rowline_tile_height) + 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) - self.vdd_position = vector(self.leftmost_xpos, self.inverter_pmos_left.get_pin("S").uc().y + drc["minwidth_metal1"]) + vdd_ypos = self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height \ + + drc["active_to_body_active"] + 0.5*(drc["minwidth_tx"] - drc["minwidth_metal1"]) + #vdd_ypos = self.inverter_pmos_left.get_pin("S").uc().y + drc["minwidth_metal1"] + self.vdd_position = vector(self.leftmost_xpos, vdd_ypos) self.vdd = self.add_layout_pin(text="vdd", layer="metal1", offset=self.vdd_position, @@ -280,11 +284,11 @@ class pbitcell(pgate.pgate): """ Add transistors """ # calculate write transistor offsets left_write_transistor_xpos = -self.inverter_tile_width \ - - (k+1)*self.write_tile_width \ + - self.inverter_to_write_spacing - self.write_nmos.active_height - k*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 \ + + self.inverter_to_write_spacing + k*self.write_tile_width \ + write_rotation_correct # add write transistors @@ -446,12 +450,12 @@ class pbitcell(pgate.pgate): """ Add transistors """ # calculate transistor offsets left_read_transistor_xpos = -self.inverter_tile_width \ - - self.num_write*self.write_tile_width \ + - self.inverter_to_write_spacing - self.write_nmos.active_height - (self.num_write-1)*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.inverter_to_write_spacing + self.write_nmos.active_height + (self.num_write-1)*self.write_tile_width \ + self.write_to_read_spacing + k*self.read_tile_width \ + read_rotation_correct @@ -708,20 +712,17 @@ class pbitcell(pgate.pgate): 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", + # for failing drc when I want to observe the gds layout + fail_position = vector(-4*drc["minwidth_metal1"], 0) # for tiling purposes + self.add_layout_pin(text="fail1", layer="metal1", offset=fail_position, - width=frail_width, - height=frail_height) + width=drc["minwidth_metal1"], + height=drc["minwidth_metal1"]) - fail_position2 = vector(-25*drc["minwidth_tx"], - 0.5 * drc["minwidth_metal1"]) - self.add_layout_pin(text="gnd2", + fail_position2 = vector(-4*drc["minwidth_metal1"], -1.5*drc["minwidth_metal1"]) + self.add_layout_pin(text="fail2", layer="metal1", offset=fail_position2, - width=frail_width, - height=frail_height) + width=drc["minwidth_metal1"], + height=drc["minwidth_metal1"]) diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 60b14ac8..5fac6c06 100644 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -11,9 +11,9 @@ import globals from globals import OPTS import debug -#@unittest.skip("SKIPPING 05_array_test") +#@unittest.skip("SKIPPING 05_array_multiport_test") -class array_test(openram_test): +class array_multiport_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) From 0cc077598efc0eb7457c7150b5a53dcee052d712 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Thu, 15 Mar 2018 12:02:38 -0700 Subject: [PATCH 13/46] Added member functions to bitcell.py and pbitcell.py for use in bitcell_array.py. bitcell_array now used only one function for every type of bitcell. --- compiler/modules/bitcell.py | 23 +++ compiler/modules/bitcell_array.py | 234 +++++------------------------- compiler/pgates/pbitcell.py | 57 +++++++- 3 files changed, 110 insertions(+), 204 deletions(-) diff --git a/compiler/modules/bitcell.py b/compiler/modules/bitcell.py index 90269692..9f110a41 100644 --- a/compiler/modules/bitcell.py +++ b/compiler/modules/bitcell.py @@ -34,3 +34,26 @@ class bitcell(design.design): c_para = spice["min_tx_drain_c"] result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = swing) return result + + 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 = ["bl[{0}]".format(col), + "br[{0}]".format(col), + "wl[{0}]".format(row), + "vdd", + "gnd"] + return bitcell_pins + + + def list_row_pins(self): + # Creates a list of row pins + row_pins = ["WL"] + return row_pins + + + def list_column_pins(self): + # Creates a list of column pins + column_pins = ["BL", "BR"] + return column_pins + + \ No newline at end of file diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 5b41ccff..83a0e236 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -30,47 +30,25 @@ class bitcell_array(design.design): self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] self.width = self.column_size*self.cell.width - if(OPTS.bitcell == "pbitcell"): - self.add_multiport_pins() - else: - self.add_pins() - + self.add_pins() self.create_layout() - - if(OPTS.bitcell == "pbitcell"): - self.add_multiport_layout_pins() - else: - self.add_layout_pins() + self.add_layout_pins() self.DRC_LVS() - + + def add_pins(self): + row_list = self.cell.list_row_pins() + column_list = self.cell.list_column_pins() for col in range(self.column_size): - self.add_pin("bl[{0}]".format(col)) - self.add_pin("br[{0}]".format(col)) + for cell_column in column_list: + self.add_pin(cell_column+"[{0}]".format(col)) for row in range(self.row_size): - self.add_pin("wl[{0}]".format(row)) + for cell_row in row_list: + self.add_pin(cell_row+"[{0}]".format(row)) self.add_pin("vdd") self.add_pin("gnd") - def add_multiport_pins(self): - self.num_write = self.cell.num_write - self.num_read = self.cell.num_read - - for col in range(self.column_size): - for k in range(self.num_write): - self.add_pin("wbl{0}[{1}]".format(k,col)) - self.add_pin("wbl_bar{0}[{1}]".format(k,col)) - for k in range(self.num_read): - self.add_pin("rbl{0}[{1}]".format(k,col)) - self.add_pin("rbl_bar{0}[{1}]".format(k,col)) - for row in range(self.row_size): - for k in range(self.num_write): - self.add_pin("wrow{0}[{1}]".format(k,row)) - for k in range(self.num_read): - self.add_pin("rrow{0}[{1}]".format(k,row)) - self.add_pin("vdd") - self.add_pin("gnd") def create_layout(self): xoffset = 0.0 @@ -86,43 +64,17 @@ class bitcell_array(design.design): else: tempy = yoffset dir_key = "" - - if(OPTS.bitcell == "pbitcell"): - bitcell_nets = [] - for k in range(self.num_write): - bitcell_nets.append("wbl{0}[{1}]".format(k,col)) - bitcell_nets.append("wbl_bar{0}[{1}]".format(k,col)) - for k in range(self.num_read): - bitcell_nets.append("rbl{0}[{1}]".format(k,col)) - bitcell_nets.append("rbl_bar{0}[{1}]".format(k,col)) - for k in range(self.num_write): - bitcell_nets.append("wrow{0}[{1}]".format(k,row)) - for k in range(self.num_read): - bitcell_nets.append("rrow{0}[{1}]".format(k,row)) - bitcell_nets.append("vdd") - bitcell_nets.append("gnd") - - self.cell_inst[row,col]=self.add_inst(name=name, - mod=self.cell, - offset=[xoffset, tempy], - mirror=dir_key) - self.connect_inst(bitcell_nets) - - else: - self.cell_inst[row,col]=self.add_inst(name=name, - mod=self.cell, - offset=[xoffset, tempy], - mirror=dir_key) - self.connect_inst(["bl[{0}]".format(col), - "br[{0}]".format(col), - "wl[{0}]".format(row), - "vdd", - "gnd"]) + + self.cell_inst[row,col]=self.add_inst(name=name, + mod=self.cell, + offset=[xoffset, tempy], + mirror=dir_key) + self.connect_inst(self.cell.list_bitcell_pins(col, row)) yoffset += self.cell.height xoffset += self.cell.width - + def add_layout_pins(self): # Our cells have multiple gnd pins for now. @@ -147,21 +99,19 @@ class bitcell_array(design.design): # overlapping cells full_width = self.width - 2*lower_x + row_list = self.cell.list_row_pins() + column_list = self.cell.list_column_pins() + offset = vector(0.0, 0.0) for col in range(self.column_size): - # get the pin of the lower row cell and make it the full width - bl_pin = self.cell_inst[0,col].get_pin("BL") - br_pin = self.cell_inst[0,col].get_pin("BR") - self.add_layout_pin(text="bl[{0}]".format(col), - layer="metal2", - offset=bl_pin.ll(), - width=bl_pin.width(), - height=full_height) - self.add_layout_pin(text="br[{0}]".format(col), - layer="metal2", - offset=br_pin.ll(), - width=br_pin.width(), - height=full_height) + # get the pins of the lower row cell and make it the full width + for cell_column in column_list: + bl_pin = self.cell_inst[0,col].get_pin(cell_column) + self.add_layout_pin(text=cell_column+"[{0}]".format(col), + layer="metal2", + offset=bl_pin.ll(), + width=bl_pin.width(), + height=full_height) # gnd offset is 0 in our cell, but it be non-zero gnd_pins = self.cell_inst[0,col].get_pins("gnd") @@ -180,112 +130,17 @@ class bitcell_array(design.design): offset.x = 0.0 for row in range(self.row_size): - wl_pin = self.cell_inst[row,0].get_pin("WL") vdd_pins = self.cell_inst[row,0].get_pins("vdd") gnd_pins = self.cell_inst[row,0].get_pins("gnd") - - for gnd_pin in gnd_pins: - if gnd_pin.layer=="metal1": - self.add_layout_pin(text="gnd", - layer="metal1", - offset=gnd_pin.ll(), - width=full_width, - height=drc["minwidth_metal1"]) - - # add vdd label and offset - # only add to even rows to avoid duplicates - for vdd_pin in vdd_pins: - if row % 2 == 0 and vdd_pin.layer=="metal1": - self.add_layout_pin(text="vdd", - layer="metal1", - offset=vdd_pin.ll(), - width=full_width, - height=drc["minwidth_metal1"]) - - # add wl label and offset - self.add_layout_pin(text="wl[{0}]".format(row), - layer="metal1", - offset=wl_pin.ll(), - width=full_width, - height=wl_pin.height()) - - # increments to the next row height - offset.y += self.cell.height - - def add_multiport_layout_pins(self): - - # Our cells have multiple gnd pins for now. - # FIXME: fix for multiple vdd too - vdd_pin = self.cell.get_pin("vdd") - - # shift it up by the overlap amount (gnd_pin) too - # must find the lower gnd pin to determine this overlap - lower_y = self.cell.height - gnd_pins = self.cell.get_pins("gnd") - for gnd_pin in gnd_pins: - if gnd_pin.layer=="metal2" and gnd_pin.by() Date: Thu, 26 Apr 2018 14:03:03 -0700 Subject: [PATCH 14/46] syntax corrections to pbitcell and modifying transistor sizes --- compiler/pgates/pbitcell.py | 112 ++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index 1baa86c2..a8e41c81 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -12,7 +12,7 @@ class pbitcell(pgate.pgate): This module implements a parametrically sized multi-port bitcell """ - def __init__(self, num_write=1, num_read=1): + def __init__(self, num_write=1, num_read=4): 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)) @@ -35,9 +35,9 @@ class pbitcell(pgate.pgate): self.add_pin("rbl_bar{}".format(k)) for k in range(self.num_write): - self.add_pin("wrow{}".format(k)) + self.add_pin("wwl{}".format(k)) for k in range(self.num_read): - self.add_pin("rrow{}".format(k)) + self.add_pin("rwl{}".format(k)) self.add_pin("vdd") self.add_pin("gnd") @@ -61,7 +61,7 @@ class pbitcell(pgate.pgate): """ Calculate transistor sizes """ # if there are no read ports then write transistors are being used as read/write ports, like in a 6T cell if(self.num_read == 0): - inverter_nmos_width = 3*parameter["min_tx_size"] + inverter_nmos_width = self.num_write*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"] # read transistor not necessary but included as to not generate errors in the code when referenced @@ -146,7 +146,7 @@ class pbitcell(pgate.pgate): self.rightmost_xpos = -self.leftmost_xpos - # bottommost position = gnd height + wrow height + rrow height + space needed between tiled bitcells + # bottommost position = gnd 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_write*self.rowline_tile_height \ @@ -265,7 +265,7 @@ class pbitcell(pgate.pgate): 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. + A write is enabled by setting a Write-Rowline (WWL) 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. @@ -278,7 +278,7 @@ class pbitcell(pgate.pgate): # 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.wwl_positions = [None] * self.num_write self.wbl_positions = [None] * self.num_write self.wbl_bar_positions = [None] * self.num_write @@ -299,23 +299,23 @@ class pbitcell(pgate.pgate): mod=self.write_nmos, offset=[left_write_transistor_xpos,0], rotate=90) - self.connect_inst(["Q", "wrow{}".format(k), "wbl{}".format(k), "gnd"]) + self.connect_inst(["Q", "wwl{}".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"]) + self.connect_inst(["Q_bar", "wwl{}".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 WWL lines """ + # calculate WWL position + wwl_ypos = self.gnd_position.y - (k+1)*self.rowline_tile_height + self.wwl_positions[k] = vector(self.leftmost_xpos, wwl_ypos) - # add pin for WROW - self.add_layout_pin(text="wrow{}".format(k), + # add pin for WWL + self.add_layout_pin(text="wwl{}".format(k), layer="metal1", - offset=self.wrow_positions[k], + offset=self.wwl_positions[k], width=self.width, height=contact.m1m2.width) @@ -346,8 +346,8 @@ class pbitcell(pgate.pgate): width=drc["minwidth_metal2"], height=self.height) - """ Gate/WROW connections """ - # add poly-to-meltal2 contacts to connect gate of write transistors to WROW (contact next to gate) + """ 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 routing to the source pins 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 @@ -374,20 +374,20 @@ class pbitcell(pgate.pgate): 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 WROW lines - left_wrow_contact = vector(left_gate_contact.x, self.wrow_positions[k].y + 0.5*contact.m1m2.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_wrow_contact, + offset=left_wwl_contact, rotate=90) - right_wrow_contact = vector(right_gate_contact.x, self.wrow_positions[k].y + 0.5*contact.m1m2.width) + 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_wrow_contact, + offset=right_wwl_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]) + # 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]) """ Drain/Storage connections """ # this path only needs to be drawn once on the last iteration of the loop @@ -424,9 +424,9 @@ class pbitcell(pgate.pgate): """ 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 transistor is connected to RWL (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. + A read is enabled by setting a Read-Rowline (RWL) high, subsequently turning on the read transistor. The Read-Bitline (RBL) is precharged to high, and when the value of Q_bar is 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. @@ -444,7 +444,7 @@ class pbitcell(pgate.pgate): 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.rwl_positions = [None] * self.num_read self.rbl_positions = [None] * self.num_read self.rbl_bar_positions = [None] * self.num_read @@ -463,42 +463,42 @@ class pbitcell(pgate.pgate): + read_rotation_correct # add read-access transistors - self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left", + self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), 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", + self.read_access_nmos_right[k] = self.add_inst(name="read_access_nmos_right{}".format(k), 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", + self.read_nmos_left[k] = self.add_inst(name="read_nmos_left{}".format(k), 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.connect_inst(["rbl{}".format(k), "rwl{}".format(k), "RA_to_R_left{}".format(k), "gnd"]) - self.read_nmos_right[k] = self.add_inst(name="read_nmos_right", + self.read_nmos_right[k] = self.add_inst(name="read_nmos_right{}".format(k), 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"]) + self.connect_inst(["rbl_bar{}".format(k), "rwl{}".format(k), "RA_to_R_right{}".format(k), "gnd"]) - """ Add RROW lines """ - # calculate RROW position - rrow_ypos = self.gnd_position.y \ + """ Add RWL lines """ + # calculate RWL position + rwl_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) + self.rwl_positions[k] = vector(self.leftmost_xpos, rwl_ypos) - # add pin for RROW - self.add_layout_pin(text="rrow{}".format(k), + # add pin for RWL + self.add_layout_pin(text="rwl{}".format(k), layer="metal1", - offset=self.rrow_positions[k], + offset=self.rwl_positions[k], width=self.width, height=contact.m1m2.width) @@ -529,8 +529,8 @@ class pbitcell(pgate.pgate): width=drc["minwidth_metal2"], height=self.height) - """ Gate of read transistor / RROW connection """ - # add poly-to-meltal2 contacts to connect gate of read transistors to RROW (contact next to gate) + """ Gate of read transistor / RWL connection """ + # add poly-to-meltal2 contacts to connect gate of read transistors to RWL (contact next to gate) contact_xpos = left_read_transistor_xpos - self.read_nmos.active_height - 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) @@ -553,20 +553,20 @@ class pbitcell(pgate.pgate): 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) + # add metal1-to-metal2 contacts to RWL lines + left_rwl_contact = vector(left_gate_contact.x, self.rwl_positions[k].y + 0.5*contact.m1m2.width) self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_rrow_contact, + offset=left_rwl_contact, rotate=90) - right_rrow_contact = vector(right_gate_contact.x, self.rrow_positions[k].y + 0.5*contact.m1m2.width) + right_rwl_contact = vector(right_gate_contact.x, self.rwl_positions[k].y + 0.5*contact.m1m2.width) self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_rrow_contact, + offset=right_rwl_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]) + # 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) @@ -723,9 +723,9 @@ class pbitcell(pgate.pgate): bitcell_pins.append("rbl{0}[{1}]".format(k,col)) bitcell_pins.append("rbl_bar{0}[{1}]".format(k,col)) for k in range(self.num_write): - bitcell_pins.append("wrow{0}[{1}]".format(k,row)) + bitcell_pins.append("wwl{0}[{1}]".format(k,row)) for k in range(self.num_read): - bitcell_pins.append("rrow{0}[{1}]".format(k,row)) + bitcell_pins.append("rwl{0}[{1}]".format(k,row)) bitcell_pins.append("vdd") bitcell_pins.append("gnd") @@ -736,9 +736,9 @@ class pbitcell(pgate.pgate): # Creates a list of row pins row_pins = [] for k in range(self.num_write): - row_pins.append("wrow{0}".format(k)) + row_pins.append("wwl{0}".format(k)) for k in range(self.num_read): - row_pins.append("rrow{0}".format(k)) + row_pins.append("rwl{0}".format(k)) return row_pins From 683f5fb9fc769810cdf18d01fa000de48b1bb894 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Thu, 26 Apr 2018 14:03:48 -0700 Subject: [PATCH 15/46] adding variable for w_ports to be used in multiport design --- compiler/options.py | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/options.py b/compiler/options.py index 1899d085..b0b14dcc 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -48,6 +48,7 @@ class options(optparse.Values): # These are the configuration parameters rw_ports = 1 r_ports = 0 + w_ports = 0 # These will get initialized by the the file supply_voltages = "" temperatures = "" From 7af95e47230d191a6811bbf2ef079bd62b1e7558 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Thu, 10 May 2018 09:38:02 -0700 Subject: [PATCH 16/46] adding read/write port functionality to the design. Now the bitcell can have read/write, write, and read ports all at once. Changed unit tests to accomodate different combinations of ports. --- compiler/pgates/pbitcell.py | 367 ++++++++++++++++++++++---- compiler/tests/04_pbitcell_1X_test.py | 55 ++++ compiler/tests/04_pbitcell_test.py | 16 +- 3 files changed, 387 insertions(+), 51 deletions(-) create mode 100644 compiler/tests/04_pbitcell_1X_test.py diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index a8e41c81..5f222ab9 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -12,28 +12,43 @@ class pbitcell(pgate.pgate): This module implements a parametrically sized multi-port bitcell """ - def __init__(self, num_write=1, num_read=4): - name = "pbitcell_{0}W_{1}R".format(num_write, num_read) + width = None + height = None + + unique_id = 1 + + def __init__(self, num_readwrite=OPTS.rw_ports, num_write=OPTS.w_ports, num_read=OPTS.r_ports): + name = "pbitcell_{0}RW_{1}W_{2}R_{3}".format(num_readwrite, num_write, num_read, pbitcell.unique_id) + pbitcell.unique_id += 1 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_readwrite = num_readwrite self.num_write = num_write self.num_read = num_read self.add_pins() self.create_layout() self.DRC_LVS() + + pbitcell.width = self.width + pbitcell.height = self.height def add_pins(self): + for k in range(self.num_readwrite): + self.add_pin("rwbl{}".format(k)) + self.add_pin("rwbl_bar{}".format(k)) for k in range(self.num_write): self.add_pin("wbl{}".format(k)) self.add_pin("wbl_bar{}".format(k)) for k in range(self.num_read): self.add_pin("rbl{}".format(k)) self.add_pin("rbl_bar{}".format(k)) - + + for k in range(self.num_readwrite): + self.add_pin("rwwl{}".format(k)) for k in range(self.num_write): self.add_pin("wwl{}".format(k)) for k in range(self.num_read): @@ -48,28 +63,30 @@ class pbitcell(pgate.pgate): self.add_globals() self.add_storage() self.add_rails() - self.add_write_ports() + if(self.num_readwrite > 0): + self.add_readwrite_ports() + if(self.num_write > 0): + self.add_write_ports() if(self.num_read > 0): self.add_read_ports() self.extend_well() self.offset_all_coordinates() - #offset = vector(0, -0.5*drc["minwidth_metal2"]) - #self.translate_all(offset) - #self.add_fail() def create_ptx(self): """ Calculate transistor sizes """ - # if there are no read ports then write transistors are being used as read/write ports, like in a 6T cell - if(self.num_read == 0): - inverter_nmos_width = self.num_write*3*parameter["min_tx_size"] + # if there are any read/write ports, then the inverter nmos is sized based the number of them + if(self.num_readwrite > 0): + inverter_nmos_width = self.num_readwrite*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"] # read transistor not necessary but included as to not generate errors in the code when referenced - - # used for the dual port case where there are separate write and read ports + readwrite_nmos_width = 1.5*parameter["min_tx_size"] + write_nmos_width = parameter["min_tx_size"] + read_nmos_width = 2*parameter["min_tx_size"] + + # if there are no read/write ports, then the inverter nmos is 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"] @@ -83,6 +100,11 @@ class pbitcell(pgate.pgate): tx_type="pmos") self.add_mod(self.inverter_pmos) + # create ptx for readwrite transitors + self.readwrite_nmos = ptx(width=readwrite_nmos_width, + tx_type="nmos") + self.add_mod(self.readwrite_nmos) + # create ptx for write transitors self.write_nmos = ptx(width=write_nmos_width, tx_type="nmos") @@ -118,6 +140,7 @@ class pbitcell(pgate.pgate): # calculations for transistor tiling (includes transistor and spacing) self.inverter_tile_width = self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing + self.readwrite_tile_width = self.write_to_write_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 @@ -131,16 +154,30 @@ class pbitcell(pgate.pgate): 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 """ - # create a flag for excluding read port calculations if they are not included in the bitcell - if(self.num_read > 0): - read_port_flag = 1 + # create a flags for excluding readwrite, write, or read port calculations if they are not included in the bitcell + if(self.num_readwrite > 0): + self.readwrite_port_flag = 1 else: - read_port_flag = 0 + self.readwrite_port_flag = 0 + + if(self.num_write > 0): + self.write_port_flag = 1 + else: + self.write_port_flag = 0 + + if(self.num_read > 0): + self.read_port_flag = 1 + else: + self.read_port_flag = 0 # leftmost position = storage width + write ports width + read ports width + read transistor gate connections + metal spacing necessary for tiling the bitcell self.leftmost_xpos = -self.inverter_tile_width \ - - self.inverter_to_write_spacing - self.write_nmos.active_height - (self.num_write-1)*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) \ + - self.inverter_to_write_spacing \ + - self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-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_write-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_read-1)*self.read_tile_width) \ - drc["minwidth_poly"] - contact.m1m2.height \ - 0.5*drc["minwidth_metal2"] @@ -149,8 +186,9 @@ class pbitcell(pgate.pgate): # bottommost position = gnd 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_readwrite*self.rowline_tile_height \ - self.num_write*self.rowline_tile_height \ - - read_port_flag*(self.num_read*self.rowline_tile_height) \ + - self.num_read*self.rowline_tile_height \ - array_tiling_offset # topmost position = height of the inverter + height of vdd @@ -262,6 +300,166 @@ class pbitcell(pgate.pgate): self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) + def add_readwrite_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 (WWL) 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 + readwrite_rotation_correct = self.readwrite_nmos.active_height + + # define write transistor variables as empty arrays based on the number of write ports + self.readwrite_nmos_left = [None] * self.num_readwrite + self.readwrite_nmos_right = [None] * self.num_readwrite + self.rwwl_positions = [None] * self.num_readwrite + self.rwbl_positions = [None] * self.num_readwrite + self.rwbl_bar_positions = [None] * self.num_readwrite + + # iterate over the number of read/write ports + for k in range(0,self.num_readwrite): + """ Add transistors """ + # calculate write transistor offsets + left_readwrite_transistor_xpos = -self.inverter_tile_width \ + - self.inverter_to_write_spacing \ + - self.readwrite_nmos.active_height - k*self.readwrite_tile_width \ + + readwrite_rotation_correct + + right_readwrite_transistor_xpos = self.inverter_tile_width \ + + self.inverter_to_write_spacing \ + + k*self.readwrite_tile_width \ + + readwrite_rotation_correct + + # add write transistors + self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), + mod=self.readwrite_nmos, + offset=[left_readwrite_transistor_xpos,0], + rotate=90) + self.connect_inst(["Q", "rwwl{}".format(k), "rwbl{}".format(k), "gnd"]) + + self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k), + mod=self.readwrite_nmos, + offset=[right_readwrite_transistor_xpos,0], + rotate=90) + self.connect_inst(["Q_bar", "rwwl{}".format(k), "rwbl_bar{}".format(k), "gnd"]) + + """ Add WWL 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) + + # add pin for RWWL + self.add_layout_pin(text="rwwl{}".format(k), + layer="metal1", + offset=self.rwwl_positions[k], + width=self.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 + 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) + + # 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="rwbl{}".format(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="rwbl_bar{}".format(k), + layer="metal2", + offset=self.rwbl_bar_positions[k], + width=drc["minwidth_metal2"], + height=self.height) + + """ 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 routing to the source pins + contact_xpos = self.readwrite_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.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) + + contact_xpos = self.readwrite_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.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]) + + """ Drain/Storage connections """ + # this path only needs to be drawn once on the last iteration of the loop + if(k == self.num_readwrite-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) + 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(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.readwrite_nmos_left[k].get_pin("D").lc().y) + self.add_path("metal1", [left_storage_contact, midL0, midL1, self.readwrite_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.readwrite_nmos_right[k].get_pin("D").rc().y) + self.add_path("metal1", [right_storage_contact, midR0, midR1, self.readwrite_nmos_right[k].get_pin("D").rc()]) + + def add_write_ports(self): """ Adds write ports to the bit cell. A single transistor acts as the write port. @@ -287,11 +485,17 @@ class pbitcell(pgate.pgate): """ Add transistors """ # calculate write transistor offsets left_write_transistor_xpos = -self.inverter_tile_width \ - - self.inverter_to_write_spacing - self.write_nmos.active_height - k*self.write_tile_width \ + - self.inverter_to_write_spacing \ + - self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-1)*self.readwrite_tile_width) \ + - self.readwrite_port_flag*self.write_to_write_spacing \ + - self.write_nmos.active_height - k*self.write_tile_width \ + write_rotation_correct right_write_transistor_xpos = self.inverter_tile_width \ - + self.inverter_to_write_spacing + k*self.write_tile_width \ + + self.inverter_to_write_spacing \ + + self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-1)*self.readwrite_tile_width) \ + + self.readwrite_port_flag*self.write_to_write_spacing \ + + k*self.write_tile_width \ + write_rotation_correct # add write transistors @@ -309,7 +513,9 @@ class pbitcell(pgate.pgate): """ Add WWL lines """ # calculate WWL position - wwl_ypos = self.gnd_position.y - (k+1)*self.rowline_tile_height + wwl_ypos = self.gnd_position.y \ + - self.num_readwrite*self.rowline_tile_height \ + - (k+1)*self.rowline_tile_height self.wwl_positions[k] = vector(self.leftmost_xpos, wwl_ypos) # add pin for WWL @@ -453,13 +659,21 @@ class pbitcell(pgate.pgate): """ Add transistors """ # calculate transistor offsets left_read_transistor_xpos = -self.inverter_tile_width \ - - self.inverter_to_write_spacing - self.write_nmos.active_height - (self.num_write-1)*self.write_tile_width \ - - self.write_to_read_spacing - self.read_nmos.active_height - k*self.read_tile_width \ + - self.inverter_to_write_spacing \ + - self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-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_write-1)*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.inverter_to_write_spacing + self.write_nmos.active_height + (self.num_write-1)*self.write_tile_width \ - + self.write_to_read_spacing + k*self.read_tile_width \ + + self.inverter_to_write_spacing \ + + self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-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_write-1)*self.write_tile_width) \ + + self.write_to_read_spacing \ + + k*self.read_tile_width \ + read_rotation_correct # add read-access transistors @@ -491,6 +705,7 @@ class pbitcell(pgate.pgate): """ Add RWL lines """ # calculate RWL position rwl_ypos = self.gnd_position.y \ + - self.num_readwrite*self.rowline_tile_height \ - self.num_write*self.rowline_tile_height \ - (k+1)*self.rowline_tile_height self.rwl_positions[k] = vector(self.leftmost_xpos, rwl_ypos) @@ -596,19 +811,24 @@ class pbitcell(pgate.pgate): 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 inverter + # mid1: continue metal1 path horizontally until at first read access gate contact # mid2: route 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(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"]) + midL1 = vector(left_gate_contact0.x, self.read_nmos_left[0].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) midL2 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], 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(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"]) + midR1 = vector(right_gate_contact0.x, 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]) @@ -711,17 +931,22 @@ class pbitcell(pgate.pgate): offset=offset, width=drc["minwidth_tx"], height=drc["minwidth_tx"]) - + 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 = [] + for k in range(self.num_readwrite): + bitcell_pins.append("rwbl{0}[{1}]".format(k,col)) + bitcell_pins.append("rwbl_bar{0}[{1}]".format(k,col)) for k in range(self.num_write): bitcell_pins.append("wbl{0}[{1}]".format(k,col)) bitcell_pins.append("wbl_bar{0}[{1}]".format(k,col)) for k in range(self.num_read): bitcell_pins.append("rbl{0}[{1}]".format(k,col)) bitcell_pins.append("rbl_bar{0}[{1}]".format(k,col)) + for k in range(self.num_readwrite): + bitcell_pins.append("rwwl{0}[{1}]".format(k,row)) for k in range(self.num_write): bitcell_pins.append("wwl{0}[{1}]".format(k,row)) for k in range(self.num_read): @@ -735,6 +960,8 @@ class pbitcell(pgate.pgate): def list_row_pins(self): # Creates a list of row pins row_pins = [] + for k in range(self.num_readwrite): + row_pins.append("rwwl{0}".format(k)) for k in range(self.num_write): row_pins.append("wwl{0}".format(k)) for k in range(self.num_read): @@ -742,10 +969,33 @@ class pbitcell(pgate.pgate): return row_pins + def list_read_row_pins(self): + # Creates a list of row pins + row_pins = [] + for k in range(self.num_readwrite): + row_pins.append("rwwl{0}".format(k)) + for k in range(self.num_read): + row_pins.append("rwl{0}".format(k)) + + return row_pins + + def list_write_row_pins(self): + # Creates a list of row pins + row_pins = [] + for k in range(self.num_readwrite): + row_pins.append("rwwl{0}".format(k)) + for k in range(self.num_write): + row_pins.append("wwl{0}".format(k)) + + return row_pins + def list_column_pins(self): # Creates a list of column pins column_pins = [] + for k in range(self.num_readwrite): + column_pins.append("rwbl{0}".format(k)) + column_pins.append("rwbl_bar{0}".format(k)) for k in range(self.num_write): column_pins.append("wbl{0}".format(k)) column_pins.append("wbl_bar{0}".format(k)) @@ -755,19 +1005,42 @@ class pbitcell(pgate.pgate): return column_pins + def list_read_column_pins(self): + # Creates a list of column pins + column_pins = [] + for k in range(self.num_readwrite): + column_pins.append("rwbl{0}".format(k)) + for k in range(self.num_read): + column_pins.append("rbl{0}".format(k)) + + return column_pins + + def list_read_bar_column_pins(self): + # Creates a list of column pins + column_pins = [] + for k in range(self.num_readwrite): + column_pins.append("rwbl_bar{0}".format(k)) + for k in range(self.num_read): + column_pins.append("rbl_bar{0}".format(k)) + + return column_pins + + def list_write_column_pins(self): + # Creates a list of column pins + column_pins = [] + for k in range(self.num_readwrite): + column_pins.append("rwbl{0}".format(k)) + for k in range(self.num_write): + column_pins.append("wbl{0}".format(k)) + + return column_pins - def add_fail(self): - # for failing drc when I want to observe the gds layout - fail_position = vector(-4*drc["minwidth_metal1"], 0) # for tiling purposes - self.add_layout_pin(text="fail1", - layer="metal1", - offset=fail_position, - width=drc["minwidth_metal1"], - height=drc["minwidth_metal1"]) - - fail_position2 = vector(-4*drc["minwidth_metal1"], -1.5*drc["minwidth_metal1"]) - self.add_layout_pin(text="fail2", - layer="metal1", - offset=fail_position2, - width=drc["minwidth_metal1"], - height=drc["minwidth_metal1"]) + def list_write_bar_column_pins(self): + # Creates a list of column pins + column_pins = [] + for k in range(self.num_readwrite): + column_pins.append("rwbl_bar{0}".format(k)) + for k in range(self.num_write): + column_pins.append("wbl_bar{0}".format(k)) + + return column_pins diff --git a/compiler/tests/04_pbitcell_1X_test.py b/compiler/tests/04_pbitcell_1X_test.py new file mode 100644 index 00000000..31ceb917 --- /dev/null +++ b/compiler/tests/04_pbitcell_1X_test.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python2.7 +""" +Run regresion tests on a parameterized bitcell +""" + +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 + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 04_pbitcell_1X_test") + + +class pbitcell_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + global verify + import verify + OPTS.check_lvsdrc = False + + import pbitcell + import tech + + debug.info(2, "Bitcell with 1 of each port: read/write, write, and read") + tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=1) + self.local_check(tx) + + debug.info(2, "Bitcell with 0 read/write ports") + tx = pbitcell.pbitcell(num_readwrite=0,num_write=1,num_read=1) + self.local_check(tx) + + debug.info(2, "Bitcell with 0 write ports") + tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=1) + self.local_check(tx) + + debug.info(2, "Bitcell with 0 read ports") + tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=0) + self.local_check(tx) + + OPTS.check_lvsdrc = True + globals.end_openram() + + +# instantiate a copy 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/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 86e3fb1f..2c7e94dc 100644 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -27,12 +27,20 @@ class pbitcell_test(openram_test): import pbitcell import tech - debug.info(2, "Bitcell with 2 write ports and 2 read ports") - tx = pbitcell.pbitcell(num_write=2,num_read=2) + debug.info(2, "Bitcell with 2 of each port: read/write, write, and read") + tx = pbitcell.pbitcell(num_readwrite=2,num_write=2,num_read=2) self.local_check(tx) - debug.info(2, "Bitcell with 2 write ports and 0 read ports") - tx = pbitcell.pbitcell(num_write=2,num_read=0) + debug.info(2, "Bitcell with 0 read/write ports") + tx = pbitcell.pbitcell(num_readwrite=0,num_write=2,num_read=2) + self.local_check(tx) + + debug.info(2, "Bitcell with 0 write ports") + tx = pbitcell.pbitcell(num_readwrite=2,num_write=0,num_read=2) + self.local_check(tx) + + debug.info(2, "Bitcell with 0 read ports") + tx = pbitcell.pbitcell(num_readwrite=2,num_write=2,num_read=0) self.local_check(tx) OPTS.check_lvsdrc = True From 3971835f242058eb204c4dd68cfe9c05e927a407 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Thu, 10 May 2018 09:40:43 -0700 Subject: [PATCH 17/46] changed pbitcell_array tests in regards to addition of read/write ports in pbitcell --- compiler/modules/bitcell.py | 33 ++++++++++++++++++++++-- compiler/tests/05_pbitcell_array_test.py | 6 ++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/compiler/modules/bitcell.py b/compiler/modules/bitcell.py index 2f8a843c..8e8a9a35 100644 --- a/compiler/modules/bitcell.py +++ b/compiler/modules/bitcell.py @@ -48,15 +48,44 @@ class bitcell(design.design): def list_row_pins(self): # Creates a list of row pins - row_pins = ["WL"] + row_pins = ["wl"] + return row_pins + + def list_read_row_pins(self): + # Creates a list of row pins + row_pins = ["wl"] + return row_pins + + def list_write_row_pins(self): + # Creates a list of row pins + row_pins = ["wl"] return row_pins def list_column_pins(self): # Creates a list of column pins - column_pins = ["BL", "BR"] + column_pins = ["bl", "br"] return column_pins + def list_read_column_pins(self): + # Creates a list of column pins + column_pins = ["bl"] + return column_pins + + def list_read_bar_column_pins(self): + # Creates a list of column pins + column_pins = ["br"] + return column_pins + + def list_write_column_pins(self): + # Creates a list of column pins + column_pins = ["bl"] + return column_pins + + def list_write_bar_column_pins(self): + # Creates a list of column pins + column_pins = ["br"] + return column_pins def analytical_power(self, proc, vdd, temp, load): """Bitcell power in nW. Only characterizes leakage.""" diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 5fac6c06..dc77e5d1 100644 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -24,12 +24,16 @@ class array_multiport_test(openram_test): import bitcell_array OPTS.bitcell = "pbitcell" + OPTS.rw_ports = 2 + OPTS.r_ports = 2 + OPTS.w_ports = 2 + debug.info(2, "Testing 4x4 array for multiport bitcell") a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) self.local_check(a) OPTS.check_lvsdrc = True - globals.end_openram() + #globals.end_openram() # instantiate a copy of the class to actually run the test if __name__ == "__main__": From 5e4d4bf6cd4597aea3133000c8b10170493597c6 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Tue, 22 May 2018 14:12:14 -0700 Subject: [PATCH 18/46] resolved conflicts with bitcell_array after PrivateRAM merge --- compiler/modules/bitcell_array.py | 403 +++++++++++++++--------------- 1 file changed, 202 insertions(+), 201 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 83a0e236..e6494bb5 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -1,201 +1,202 @@ -import debug -import design -from tech import drc, spice -from vector import vector -from globals import OPTS - - - -class bitcell_array(design.design): - """ - Creates a rows x cols array of memory cells. Assumes bit-lines - and word line is connected by abutment. - Connects the word lines and bit lines. - """ - - def __init__(self, cols, rows, name="bitcell_array"): - design.design.__init__(self, name) - debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) - - - self.column_size = cols - self.row_size = rows - - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.cell = self.mod_bitcell() - self.add_mod(self.cell) - - # We increase it by a well enclosure so the precharges don't overlap our wells - self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] - self.width = self.column_size*self.cell.width - - self.add_pins() - self.create_layout() - self.add_layout_pins() - - self.DRC_LVS() - - - def add_pins(self): - row_list = self.cell.list_row_pins() - column_list = self.cell.list_column_pins() - for col in range(self.column_size): - for cell_column in column_list: - self.add_pin(cell_column+"[{0}]".format(col)) - for row in range(self.row_size): - for cell_row in row_list: - self.add_pin(cell_row+"[{0}]".format(row)) - self.add_pin("vdd") - self.add_pin("gnd") - - - def create_layout(self): - xoffset = 0.0 - self.cell_inst = {} - for col in range(self.column_size): - yoffset = 0.0 - for row in range(self.row_size): - name = "bit_r{0}_c{1}".format(row, col) - - if row % 2: - tempy = yoffset + self.cell.height - dir_key = "MX" - else: - tempy = yoffset - dir_key = "" - - self.cell_inst[row,col]=self.add_inst(name=name, - mod=self.cell, - offset=[xoffset, tempy], - mirror=dir_key) - self.connect_inst(self.cell.list_bitcell_pins(col, row)) - - yoffset += self.cell.height - xoffset += self.cell.width - - - def add_layout_pins(self): - - # Our cells have multiple gnd pins for now. - # FIXME: fix for multiple vdd too - vdd_pin = self.cell.get_pin("vdd") - - # shift it up by the overlap amount (gnd_pin) too - # must find the lower gnd pin to determine this overlap - lower_y = self.cell.height - gnd_pins = self.cell.get_pins("gnd") - for gnd_pin in gnd_pins: - if gnd_pin.layer=="metal2" and gnd_pin.by() Date: Tue, 22 May 2018 14:16:51 -0700 Subject: [PATCH 19/46] changed case of handmade bitcell pins from upper case to lower case. Made changes in other modules that are affected by this case. Only for SCMOS for this commit --- compiler/modules/bitcell.py | 36 ++++++++++----------- compiler/pgates/precharge.py | 8 ++--- compiler/pgates/single_level_column_mux.py | 4 +-- technology/scn3me_subm/gds_lib/cell_6t.gds | Bin 5916 -> 6144 bytes 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/compiler/modules/bitcell.py b/compiler/modules/bitcell.py index 473634f2..33707b81 100644 --- a/compiler/modules/bitcell.py +++ b/compiler/modules/bitcell.py @@ -11,7 +11,7 @@ class bitcell(design.design): library. """ - pin_names = ["BL", "BR", "WL", "vdd", "gnd"] + pin_names = ["bl", "br", "wl", "vdd", "gnd"] (width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"], layer["boundary"]) @@ -37,7 +37,7 @@ class bitcell(design.design): 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 + """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ bitcell_pins = ["bl[{0}]".format(col), "br[{0}]".format(col), "wl[{0}]".format(row), @@ -47,44 +47,44 @@ class bitcell(design.design): def list_row_pins(self): - # Creates a list of row pins - row_pins = ["WL"] + """ Creates a list of all row pins (except for gnd and vdd) """ + row_pins = ["wl"] return row_pins def list_read_row_pins(self): - # Creates a list of row pins - row_pins = ["WL"] + """ Creates a list of row pins associated with read ports """ + row_pins = ["wl"] return row_pins def list_write_row_pins(self): - # Creates a list of row pins - row_pins = ["WL"] + """ Creates a list of row pins associated with write ports """ + row_pins = ["wl"] return row_pins def list_column_pins(self): - # Creates a list of column pins - column_pins = ["BL", "BR"] + """ Creates a list of all column pins (except for gnd and vdd) """ + column_pins = ["bl", "br"] return column_pins def list_read_column_pins(self): - # Creates a list of column pins - column_pins = ["BL"] + """ Creates a list of column pins associated with read ports """ + column_pins = ["bl"] return column_pins def list_read_bar_column_pins(self): - # Creates a list of column pins - column_pins = ["BR"] + """ Creates a list of column pins associated with read_bar ports """ + column_pins = ["br"] return column_pins def list_write_column_pins(self): - # Creates a list of column pins - column_pins = ["BL"] + """ Creates a list of column pins associated with write ports """ + column_pins = ["bl"] return column_pins def list_write_bar_column_pins(self): - # Creates a list of column pins - column_pins = ["BR"] + """ Creates a list of column pins asscociated with write_bar ports""" + column_pins = ["br"] return column_pins def analytical_power(self, proc, vdd, temp, load): diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 665bb710..c329efbe 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -81,12 +81,12 @@ class precharge(pgate.pgate): """Adds both the upper_pmos and lower_pmos to the module""" # 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("BL").lx(), + self.lower_pmos_position = vector(self.bitcell.get_pin("bl").lx(), self.pmos.active_offset.y) self.lower_pmos_inst=self.add_inst(name="lower_pmos", mod=self.pmos, offset=self.lower_pmos_position) - self.connect_inst(["bl", "en", "BR", "vdd"]) + self.connect_inst(["bl", "en", "br", "vdd"]) # adds the upper pmos(s) to layout ydiff = self.pmos.height + 2*self.m1_space + contact.poly.width @@ -158,7 +158,7 @@ class precharge(pgate.pgate): def add_bitlines(self): """Adds both bit-line and bit-line-bar to the module""" # adds the BL on metal 2 - offset = vector(self.bitcell.get_pin("BL").cx(),0) - vector(0.5 * self.m2_width,0) + offset = vector(self.bitcell.get_pin("bl").cx(),0) - vector(0.5 * self.m2_width,0) self.add_layout_pin(text="bl", layer="metal2", offset=offset, @@ -166,7 +166,7 @@ class precharge(pgate.pgate): height=self.height) # adds the BR on metal 2 - offset = vector(self.bitcell.get_pin("BR").cx(),0) - vector(0.5 * self.m2_width,0) + offset = vector(self.bitcell.get_pin("br").cx(),0) - vector(0.5 * self.m2_width,0) self.add_layout_pin(text="br", layer="metal2", offset=offset, diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 24f3b1fa..22dde391 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -39,8 +39,8 @@ class single_level_column_mux(design.design): def add_bitline_pins(self): """ Add the top and bottom pins to this cell """ - bl_pos = vector(self.bitcell.get_pin("BL").lx(), 0) - br_pos = vector(self.bitcell.get_pin("BR").lx(), 0) + bl_pos = vector(self.bitcell.get_pin("bl").lx(), 0) + br_pos = vector(self.bitcell.get_pin("br").lx(), 0) # bl and br self.add_layout_pin(text="bl", diff --git a/technology/scn3me_subm/gds_lib/cell_6t.gds b/technology/scn3me_subm/gds_lib/cell_6t.gds index c6fdb0e8208d82752b8df1f873200eaf17b8e3c1..ec9264568959cc70edee2dc6cb55ac528ede4993 100644 GIT binary patch literal 6144 zcmd6rJ&YAq6vxlL_jY)=&*kk3>@HaZf(ZKoEbJ+D^l^W4=FiNmo_WT~O_m?|-}9ey?#J9a zcP+D)Wi~s~vPo;(4x6x!_0#`HZQRDJXQ!;!%2qGE{l*7t@0)FJkG}BYrzc+e@vHmK zon1eA^UK%H*z9P7H_P6-w6^y8v&q}(nq`l-Y~Hf*mYFT~+k?UHH_i4va6bQo*|C#m zqV#3e0q-bysheADE(%$dk2F*zlLUG>t=g@5~b&C@OwP|9n)s> zOC@<;@!Zfqw(fa|<+p!Xl-`uT7L(`4PaXaowb#b4*8c-n&1Puz?`5WULz=Uewf>9ODQ9=zjZ?UP@Wo*3l!+KHo9eAFOHALb_>KjTGyQF>ySpKlKG zK;rp!5T(cYecTS-apGg#MCrr)a~=0nH~B^B>3P-qkCgN8=Ue-lbM&sCU!wH(waCx6 zr>}R&S@_heC3zo;_8`CS735>jU<{{kl$4$r`nxNhAMQMB_Sk17$zQZL`tcFR8sK|V zl%Ck=CmuhwvyY0>6GK0-`Xx7hQF>zNXOCr!A>&WYqV&W@Kk@ikugsq)Ju&oiUJwsi zclbr=iJ@QP23hyapGg0U_C`PP#F3ZtMwFgd^&c(QzrQbdd2h(goQcvCLqF#Obwa*@ zm^)GWrunPopWN`h4>I1=A+jeI?Roy5-~WzTwv+o`HZ{V(yZE=)Z~NYnYRzV{kJf4x zCx>o~^w8gSddk-_JEl{5&T5Zq{w-aP+tPI(HzoZ2%hH?t9y5B^$w>2+FG}C^#@F(* zQxN}8JBIYcuzp6DTLt9ngZ!fO#L&;|(r3tQF@B=-#6~~y_?ZLV98r2==&#;#iRaA~ z$zQaGepV&@zZrj#=M?Q>{j59UAvZ++FG^1g{p>WvL2jyyzbL)Quezab zx+4CfJ;?9lrkgc&Gk&797Nw`>Rr4RO?qAb4{MN(z^;@ne{cks}n!mf^ zaqRB+*)yEdoBhl&J2PYsIb}rYi9!9IWB(y1K5~oFW51sW+>9nS%(yA>E{W20W9s>d zu^gQk{I%!!DksL9?|H?;9DGw1j^d0N=3t+G=2z>^WFM}srbCLqL=N-t<&b`o=bd-| zttWSE@^l~dzdZjAT=libm}}iRrRS{9UmCyC{~ylJnx8$AcLtJ|^Fx%L80Kfc;eCVn z*#AW7o8F&V{_6RSkMqLm{V&>s{KV+jebkJiAjZC#{S8>rt#U<^C;c Lt)$oMfBf?h%e|6$ literal 5916 zcmbuDJ&cr96owD8v$G?tIJ@q`E?Hm=g6uE5>?-R|3>rGNFiIjZB$~KJ3SuY>p`@_T zgo1*?L<$NLEhsD~wy>ZuhLXatL*4ElH_cx7%WQR{=IcL(E_!=&%uN2det+~` z-4KbRxUly<^)J(9TeJ}DJIx0pS#jV=Y{EKz{!ZX)2o18KeSqthS zx?c3Z^ouX<_h#Z;Q*nc?XZ&Wnw!=q${2B*3L&6mqKo_zqPab@FZXk0&7YFz&;MqI zE_!>`zkf4)2XEEHhc4#*-R?g>NA4YqHOH=pF8bKA`I#qU=d2T%54u?Lk3P~EL^H<1 z{DVC`n}7di_zvC@!^iybSM3>pc-Uw36_@UWF1nua$DHvoS8?f1=wivg?{WATFMfy* zT`c*jGd}7s;zJkn{+K7Zn7^o+imqqpPab^a7nuXPIN)DS_fLC0ysS4dAn$E-(e*U{ zQ~s$NW;?H%iNsM{=z7-AzTv(7;+dM{MHdI&-+lR6cg{%sBJt2g*R%QQ$NhQFshY%5 z+^Rk6XU=@6waD4UchSFR{nUs11AY;2Rkt1o?hk$QrypY{mq;ANt=fzEx%aYuB5@QK z4%~nH@^i)wKZyB?oZsl8>)HI&p1iD|Nd3{peExW641E#r9dY_z=yt~u2W*yjv=;FZo_vI&Fx>l?mYk@9$dz%0F;P`p((O=}f$N0s8 z_g~_V^M`qJ{)mUKgf0%uKk>&ogO5EgQa8>YvE*;BN3NY$#TW}UVN6wfcK%a0!ng0S z7(Uhnf7PD)SL^!4bMo~4JM@i{Puh!q?mX%JdF`5fL2u9aVYnn{EU;e=A0CXg)X|DnSa!WzZIyTc;t5IqU#xdb8NbLp&_&m? ze#S{Z@{5cEU39(ZColJQkvNJAOZj=uSa)hAvVJ-{s`hMt-ZSalgSzRxBYJzrAI}-@ zE%J+;Mf@!y4!pmT^TTYU9evZkpLmu>+VMs(e=#uW6v;8o*xk(dq8wO>;HfC<2j4E z&98`&m* Date: Tue, 22 May 2018 14:19:26 -0700 Subject: [PATCH 20/46] changing case of pins in handmade cell_6t for freepdk45 --- technology/freepdk45/gds_lib/cell_6t.gds | Bin 20480 -> 20480 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/cell_6t.gds b/technology/freepdk45/gds_lib/cell_6t.gds index e48beb23e356e04ac06357151502cb60f882171c..bde8742aa0bb9db6a3713b09ad30979434591af0 100644 GIT binary patch delta 946 zcmYjPU27Xh6x~@%tIC!?WXqOiTeV#$qLyV@mTao8%+78$F*vIvkkE%x3Jr-Lf()f@ zX%S^T_H$b7IZvff0#05^=tCAlaD-Tw(5DdclpoN9{sBWN#)#aKQ1Wm%_s*Pq@43Us zyG-6?d#MXyZ9wbQ@>)`h>Bh}l8()6g{6^CfJz+lc^q2duuKiQ_==0=@#)Gyt71l}H zrx~Ol)r#7%_J8gE7c=OxRcx`JU@=oMYyZgHmnD>X1bqEx+|gGcbjrg5$DzwO3Y~)& zIu9#ks*r=HBy;qogX2Mlt-&AF;2vHK6d(r#1R>~`5dC`?>MtOO3CI{nw{I7+Zx2`d z^62&obO^k67eQ1)MyW39AQY`be&C@Sk?2GOyvP+aA|(Wd2W}_~8Pty)$#4nBaS1=( zfQ)nS_<8I^i#Q%8_rr8HT*6kW0Wl%e1hq~DDn(V}QjJRlNeeqk6C&v%mRvzFMpt7l z+_4o!VKD0A&M0j^Dinp!5-X4i>X9IOi8_K2Qja)rMwYOZj$=3fF&<3KU_D=imo=d0 z>k#uzySBmh6P%Afx}y{5zJd)3nqdrIm(hFVwN>kj$UtG zsCim!+V+FA$KYlDP#N-mR`}vW#(ZmTo2fbOch2-M?96VfS$Pno8G>oDH~oHL+J;CA zm8Q+6XzUcF#NA06KS|n2(oWins57yO%~}P&<#RFigQn5HA^SI)cwPM@@;3e&_o^4M zQA@z9=8&ykN4LV!uGsJ@7R-u?Zkh5jpt*287;-dzpQSrxvwM8?h1Hk}l?e_GIW zbP62xvh#51Hlw*~*c1Yb;u8KfZ5)~$-xF6&ffLU0nA>O(mx)i#2^b0Z1sVHOzimr~_F4z6HT$4$G5^#dLx&FcAKNs(Wm7xb@!NvtKXP;Uy)h2n z_zIbZgTGt{zY;!k3lOy-7%u$+;zySRp1VWXc*kSJf}Z5%`hU6v)cUypXQQgW0Y3gs AMgRZ+ delta 981 zcmYLHOKTff6uws)$uHTF981>2c7#)Gn&@FkHfctqJ699M1wGQ*EwqaeT4=pWg27D) ziWrYgR<2X*`Ig-nOuGu%%q9e*OoGWOi>&eovM7}93_-YM=uvCh#o;@T`EoY&dH=yo3$DWooS-yLIwLpm zGE#vVG3>|&PAKJ!ll1WtHj)yhx_f+wUs;le#nMKXDwMx+wh$<3pXYv8Y}fo@iyL+GX= z_$-@;mk~J17Ri)qSD z3v^RCdi&+S8m3f;R1n*Q+EfmIqzVJ`ESLy>)x@uycX z^BJt=CAH3klrUo~&>wHC;+t|Y#5?~bnmEWO(amKrCu8Wy0L@StKqCG2T5Nu#rQT*1( Date: Tue, 22 May 2018 14:51:42 -0700 Subject: [PATCH 21/46] small changes to pbitcell --- compiler/pgates/pbitcell.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index 5f222ab9..fbde4e1e 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -72,6 +72,7 @@ class pbitcell(pgate.pgate): self.extend_well() self.offset_all_coordinates() + def create_ptx(self): """ Calculate transistor sizes """ # if there are any read/write ports, then the inverter nmos is sized based the number of them @@ -934,7 +935,7 @@ class pbitcell(pgate.pgate): 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 + """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ bitcell_pins = [] for k in range(self.num_readwrite): bitcell_pins.append("rwbl{0}[{1}]".format(k,col)) @@ -958,7 +959,7 @@ class pbitcell(pgate.pgate): def list_row_pins(self): - # Creates a list of row pins + """ Creates a list of all row pins (except for gnd and vdd) """ row_pins = [] for k in range(self.num_readwrite): row_pins.append("rwwl{0}".format(k)) @@ -970,7 +971,7 @@ class pbitcell(pgate.pgate): return row_pins def list_read_row_pins(self): - # Creates a list of row pins + """ Creates a list of row pins associated with read ports """ row_pins = [] for k in range(self.num_readwrite): row_pins.append("rwwl{0}".format(k)) @@ -980,7 +981,7 @@ class pbitcell(pgate.pgate): return row_pins def list_write_row_pins(self): - # Creates a list of row pins + """ Creates a list of row pins associated with write ports """ row_pins = [] for k in range(self.num_readwrite): row_pins.append("rwwl{0}".format(k)) @@ -991,7 +992,7 @@ class pbitcell(pgate.pgate): def list_column_pins(self): - # Creates a list of column pins + """ Creates a list of all column pins """ column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl{0}".format(k)) @@ -1006,7 +1007,7 @@ class pbitcell(pgate.pgate): return column_pins def list_read_column_pins(self): - # Creates a list of column pins + """ Creates a list of column pins associated with read ports """ column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl{0}".format(k)) @@ -1016,7 +1017,7 @@ class pbitcell(pgate.pgate): return column_pins def list_read_bar_column_pins(self): - # Creates a list of column pins + """ Creates a list of column pins associated with read_bar ports """ column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl_bar{0}".format(k)) @@ -1026,7 +1027,7 @@ class pbitcell(pgate.pgate): return column_pins def list_write_column_pins(self): - # Creates a list of column pins + """ Creates a list of column pins associated with write ports """ column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl{0}".format(k)) @@ -1036,7 +1037,7 @@ class pbitcell(pgate.pgate): return column_pins def list_write_bar_column_pins(self): - # Creates a list of column pins + """ Creates a list of column pins asscociated with write_bar ports""" column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl_bar{0}".format(k)) From 9e739d67d4cf027329dec0acd0906c2d58228e90 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Tue, 29 May 2018 11:54:10 -0700 Subject: [PATCH 22/46] python 3 changes d.iterkeys() -> iter(d.keys()) --- compiler/verify/calibre.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index e947c3ad..38e92a2c 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -89,7 +89,7 @@ def run_drc(cell_name, gds_name): # write the runset file f = open(OPTS.openram_temp + "drc_runset", "w") - for k in sorted(drc_runset.iterkeys()): + for k in sorted(iter(drc_runset.keys())): f.write("*{0}: {1}\n".format(k, drc_runset[k])) f.close() @@ -177,7 +177,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): # write the runset file f = open(OPTS.openram_temp + "lvs_runset", "w") - for k in sorted(lvs_runset.iterkeys()): + for k in sorted(iter(lvs_runset.keys())): f.write("*{0}: {1}\n".format(k, lvs_runset[k])) f.close() @@ -286,7 +286,7 @@ def run_pex(cell_name, gds_name, sp_name, output=None): # write the runset file f = open(OPTS.openram_temp + "pex_runset", "w") - for k in sorted(pex_runset.iterkeys()): + for k in sorted(iter(pex_runset.keys())): f.write("*{0}: {1}\n".format(k, pex_runset[k])) f.close() From e19a422696886429b38ec362d0ea4b4c985f5c17 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Thu, 31 May 2018 17:39:51 -0700 Subject: [PATCH 23/46] simplfying calculations in pbitcell and changing pbitcell_array_test to check different port combinations --- compiler/modules/bitcell_array.py | 2 +- compiler/pgates/pbitcell.py | 304 +++++++++++++++-------- compiler/tests/05_pbitcell_array_test.py | 13 +- 3 files changed, 211 insertions(+), 108 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index e6494bb5..d538388a 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -21,7 +21,7 @@ class bitcell_array(design.design): self.column_size = cols self.row_size = rows - #from importlib import reload + from importlib import reload c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) self.cell = self.mod_bitcell() diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index fbde4e1e..bedb94b9 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -36,7 +36,9 @@ class pbitcell(pgate.pgate): def add_pins(self): - + """ + Adding pins for pbitcell module + """ for k in range(self.num_readwrite): self.add_pin("rwbl{}".format(k)) self.add_pin("rwbl_bar{}".format(k)) @@ -60,7 +62,8 @@ class pbitcell(pgate.pgate): def create_layout(self): self.create_ptx() - self.add_globals() + self.calculate_spacing() + self.calculate_postions() self.add_storage() self.add_rails() if(self.num_readwrite > 0): @@ -74,7 +77,11 @@ class pbitcell(pgate.pgate): def create_ptx(self): - """ Calculate transistor sizes """ + """ + Calculate transistor sizes and create ptx for read/write, write, and read ports + """ + + """ calculate transistor sizes """ # if there are any read/write ports, then the inverter nmos is sized based the number of them if(self.num_readwrite > 0): inverter_nmos_width = self.num_readwrite*3*parameter["min_tx_size"] @@ -117,61 +124,112 @@ class pbitcell(pgate.pgate): self.add_mod(self.read_nmos) - def add_globals(self): - """ Define pbitcell global variables """ - # 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.write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height) + def calculate_spacing(self): + """ + Calculate transistor spacings + """ - # calculation for transistor spacing (exact solutions) + """ 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) + + # calculate the distance threshold for different gate contact spacings + self.gate_contact_thres = drc["poly_to_active"] - drc["minwidth_metal2"] + + """ 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"] - self.write_to_write_spacing = drc["minwidth_metal2"] + self.write_nmos_contact_extension + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] - self.write_to_read_spacing = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.write_nmos_contact_extension - self.read_to_read_spacing = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width - # calculation for transistor spacing (symmetric solutions) - #self.inverter_to_inverter_spacing = 3*parameter["min_tx_size"] - #self.inverter_to_write_spacing = need to calculate - #spacing_option1 = contact.poly.width + 2*drc["poly_to_field_poly"] + 2*drc["poly_extend_active"] - #spacing_option2 = contact.poly.width + 2*drc["minwidth_metal2"] + 2*self.write_nmos_contact_extension - #self.write_to_write_spacing = max(spacing_option1, spacing_option2) - #self.write_to_read_spacing = drc["poly_to_field_poly"] + 2*contact.poly.width + 2*drc["minwidth_metal2"] + 2*self.write_nmos_contact_extension - #self.read_to_read_spacing = drc["minwidth_metal1"] + 2*contact.poly.width + 2*drc["minwidth_poly"] + # 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_field_poly"] + drc["poly_extend_active"] + else: + self.readwrite_to_readwrite_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] - # calculations for transistor tiling (includes transistor and spacing) + # 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_field_poly"] + drc["poly_extend_active"] + else: + self.write_to_write_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + + # 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_write > 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_field_poly"] + + """ 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.write_to_write_spacing + self.readwrite_nmos.active_height + 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 + """ calculation for row line tiling """ self.rail_tile_height = drc["active_to_body_active"] + 0.5*(drc["minwidth_tx"] - drc["minwidth_metal1"]) + drc["minwidth_metal1"] self.rowline_tile_height = drc["minwidth_metal1"] + contact.m1m2.width - # calculations related to inverter connections + """ 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.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_field_poly"] + 1.5*contact.poly.width - """ Calculations for the edges of the cell """ - # create a flags for excluding readwrite, write, or read port calculations if they are not included in the bitcell + + def calculate_postions(self): + """ + Calculate positions that describe the edges of the cell + """ + # create flags for excluding readwrite, write, or read port calculations if they are not included in the bitcell if(self.num_readwrite > 0): - self.readwrite_port_flag = 1 + self.readwrite_port_flag = True else: - self.readwrite_port_flag = 0 + self.readwrite_port_flag = False if(self.num_write > 0): - self.write_port_flag = 1 + self.write_port_flag = True else: - self.write_port_flag = 0 + self.write_port_flag = False if(self.num_read > 0): - self.read_port_flag = 1 + self.read_port_flag = True else: - self.read_port_flag = 0 + self.read_port_flag = False - # leftmost position = storage width + write ports width + read ports width + read transistor gate connections + metal spacing necessary for tiling the bitcell + # determine the distance of the leftmost/rightmost transistor gate connection + if (self.num_read > 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_readwrite-1)*self.readwrite_tile_width) \ @@ -179,12 +237,12 @@ class pbitcell(pgate.pgate): - self.write_port_flag*(self.write_nmos.active_height + (self.num_write-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_read-1)*self.read_tile_width) \ - - drc["minwidth_poly"] - contact.m1m2.height \ + - end_connection \ - 0.5*drc["minwidth_metal2"] self.rightmost_xpos = -self.leftmost_xpos - # bottommost position = gnd height + wwl height + rwl height + space needed between tiled bitcells + # 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_readwrite*self.rowline_tile_height \ @@ -254,14 +312,18 @@ class pbitcell(pgate.pgate): offset=contact_offset_right, rotate=90) - # connect contacts to gate poly (cross couple) + # connect contacts to gate poly (cross couple connections) gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, contact_offset_left.y) self.add_path("poly", [contact_offset_left, gate_offset_right]) 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 add_rails(self): """ Add gnd and vdd rails and connects them to the inverters @@ -277,7 +339,6 @@ class pbitcell(pgate.pgate): vdd_ypos = self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height \ + drc["active_to_body_active"] + 0.5*(drc["minwidth_tx"] - drc["minwidth_metal1"]) - #vdd_ypos = self.inverter_pmos_left.get_pin("S").uc().y + drc["minwidth_metal1"] self.vdd_position = vector(self.leftmost_xpos, vdd_ypos) self.vdd = self.add_layout_pin(text="vdd", layer="metal1", @@ -303,15 +364,16 @@ class pbitcell(pgate.pgate): def add_readwrite_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 (WWL) 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. + Adds read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell. + A read or write is enabled by setting a Read-Write-Wordline (RWWL) high, subsequently turning on the transistor. + The transistor is connected between a Read-Write-Bitline (RWBL) and the storage component of the cell (Q). + In a write operation, driving RWBL high or low sets the value of the cell. + In a read operation, RWBL is precharged, then is either remains high or is discharged depending on the value of the cell. + This is a differential design, so each write port has a mirrored port that connects RWBL_bar to Q_bar. """ """ Define variables relevant to write transistors """ - # define offset correction due to rotation of the ptx cell + # define offset correction due to rotation of the ptx module readwrite_rotation_correct = self.readwrite_nmos.active_height # define write transistor variables as empty arrays based on the number of write ports @@ -319,23 +381,23 @@ class pbitcell(pgate.pgate): self.readwrite_nmos_right = [None] * self.num_readwrite self.rwwl_positions = [None] * self.num_readwrite self.rwbl_positions = [None] * self.num_readwrite - self.rwbl_bar_positions = [None] * self.num_readwrite + self.rwbl_bar_positions = [None] * self.num_readwrite # iterate over the number of read/write ports for k in range(0,self.num_readwrite): """ Add transistors """ - # calculate write transistor offsets - left_readwrite_transistor_xpos = -self.inverter_tile_width \ + # 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 - right_readwrite_transistor_xpos = self.inverter_tile_width \ + right_readwrite_transistor_xpos = self.right_building_edge \ + self.inverter_to_write_spacing \ + k*self.readwrite_tile_width \ + readwrite_rotation_correct - # add write transistors + # add read/write transistors self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), mod=self.readwrite_nmos, offset=[left_readwrite_transistor_xpos,0], @@ -348,7 +410,7 @@ class pbitcell(pgate.pgate): rotate=90) self.connect_inst(["Q_bar", "rwwl{}".format(k), "rwbl_bar{}".format(k), "gnd"]) - """ Add WWL lines """ + """ 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) @@ -360,8 +422,8 @@ class pbitcell(pgate.pgate): width=self.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 + """ 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, @@ -387,10 +449,13 @@ class pbitcell(pgate.pgate): width=drc["minwidth_metal2"], height=self.height) - """ 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 routing to the source pins - contact_xpos = self.readwrite_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width + """ 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 = left_readwrite_transistor_xpos - self.readwrite_nmos.active_height - drc["poly_to_active"] - 0.5*contact.m1m2.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) @@ -399,7 +464,10 @@ class pbitcell(pgate.pgate): self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=left_gate_contact) - contact_xpos = self.readwrite_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width + 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 = right_readwrite_transistor_xpos + drc["poly_to_active"] + 0.5*contact.m1m2.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) @@ -433,7 +501,7 @@ class pbitcell(pgate.pgate): """ Drain/Storage connections """ # this path only needs to be drawn once on the last iteration of the loop if(k == self.num_readwrite-1): - # add contacts to connect gate of inverters to drain of write transistors + # 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_field_poly"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=left_storage_contact, @@ -451,7 +519,7 @@ class pbitcell(pgate.pgate): 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) + # connect contacts to drains of read/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.readwrite_nmos_left[k].get_pin("D").lc().y) self.add_path("metal1", [left_storage_contact, midL0, midL1, self.readwrite_nmos_left[k].get_pin("D").lc()]) @@ -459,19 +527,25 @@ class pbitcell(pgate.pgate): 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.readwrite_nmos_right[k].get_pin("D").rc().y) self.add_path("metal1", [right_storage_contact, midR0, midR1, self.readwrite_nmos_right[k].get_pin("D").rc()]) + # end if + # end for + + """ 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 add_write_ports(self): """ - Adds write ports to the bit cell. A single transistor acts as the write port. + Adds write ports to the bit cell. A differential pair of transistors can write only. A write is enabled by setting a Write-Rowline (WWL) 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. + In a write operation, 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 + # define offset correction due to rotation of the ptx module write_rotation_correct = self.write_nmos.active_height # define write transistor variables as empty arrays based on the number of write ports @@ -485,17 +559,15 @@ class pbitcell(pgate.pgate): for k in range(0,self.num_write): """ Add transistors """ # calculate write transistor offsets - left_write_transistor_xpos = -self.inverter_tile_width \ - - self.inverter_to_write_spacing \ - - self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-1)*self.readwrite_tile_width) \ - - self.readwrite_port_flag*self.write_to_write_spacing \ + 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 - right_write_transistor_xpos = self.inverter_tile_width \ - + self.inverter_to_write_spacing \ - + self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-1)*self.readwrite_tile_width) \ - + self.readwrite_port_flag*self.write_to_write_spacing \ + 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 @@ -555,8 +627,11 @@ class pbitcell(pgate.pgate): """ 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 routing to the source pins - contact_xpos = self.write_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width + # 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 = left_write_transistor_xpos - self.write_nmos.active_height - drc["poly_to_active"] - 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) @@ -565,7 +640,10 @@ class pbitcell(pgate.pgate): self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=left_gate_contact) - contact_xpos = self.write_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width + 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 = right_write_transistor_xpos + drc["poly_to_active"] + 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) @@ -625,12 +703,18 @@ class pbitcell(pgate.pgate): 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()]) + # end if + # end for + + """ 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 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". + Adds read ports to the bit cell. A differential pair of ports can read only. + Two transistors function as a read port, denoted as the "read transistor" and the "read-access transistor". The read transistor is connected to RWL (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 (RWL) high, subsequently turning on the read transistor. @@ -640,7 +724,7 @@ class pbitcell(pgate.pgate): """ """ Define variables relevant to read transistors """ - # define offset correction due to rotation of the ptx cell + # 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 @@ -659,20 +743,12 @@ class pbitcell(pgate.pgate): for k in range(0,self.num_read): """ Add transistors """ # calculate transistor offsets - left_read_transistor_xpos = -self.inverter_tile_width \ - - self.inverter_to_write_spacing \ - - self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-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_write-1)*self.write_tile_width) \ + 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 - right_read_transistor_xpos = self.inverter_tile_width \ - + self.inverter_to_write_spacing \ - + self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-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_write-1)*self.write_tile_width) \ + right_read_transistor_xpos = self.right_building_edge \ + self.write_to_read_spacing \ + k*self.read_tile_width \ + read_rotation_correct @@ -747,7 +823,10 @@ class pbitcell(pgate.pgate): """ Gate of read transistor / RWL connection """ # add poly-to-meltal2 contacts to connect gate of read transistors to RWL (contact next to gate) - contact_xpos = left_read_transistor_xpos - self.read_nmos.active_height - drc["minwidth_poly"] - 0.5*contact.poly.width + 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 + else: + contact_xpos = left_read_transistor_xpos - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.m1m2.width contact_ypos = self.read_nmos_left[k].get_pin("G").lc().y left_gate_contact = vector(contact_xpos, contact_ypos) @@ -756,7 +835,10 @@ class pbitcell(pgate.pgate): 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 + 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 = right_read_transistor_xpos + drc["poly_to_active"] + 0.5*contact.m1m2.width contact_ypos = self.read_nmos_right[k].get_pin("G").rc().y right_gate_contact = vector(contact_xpos, contact_ypos) @@ -794,14 +876,20 @@ class pbitcell(pgate.pgate): """ 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 + 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 = left_read_transistor_xpos + drc["poly_to_active"] + 0.5*contact.m1m2.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 - self.read_nmos.active_height - drc["minwidth_poly"] - 0.5*contact.poly.width + 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 = right_read_transistor_xpos - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.m1m2.width contact_ypos = self.read_access_nmos_right[k].get_pin("G").lc().y right_gate_contact = vector(contact_xpos, contact_ypos) @@ -820,25 +908,26 @@ class pbitcell(pgate.pgate): # 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 down to be level with inverter output + # 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(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.cross_couple_upper_ypos) + 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(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.cross_couple_upper_ypos) + 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]) + self.add_path("metal1", [right_gate_contact, midR0, midR1, midR2, right_inverter_offset]) + # end for def extend_well(self): """ - Connects wells between ptx cells to avoid drc spacing issues. - Since the pwell of the read ports rise higher than the pmos of the inverters, + Connects wells between ptx modules to avoid drc spacing issues. + 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. """ @@ -848,17 +937,22 @@ class pbitcell(pgate.pgate): self.add_rect(layer="pwell", offset=offset, width=self.width, - height=well_height) - - """ extend pwell over write transistors to the height of the write transistor well """ - # calculate the edge of the write transistor well closest to the center - 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"] + 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_write > 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, @@ -874,8 +968,8 @@ class pbitcell(pgate.pgate): """ extend pwell over the read transistors to the height of the bitcell """ if(self.num_read > 0): # calculate the edge of the read transistor well clostest to the center - 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"] + 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) diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index dc77e5d1..28c7ce19 100644 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -28,12 +28,21 @@ class array_multiport_test(openram_test): OPTS.r_ports = 2 OPTS.w_ports = 2 - debug.info(2, "Testing 4x4 array for multiport bitcell") + debug.info(2, "Testing 4x4 array for multiport bitcell, with read ports at the edge of the bit cell") a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) self.local_check(a) + + OPTS.rw_ports = 2 + OPTS.r_ports = 0 + OPTS.w_ports = 2 + + debug.info(2, "Testing 4x4 array for multiport bitcell, with read/write ports at the edge of the bit cell") + a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) + self.local_check(a) + OPTS.check_lvsdrc = True - #globals.end_openram() + globals.end_openram() # instantiate a copy of the class to actually run the test if __name__ == "__main__": From fea304eac1973ca0981c28a41a9dcd5ea9b0d627 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Thu, 31 May 2018 18:31:34 -0700 Subject: [PATCH 24/46] corrected gate to contact spacing --- compiler/pgates/pbitcell.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index bedb94b9..c3c54a71 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -455,7 +455,7 @@ class pbitcell(pgate.pgate): 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 = left_readwrite_transistor_xpos - self.readwrite_nmos.active_height - drc["poly_to_active"] - 0.5*contact.m1m2.width + contact_xpos = left_readwrite_transistor_xpos - 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) @@ -467,7 +467,7 @@ class pbitcell(pgate.pgate): 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 = right_readwrite_transistor_xpos + drc["poly_to_active"] + 0.5*contact.m1m2.width + contact_xpos = right_readwrite_transistor_xpos + 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) @@ -631,7 +631,7 @@ class pbitcell(pgate.pgate): 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 = left_write_transistor_xpos - self.write_nmos.active_height - drc["poly_to_active"] - 0.5*contact.m1m2.width + contact_xpos = left_write_transistor_xpos - 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) @@ -643,7 +643,7 @@ class pbitcell(pgate.pgate): 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 = right_write_transistor_xpos + drc["poly_to_active"] + 0.5*contact.m1m2.width + contact_xpos = right_write_transistor_xpos + 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) @@ -826,7 +826,7 @@ class pbitcell(pgate.pgate): 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 else: - contact_xpos = left_read_transistor_xpos - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.m1m2.width + contact_xpos = left_read_transistor_xpos - 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) @@ -838,7 +838,7 @@ class pbitcell(pgate.pgate): 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 = right_read_transistor_xpos + drc["poly_to_active"] + 0.5*contact.m1m2.width + contact_xpos = right_read_transistor_xpos + 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) @@ -852,12 +852,12 @@ class pbitcell(pgate.pgate): 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.m1m2.width) + 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.m1m2.width) + 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) @@ -879,7 +879,7 @@ class pbitcell(pgate.pgate): 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 = left_read_transistor_xpos + drc["poly_to_active"] + 0.5*contact.m1m2.width + contact_xpos = left_read_transistor_xpos + 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) @@ -889,7 +889,7 @@ class pbitcell(pgate.pgate): 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 = right_read_transistor_xpos - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.m1m2.width + contact_xpos = right_read_transistor_xpos - 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) From d7a024b8fc2a79fda0cb9a59a52be6b4687027e3 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Sun, 3 Jun 2018 19:36:48 -0700 Subject: [PATCH 25/46] adding another important port combination to unit tests --- compiler/tests/04_pbitcell_1X_test.py | 4 ++++ compiler/tests/04_pbitcell_test.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/compiler/tests/04_pbitcell_1X_test.py b/compiler/tests/04_pbitcell_1X_test.py index 31ceb917..59ca3457 100644 --- a/compiler/tests/04_pbitcell_1X_test.py +++ b/compiler/tests/04_pbitcell_1X_test.py @@ -42,6 +42,10 @@ class pbitcell_test(openram_test): debug.info(2, "Bitcell with 0 read ports") tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=0) self.local_check(tx) + + debug.info(2, "Bitcell with 0 read ports and 0 write ports") + tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=0) + self.local_check(tx) OPTS.check_lvsdrc = True globals.end_openram() diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 2c7e94dc..4d51aeec 100644 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -42,6 +42,10 @@ class pbitcell_test(openram_test): debug.info(2, "Bitcell with 0 read ports") tx = pbitcell.pbitcell(num_readwrite=2,num_write=2,num_read=0) self.local_check(tx) + + debug.info(2, "Bitcell with 0 read ports and 0 write ports") + tx = pbitcell.pbitcell(num_readwrite=2,num_write=0,num_read=0) + self.local_check(tx) OPTS.check_lvsdrc = True globals.end_openram() From 2833b706c7adaeec0e7602ebd989ea7c3fb93b92 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 09:23:23 -0700 Subject: [PATCH 26/46] Fix duplicate name check for some modules by checking if name is a substring. Allows pbitcell to pass. --- compiler/base/design.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/base/design.py b/compiler/base/design.py index c1c976c5..794bb79a 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -40,10 +40,12 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout): 'hierarchical_predecode3x8'] if name not in design.name_map: design.name_map.append(name) - elif self.__class__.__name__ in ok_list: - pass else: - debug.error("Duplicate layout reference name {0} of class {1}. GDS2 requires names be unique.".format(name,self.__class__),-1) + for ok_names in ok_list: + if ok_names in self.__class__.__name__: + break + else: + debug.error("Duplicate layout reference name {0} of class {1}. GDS2 requires names be unique.".format(name,self.__class__),-1) def setup_drc_constants(self): """ These are some DRC constants used in many places in the compiler.""" From 8cee26bc8c6cabcb1df58a2ad924a38e628b17d4 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 09:23:43 -0700 Subject: [PATCH 27/46] Allow python 3.5. Make easier to revise required version. --- compiler/globals.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/globals.py b/compiler/globals.py index 58922e52..86d091b1 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -89,11 +89,13 @@ def print_banner(): def check_versions(): """ Run some checks of required software versions. """ - # Now require python >=3.6 + # Now require python >=3.5 major_python_version = sys.version_info.major minor_python_version = sys.version_info.minor - if not (major_python_version == 3 and minor_python_version >= 6): - debug.error("Python 3.6 or greater is required.",-1) + major_required = 3 + minor_required = 5 + if not (major_python_version == major_required and minor_python_version >= minor_required): + debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1) # FIXME: Check versions of other tools here?? # or, this could be done in each module (e.g. verify, characterizer, etc.) From df2dce2439a4da184b3caacd6836b255411a1374 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 09:45:07 -0700 Subject: [PATCH 28/46] Fix module import names for python3. Rename parse function to something meaningful. --- compiler/characterizer/charutils.py | 3 ++- compiler/characterizer/delay.py | 28 ++++++++++----------- compiler/characterizer/setup_hold.py | 8 +++--- compiler/tests/21_hspice_delay_test.py | 2 +- compiler/tests/21_hspice_setuphold_test.py | 2 +- compiler/tests/21_ngspice_delay_test.py | 2 +- compiler/tests/21_ngspice_setuphold_test.py | 2 +- 7 files changed, 24 insertions(+), 23 deletions(-) diff --git a/compiler/characterizer/charutils.py b/compiler/characterizer/charutils.py index 38ed8957..bc4beb88 100644 --- a/compiler/characterizer/charutils.py +++ b/compiler/characterizer/charutils.py @@ -8,7 +8,7 @@ def relative_compare(value1,value2,error_tolerance=0.001): return (abs(value1 - value2) / max(value1,value2) <= error_tolerance) -def parse_output(filename, key): +def parse_spice_list(filename, key): """Parses a hspice output.lis file for a key value""" if OPTS.spice_name == "xa" : # customsim has a different output file name @@ -22,6 +22,7 @@ def parse_output(filename, key): except IOError: debug.error("Unable to open spice output file: {0}".format(full_filename),1) contents = f.read() + f.close() # val = re.search(r"{0}\s*=\s*(-?\d+.?\d*\S*)\s+.*".format(key), contents) val = re.search(r"{0}\s*=\s*(-?\d+.?\d*[e]?[-+]?[0-9]*\S*)\s+.*".format(key), contents) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 6b58d5d3..a7b2df7c 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -339,16 +339,16 @@ class delay(): # Checking from not data_value to data_value self.write_delay_stimulus() self.stim.run_sim() - delay_hl = parse_output("timing", "delay_hl") - delay_lh = parse_output("timing", "delay_lh") - slew_hl = parse_output("timing", "slew_hl") - slew_lh = parse_output("timing", "slew_lh") + delay_hl = parse_spice_list("timing", "delay_hl") + delay_lh = parse_spice_list("timing", "delay_lh") + slew_hl = parse_spice_list("timing", "slew_hl") + slew_lh = parse_spice_list("timing", "slew_lh") delays = (delay_hl, delay_lh, slew_hl, slew_lh) - read0_power=parse_output("timing", "read0_power") - write0_power=parse_output("timing", "write0_power") - read1_power=parse_output("timing", "read1_power") - write1_power=parse_output("timing", "write1_power") + read0_power=parse_spice_list("timing", "read0_power") + write0_power=parse_spice_list("timing", "write0_power") + read1_power=parse_spice_list("timing", "read1_power") + write1_power=parse_spice_list("timing", "write1_power") if not self.check_valid_delays(delays): return (False,{}) @@ -378,13 +378,13 @@ class delay(): self.write_power_stimulus(trim=False) self.stim.run_sim() - leakage_power=parse_output("timing", "leakage_power") + leakage_power=parse_spice_list("timing", "leakage_power") debug.check(leakage_power!="Failed","Could not measure leakage power.") self.write_power_stimulus(trim=True) self.stim.run_sim() - trim_leakage_power=parse_output("timing", "leakage_power") + trim_leakage_power=parse_spice_list("timing", "leakage_power") debug.check(trim_leakage_power!="Failed","Could not measure leakage power.") # For debug, you sometimes want to inspect each simulation. @@ -473,10 +473,10 @@ class delay(): # Checking from not data_value to data_value self.write_delay_stimulus() self.stim.run_sim() - delay_hl = parse_output("timing", "delay_hl") - delay_lh = parse_output("timing", "delay_lh") - slew_hl = parse_output("timing", "slew_hl") - slew_lh = parse_output("timing", "slew_lh") + delay_hl = parse_spice_list("timing", "delay_hl") + delay_lh = parse_spice_list("timing", "delay_lh") + slew_hl = parse_spice_list("timing", "slew_hl") + slew_lh = parse_spice_list("timing", "slew_lh") # if it failed or the read was longer than a period if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float: debug.info(2,"Invalid measures: Period {0}, delay_hl={1}ns, delay_lh={2}ns slew_hl={3}ns slew_lh={4}ns".format(self.period, diff --git a/compiler/characterizer/setup_hold.py b/compiler/characterizer/setup_hold.py index 4f261b5a..aaeff0cd 100644 --- a/compiler/characterizer/setup_hold.py +++ b/compiler/characterizer/setup_hold.py @@ -186,8 +186,8 @@ class setup_hold(): target_time=feasible_bound, correct_value=correct_value) self.stim.run_sim() - ideal_clk_to_q = convert_to_float(parse_output("timing", "clk2q_delay")) - setuphold_time = convert_to_float(parse_output("timing", "setup_hold_time")) + ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay")) + setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time")) debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time)) if type(ideal_clk_to_q)!=float or type(setuphold_time)!=float: @@ -219,8 +219,8 @@ class setup_hold(): self.stim.run_sim() - clk_to_q = convert_to_float(parse_output("timing", "clk2q_delay")) - setuphold_time = convert_to_float(parse_output("timing", "setup_hold_time")) + clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay")) + setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time")) if type(clk_to_q)==float and (clk_to_q<1.1*ideal_clk_to_q) and type(setuphold_time)==float: if mode == "SETUP": # SETUP is clk-din, not din-clk setuphold_time *= -1e9 diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index 731c0d90..9ac3db2e 100644 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -45,7 +45,7 @@ class timing_sram_test(openram_test): debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - d = delay.delay(s,tempspice,corner) + d = delay(s,tempspice,corner) import tech loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] diff --git a/compiler/tests/21_hspice_setuphold_test.py b/compiler/tests/21_hspice_setuphold_test.py index 4d469ac3..78403a56 100644 --- a/compiler/tests/21_hspice_setuphold_test.py +++ b/compiler/tests/21_hspice_setuphold_test.py @@ -33,7 +33,7 @@ class timing_setup_test(openram_test): slews = [tech.spice["rise_time"]*2] corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - sh = setup_hold.setup_hold(corner) + sh = setup_hold(corner) data = sh.analyze(slews,slews) #print data if OPTS.tech_name == "freepdk45": diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 6ebf4dd2..7f7bfd39 100644 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -43,7 +43,7 @@ class timing_sram_test(openram_test): debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - d = delay.delay(s,tempspice,corner) + d = delay(s,tempspice,corner) import tech loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] diff --git a/compiler/tests/21_ngspice_setuphold_test.py b/compiler/tests/21_ngspice_setuphold_test.py index 9bf0fd4c..9a8ff67c 100644 --- a/compiler/tests/21_ngspice_setuphold_test.py +++ b/compiler/tests/21_ngspice_setuphold_test.py @@ -32,7 +32,7 @@ class timing_setup_test(openram_test): slews = [tech.spice["rise_time"]*2] corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - sh = setup_hold.setup_hold(corner) + sh = setup_hold(corner) data = sh.analyze(slews,slews) #print data if OPTS.tech_name == "freepdk45": From 3becf92e7c87e8460bafc0aa13090e18d7989ece Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 10:00:23 -0700 Subject: [PATCH 29/46] Combine pbitcell tests into one unit test --- compiler/tests/04_pbitcell_1X_test.py | 59 --------------------------- compiler/tests/04_pbitcell_test.py | 20 +++++++++ 2 files changed, 20 insertions(+), 59 deletions(-) delete mode 100644 compiler/tests/04_pbitcell_1X_test.py diff --git a/compiler/tests/04_pbitcell_1X_test.py b/compiler/tests/04_pbitcell_1X_test.py deleted file mode 100644 index 59ca3457..00000000 --- a/compiler/tests/04_pbitcell_1X_test.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python2.7 -""" -Run regresion tests on a parameterized bitcell -""" - -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 - -OPTS = globals.OPTS - -#@unittest.skip("SKIPPING 04_pbitcell_1X_test") - - -class pbitcell_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - OPTS.check_lvsdrc = False - - import pbitcell - import tech - - debug.info(2, "Bitcell with 1 of each port: read/write, write, and read") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=1) - self.local_check(tx) - - debug.info(2, "Bitcell with 0 read/write ports") - tx = pbitcell.pbitcell(num_readwrite=0,num_write=1,num_read=1) - self.local_check(tx) - - debug.info(2, "Bitcell with 0 write ports") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=1) - self.local_check(tx) - - debug.info(2, "Bitcell with 0 read ports") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=0) - self.local_check(tx) - - debug.info(2, "Bitcell with 0 read ports and 0 write ports") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=0) - self.local_check(tx) - - OPTS.check_lvsdrc = True - globals.end_openram() - - -# instantiate a copy 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/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 4d51aeec..b942eced 100644 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -27,6 +27,26 @@ class pbitcell_test(openram_test): import pbitcell import tech + debug.info(2, "Bitcell with 1 of each port: read/write, write, and read") + tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=1) + self.local_check(tx) + + debug.info(2, "Bitcell with 0 read/write ports") + tx = pbitcell.pbitcell(num_readwrite=0,num_write=1,num_read=1) + self.local_check(tx) + + debug.info(2, "Bitcell with 0 write ports") + tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=1) + self.local_check(tx) + + debug.info(2, "Bitcell with 0 read ports") + tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=0) + self.local_check(tx) + + debug.info(2, "Bitcell with 0 read ports and 0 write ports") + tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=0) + self.local_check(tx) + debug.info(2, "Bitcell with 2 of each port: read/write, write, and read") tx = pbitcell.pbitcell(num_readwrite=2,num_write=2,num_read=2) self.local_check(tx) From d0f34b46a0c8349aaab6faf833f7f89ad59ee5d9 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 10:05:40 -0700 Subject: [PATCH 30/46] Update README with correct compiler directory structure. --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 58c8e505..49700434 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ https://github.com/mguthaus/OpenRAM/blob/master/OpenRAM_ICCAD_2016_presentation. The OpenRAM compiler has very few dependencies: * ngspice-26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later) -* Python 2.7 and higher (currently excludes Python 3 and up) +* Python 3.5 and higher * Python numpy * a setup script for each technology * a technology directory for each technology with the base cells @@ -28,6 +28,10 @@ For example, in csh/tcsh, add to your .cshrc/.tcshrc: setenv OPENRAM_HOME "$HOME/OpenRAM/compiler" setenv OPENRAM_TECH "$HOME/OpenRAM/technology" ``` + +We include the tech files necessary for FreePDK and SCMOS. The SCMOS +spice models, however, are generic and should be replaced with foundry +models. If you are using FreePDK, you should also have that set up and have the environment variable point to the PDK. For example, in bash, add to your .bashrc: @@ -44,13 +48,16 @@ We do not distribute the PDK, but you may get it from: If you are using SCMOS, you should install Magic and netgen from: http://opencircuitdesign.com/magic/ http://opencircuitdesign.com/netgen/ -In addition, you will need to install the MOSIS SCMOS rules for scn3me_subm -that are part of QFlow: +We have included the SCN3ME design rules from QFlow: http://opencircuitdesign.com/qflow/ # DIRECTORY STRUCTURE * compiler - openram compiler itself (pointed to by OPENRAM_HOME) + * compiler/base - base data structure modules + * compiler/pgates - parameterized cells (e.g. logic gates) + * compiler/modules - high-level modules (e.g. decoders, etc.) + * compiler/verify - DRC and LVS verification wrappers * compiler/characterizer - timing characterization code * compiler/gdsMill - GDSII reader/writer * compiler/router - detailed router @@ -59,6 +66,8 @@ that are part of QFlow: * technology/freepdk45 - example configuration library for freepdk45 technology node * technology/scn3me_subm - example configuration library SCMOS technology node * technology/setup_scripts - setup scripts to customize your PDKs and OpenRAM technologies +* docs - LaTeX manual (likely outdated) +* lib - IP library of pregenerated memories # UNIT TESTS From 69921b0844dbcf75db5dfb558f227d93e8c93a7a Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 11:35:29 -0700 Subject: [PATCH 31/46] Add enclosing well to column mux. Move well contact to cell boundary. --- compiler/pgates/single_level_column_mux.py | 74 ++++++++++++---------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index f442b34b..6b7d7988 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -31,7 +31,7 @@ class single_level_column_mux(design.design): self.add_ptx() self.pin_height = 2*self.m2_width self.width = self.bitcell.width - self.height = self.nmos2.uy() + self.pin_height + self.height = self.nmos_upper.uy() + self.pin_height self.connect_poly() self.add_bitline_pins() self.connect_bitlines() @@ -67,32 +67,32 @@ class single_level_column_mux(design.design): def add_ptx(self): """ Create the two pass gate NMOS transistors to switch the bitlines""" - # Adds nmos1,nmos2 to the module + # Adds nmos_lower,nmos_upper to the module self.nmos = ptx(width=self.ptx_width) self.add_mod(self.nmos) # Space it in the center - nmos1_position = self.nmos.active_offset.scale(0,1) + vector(0.5*self.bitcell.width-0.5*self.nmos.active_width,0) - self.nmos1=self.add_inst(name="mux_tx1", + nmos_lower_position = self.nmos.active_offset.scale(0,1) + vector(0.5*self.bitcell.width-0.5*self.nmos.active_width,0) + self.nmos_lower=self.add_inst(name="mux_tx1", mod=self.nmos, - offset=nmos1_position) + offset=nmos_lower_position) self.connect_inst(["bl", "sel", "bl_out", "gnd"]) # This aligns it directly above the other tx with gates abutting - nmos2_position = nmos1_position + vector(0,self.nmos.active_height + self.poly_space) - self.nmos2=self.add_inst(name="mux_tx2", + nmos_upper_position = nmos_lower_position + vector(0,self.nmos.active_height + self.poly_space) + self.nmos_upper=self.add_inst(name="mux_tx2", mod=self.nmos, - offset=nmos2_position) + offset=nmos_upper_position) self.connect_inst(["br", "sel", "br_out", "gnd"]) def connect_poly(self): """ Connect the poly gate of the two pass transistors """ - height=self.nmos2.get_pin("G").uy() - self.nmos1.get_pin("G").by() + height=self.nmos_upper.get_pin("G").uy() - self.nmos_lower.get_pin("G").by() self.add_layout_pin(text="sel", layer="poly", - offset=self.nmos1.get_pin("G").ll(), + offset=self.nmos_lower.get_pin("G").ll(), height=height) @@ -105,36 +105,36 @@ class single_level_column_mux(design.design): br_out_pin = self.get_pin("br_out") # These are on metal1 - nmos1_s_pin = self.nmos1.get_pin("S") - nmos1_d_pin = self.nmos1.get_pin("D") - nmos2_s_pin = self.nmos2.get_pin("S") - nmos2_d_pin = self.nmos2.get_pin("D") + nmos_lower_s_pin = self.nmos_lower.get_pin("S") + nmos_lower_d_pin = self.nmos_lower.get_pin("D") + nmos_upper_s_pin = self.nmos_upper.get_pin("S") + nmos_upper_d_pin = self.nmos_upper.get_pin("D") - # Add vias to bl, br_out, nmos2/S, nmos1/D + # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D self.add_via_center(layers=("metal1","via1","metal2"), offset=bl_pin.bc()) self.add_via_center(layers=("metal1","via1","metal2"), offset=br_out_pin.uc()) self.add_via_center(layers=("metal1","via1","metal2"), - offset=nmos2_s_pin.center()) + offset=nmos_upper_s_pin.center()) self.add_via_center(layers=("metal1","via1","metal2"), - offset=nmos1_d_pin.center()) + offset=nmos_lower_d_pin.center()) - # bl -> nmos2/D on metal1 - # bl_out -> nmos2/S on metal2 - self.add_path("metal1",[bl_pin.ll(), vector(nmos2_d_pin.cx(),bl_pin.by()), nmos2_d_pin.center()]) + # bl -> nmos_upper/D on metal1 + # bl_out -> nmos_upper/S on metal2 + self.add_path("metal1",[bl_pin.ll(), vector(nmos_upper_d_pin.cx(),bl_pin.by()), nmos_upper_d_pin.center()]) # halfway up, move over - mid1 = bl_out_pin.uc().scale(1,0.5)+nmos2_s_pin.bc().scale(0,0.5) - mid2 = bl_out_pin.uc().scale(0,0.5)+nmos2_s_pin.bc().scale(1,0.5) - self.add_path("metal2",[bl_out_pin.uc(), mid1, mid2, nmos2_s_pin.bc()]) + mid1 = bl_out_pin.uc().scale(1,0.5)+nmos_upper_s_pin.bc().scale(0,0.5) + mid2 = bl_out_pin.uc().scale(0,0.5)+nmos_upper_s_pin.bc().scale(1,0.5) + self.add_path("metal2",[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()]) - # br -> nmos1/D on metal2 - # br_out -> nmos1/S on metal1 - self.add_path("metal1",[br_out_pin.uc(), vector(nmos1_s_pin.cx(),br_out_pin.uy()), nmos1_s_pin.center()]) + # br -> nmos_lower/D on metal2 + # br_out -> nmos_lower/S on metal1 + self.add_path("metal1",[br_out_pin.uc(), vector(nmos_lower_s_pin.cx(),br_out_pin.uy()), nmos_lower_s_pin.center()]) # halfway up, move over - mid1 = br_pin.bc().scale(1,0.5)+nmos1_d_pin.uc().scale(0,0.5) - mid2 = br_pin.bc().scale(0,0.5)+nmos1_d_pin.uc().scale(1,0.5) - self.add_path("metal2",[br_pin.bc(), mid1, mid2, nmos1_d_pin.uc()]) + mid1 = br_pin.bc().scale(1,0.5)+nmos_lower_d_pin.uc().scale(0,0.5) + mid2 = br_pin.bc().scale(0,0.5)+nmos_lower_d_pin.uc().scale(1,0.5) + self.add_path("metal2",[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()]) def add_wells(self): @@ -144,11 +144,11 @@ class single_level_column_mux(design.design): """ # Add it to the right, aligned in between the two tx - active_pos = self.nmos2.lr().scale(0,0.5) + self.nmos1.ur().scale(1,0.5) - self.add_via_center(layers=("active", "contact", "metal1"), - offset=active_pos, - implant_type="p", - well_type="p") + active_pos = vector(self.bitcell.width,self.nmos_upper.by()) + active_via = self.add_via_center(layers=("active", "contact", "metal1"), + offset=active_pos, + implant_type="p", + well_type="p") # Add the M1->M2->M3 stack @@ -159,6 +159,12 @@ class single_level_column_mux(design.design): self.add_layout_pin_rect_center(text="gnd", layer="metal3", offset=active_pos) + + # Add well enclosure over all the tx and contact + self.add_rect(layer="pwell", + offset=vector(0,0), + width=self.bitcell.width, + height=self.height) From fa17d5e7f355d6d931a8ee6fd766faa4b76572e9 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 11:36:30 -0700 Subject: [PATCH 32/46] Change permissions of tests to be executable so you don't have to type python each time. --- compiler/tests/00_code_format_check_test.py | 0 compiler/tests/01_library_drc_test.py | 0 compiler/tests/02_library_lvs_test.py | 0 compiler/tests/03_contact_test.py | 0 compiler/tests/03_path_test.py | 0 compiler/tests/03_ptx_1finger_nmos_test.py | 0 compiler/tests/03_ptx_1finger_pmos_test.py | 0 compiler/tests/03_ptx_3finger_nmos_test.py | 0 compiler/tests/03_ptx_3finger_pmos_test.py | 0 compiler/tests/03_ptx_4finger_nmos_test.py | 0 compiler/tests/03_ptx_4finger_pmos_test.py | 0 compiler/tests/03_wire_test.py | 0 compiler/tests/04_pbitcell_test.py | 0 compiler/tests/04_pinv_10x_test.py | 0 compiler/tests/04_pinv_1x_beta_test.py | 0 compiler/tests/04_pinv_1x_test.py | 0 compiler/tests/04_pinv_2x_test.py | 0 compiler/tests/04_pinvbuf_test.py | 0 compiler/tests/04_pnand2_test.py | 0 compiler/tests/04_pnand3_test.py | 0 compiler/tests/04_pnor2_test.py | 0 compiler/tests/04_precharge_test.py | 0 compiler/tests/04_single_level_column_mux_test.py | 0 compiler/tests/05_bitcell_array_test.py | 0 compiler/tests/05_pbitcell_array_test.py | 0 compiler/tests/06_hierarchical_decoder_test.py | 0 compiler/tests/06_hierarchical_predecode2x4_test.py | 0 compiler/tests/06_hierarchical_predecode3x8_test.py | 0 compiler/tests/07_single_level_column_mux_array_test.py | 0 compiler/tests/08_precharge_array_test.py | 0 compiler/tests/08_wordline_driver_test.py | 0 compiler/tests/09_sense_amp_array_test.py | 0 compiler/tests/10_write_driver_array_test.py | 0 compiler/tests/11_dff_array_test.py | 0 compiler/tests/11_dff_buf_array_test.py | 0 compiler/tests/11_dff_buf_test.py | 0 compiler/tests/11_dff_inv_array_test.py | 0 compiler/tests/11_dff_inv_test.py | 0 compiler/tests/11_ms_flop_array_test.py | 0 compiler/tests/12_tri_gate_array_test.py | 0 compiler/tests/13_delay_chain_test.py | 0 compiler/tests/14_replica_bitline_test.py | 0 compiler/tests/16_control_logic_test.py | 0 compiler/tests/19_bank_select_test.py | 0 compiler/tests/19_multi_bank_test.py | 0 compiler/tests/19_single_bank_test.py | 0 compiler/tests/20_sram_1bank_test.py | 0 compiler/tests/20_sram_2bank_test.py | 0 compiler/tests/20_sram_4bank_test.py | 0 compiler/tests/21_hspice_delay_test.py | 0 compiler/tests/21_hspice_setuphold_test.py | 0 compiler/tests/21_ngspice_delay_test.py | 0 compiler/tests/21_ngspice_setuphold_test.py | 0 compiler/tests/22_sram_func_test.py | 0 compiler/tests/23_lib_sram_model_test.py | 0 compiler/tests/23_lib_sram_prune_test.py | 0 compiler/tests/23_lib_sram_test.py | 0 compiler/tests/24_lef_sram_test.py | 0 compiler/tests/25_verilog_sram_test.py | 0 compiler/tests/30_openram_test.py | 0 compiler/tests/regress.py | 0 61 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 compiler/tests/00_code_format_check_test.py mode change 100644 => 100755 compiler/tests/01_library_drc_test.py mode change 100644 => 100755 compiler/tests/02_library_lvs_test.py mode change 100644 => 100755 compiler/tests/03_contact_test.py mode change 100644 => 100755 compiler/tests/03_path_test.py mode change 100644 => 100755 compiler/tests/03_ptx_1finger_nmos_test.py mode change 100644 => 100755 compiler/tests/03_ptx_1finger_pmos_test.py mode change 100644 => 100755 compiler/tests/03_ptx_3finger_nmos_test.py mode change 100644 => 100755 compiler/tests/03_ptx_3finger_pmos_test.py mode change 100644 => 100755 compiler/tests/03_ptx_4finger_nmos_test.py mode change 100644 => 100755 compiler/tests/03_ptx_4finger_pmos_test.py mode change 100644 => 100755 compiler/tests/03_wire_test.py mode change 100644 => 100755 compiler/tests/04_pbitcell_test.py mode change 100644 => 100755 compiler/tests/04_pinv_10x_test.py mode change 100644 => 100755 compiler/tests/04_pinv_1x_beta_test.py mode change 100644 => 100755 compiler/tests/04_pinv_1x_test.py mode change 100644 => 100755 compiler/tests/04_pinv_2x_test.py mode change 100644 => 100755 compiler/tests/04_pinvbuf_test.py mode change 100644 => 100755 compiler/tests/04_pnand2_test.py mode change 100644 => 100755 compiler/tests/04_pnand3_test.py mode change 100644 => 100755 compiler/tests/04_pnor2_test.py mode change 100644 => 100755 compiler/tests/04_precharge_test.py mode change 100644 => 100755 compiler/tests/04_single_level_column_mux_test.py mode change 100644 => 100755 compiler/tests/05_bitcell_array_test.py mode change 100644 => 100755 compiler/tests/05_pbitcell_array_test.py mode change 100644 => 100755 compiler/tests/06_hierarchical_decoder_test.py mode change 100644 => 100755 compiler/tests/06_hierarchical_predecode2x4_test.py mode change 100644 => 100755 compiler/tests/06_hierarchical_predecode3x8_test.py mode change 100644 => 100755 compiler/tests/07_single_level_column_mux_array_test.py mode change 100644 => 100755 compiler/tests/08_precharge_array_test.py mode change 100644 => 100755 compiler/tests/08_wordline_driver_test.py mode change 100644 => 100755 compiler/tests/09_sense_amp_array_test.py mode change 100644 => 100755 compiler/tests/10_write_driver_array_test.py mode change 100644 => 100755 compiler/tests/11_dff_array_test.py mode change 100644 => 100755 compiler/tests/11_dff_buf_array_test.py mode change 100644 => 100755 compiler/tests/11_dff_buf_test.py mode change 100644 => 100755 compiler/tests/11_dff_inv_array_test.py mode change 100644 => 100755 compiler/tests/11_dff_inv_test.py mode change 100644 => 100755 compiler/tests/11_ms_flop_array_test.py mode change 100644 => 100755 compiler/tests/12_tri_gate_array_test.py mode change 100644 => 100755 compiler/tests/13_delay_chain_test.py mode change 100644 => 100755 compiler/tests/14_replica_bitline_test.py mode change 100644 => 100755 compiler/tests/16_control_logic_test.py mode change 100644 => 100755 compiler/tests/19_bank_select_test.py mode change 100644 => 100755 compiler/tests/19_multi_bank_test.py mode change 100644 => 100755 compiler/tests/19_single_bank_test.py mode change 100644 => 100755 compiler/tests/20_sram_1bank_test.py mode change 100644 => 100755 compiler/tests/20_sram_2bank_test.py mode change 100644 => 100755 compiler/tests/20_sram_4bank_test.py mode change 100644 => 100755 compiler/tests/21_hspice_delay_test.py mode change 100644 => 100755 compiler/tests/21_hspice_setuphold_test.py mode change 100644 => 100755 compiler/tests/21_ngspice_delay_test.py mode change 100644 => 100755 compiler/tests/21_ngspice_setuphold_test.py mode change 100644 => 100755 compiler/tests/22_sram_func_test.py mode change 100644 => 100755 compiler/tests/23_lib_sram_model_test.py mode change 100644 => 100755 compiler/tests/23_lib_sram_prune_test.py mode change 100644 => 100755 compiler/tests/23_lib_sram_test.py mode change 100644 => 100755 compiler/tests/24_lef_sram_test.py mode change 100644 => 100755 compiler/tests/25_verilog_sram_test.py mode change 100644 => 100755 compiler/tests/30_openram_test.py mode change 100644 => 100755 compiler/tests/regress.py diff --git a/compiler/tests/00_code_format_check_test.py b/compiler/tests/00_code_format_check_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/01_library_drc_test.py b/compiler/tests/01_library_drc_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/03_path_test.py b/compiler/tests/03_path_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/03_ptx_1finger_nmos_test.py b/compiler/tests/03_ptx_1finger_nmos_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/03_ptx_1finger_pmos_test.py b/compiler/tests/03_ptx_1finger_pmos_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/03_ptx_3finger_nmos_test.py b/compiler/tests/03_ptx_3finger_nmos_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/03_ptx_3finger_pmos_test.py b/compiler/tests/03_ptx_3finger_pmos_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/03_ptx_4finger_nmos_test.py b/compiler/tests/03_ptx_4finger_nmos_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/03_ptx_4finger_pmos_test.py b/compiler/tests/03_ptx_4finger_pmos_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/03_wire_test.py b/compiler/tests/03_wire_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/04_pinv_10x_test.py b/compiler/tests/04_pinv_10x_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/04_pinv_1x_beta_test.py b/compiler/tests/04_pinv_1x_beta_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/04_pinv_1x_test.py b/compiler/tests/04_pinv_1x_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/04_pinv_2x_test.py b/compiler/tests/04_pinv_2x_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/04_pinvbuf_test.py b/compiler/tests/04_pinvbuf_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/04_pnand2_test.py b/compiler/tests/04_pnand2_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/04_pnand3_test.py b/compiler/tests/04_pnand3_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/04_pnor2_test.py b/compiler/tests/04_pnor2_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/04_single_level_column_mux_test.py b/compiler/tests/04_single_level_column_mux_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/08_wordline_driver_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/11_dff_array_test.py b/compiler/tests/11_dff_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/11_dff_buf_array_test.py b/compiler/tests/11_dff_buf_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/11_dff_buf_test.py b/compiler/tests/11_dff_buf_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/11_dff_inv_array_test.py b/compiler/tests/11_dff_inv_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/11_dff_inv_test.py b/compiler/tests/11_dff_inv_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/11_ms_flop_array_test.py b/compiler/tests/11_ms_flop_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/12_tri_gate_array_test.py b/compiler/tests/12_tri_gate_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/13_delay_chain_test.py b/compiler/tests/13_delay_chain_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/14_replica_bitline_test.py b/compiler/tests/14_replica_bitline_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/16_control_logic_test.py b/compiler/tests/16_control_logic_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/19_bank_select_test.py b/compiler/tests/19_bank_select_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/20_sram_1bank_test.py b/compiler/tests/20_sram_1bank_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/20_sram_4bank_test.py b/compiler/tests/20_sram_4bank_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/21_hspice_setuphold_test.py b/compiler/tests/21_hspice_setuphold_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/21_ngspice_setuphold_test.py b/compiler/tests/21_ngspice_setuphold_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/22_sram_func_test.py b/compiler/tests/22_sram_func_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/24_lef_sram_test.py b/compiler/tests/24_lef_sram_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/regress.py b/compiler/tests/regress.py old mode 100644 new mode 100755 From ac7aa4537c9117ea3b604cf26a96940b8ad3bfbe Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 11:49:02 -0700 Subject: [PATCH 33/46] Remove uniqe pbitcell id since it isn't needed. Convert dos EOL to unix EOL characters. Convert python2.7 to python3 in pbitcell. --- compiler/pgates/pbitcell.py | 5 +- compiler/tests/05_pbitcell_array_test.py | 104 +++++++++++------------ 2 files changed, 53 insertions(+), 56 deletions(-) diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index c3c54a71..3d090432 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -15,11 +15,8 @@ class pbitcell(pgate.pgate): width = None height = None - unique_id = 1 - def __init__(self, num_readwrite=OPTS.rw_ports, num_write=OPTS.w_ports, num_read=OPTS.r_ports): - name = "pbitcell_{0}RW_{1}W_{2}R_{3}".format(num_readwrite, num_write, num_read, pbitcell.unique_id) - pbitcell.unique_id += 1 + name = "pbitcell_{0}RW_{1}W_{2}R".format(num_readwrite, 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)) diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 28c7ce19..058548c8 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -1,52 +1,52 @@ -#!/usr/bin/env python2.7 -""" -Run a regresion test on a basic array -""" - -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 05_array_multiport_test") - -class array_multiport_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - OPTS.check_lvsdrc = False - - import bitcell_array - - OPTS.bitcell = "pbitcell" - OPTS.rw_ports = 2 - OPTS.r_ports = 2 - OPTS.w_ports = 2 - - debug.info(2, "Testing 4x4 array for multiport bitcell, with read ports at the edge of the bit cell") - a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) - self.local_check(a) - - OPTS.rw_ports = 2 - OPTS.r_ports = 0 - OPTS.w_ports = 2 - - debug.info(2, "Testing 4x4 array for multiport bitcell, with read/write ports at the edge of the bit cell") - a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) - self.local_check(a) - - - OPTS.check_lvsdrc = True - globals.end_openram() - -# instantiate a copy 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() +#!/usr/bin/env python3 +""" +Run a regression test on a basic array +""" + +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 05_array_multiport_test") + +class array_multiport_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + global verify + import verify + OPTS.check_lvsdrc = False + + import bitcell_array + + OPTS.bitcell = "pbitcell" + OPTS.rw_ports = 2 + OPTS.r_ports = 2 + OPTS.w_ports = 2 + + debug.info(2, "Testing 4x4 array for multiport bitcell, with read ports at the edge of the bit cell") + a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) + self.local_check(a) + + OPTS.rw_ports = 2 + OPTS.r_ports = 0 + OPTS.w_ports = 2 + + debug.info(2, "Testing 4x4 array for multiport bitcell, with read/write ports at the edge of the bit cell") + a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) + self.local_check(a) + + + OPTS.check_lvsdrc = True + globals.end_openram() + +# instantiate a copy 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() From 721f935d6679173a1d03c109c01cbc69771c618a Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Fri, 29 Jun 2018 12:00:36 -0700 Subject: [PATCH 34/46] changing pbitcell tests to revert OPTS.bitcell to bitcell after tests --- compiler/tests/04_pbitcell_1X_test.py | 1 + compiler/tests/04_pbitcell_test.py | 1 + compiler/tests/05_pbitcell_array_test.py | 1 + 3 files changed, 3 insertions(+) diff --git a/compiler/tests/04_pbitcell_1X_test.py b/compiler/tests/04_pbitcell_1X_test.py index 59ca3457..42191a55 100644 --- a/compiler/tests/04_pbitcell_1X_test.py +++ b/compiler/tests/04_pbitcell_1X_test.py @@ -49,6 +49,7 @@ class pbitcell_test(openram_test): OPTS.check_lvsdrc = True globals.end_openram() + OPTS.bitcell = "bitcell" # instantiate a copy of the class to actually run the test diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 4d51aeec..5aaa9407 100644 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -49,6 +49,7 @@ class pbitcell_test(openram_test): OPTS.check_lvsdrc = True globals.end_openram() + OPTS.bitcell = "bitcell" # instantiate a copy of the class to actually run the test diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 28c7ce19..105268fe 100644 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -43,6 +43,7 @@ class array_multiport_test(openram_test): OPTS.check_lvsdrc = True globals.end_openram() + OPTS.bitcell = "bitcell" # instantiate a copy of the class to actually run the test if __name__ == "__main__": From 32099646cf6a9d6a698a664f56177f70ac819f72 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 12:45:26 -0700 Subject: [PATCH 35/46] Add back fix to revert bitcell from pbitcell. --- compiler/tests/05_pbitcell_array_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 058548c8..2dfed383 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -41,6 +41,7 @@ class array_multiport_test(openram_test): self.local_check(a) + OPTS.bitcell = "bitcell" OPTS.check_lvsdrc = True globals.end_openram() From 6cd1779f7b7299bc271e96e3e16809f73971b259 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 12:47:22 -0700 Subject: [PATCH 36/46] Rename pex test so that it ends with _test and will be run by regress.py. --- compiler/tests/{22_pex_func_test_with_pinv.py => 22_pex_test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler/tests/{22_pex_func_test_with_pinv.py => 22_pex_test.py} (100%) diff --git a/compiler/tests/22_pex_func_test_with_pinv.py b/compiler/tests/22_pex_test.py similarity index 100% rename from compiler/tests/22_pex_func_test_with_pinv.py rename to compiler/tests/22_pex_test.py From 8d61ccbc6f672e8f906fbb31ebab4e11e8f6a486 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 15:11:14 -0700 Subject: [PATCH 37/46] Convert byte string to string. --- compiler/tests/testutils.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 3948bf38..caef7211 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -91,25 +91,25 @@ class openram_test(unittest.TestCase): import re import debug + numeric_const_pattern = r""" + [-+]? # optional sign + (?: + (?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc + | + (?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc + ) + # followed by optional exponent part if desired + (?: [Ee] [+-]? \d+ ) ? + """ + rx = re.compile(numeric_const_pattern, re.VERBOSE) with open(f1, 'rb') as fp1, open(f2, 'rb') as fp2: while True: - b1 = fp1.readline() - b2 = fp2.readline() + b1 = fp1.readline().decode('utf-8') + b2 = fp2.readline().decode('utf-8') #print "b1:",b1, #print "b2:",b2, # 1. Find all of the floats using a regex - numeric_const_pattern = r""" - [-+]? # optional sign - (?: - (?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc - | - (?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc - ) - # followed by optional exponent part if desired - (?: [Ee] [+-]? \d+ ) ? - """ - rx = re.compile(numeric_const_pattern, re.VERBOSE) b1_floats=rx.findall(b1) b2_floats=rx.findall(b2) debug.info(3,"b1_floats: "+str(b1_floats)) @@ -117,9 +117,9 @@ class openram_test(unittest.TestCase): # 2. Remove the floats from the string for f in b1_floats: - b1=b1.replace(str(f),"",1) + b1=b1.replace(f,"",1) for f in b2_floats: - b2=b2.replace(str(f),"",1) + b2=b2.replace(f,"",1) #print "b1:",b1, #print "b2:",b2, From 3de81c8a678270f9aa4eb820daf382b70d52d31e Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 15:11:41 -0700 Subject: [PATCH 38/46] Close files in trim spice and delay. --- compiler/characterizer/trim_spice.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index ead653b9..d3be0dc8 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -17,6 +17,7 @@ class trim_spice(): # Load the file into a buffer for performance sp = open(self.sp_file, "r") self.spice = sp.readlines() + sp.close() for i in range(len(self.spice)): self.spice[i] = self.spice[i].rstrip(" \n") @@ -97,6 +98,7 @@ class trim_spice(): # Finally, write out the buffer as the new reduced file sp = open(self.reduced_spfile, "w") sp.write("\n".join(self.sp_buffer)) + sp.close() def remove_insts(self, subckt_name, keep_inst_list): From 6ac24dbf0cc1a442cadfba397ab65c2d1c8fd900 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 15:12:15 -0700 Subject: [PATCH 39/46] Fix module name for python3 --- compiler/tests/23_lib_sram_model_test.py | 2 +- compiler/tests/23_lib_sram_prune_test.py | 2 +- compiler/tests/23_lib_sram_test.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index 86ab2124..9eb8ac5b 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -30,7 +30,7 @@ class lib_test(openram_test): tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) - lib.lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=True) + lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=True) # get all of the .lib files generated files = os.listdir(OPTS.openram_temp) diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py index 9d26c1ba..4227bc44 100755 --- a/compiler/tests/23_lib_sram_prune_test.py +++ b/compiler/tests/23_lib_sram_prune_test.py @@ -40,7 +40,7 @@ class lib_test(openram_test): tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) - lib.lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False) + lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False) # get all of the .lib files generated files = os.listdir(OPTS.openram_temp) diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index 0bbd63c4..46750250 100755 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -40,7 +40,7 @@ class lib_test(openram_test): tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) - lib.lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False) + lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False) # get all of the .lib files generated files = os.listdir(OPTS.openram_temp) From 99fe3b87feccae17c987fdb6d261fcbcbe12ea9f Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 15:22:58 -0700 Subject: [PATCH 40/46] Remove temp file. Fixing indexing of sense amp outputs. --- compiler/modules/sense_amp_array.py | 13 +- compiler/tests/#03_ptx_4finger_pmos_test.py# | 154 ------------------- 2 files changed, 5 insertions(+), 162 deletions(-) delete mode 100644 compiler/tests/#03_ptx_4finger_pmos_test.py# diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index c56fc047..b4c6342c 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -33,9 +33,8 @@ class sense_amp_array(design.design): def add_pins(self): - for i in range(0,self.row_size,self.words_per_row): - index = int(i/self.words_per_row) - self.add_pin("data[{0}]".format(index)) + for i in range(0,self.word_size): + self.add_pin("data[{0}]".format(i)) self.add_pin("bl[{0}]".format(i)) self.add_pin("br[{0}]".format(i)) @@ -55,7 +54,7 @@ class sense_amp_array(design.design): br_pin = self.amp.get_pin("br") dout_pin = self.amp.get_pin("dout") - for i in range(0,self.row_size,self.words_per_row): + for i in range(0,self.word_size): name = "sa_d{0}".format(i) amp_position = vector(self.amp.width * i, 0) @@ -64,14 +63,12 @@ class sense_amp_array(design.design): br_offset = amp_position + br_pin.ll().scale(1,0) dout_offset = amp_position + dout_pin.ll() - index = int(i/self.words_per_row) - inst = self.add_inst(name=name, mod=self.amp, offset=amp_position) self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), - "data[{0}]".format(index), + "data[{0}]".format(i), "en", "vdd", "gnd"]) @@ -100,7 +97,7 @@ class sense_amp_array(design.design): width=br_pin.width(), height=br_pin.height()) - self.add_layout_pin(text="data[{0}]".format(index), + self.add_layout_pin(text="data[{0}]".format(i), layer="metal2", offset=dout_offset, width=dout_pin.width(), diff --git a/compiler/tests/#03_ptx_4finger_pmos_test.py# b/compiler/tests/#03_ptx_4finger_pmos_test.py# deleted file mode 100644 index 43931cc3..00000000 --- a/compiler/tests/#03_ptx_4finger_pmos_test.py# +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python2.7 -"Run a regresion test on a basic parameterized transistors" - -import unittest -from testutils import header -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -from globals import OPTS -import debug - -class ptx_test(unittest.TestCase): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - OPTS.check_lvsdrc = False - - import ptx - import tech - - debug.info(2, "Checking three fingers PMOS") - fet = ptx.ptx(width=tech.drc["minwidth_tx"], - mults=4, - tx_type="pmos", - connect_active=True, - connect_poly=True) - self.local_check(fet) - - OPTS.check_lvsdrc = True - globals.end_openram() - - def add_mods(self, fet): - self.create_contacts() - self.add_well_extension(fet) - self.add_wire_extension(fet) - self.add_well_tiedown(fet) - self.add_poly_tiedown(fet) - - def create_contacts(self): - layer_stack = ("active", "contact", "metal1") - self.well_contact = contact.contact(layer_stack) - - layer_stack = ("poly", "contact", "metal1") - self.poly_contact = contact.contact(layer_stack) - - def add_well_tiedown(self, fet): - offset = [fet.active_contact_positions[0][0], - fet.active_contact_positions[0][1] + fet.well_height] - fet.add_inst(name="well_tap", - mod=self.well_contact, - offset=offset, - mirror="R0", - rotate=0) - fet.well_contact = self.well_contact - fet.well_tiedown_location = offset - - def add_well_extension(self, fet): - well_define = {"pmos": "nwell", - "nmos": "pwell"} - well_type = well_define[fet.tx_type] - offset = getattr(fet,"{}_position".format(well_type)) - if tech.info["has_{0}".format(well_type)]: - fet.add_rect(layerNumber=tech.layer[well_type], - offset=offset, - width=fet.well_width, - height=2 * fet.well_height) - fet.add_rect(layerNumber=tech.layer["{0}implant".format(fet.tx_type[0])], - offset=offset, - width=fet.well_width, - height=2 * fet.well_height) - fet.add_rect(layerNumber=tech.layer["vtg"], - offset=offset, - width=fet.well_width, - height=2 * fet.well_height) - - well_type = "{0}well".format(fet.tx_type[0]) - offset[1] = offset[1] - 3 * fet.well_height - if tech.info["has_{0}".format(well_type)]: - fet.add_rect(layerNumber=tech.layer[well_type], - offset=offset, - width=fet.well_width, - height=3 * fet.well_height) - fet.add_rect(layerNumber=tech.layer["{0}implant".format(well_define[fet.tx_type][ - 0])], - offset=offset, - width=fet.well_width, - height=3 * fet.well_height) - fet.add_rect(layerNumber=tech.layer["vtg"], - offset=offset, - width=fet.well_width, - height=3 * fet.well_height) - - def add_wire_extension(self, fet): - xcorrect = (fet.active_contact.width / 2) - (tech.drc["minwidth_metal1"] / 2) - offset = [fet.active_contact_positions[0][0] + xcorrect, - fet.active_contact_positions[0][1]] - fet.add_rect(layerNumber=tech.layer["metal1"], - offset=offset, - width=tech.drc["minwidth_metal1"], - height=fet.well_height) - - offset = [fet.active_contact_positions[-1][0] + xcorrect, - fet.active_contact_positions[-1][1] - 2 * fet.well_height] - fet.add_rect(layerNumber=tech.layer["metal1"], - offset=offset, - width=tech.drc["minwidth_metal1"], - height=2 * fet.well_height) - - offset = [fet.poly_positions[-1][0], - fet.poly_positions[-1][1] - (fet.well_height)] - fet.add_rect(layerNumber=tech.layer["poly"], - offset=offset, - width=tech.drc["minwidth_poly"], - height=fet.well_height) - - def add_poly_tiedown(self, fet): - xcorrect = abs(self.poly_contact.upper_layer_vertical_enclosure - - self.poly_contact.lower_layer_vertical_enclosure) - offset = [fet.poly_positions[-1][0] - xcorrect, - fet.poly_positions[-1][1] - (fet.well_height)] - fet.add_inst(name="poly_contact", - mod=self.poly_contact, - offset=offset, - mirror="R270") - - - offset = [fet.active_contact_positions[-1][0], fet.active_contact_positions - [-1][1] - 2 * fet.well_height - self.well_contact.height] - fet.poly_tiedown_location = offset - fet.add_inst(name="n_tiedown", - mod=self.well_contact, - offset=offset) - tech.ptx_port.add_custom_layer(fet) - - def local_check(self, fet): - tempspice = OPTS.openram_temp + "temp.sp" - tempgds = OPTS.openram_temp + "temp.gds" - - fet.sp_write(tempspice) - fet.gds_write(tempgds) - - self.assertFalse(verify.run_drc(fet.name, tempgds)) - - os.remove(tempspice) - os.remove(tempgds) - -# instantiate a copy 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() From cc815f4c334ee6ed0071e2ee9d61e0e5c0a630b8 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 29 Jun 2018 15:30:17 -0700 Subject: [PATCH 41/46] Fix sense amp spacing after modifying index to be increment by one. --- compiler/modules/sense_amp_array.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index b4c6342c..5d16b323 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -54,10 +54,11 @@ class sense_amp_array(design.design): br_pin = self.amp.get_pin("br") dout_pin = self.amp.get_pin("dout") + amp_spacing = self.amp.width * self.words_per_row for i in range(0,self.word_size): name = "sa_d{0}".format(i) - amp_position = vector(self.amp.width * i, 0) + amp_position = vector(amp_spacing * i, 0) bl_offset = amp_position + bl_pin.ll().scale(1,0) br_offset = amp_position + br_pin.ll().scale(1,0) From 68ad436f865f232f90a33108951037435d252dab Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 3 Jul 2018 11:43:23 -0700 Subject: [PATCH 42/46] Add dummy gitlab ci script --- .gitlab-ci.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..82c973ec --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,3 @@ +freepdk45: + script: "python3 tests/regress.py -t freepdk45" + From ded87dbbe74e0ffcaa8750d0746a129570391069 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 3 Jul 2018 11:49:29 -0700 Subject: [PATCH 43/46] Add regress runner scripts --- .gitlab-ci.yml | 5 ++++- regress_freepdk45.sh | 9 +++++++++ regress_scn3me_subm.sh | 9 +++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100755 regress_freepdk45.sh create mode 100755 regress_scn3me_subm.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 82c973ec..736819b4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,6 @@ freepdk45: - script: "python3 tests/regress.py -t freepdk45" + script: "regress_freepdk45.sh" + +scn3me_subm: + script: "regress_scn3me_subm.sh" diff --git a/regress_freepdk45.sh b/regress_freepdk45.sh new file mode 100755 index 00000000..e149f7d5 --- /dev/null +++ b/regress_freepdk45.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# This is a bash utility script to set the paths to the current +# directory of OpenRAM. It must be sourced in the local directory +# like this: +# source setpaths.sh + +export OPENRAM_HOME="`pwd`/compiler" +export OPENRAM_TECH="`pwd`/technology" +python3 $OPENRAM_HOME/tests/regress.py -t freepdk45 diff --git a/regress_scn3me_subm.sh b/regress_scn3me_subm.sh new file mode 100755 index 00000000..ed3f0cb7 --- /dev/null +++ b/regress_scn3me_subm.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# This is a bash utility script to set the paths to the current +# directory of OpenRAM. It must be sourced in the local directory +# like this: +# source setpaths.sh + +export OPENRAM_HOME="`pwd`/compiler" +export OPENRAM_TECH="`pwd`/technology" +python3 $OPENRAM_HOME/tests/regress.py -t scnn3me_subm From cfc7e4972700628447ab30fc94e19e1ec66acca0 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 5 Jul 2018 13:15:37 -0700 Subject: [PATCH 44/46] Test change to README to test repo mirroring --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 49700434..d67155ca 100644 --- a/README.md +++ b/README.md @@ -241,3 +241,4 @@ ui().importCds("default", When you import a gds file, make sure to attach the correct tech lib or you will get incorrect layers in the resulting library. +TEST From 8ad503d0212efef6b27691b529826b8cc79fc45d Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 5 Jul 2018 14:31:31 -0700 Subject: [PATCH 45/46] Remove regress scripts from repo. --- regress_freepdk45.sh | 9 --------- regress_scn3me_subm.sh | 9 --------- 2 files changed, 18 deletions(-) delete mode 100755 regress_freepdk45.sh delete mode 100755 regress_scn3me_subm.sh diff --git a/regress_freepdk45.sh b/regress_freepdk45.sh deleted file mode 100755 index e149f7d5..00000000 --- a/regress_freepdk45.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# This is a bash utility script to set the paths to the current -# directory of OpenRAM. It must be sourced in the local directory -# like this: -# source setpaths.sh - -export OPENRAM_HOME="`pwd`/compiler" -export OPENRAM_TECH="`pwd`/technology" -python3 $OPENRAM_HOME/tests/regress.py -t freepdk45 diff --git a/regress_scn3me_subm.sh b/regress_scn3me_subm.sh deleted file mode 100755 index ed3f0cb7..00000000 --- a/regress_scn3me_subm.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# This is a bash utility script to set the paths to the current -# directory of OpenRAM. It must be sourced in the local directory -# like this: -# source setpaths.sh - -export OPENRAM_HOME="`pwd`/compiler" -export OPENRAM_TECH="`pwd`/technology" -python3 $OPENRAM_HOME/tests/regress.py -t scnn3me_subm From 60b4ebd6a07ee85e06b599957ff0bdbff3765d5e Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 5 Jul 2018 14:33:42 -0700 Subject: [PATCH 46/46] Update full path of gitlab runner --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 736819b4..6c8138fd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,6 @@ freepdk45: - script: "regress_freepdk45.sh" + script: "/home/gitlab-runner/regress_freepdk45.sh" scn3me_subm: - script: "regress_scn3me_subm.sh" + script: "/home/gitlab-runner/regress_scn3me_subm.sh"