import sys from tech import drc, spice import debug from math import log,sqrt,ceil import datetime import getpass from vector import vector from globals import OPTS, print_time from sram_base import sram_base from bank import bank from dff_buf_array import dff_buf_array from dff_array import dff_array class sram_4bank(sram_base): """ Procedures specific to a four bank SRAM. """ def __init__(self, word_size, num_words, name): sram_base.__init__(self, word_size, num_words, 4, name) def compute_bank_offsets(self): """ Compute the overall offsets for a four bank SRAM """ # The main difference is that the four bank SRAM has the data bus in the middle of the four banks # as opposed to the top of the banks. # In 4 bank SRAM, the height is determined by the bank decoder and address flop self.vertical_bus_height = 2*self.bank.height + 4*self.bank_to_bus_distance + self.data_bus_height \ + self.supply_bus_height + self.msb_decoder.height + self.msb_address.width # The address bus extends down through the power rails, but control and bank_sel bus don't self.addr_bus_height = self.vertical_bus_height self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 0) self.data_bus_offset = vector(0, self.bank.height + self.bank_to_bus_distance) self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height \ + self.bank.height + 2*self.bank_to_bus_distance) self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height) self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0) self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0) # Control is placed at the top above the control bus and everything self.control_logic_position = vector(0, self.control_bus_offset.y + self.control_bus_height + self.m1_pitch) # Bank select flops get put to the right of control logic above bank1 and the buses # Leave a pitch to get the vdd rails up to M2 self.msb_address_position = vector(self.bank_inst[1].lx() + 3*self.supply_rail_pitch, self.supply_bus_offset.y + self.supply_bus_height \ + 2*self.m1_pitch + self.msb_address.width) # Decoder goes above the MSB address flops, and is flipped in Y # separate the two by a bank to bus distance for nwell rules, just in case self.msb_decoder_position = self.msb_address_position + vector(self.msb_decoder.width, self.bank_to_bus_distance) def add_modules(self): """ Adds the modules and the buses to the top level """ self.compute_bus_sizes() self.add_banks() self.compute_bank_offsets() self.add_busses() self.add_logic() self.width = self.bank_inst[1].ur().x self.height = max(self.control_logic_inst.uy(),self.msb_decoder_inst.uy()) def add_banks(self): # Placement of bank 0 (upper left) bank_position_0 = vector(self.bank.width, self.bank.height + self.data_bus_height + 2*self.bank_to_bus_distance) self.bank_inst=[self.add_bank(0, bank_position_0, 1, -1)] # Placement of bank 1 (upper right) x_off = self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance bank_position_1 = vector(x_off, bank_position_0.y) self.bank_inst.append(self.add_bank(1, bank_position_1, 1, 1)) # Placement of bank 2 (bottom left) y_off = self.bank.height bank_position_2 = vector(bank_position_0.x, y_off) self.bank_inst.append(self.add_bank(2, bank_position_2, -1, -1)) # Placement of bank 3 (bottom right) bank_position_3 = vector(bank_position_1.x, bank_position_2.y) self.bank_inst.append(self.add_bank(3, bank_position_3, -1, 1)) def add_logic(self): """ Add the control and MSB decode/bank select logic for four banks """ self.add_control_logic(position=self.control_logic_position) self.msb_address_inst = self.add_inst(name="msb_address", mod=self.msb_address, offset=self.msb_address_position, rotate=270) self.msb_bank_sel_addr = ["ADDR[{}]".format(i) for i in range(self.addr_size-2,self.addr_size,1)] temp = list(self.msb_bank_sel_addr) temp.extend(["msb{0}[{1}]".format(j,i) for i in range(2) for j in ["","_bar"]]) temp.extend(["clk_buf", "vdd", "gnd"]) self.connect_inst(temp) self.msb_decoder_inst = self.add_inst(name="msb_decoder", mod=self.msb_decoder, offset=self.msb_decoder_position, mirror="MY") temp = ["msb[{}]".format(i) for i in range(2)] temp.extend(["bank_sel[{}]".format(i) for i in range(4)]) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) def route_double_msb_address(self): """ Route two MSB address bits and the bank decoder for 4-bank SRAM """ # connect the MSB flops to the address input bus for i in [0,1]: msb_pins = self.msb_address_inst.get_pins("din[{}]".format(i)) for msb_pin in msb_pins: if msb_pin.layer == "metal3": msb_pin_pos = msb_pin.lc() break rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr[i]].x,msb_pin_pos.y) self.add_path("metal3",[msb_pin_pos,rail_pos]) self.add_via_center(("metal2","via2","metal3"),rail_pos) # Connect clk clk_pin = self.msb_address_inst.get_pin("clk") clk_pos = clk_pin.bc() rail_pos = self.horz_control_bus_positions["clk_buf"] bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y) self.add_path("metal1",[clk_pos,bend_pos,rail_pos]) # Connect bank decoder outputs to the bank select vertical bus wires for i in range(self.num_banks): msb_pin = self.msb_decoder_inst.get_pin("out[{}]".format(i)) msb_pin_pos = msb_pin.lc() rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,msb_pin_pos.y) self.add_path("metal1",[msb_pin_pos,rail_pos]) self.add_via_center(("metal1","via1","metal2"),rail_pos) # connect MSB flop outputs to the bank decoder inputs msb_pin = self.msb_address_inst.get_pin("dout[0]") msb_pin_pos = msb_pin.rc() in_pin = self.msb_decoder_inst.get_pin("in[0]") in_pos = in_pin.bc() + vector(0,1*self.m2_pitch,) # pin is up from bottom out_pos = msb_pin_pos + vector(1*self.m2_pitch,0) # route out to the right up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos]) self.add_via_center(("metal1","via1","metal2"),in_pos) self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90) msb_pin = self.msb_address_inst.get_pin("dout[1]") msb_pin_pos = msb_pin.rc() in_pin = self.msb_decoder_inst.get_pin("in[1]") in_pos = in_pin.bc() + vector(0,self.bitcell.height+self.m2_pitch) # route the next row up out_pos = msb_pin_pos + vector(2*self.m2_pitch,0) # route out to the right up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos]) self.add_via_center(("metal1","via1","metal2"),in_pos) self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90) self.route_double_msb_address_supplies() def route_double_msb_address_supplies(self): """ Route the vdd/gnd bits of the 2-bit bank decoder. """ # Route the right-most vdd/gnd of the right upper bank to the top of the decoder vdd_pins = self.bank_inst[1].get_pins("vdd") left_bank_vdd_pin = None right_bank_vdd_pin = None for vdd_pin in vdd_pins: if vdd_pin.layer != "metal2": continue if left_bank_vdd_pin == None or vdd_pin.lx()right_bank_vdd_pin.lx(): right_bank_vdd_pin = vdd_pin # Route to top self.add_rect(layer="metal2", offset=vdd_pin.ul(), height=self.height-vdd_pin.uy(), width=vdd_pin.width()) gnd_pins = self.bank_inst[1].get_pins("gnd") left_bank_gnd_pin = None right_bank_gnd_pin = None for gnd_pin in gnd_pins: if gnd_pin.layer != "metal2": continue if left_bank_gnd_pin == None or gnd_pin.lx()right_bank_gnd_pin.lx(): right_bank_gnd_pin = gnd_pin # Route to top self.add_rect(layer="metal2", offset=gnd_pin.ul(), height=self.height-gnd_pin.uy(), width=gnd_pin.width()) # Connect bank decoder vdd/gnd supplies using the previous bank pins vdd_pins = self.msb_decoder_inst.get_pins("vdd") for vdd_pin in vdd_pins: if vdd_pin.layer != "metal1": continue rail1_pos = vector(left_bank_vdd_pin.cx(),vdd_pin.cy()) rail2_pos = vector(right_bank_vdd_pin.cx(),vdd_pin.cy()) self.add_path("metal1",[rail1_pos,rail2_pos]) self.add_via_center(layers=("metal1","via1","metal2"), offset=rail1_pos, rotate=90, size=[1,3]) self.add_via_center(layers=("metal1","via1","metal2"), offset=rail2_pos, rotate=90, size=[1,3]) gnd_pins = self.msb_decoder_inst.get_pins("gnd") for gnd_pin in gnd_pins: if gnd_pin.layer != "metal1": continue rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy()) rail2_pos = vector(right_bank_gnd_pin.cx(),gnd_pin.cy()) self.add_path("metal1",[rail1_pos,rail2_pos]) self.add_via_center(layers=("metal1","via1","metal2"), offset=rail1_pos, rotate=90, size=[1,3]) self.add_via_center(layers=("metal1","via1","metal2"), offset=rail2_pos, rotate=90, size=[1,3]) # connect the bank MSB flop supplies vdd_pins = self.msb_address_inst.get_pins("vdd") # vdd pins go down to the rail for vdd_pin in vdd_pins: if vdd_pin.layer != "metal1": continue vdd_pos = vdd_pin.bc() down_pos = vdd_pos - vector(0,self.m1_pitch) rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y) self.add_path("metal1",[vdd_pos,down_pos]) self.add_via_center(layers=("metal1","via1","metal2"), offset=down_pos, rotate=90) self.add_path("metal2",[down_pos,rail_pos]) self.add_via_center(layers=("metal1","via1","metal2"), offset=rail_pos) # gnd pins go right to the rail gnd_pins = self.msb_address_inst.get_pins("gnd") for gnd_pin in gnd_pins: if gnd_pin.layer != "metal2": continue rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy()) self.add_path("metal1",[rail1_pos,gnd_pin.lc()]) self.add_via_center(layers=("metal1","via1","metal2"), offset=gnd_pin.lc(), rotate=90) self.add_via_center(layers=("metal1","via1","metal2"), offset=rail1_pos, rotate=90, size=[1,3]) def route(self): """ Route all of the signals for the four bank SRAM. """ self.route_shared_banks() # connect the data output to the data bus for n in self.data_bus_names: for i in [0,1]: pin_pos = self.bank_inst[i].get_pin(n).bc() rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y) self.add_path("metal2",[pin_pos,rail_pos]) self.add_via_center(("metal2","via2","metal3"),rail_pos) for i in [2,3]: pin_pos = self.bank_inst[i].get_pin(n).uc() rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y) self.add_path("metal2",[pin_pos,rail_pos]) self.add_via_center(("metal2","via2","metal3"),rail_pos) # route msb address bits # route 2:4 decoder self.route_double_msb_address() # connect the banks to the vertical address bus # connect the banks to the vertical control bus for n in self.addr_bus_names + self.control_bus_names: # Skip these from the horizontal bus if n in ["vdd", "gnd"]: continue # This will be the bank select, so skip it if n in self.msb_bank_sel_addr: continue for bank_id in [0,2]: pin0_pos = self.bank_inst[bank_id].get_pin(n).rc() pin1_pos = self.bank_inst[bank_id+1].get_pin(n).lc() rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y) self.add_path("metal3",[pin0_pos,pin1_pos]) self.add_via_center(("metal2","via2","metal3"),rail_pos) self.route_bank_supply_rails(left_banks=[0,2], bottom_banks=[2,3]) def add_lvs_correspondence_points(self): """ This adds some points for easier debugging if LVS goes wrong. These should probably be turned off by default though, since extraction will show these as ports in the extracted netlist. """ if self.num_banks==1: return for n in self.control_bus_names: self.add_label(text=n, layer="metal2", offset=self.vert_control_bus_positions[n]) for n in self.bank_sel_bus_names: self.add_label(text=n, layer="metal2", offset=self.vert_control_bus_positions[n])