diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 5d67ff6e..6b77589e 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -21,7 +21,8 @@ class bank(design.design): mod_list = ["tri_gate", "bitcell", "decoder", "ms_flop_array", "wordline_driver", "bitcell_array", "sense_amp_array", "precharge_array", - "column_mux_array", "write_driver_array", "tri_gate_array"] + "column_mux_array", "write_driver_array", "tri_gate_array", + "bank_select"] for mod_name in mod_list: config_mod_name = getattr(OPTS, mod_name) class_file = reload(__import__(config_mod_name)) @@ -50,11 +51,13 @@ class bank(design.design): self.create_modules() self.add_modules() self.setup_layout_constraints() + self.route_power_ring(self.core_bbox) + + if self.num_banks > 1: + self.add_bank_select() + self.route_layout() - # Add and route the bank select logic - if(self.num_banks > 1): - self.add_bank_select() # Can remove the following, but it helps for debug! self.add_lvs_correspondence_points() @@ -72,7 +75,7 @@ class bank(design.design): # For more than one bank, we have a bank select and name # the signals gated_*. - if(self.num_banks > 1): + if self.num_banks > 1: self.add_pin("bank_sel") for pin in ["s_en","w_en","tri_en_bar","tri_en", "clk_bar","clk_buf","vdd","gnd"]: @@ -80,7 +83,6 @@ class bank(design.design): def route_layout(self): """ Create routing amoung the modules """ - self.route_power_ring(self.core_bbox) self.create_central_bus() self.route_precharge_to_bitcell_array() self.route_sense_amp_to_trigate() @@ -91,6 +93,9 @@ class bank(design.design): self.route_msf_address() self.route_control_lines() self.add_control_pins() + if self.num_banks > 1: + self.route_bank_select() + self.route_vdd_supply() self.route_gnd_supply() @@ -108,7 +113,7 @@ class bank(design.design): self.column_mux_height = 0 if self.col_addr_size > 1: # size 1 is from addr FF self.add_column_decoder() - + self.add_sense_amp_array() self.add_write_driver_array() self.add_msf_data_in() @@ -139,7 +144,7 @@ class bank(design.design): # The order of the control signals on the control bus: self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_bar", "w_en", "s_en"] # These will be outputs of the gaters if this is multibank - if self.num_banks>1: + if self.num_banks > 1: self.control_signals = ["gated_"+str for str in self.input_control_signals] else: self.control_signals = self.input_control_signals @@ -177,7 +182,7 @@ class bank(design.design): self.precharge_array = self.mod_precharge_array(columns=self.num_cols) self.add_mod(self.precharge_array) - if(self.col_addr_size > 0): + if self.col_addr_size > 0: self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols, word_size=self.word_size) self.add_mod(self.column_mux_array) @@ -213,6 +218,10 @@ class bank(design.design): self.inv = pinv() self.add_mod(self.inv) + + if(self.num_banks > 1): + self.bank_select = self.mod_bank_select() + self.add_mod(self.bank_select) def add_bitcell_array(self): @@ -452,133 +461,37 @@ class bank(design.design): temp.extend(["vdd", "gnd"]) self.connect_inst(temp) + def add_bank_select(self): - """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 - """ + """ Instantiate the bank select logic. """ + xoffset = self.left_vdd_x_offset + self.supply_rail_pitch + # extra space to allow vias + yoffset = self.min_point + 2*self.supply_rail_pitch + self.m1_space + self.bank_select_pos = vector(xoffset,yoffset) + self.bank_select_inst = self.add_inst(name="bank_select", + mod=self.bank_select, + offset=self.bank_select_pos) - # 4x Inverter - self.inv4x = pinv(4) - self.add_mod(self.inv4x) - - self.nor2 = pnor2() - self.add_mod(self.nor2) - - self.nand2 = pnand2() - self.add_mod(self.nand2) - - # left of gnd rail is the "bus start" - bus_start = self.start_of_right_central_bus - self.m2_space - xoffset_nand = bus_start - self.nand2.width - self.inv4x.width - drc["pwell_to_nwell"] - xoffset_nor = bus_start - self.nor2.width - self.inv4x.width - drc["pwell_to_nwell"] - xoffset_inv = bus_start - self.inv4x.width - xoffset_bank_sel_inv = xoffset_nor - self.inv.width - 3*self.m2_pitch - xoffset_inputs = xoffset_bank_sel_inv - 6*self.m2_pitch - - # bank select inverter - self.bank_select_inv_position = vector(xoffset_bank_sel_inv, - self.min_point) - # bank select inverter (must be made unique if more than one OR) - bank_sel_inv=self.add_inst(name="bank_sel_inv", - mod=self.inv, - offset=[xoffset_bank_sel_inv,self.min_point]) - self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"]) - - # bank_sel is vertical wire - bank_sel_inv_pin = bank_sel_inv.get_pin("A") - xoffset_bank_sel = bank_sel_inv_pin.lx() - bank_sel_line_pos = vector(xoffset_bank_sel, self.min_point) - bank_sel_line_end = vector(xoffset_bank_sel, self.decoder_min_point-self.m2_pitch) - 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(self.left_vdd_x_offset, self.min_point) - bank_sel_pin_end=vector(bank_sel_line_pos.x, bank_sel_pin_pos.y) - self.add_layout_pin_center_segment(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 = 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, self.min_point), - height=2*self.inv.height) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=bank_sel_bar_pin.rc()) - + temp = [] 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) + temp.append(self.input_control_signals[i]) + for i in range(self.num_control_lines): + temp.append(self.control_signals[i]) + temp.extend(["vdd", "gnd"]) + self.connect_inst(temp) - y_offset = self.min_point + self.inv.height * i - 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", "tri_en_bar"): - - logic_inst=self.add_inst(name=name_nor, - mod=self.nor2, - offset=[xoffset_nor, y_offset], - mirror=mirror) - self.connect_inst([input_name, - "bank_sel_bar", - gated_name+"_temp_bar", - "vdd", - "gnd"]) - xoffset_bank_signal = xoffset_bank_sel_bar - - - # the rest are AND (nand2+inv) gates - else: - logic_inst=self.add_inst(name=name_nand, - mod=self.nand2, - offset=[xoffset_nand, y_offset], - mirror=mirror) - bank_sel_signal = "bank_sel" - self.connect_inst([input_name, - "bank_sel", - gated_name+"_temp_bar", - "vdd", - "gnd"]) - xoffset_bank_signal = xoffset_bank_sel - - # They all get inverters on the output - inv_inst=self.add_inst(name=name_inv, - mod=self.inv4x, - offset=[xoffset_inv, y_offset], - mirror=mirror) - self.connect_inst([gated_name+"_temp_bar", - gated_name, - "vdd", - "gnd"]) - - - # 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]) + def route_bank_select(self): + """ Route the bank select logic. """ + for input_name in self.input_control_signals+["bank_sel"]: + in_pos = self.bank_select_inst.get_pin(input_name).lc() + self.add_layout_pin_center_segment(text=input_name, + layer="metal3", + start=vector(self.left_gnd_x_offset,in_pos.y), + end=in_pos) + for gated_name in self.control_signals: # Connect the inverter output to the central bus - out_pos = inv_inst.get_pin("Z").rc() + out_pos = self.bank_select_inst.get_pin(gated_name).rc() bus_pos = vector(self.central_line_xoffset[gated_name], out_pos.y) self.add_path("metal3",[out_pos, bus_pos]) self.add_via_center(layers=("metal2", "via2", "metal3"), @@ -590,50 +503,7 @@ class bank(design.design): self.add_via_center(layers=("metal2", "via2", "metal3"), offset=out_pos, rotate=90) - - # 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(self.left_vdd_x_offset,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_center_segment(text=input_name, - layer="metal3", - start=input_pos, - end=logic_pos) - - - - # Add vdd/gnd supply rails - gnd_pin = inv_inst.get_pin("gnd") - left_gnd_pos = vector(self.left_gnd_x_center, gnd_pos.cy()) - self.add_path("metal1",[left_gnd_pos, gnd_pos.rc()]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left_gnd_pos, - size = (1,3), - rotate=90) - - vdd_pin = inv_inst.get_pin("vdd") - left_vdd_pos = vector(self.left_vdd_x_center, vdd_pin.cy()) - self.add_path("metal1",[left_vdd_pos, vdd_pin.rc()]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left_vdd_pos, - size = (1,3), - rotate=90) - - + def setup_layout_constraints(self): """ Calculating layout constraints, width, height etc """ @@ -653,7 +523,7 @@ class bank(design.design): if self.num_banks>1: # The control gating logic is below the decoder # Min of the control gating logic and tri gate. - self.min_point = min(self.decoder_min_point - self.num_control_lines * self.bitcell.height, tri_gate_min_point) + self.min_point = min(self.decoder_min_point - self.bank_select.height, tri_gate_min_point) else: # Just the min of the decoder logic logic and tri gate. self.min_point = min(self.decoder_min_point, addr_min_point, tri_gate_min_point) @@ -961,30 +831,6 @@ class bank(design.design): self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) - # route the gnd rails, add contact to rail as well - for gnd_pin in self.wordline_driver_inst.get_pins("gnd"): - gnd_pos = gnd_pin.rc() - left_rail_pos = vector(self.left_gnd_x_center, gnd_pos.y) - self.add_path("metal1", [left_rail_pos, gnd_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left_rail_pos, - size = (1,3), - rotate=90) - - # route the vdd rails - for vdd_pin in self.wordline_driver_inst.get_pins("vdd"): - vdd_pos = vdd_pin.rc() - left_rail_pos = vector(self.left_vdd_x_center, vdd_pos.y) - right_rail_pos = vector(self.right_vdd_x_center, vdd_pos.y) - self.add_path("metal1", [left_rail_pos, right_rail_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left_rail_pos, - size = (1,3), - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=right_rail_pos, - size = (1,3), - rotate=90) @@ -1237,9 +1083,11 @@ class bank(design.design): def route_vdd_supply(self): """ Route vdd for the precharge, sense amp, write_driver, data FF, tristate """ - for inst in [self.precharge_array_inst, self.sense_amp_array_inst, - self.write_driver_array_inst, self.msf_data_in_inst, - self.tri_gate_array_inst]: + # Route the vdd rails to the RIGHT + modules = [self.precharge_array_inst, self.sense_amp_array_inst, + self.write_driver_array_inst, self.msf_data_in_inst, + self.tri_gate_array_inst] + for inst in modules: for vdd_pin in inst.get_pins("vdd"): self.add_rect(layer="metal1", offset=vdd_pin.ll(), @@ -1250,12 +1098,39 @@ class bank(design.design): offset=via_position, size = (1,3), rotate=90) + + # Route the vdd rails to the LEFT + for vdd_pin in self.wordline_driver_inst.get_pins("vdd"): + vdd_pos = vdd_pin.rc() + left_rail_pos = vector(self.left_vdd_x_center, vdd_pos.y) + right_rail_pos = vector(self.right_vdd_x_center, vdd_pos.y) + self.add_path("metal1", [left_rail_pos, right_rail_pos]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=left_rail_pos, + size = (1,3), + rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=right_rail_pos, + size = (1,3), + rotate=90) + if self.num_banks>1: + for vdd_pin in self.bank_select_inst.get_pins("vdd"): + vdd_pos = vdd_pin.rc() + left_rail_pos = vector(self.left_vdd_x_center, vdd_pos.y) + self.add_path("metal1", [left_rail_pos, vdd_pos]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=left_rail_pos, + size = (1,3), + rotate=90) + def route_gnd_supply(self): - """ Route gnd for the precharge, sense amp, write_driver, data FF, tristate """ + """ Route gnd rails""" + # Route the gnd rails to the RIGHT # precharge is connected by abutment - for inst in [ self.tri_gate_array_inst, self.sense_amp_array_inst, self.msf_data_in_inst, self.write_driver_array_inst]: + modules = [ self.tri_gate_array_inst, self.sense_amp_array_inst, self.msf_data_in_inst, self.write_driver_array_inst] + for inst in modules: for gnd_pin in inst.get_pins("gnd"): if gnd_pin.layer != "metal1": continue @@ -1269,6 +1144,20 @@ class bank(design.design): size = (1,3), rotate=90) + # Route the gnd rails to the LEFT + modules = [self.wordline_driver_inst] + if self.num_banks>1: + modules.append(self.bank_select_inst) + for inst in modules: + for gnd_pin in inst.get_pins("gnd"): + gnd_pos = gnd_pin.rc() + left_rail_pos = vector(self.left_gnd_x_center, gnd_pos.y) + self.add_path("metal1", [left_rail_pos, gnd_pos]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=left_rail_pos, + size = (1,3), + rotate=90) + def add_control_pins(self): """ Add the control signal input pins """ diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 83c1c2aa..de79f08b 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -31,6 +31,8 @@ class bank_select(design.design): for i in range(self.num_control_lines): gated_name = self.control_signals[i] self.add_pin(gated_name) + self.add_pin("vdd") + self.add_pin("gnd") self.create_modules() self.calculate_module_offsets() @@ -61,26 +63,26 @@ class bank_select(design.design): self.m1_pitch = contact.m1m2.height + max(self.m1_space,self.m2_space) self.m2_pitch = contact.m2m3.height + max(self.m2_space,self.m3_space) - # left of gnd rail is the "bus start" 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 - # Above the bottom rails (plus a pitch to allow vias) - self.yoffset_minpoint = self.m1_pitch 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 add_modules(self): # bank select inverter - self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, - self.yoffset_minpoint) + 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=self.add_inst(name="bank_sel_inv", mod=self.inv, - offset=[self.xoffset_bank_sel_inv, self.yoffset_minpoint]) + offset=[self.xoffset_bank_sel_inv, 0]) self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"]) self.logic_inst = [] @@ -92,7 +94,7 @@ class bank_select(design.design): name_nor = "nor_{}".format(input_name) name_inv = "inv_{}".format(input_name) - y_offset = self.yoffset_minpoint + self.inv.height * i + y_offset = self.inv.height * i if i%2: y_offset += self.inv.height mirror = "MX" @@ -143,14 +145,14 @@ class bank_select(design.design): # 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, self.yoffset_minpoint) + 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, self.yoffset_minpoint) + 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_center_segment(text="bank_sel", layer="metal3", @@ -165,7 +167,7 @@ class bank_select(design.design): 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, self.yoffset_minpoint), + offset=vector(xoffset_bank_sel_bar, 0), height=2*self.inv.height) self.add_via_center(layers=("metal1","via1","metal2"), offset=bank_sel_bar_pin.rc()) diff --git a/compiler/sram.py b/compiler/sram.py index 6e10a794..41d8fd2d 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -68,7 +68,7 @@ class sram(design.design): # Can remove the following, but it helps for debug! self.add_lvs_correspondence_points() - self.offset_all_coordinates() + #self.offset_all_coordinates() sizes = self.find_highest_coords() self.width = sizes[0] self.height = sizes[1] @@ -175,10 +175,12 @@ class sram(design.design): def add_four_bank_modules(self): """ Adds the modules and the buses to the top level """ - self.compute_four_bank_offsets() + self.compute_bus_sizes() self.add_four_banks() + self.compute_four_bank_offsets() + self.add_busses() self.add_four_bank_logic() @@ -190,10 +192,12 @@ class sram(design.design): def add_two_bank_modules(self): """ Adds the modules and the buses to the top level """ - self.compute_two_bank_offsets() + self.compute_bus_sizes() self.add_two_banks() + self.compute_two_bank_offsets() + self.add_busses() self.add_two_bank_logic() @@ -233,7 +237,6 @@ class sram(design.design): self.add_path("metal3",[pin_pos,rail_pos]) self.add_via_center(("metal2","via2","metal3"),rail_pos) - def route_four_banks(self): """ Route all of the signals for the four bank SRAM. """ @@ -274,7 +277,7 @@ class sram(design.design): self.add_via_center(("metal2","via2","metal3"),rail_pos) - self.route_bank_supply_rails(bottom_banks=[2,3]) + self.route_bank_supply_rails(left_banks=[0,2], bottom_banks=[2,3]) @@ -310,16 +313,14 @@ class sram(design.design): # 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. - self.compute_bus_sizes() - # 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 + 2*self.power_rail_pitch + self.addr_bus_height = self.vertical_bus_height - self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 2*self.power_rail_pitch) - self.data_bus_offset = vector(0, 2*self.power_rail_pitch + self.bank.height + self.bank_to_bus_distance) + 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) @@ -330,7 +331,7 @@ class sram(design.design): # 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(max(self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance, self.control_logic.width), + 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 @@ -341,15 +342,13 @@ class sram(design.design): def compute_two_bank_offsets(self): """ Compute the overall offsets for a two bank SRAM """ - self.compute_bus_sizes() - # In 2 bank SRAM, the height is determined by the control bus which is higher than the msb address self.vertical_bus_height = self.bank.height + 2*self.bank_to_bus_distance + self.data_bus_height + self.control_bus_height # 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 + 2*self.power_rail_pitch + self.addr_bus_height = self.vertical_bus_height - self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 2*self.power_rail_pitch) - self.data_bus_offset = vector(0, 2*self.power_rail_pitch + self.bank.height + self.bank_to_bus_distance) + 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.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) @@ -360,7 +359,7 @@ class sram(design.design): # 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(max(self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance,self.control_logic.width), + 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) def add_busses(self): @@ -419,7 +418,7 @@ class sram(design.design): pitch=self.m1_pitch, offset=self.supply_bus_offset+vector(0,self.m1_pitch), names=["gnd"], - length=self.supply_bus_width-2*self.power_rail_width, + length=self.supply_bus_width, vertical=False)) self.horz_control_bus_positions.update(self.create_bus(layer="metal1", pitch=self.m1_pitch, @@ -497,13 +496,11 @@ class sram(design.design): self.add_via_center(("metal2","via2","metal3"),rail_pos) - self.route_bank_supply_rails(bottom_banks=[0,1]) + self.route_bank_supply_rails(left_banks=[0], bottom_banks=[0,1]) 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]: @@ -551,68 +548,107 @@ class sram(design.design): 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") - highest_x = None - for bank_vdd_pin in vdd_pins: - if highest_x == None or bank_vdd_pin.lx()>highest_x: - highest_x = bank_vdd_pin.lx() - bank_vdd_pos = bank_vdd_pin.ul() - # Route to top - self.add_rect(layer="metal1", - offset=bank_vdd_pos, - height=self.height-bank_vdd_pos.y, - width=bank_vdd_pin.width()) + 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") - highest_x = None - for bank_gnd_pin in gnd_pins: - if highest_x == None or bank_gnd_pin.lx()>highest_x: - highest_x = bank_gnd_pin.lx() - bank_gnd_pos = bank_gnd_pin.ul() - # Route to top - self.add_rect(layer="metal2", - offset=bank_gnd_pos, - height=self.height-bank_gnd_pos.y, - width=bank_gnd_pin.width()) + 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_vdd_pos and bank_gnd_pos + # 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 - vdd_pos = vdd_pin.rc() - rail_pos = vector(bank_vdd_pos.x+bank_vdd_pin.width(),vdd_pos.y) - self.add_path("metal1",[vdd_pos,rail_pos]) + 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 - gnd_pos = gnd_pin.rc() - rail_pos = vector(bank_gnd_pos.x+bank_gnd_pin.width(),gnd_pos.y) - self.add_path("metal1",[gnd_pos,rail_pos]) - self.add_via(("metal1","via1","metal2"),rail_pos- vector(0,0.5*self.m1_width),rotate=90,size=[1,3]) + 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 + 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(("metal1","via1","metal2"),down_pos,rotate=90) + 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(("metal1","via1","metal2"),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 - gnd_pos = gnd_pin.rc() - rail_pos = vector(bank_gnd_pos.x+bank_gnd_pin.width(),gnd_pos.y) - self.add_path("metal1",[gnd_pos,rail_pos]) - self.add_via_center(("metal1","via1","metal2"),gnd_pos,rotate=90) - self.add_via(("metal1","via1","metal2"),rail_pos- vector(0,0.5*self.m1_width),rotate=90,size=[1,3]) + 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_single_msb_address(self): """ Route one MSB address bit for 2-bank SRAM """ @@ -681,65 +717,65 @@ class sram(design.design): - def route_bank_supply_rails(self, bottom_banks): + def route_bank_supply_rails(self, left_banks, bottom_banks): """ Create rails at bottom. Connect veritcal rails to top and bottom. """ - - self.add_layout_pin(text="gnd", - layer="metal1", - offset=vector(0,0), - height=self.power_rail_width, - width=self.width) - self.add_layout_pin(text="vdd", - layer="metal1", - offset=vector(0,self.power_rail_pitch), - height=self.power_rail_width, - width=self.width) + for i in left_banks: + vdd_pins = self.bank_inst[i].get_pins("vdd") + for vdd_pin in vdd_pins: + if vdd_pin.layer != "metal1": + continue + self.add_layout_pin(text="vdd", + layer=vdd_pin.layer, + offset=vdd_pin.ll(), + height=vdd_pin.height(), + width=self.width) + + gnd_pins = self.bank_inst[i].get_pins("gnd") + for gnd_pin in gnd_pins: + if gnd_pin.layer != "metal1": + continue + self.add_layout_pin(text="gnd", + layer=gnd_pin.layer, + offset=gnd_pin.ll(), + height=gnd_pin.height(), + width=self.width) + # route bank vertical rails to bottom for i in bottom_banks: vdd_pins = self.bank_inst[i].get_pins("vdd") for vdd_pin in vdd_pins: - vdd_pos = vdd_pin.ul() - # Route to bottom - self.add_rect(layer="metal2", - offset=vector(vdd_pos.x,self.power_rail_pitch), - height=self.horz_control_bus_positions["vdd"].y-self.power_rail_pitch, + if vdd_pin.layer != "metal2": + continue + # Route from bottom to top + self.add_rect(layer=vdd_pin.layer, + offset=vdd_pin.ll(), + height=self.horz_control_bus_positions["vdd"].y, width=vdd_pin.width()) # Add vias at top - rail_pos = vector(vdd_pin.ur().x,self.horz_control_bus_positions["vdd"].y) - self.add_via(layers=("metal1","via1","metal2"), - offset=rail_pos - vector(0,0.5*self.m1_width), - rotate=90, - size=[1,3]) - # Add vias at bottom - rail_pos = vector(vdd_pin.lr().x,self.power_rail_pitch) - self.add_via(layers=("metal1","via1","metal2"), + rail_pos = vector(vdd_pin.cx(),self.horz_control_bus_positions["vdd"].y) + self.add_via_center(layers=("metal1","via1","metal2"), offset=rail_pos, rotate=90, - size=[2,3]) + size=[1,3]) gnd_pins = self.bank_inst[i].get_pins("gnd") for gnd_pin in gnd_pins: - gnd_pos = gnd_pin.ul() - # Route to bottom - self.add_rect(layer="metal2", - offset=vector(gnd_pos.x,0), - height=self.horz_control_bus_positions["gnd"].y, # route to the top bank + if gnd_pin.layer != "metal2": + continue + # Route from bottom to top + self.add_rect(layer=gnd_pin.layer, + offset=gnd_pin.ll(), + height=self.horz_control_bus_positions["gnd"].y, width=gnd_pin.width()) # Add vias at top - right_rail_pos = vector(gnd_pin.ur().x,self.horz_control_bus_positions["gnd"].y) - self.add_via(layers=("metal1","via1","metal2"), - offset=right_rail_pos - vector(0,0.5*self.m1_width), + rail_pos = vector(gnd_pin.cx(),self.horz_control_bus_positions["gnd"].y) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=rail_pos, rotate=90, - size=[1,3]) - # Add vias at bottom - right_rail_pos = vector(gnd_pin.lr().x,0) - self.add_via(layers=("metal2","via2","metal3"), - offset=right_rail_pos, - rotate=90, - size=[2,3]) + size=[1,3]) @@ -775,9 +811,9 @@ class sram(design.design): self.bank_count = 0 - self.power_rail_width = self.bank.vdd_rail_width + self.supply_rail_width = self.bank.supply_rail_width # Leave some extra space for the pitch - self.power_rail_pitch = self.bank.vdd_rail_width + 2*self.m3_space + self.supply_rail_pitch = self.bank.supply_rail_pitch @@ -897,12 +933,13 @@ class sram(design.design): # No orientation or offset self.bank_inst = self.add_bank(0, [0, 0], 1, 1) - - # Control logic is placed to the left of the blank even with the - # decoder bottom. A small gap is in the x-dimension. + + # 3/5/18 MRG: Cannot reference positions inside submodules because boundaries + # are not recomputed using instance placement. So, place the control logic such that it aligns + # with the top of the SRAM. control_gap = 2*self.m3_width pos = vector(-control_gap, - self.bank.row_decoder_inst.by() + 2*self.m3_width) + self.bank.height-self.control_logic.width) self.add_control_logic(position=pos, rotate=90) @@ -930,7 +967,7 @@ class sram(design.design): def add_two_banks(self): # Placement of bank 0 (left) bank_position_0 = vector(self.bank.width, - self.bank.height + 2*self.power_rail_pitch) + self.bank.height) self.bank_inst=[self.add_bank(0, bank_position_0, -1, -1)] # Placement of bank 1 (right) @@ -943,7 +980,7 @@ class sram(design.design): # 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 + 2*self.power_rail_pitch) + 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) @@ -952,7 +989,7 @@ class sram(design.design): self.bank_inst.append(self.add_bank(1, bank_position_1, 1, 1)) # Placement of bank 2 (bottom left) - y_off = self.bank.height + 2*self.power_rail_pitch + 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)) @@ -983,19 +1020,33 @@ class sram(design.design): dest_pin = self.bank_inst.get_pin(n) self.connect_rail_from_left_m2m3(src_pin, dest_pin) + + # Find the left-most metal2 rails + leftmost_vdd_rail = None + for vdd_pin in self.bank_inst.get_pins("vdd"): + if vdd_pin.layer != "metal2": + continue + if leftmost_vdd_rail == None or vdd_pin.lx() < leftmost_vdd_rail.lx(): + leftmost_vdd_rail = vdd_pin + leftmost_gnd_rail = None + for gnd_pin in self.bank_inst.get_pins("gnd"): + if gnd_pin.layer != "metal2": + continue + if leftmost_gnd_rail == None or gnd_pin.lx() < leftmost_gnd_rail.lx(): + leftmost_gnd_rail = gnd_pin + + src_pins = self.control_logic_inst.get_pins("vdd") for src_pin in src_pins: if src_pin.layer != "metal2": continue - dest_pin = self.bank_inst.get_pins("vdd")[1] - self.connect_rail_from_left_m2m1(src_pin,dest_pin) + self.connect_rail_from_left_m2m3(src_pin,leftmost_vdd_rail) src_pins = self.control_logic_inst.get_pins("gnd") for src_pin in src_pins: if src_pin.layer != "metal2": continue - dest_pin = self.bank_inst.get_pin("gnd") - self.connect_rail_from_left_m2m3(src_pin,dest_pin) + self.add_path("metal2", [src_pin.rc(), vector(leftmost_gnd_rail.cx(), src_pin.cy())])