import sys from tech import drc, parameter import debug import design import contact from pinv import pinv from pnand2 import pnand2 from pnor2 import pnor2 from vector import vector from globals import OPTS class bank_select(design.design): """Create a bank select signal that is combined with an array of NOR+INV gates to gate the control signals in case of multiple banks are created in upper level SRAM module """ def __init__(self, name="bank_select", port="rw"): design.design.__init__(self, name) self.port = port self.create_netlist() if not OPTS.netlist_only: self.create_layout() def create_netlist(self): self.add_pins() self.add_modules() self.create_instances() def create_layout(self): self.calculate_module_offsets() self.place_instances() self.route_instances() self.DRC_LVS() def add_pins(self): # Number of control lines in the bus if self.port == "rw": self.num_control_lines = 4 else: self.num_control_lines = 3 # The order of the control signals on the control bus: # FIXME: Update for multiport (these names are not right) self.input_control_signals = ["clk_buf", "clk_buf_bar"] if (self.port == "rw") or (self.port == "w"): self.input_control_signals.append("w_en") if (self.port == "rw") or (self.port == "r"): self.input_control_signals.append("s_en") # These will be outputs of the gaters if this is multibank self.control_signals = ["gated_"+str for str in self.input_control_signals] self.add_pin_list(self.input_control_signals, "INPUT") self.add_pin("bank_sel") self.add_pin_list(self.control_signals, "OUTPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") def add_modules(self): """ Create modules for later instantiation """ from importlib import reload c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() height = self.bitcell.height + drc("poly_to_active") # 1x Inverter self.inv_sel = pinv(height=height) self.add_mod(self.inv_sel) # 4x Inverter self.inv = self.inv4x = pinv(4) self.add_mod(self.inv4x) self.nor2 = pnor2(height=height) self.add_mod(self.nor2) self.inv4x_nor = pinv(size=4, height=height) self.add_mod(self.inv4x_nor) self.nand2 = pnand2() self.add_mod(self.nand2) def calculate_module_offsets(self): self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") self.xoffset_inv = max(self.xoffset_nand + self.nand2.width, self.xoffset_nor + self.nor2.width) self.xoffset_bank_sel_inv = 0 self.xoffset_inputs = 0 self.yoffset_maxpoint = self.num_control_lines * self.inv.height # Include the M1 pitches for the supply rails and spacing self.height = self.yoffset_maxpoint + 2*self.m1_pitch self.width = self.xoffset_inv + self.inv4x.width def create_instances(self): self.bank_sel_inv=self.add_inst(name="bank_sel_inv", mod=self.inv_sel) self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"]) self.logic_inst = [] self.inv_inst = [] for i in range(self.num_control_lines): input_name = self.input_control_signals[i] gated_name = self.control_signals[i] name_nand = "nand_{}".format(input_name) name_nor = "nor_{}".format(input_name) name_inv = "inv_{}".format(input_name) # These require OR (nor2+inv) gates since they are active low. # (writes occur on clk low) if input_name in ("clk_buf"): self.logic_inst.append(self.add_inst(name=name_nor, mod=self.nor2)) self.connect_inst([input_name, "bank_sel_bar", gated_name+"_temp_bar", "vdd", "gnd"]) # They all get inverters on the output self.inv_inst.append(self.add_inst(name=name_inv, mod=self.inv4x_nor)) self.connect_inst([gated_name+"_temp_bar", gated_name, "vdd", "gnd"]) # the rest are AND (nand2+inv) gates else: self.logic_inst.append(self.add_inst(name=name_nand, mod=self.nand2)) self.connect_inst([input_name, "bank_sel", gated_name+"_temp_bar", "vdd", "gnd"]) # They all get inverters on the output self.inv_inst.append(self.add_inst(name=name_inv, mod=self.inv4x)) self.connect_inst([gated_name+"_temp_bar", gated_name, "vdd", "gnd"]) def place_instances(self): # bank select inverter self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0) # bank select inverter (must be made unique if more than one OR) self.bank_sel_inv.place(vector(self.xoffset_bank_sel_inv, 0)) for i in range(self.num_control_lines): logic_inst = self.logic_inst[i] inv_inst = self.inv_inst[i] input_name = self.input_control_signals[i] if i == 0: y_offset = 0 else: y_offset = self.inv4x_nor.height + self.inv.height * (i-1) if i%2: y_offset += self.inv.height mirror = "MX" else: mirror = "" # These require OR (nor2+inv) gates since they are active low. # (writes occur on clk low) if input_name in ("clk_buf"): logic_inst.place(offset=[self.xoffset_nor, y_offset], mirror=mirror) # the rest are AND (nand2+inv) gates else: logic_inst.place(offset=[self.xoffset_nand, y_offset], mirror=mirror) # They all get inverters on the output inv_inst.place(offset=[self.xoffset_inv, y_offset], mirror=mirror) def route_instances(self): # bank_sel is vertical wire bank_sel_inv_pin = self.bank_sel_inv.get_pin("A") xoffset_bank_sel = bank_sel_inv_pin.lx() bank_sel_line_pos = vector(xoffset_bank_sel, 0) bank_sel_line_end = vector(xoffset_bank_sel, self.yoffset_maxpoint) self.add_path("metal2", [bank_sel_line_pos, bank_sel_line_end]) self.add_via_center(layers=("metal1","via1","metal2"), offset=bank_sel_inv_pin.lc()) # Route the pin to the left edge as well bank_sel_pin_pos=vector(0, 0) bank_sel_pin_end=vector(bank_sel_line_pos.x, bank_sel_pin_pos.y) self.add_layout_pin_segment_center(text="bank_sel", layer="metal3", start=bank_sel_pin_pos, end=bank_sel_pin_end) self.add_via_center(layers=("metal2","via2","metal3"), offset=bank_sel_pin_end, rotate=90) # bank_sel_bar is vertical wire bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z") xoffset_bank_sel_bar = bank_sel_bar_pin.rx() self.add_label_pin(text="bank_sel_bar", layer="metal2", offset=vector(xoffset_bank_sel_bar, 0), height=self.inv.height) self.add_via_center(layers=("metal1","via1","metal2"), offset=bank_sel_bar_pin.rc()) for i in range(self.num_control_lines): logic_inst = self.logic_inst[i] inv_inst = self.inv_inst[i] input_name = self.input_control_signals[i] gated_name = self.control_signals[i] if input_name in ("clk_buf"): xoffset_bank_signal = xoffset_bank_sel_bar else: xoffset_bank_signal = xoffset_bank_sel # Connect the logic output to inverter input pre = logic_inst.get_pin("Z").lc() out_position = logic_inst.get_pin("Z").rc() + vector(0.5*self.m1_width,0) in_position = inv_inst.get_pin("A").lc() + vector(0.5*self.m1_width,0) post = inv_inst.get_pin("A").rc() self.add_path("metal1", [pre, out_position, in_position, post]) # Connect the logic B input to bank_sel/bank_sel_bar logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1m2.height,0) input_pos = vector(xoffset_bank_signal, logic_pos.y) self.add_path("metal2",[logic_pos, input_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=logic_pos, rotate=90) # Connect the logic A input to the input pin logic_pos = logic_inst.get_pin("A").lc() input_pos = vector(0,logic_pos.y) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=logic_pos, rotate=90) self.add_via_center(layers=("metal2", "via2", "metal3"), offset=logic_pos, rotate=90) self.add_layout_pin_segment_center(text=input_name, layer="metal3", start=input_pos, end=logic_pos) # Add output pins out_pin = inv_inst.get_pin("Z") self.add_layout_pin(text=gated_name, layer=out_pin.layer, offset=out_pin.ll(), width=inv_inst.rx() - out_pin.lx(), height=out_pin.height()) # Find the x offsets for where the vias/pins should be placed a_xoffset = self.logic_inst[0].lx() b_xoffset = self.inv_inst[0].lx() for num in range(self.num_control_lines): # Route both supplies for n in ["vdd", "gnd"]: supply_pin = self.inv_inst[num].get_pin(n) supply_offset = supply_pin.ll().scale(0,1) self.add_rect(layer="metal1", offset=supply_offset, width=self.width) # Add pins in two locations for xoffset in [a_xoffset, b_xoffset]: pin_pos = vector(xoffset, supply_pin.cy()) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=pin_pos, rotate=90) self.add_via_center(layers=("metal2", "via2", "metal3"), offset=pin_pos, rotate=90) self.add_layout_pin_rect_center(text=n, layer="metal3", offset=pin_pos) # Add vdd/gnd supply rails gnd_pin = inv_inst.get_pin("gnd") left_gnd_pos = vector(0, gnd_pin.cy()) self.add_layout_pin_segment_center(text="gnd", layer="metal1", start=left_gnd_pos, end=gnd_pin.rc()) vdd_pin = inv_inst.get_pin("vdd") left_vdd_pos = vector(0, vdd_pin.cy()) self.add_layout_pin_segment_center(text="vdd", layer="metal1", start=left_vdd_pos, end=vdd_pin.rc())