SRAM single bank passing DRC/LVS.

This commit is contained in:
Matt Guthaus 2017-09-13 15:46:41 -07:00
parent 3ea003c367
commit d29dd03373
16 changed files with 561 additions and 805 deletions

View File

@ -38,13 +38,24 @@ class bank(design.design):
self.words_per_row = words_per_row self.words_per_row = words_per_row
self.num_banks = num_banks self.num_banks = num_banks
# The local control signals are gated when we have bank select logic,
# so this prefix will be added to all of the signals.
if self.num_banks>1:
self.prefix="gated_"
else:
self.prefix=""
self.compute_sizes() self.compute_sizes()
self.add_pins() self.add_pins()
self.create_modules() self.create_modules()
self.add_modules() self.add_modules()
self.setup_layout_constraints() self.setup_layout_constraints()
self.route_layout() 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! # Can remove the following, but it helps for debug!
self.add_lvs_correspondence_points() self.add_lvs_correspondence_points()
@ -61,14 +72,9 @@ class bank(design.design):
# the signals gated_*. # the signals gated_*.
if(self.num_banks > 1): if(self.num_banks > 1):
self.add_pin("bank_select") self.add_pin("bank_select")
self.add_pin("s_en") for pin in ["s_en","w_en","tri_en_bar","tri_en",
self.add_pin("w_en") "clk_bar","clk","vdd","gnd"]:
self.add_pin("tri_en_bar") self.add_pin(pin)
self.add_pin("tri_en")
self.add_pin("clk_bar")
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
def route_layout(self): def route_layout(self):
""" Create routing amoung the modules """ """ Create routing amoung the modules """
@ -82,12 +88,11 @@ class bank(design.design):
self.route_msf_address() self.route_msf_address()
self.route_control_lines() self.route_control_lines()
self.add_control_pins() self.add_control_pins()
if self.num_banks > 1:
self.route_bank_select()
self.route_vdd_supply() self.route_vdd_supply()
self.route_gnd_supply() self.route_gnd_supply()
#self.offset_all_coordinates() self.offset_all_coordinates()
def add_modules(self): def add_modules(self):
""" Add modules. The order should not matter! """ """ Add modules. The order should not matter! """
@ -109,8 +114,6 @@ class bank(design.design):
self.add_row_decoder() self.add_row_decoder()
self.add_wordline_driver() self.add_wordline_driver()
self.add_msf_address() self.add_msf_address()
if(self.num_banks > 1):
self.add_bank_select()
def compute_sizes(self): def compute_sizes(self):
""" Computes the required sizes to create the bank """ """ Computes the required sizes to create the bank """
@ -132,9 +135,12 @@ class bank(design.design):
# Number of control lines in the bus # Number of control lines in the bus
self.num_control_lines = 6 self.num_control_lines = 6
# The order of the control signals on the control bus: # The order of the control signals on the control bus:
self.control_signals = ["clk", "tri_en_bar", "tri_en", "clk_bar", "w_en", "s_en"] self.input_control_signals = ["clk", "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.control_signals] self.control_signals = ["gated_"+str for str in self.input_control_signals]
else:
self.control_signals = self.input_control_signals
# The central bus is the column address (both polarities), row address # The central bus is the column address (both polarities), row address
if self.col_addr_size>0: if self.col_addr_size>0:
self.num_addr_lines = 2**self.col_addr_size + self.row_addr_size self.num_addr_lines = 2**self.col_addr_size + self.row_addr_size
@ -146,6 +152,7 @@ class bank(design.design):
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3")) self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
self.m1_pitch = self.m1m2_via.height + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"]) self.m1_pitch = self.m1m2_via.height + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m2_pitch = self.m2m3_via.height + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"]) self.m2_pitch = self.m2m3_via.height + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
self.m1_width = drc["minwidth_metal1"]
self.m2_width = drc["minwidth_metal2"] self.m2_width = drc["minwidth_metal2"]
self.m3_width = drc["minwidth_metal3"] self.m3_width = drc["minwidth_metal3"]
@ -212,13 +219,6 @@ class bank(design.design):
self.inv = pinv() self.inv = pinv()
self.add_mod(self.inv) self.add_mod(self.inv)
# 4x Inverter
self.inv4x = pinv(nmos_width=4*drc["minwidth_tx"])
self.add_mod(self.inv4x)
self.nor2 = nor_2()
self.add_mod(self.nor2)
# Vertical metal rail gap definition # Vertical metal rail gap definition
self.metal2_extend_contact = (self.m1m2_via.second_layer_height self.metal2_extend_contact = (self.m1m2_via.second_layer_height
- self.m1m2_via.contact_width) / 2 - self.m1m2_via.contact_width) / 2
@ -226,6 +226,7 @@ class bank(design.design):
self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"] self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"]
self.via_shift = (self.m1m2_via.second_layer_width self.via_shift = (self.m1m2_via.second_layer_width
- self.m1m2_via.first_layer_width) / 2 - self.m1m2_via.first_layer_width) / 2
self.via_shift_offset = vector(0,self.via_shift)
def add_bitcell_array(self): def add_bitcell_array(self):
""" Adding Bitcell Array """ """ Adding Bitcell Array """
@ -257,7 +258,7 @@ class bank(design.design):
for i in range(self.num_cols): for i in range(self.num_cols):
temp.append("bl[{0}]".format(i)) temp.append("bl[{0}]".format(i))
temp.append("br[{0}]".format(i)) temp.append("br[{0}]".format(i))
temp.extend(["clk_bar", "vdd"]) temp.extend([self.prefix+"clk_bar", "vdd"])
self.connect_inst(temp) self.connect_inst(temp)
def add_column_mux_array(self): def add_column_mux_array(self):
@ -296,7 +297,7 @@ class bank(design.design):
temp.append("bl_out[{0}]".format(i)) temp.append("bl_out[{0}]".format(i))
temp.append("br_out[{0}]".format(i)) temp.append("br_out[{0}]".format(i))
temp.extend(["s_en", "vdd", "gnd"]) temp.extend([self.prefix+"s_en", "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def add_write_driver_array(self): def add_write_driver_array(self):
@ -317,7 +318,7 @@ class bank(design.design):
else: else:
temp.append("bl_out[{0}]".format(i)) temp.append("bl_out[{0}]".format(i))
temp.append("br_out[{0}]".format(i)) temp.append("br_out[{0}]".format(i))
temp.extend(["w_en", "vdd", "gnd"]) temp.extend([self.prefix+"w_en", "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def add_msf_data_in(self): def add_msf_data_in(self):
@ -335,7 +336,7 @@ class bank(design.design):
for i in range(self.word_size): for i in range(self.word_size):
temp.append("data_in[{0}]".format(i)) temp.append("data_in[{0}]".format(i))
temp.append("data_in_bar[{0}]".format(i)) temp.append("data_in_bar[{0}]".format(i))
temp.extend(["clk_bar", "vdd", "gnd"]) temp.extend([self.prefix+"clk_bar", "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def add_tri_gate_array(self): def add_tri_gate_array(self):
@ -351,7 +352,7 @@ class bank(design.design):
temp.append("data_out[{0}]".format(i)) temp.append("data_out[{0}]".format(i))
for i in range(self.word_size): for i in range(self.word_size):
temp.append("DATA[{0}]".format(i)) temp.append("DATA[{0}]".format(i))
temp.extend(["tri_en", "tri_en_bar", "vdd", "gnd"]) temp.extend([self.prefix+"tri_en", self.prefix+"tri_en_bar", "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def add_row_decoder(self): def add_row_decoder(self):
@ -396,11 +397,7 @@ class bank(design.design):
temp.append("dec_out[{0}]".format(i)) temp.append("dec_out[{0}]".format(i))
for i in range(self.num_rows): for i in range(self.num_rows):
temp.append("wl[{0}]".format(i)) temp.append("wl[{0}]".format(i))
temp.append(self.prefix+"clk")
if(self.num_banks > 1):
temp.append("gated_clk")
else:
temp.append("clk")
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
@ -429,10 +426,7 @@ class bank(design.design):
temp.extend(["sel[1]","sel[0]"]) temp.extend(["sel[1]","sel[0]"])
else: else:
temp.extend(["A[{0}]".format(i),"A_bar[{0}]".format(i)]) temp.extend(["A[{0}]".format(i),"A_bar[{0}]".format(i)])
if(self.num_banks > 1): temp.append(self.prefix+"clk")
temp.append("gated_clk")
else:
temp.append("clk")
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
@ -440,12 +434,15 @@ class bank(design.design):
def add_column_decoder(self): def add_column_decoder(self):
""" Create a 2:4 decoder to decode column select lines if the col_addr_size = 4 """ """ Create a 2:4 decoder to decode column select lines if the col_addr_size = 4 """
# FIXME: Should just load this rather than reference a level down
if self.col_addr_size == 1: if self.col_addr_size == 1:
return # This is done from the FF outputs directly return # This is done from the FF outputs directly
if self.col_addr_size == 2: if self.col_addr_size == 2:
# FIXME: Should just load this rather than reference a level down
self.col_decoder = self.decoder.pre2_4 self.col_decoder = self.decoder.pre2_4
elif self.col_addr_size == 3: elif self.col_addr_size == 3:
debug.error("8 way column mux not yet supported...", -1)
# FIXME: Should just load this rather than reference a level down
self.col_decoder = self.decoder.pre3_8 self.col_decoder = self.decoder.pre3_8
else: else:
# No error checking before? # No error checking before?
@ -473,103 +470,182 @@ class bank(design.design):
NOR+INV gates to gate the control signals in case of multiple NOR+INV gates to gate the control signals in case of multiple
banks are created in upper level SRAM module banks are created in upper level SRAM module
""" """
xoffset_nor = self.start_of_left_central_bus + self.nor2.width + self.inv4x.width
xoffset_inv = xoffset_nor + self.nor2.width # 4x Inverter
self.bank_select_or_position = vector(-xoffset_nor, self.min_point) self.inv4x = pinv(nmos_width=4*drc["minwidth_tx"])
self.add_mod(self.inv4x)
self.nor2 = nor_2()
self.add_mod(self.nor2)
self.nand2 = nand_2()
self.add_mod(self.nand2)
# left of gnd rail is the "bus start"
bus_start = self.gnd_x_offset - 2*drc["minwidth_metal2"]
xoffset_nand = bus_start - self.nand2.width - self.inv4x.width - 2*self.m2_pitch
xoffset_nor = bus_start - self.nor2.width - self.inv4x.width - 2*self.m2_pitch
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 # bank select inverter
self.bank_select_inv_position = vector(self.bank_select_or_position.x self.bank_select_inv_position = vector(xoffset_bank_sel_inv,
- 5 * drc["minwidth_metal2"]
- self.inv4x.width,
self.min_point) self.min_point)
self.add_inst(name="bank_select_inv", # bank select inverter (must be made unique if more than one OR)
mod=self.inv4x, bank_sel_inv=self.add_inst(name="bank_sel_inv",
offset=self.bank_select_inv_position) mod=self.inv,
self.connect_inst(["bank_select", "bank_select_bar", "vdd", "gnd"]) offset=[xoffset_bank_sel_inv,self.min_point])
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
# bank_sel is vertical wire
xoffset_bank_sel = xoffset_bank_sel_inv
self.add_label_pin(text="bank_sel",
layer="metal2",
offset=vector(xoffset_bank_sel, self.min_point),
width=self.m2_width,
height=self.decoder_min_point-self.min_point-self.m2_pitch)
in_pin = bank_sel_inv.get_pin("A").ll()
self.add_via(layers=("metal1","via1","metal2"),
offset=in_pin)
self.add_layout_pin(text="bank_sel",
layer="metal3",
offset=vector(self.left_vdd_x_offset, self.min_point),
width=xoffset_bank_sel - self.left_vdd_x_offset,
height=self.m3_width)
self.add_via(layers=("metal2","via2","metal3"),
offset=vector(xoffset_bank_sel_inv, self.min_point))
for i in range(self.numb_control_lines): # bank_sel_bar is vertical wire
# central control bus index xoffset_bank_sel_bar = xoffset_bank_sel_inv+self.inv.width-self.m2_width
# 5 = clk,4 = tri_en_bar,3 = tri_en,2 = clk_bar,1 = w_en,0 = s_en self.add_label_pin(text="bank_sel_bar",
name_nor = "bank_selector_nor_{0}".format(i) layer="metal2",
name_inv = "bank_selector_inv_{0}".format(i) offset=vector(xoffset_bank_sel_bar, self.min_point),
nor2_inv_connection_height = self.inv4x.A_position.y - self.nor2.Z_position.y + 0.5 * drc["minwidth_metal1"] width=self.m2_width,
height=2*self.inv.height)
out_pin = bank_sel_inv.get_pin("Z").lr()
self.add_via(layers=("metal1","via1","metal2"),
offset=out_pin-vector(self.m2_width,0))
if (i % 2):
y_offset = self.min_point + self.inv.height*(i + 1)
mod_dir = "MX" for i in range(self.num_control_lines):
# nor2 output to inv input input_name = self.input_control_signals[i]
y_correct = self.nor2.Z_position.y + nor2_inv_connection_height - 0.5 * drc["minwidth_metal1"] gated_name = self.control_signals[i]
name_nand = "nand_{}".format(input_name)
name_nor = "nor_{}".format(input_name)
name_inv = "inv_{}".format(input_name)
y_offset = self.min_point + self.inv.height * i
if i%2:
y_offset += self.inv.height
mirror = "MX"
else: else:
y_offset = self.min_point + self.inv.height*i mirror = ""
mod_dir = "R0"
# nor2 output to inv input # These require OR (nor2+inv) gates since they are active low.
y_correct = 0.5 * drc["minwidth_metal1"] - self.nor2.Z_position.y # (writes occur on clk low)
connection = vector(xoffset_inv, y_offset - y_correct) if input_name in ("clk", "tri_en_bar"):
if i == 3:
self.add_inst(name=name_nor,
mod=self.nor2,
offset=[xoffset_nor, y_offset],
mirror=mod_dir)
self.connect_inst(["gated_tri_en_bar",
"bank_select_bar",
self.control_signals[i].format(i),
"vdd",
"gnd"])
# connect the metal1 layer to connect to the old inv output
offset = connection - vector(0, 0.5*drc["minwidth_metal1"])
self.add_rect(layer="metal1",
offset=offset,
width=self.inv4x.width,
height=drc["minwidth_metal1"])
elif i == 5:
offset = [xoffset_nor, y_offset - self.nor2.A_position.y
- 0.5*drc["minwidth_metal1"]]
self.add_rect(layer="metal1",
offset=offset,
width=self.nor2.width + self.inv4x.width,
height=drc["minwidth_metal1"])
else:
self.add_inst(name=name_nor,
mod=self.nor2,
offset=[xoffset_nor, y_offset],
mirror=mod_dir)
self.connect_inst([self.gated_control_signals[i],
"bank_select_bar",
"net_block_nor_inv[{0}]".format(i),
"vdd",
"gnd"])
self.add_inst(name=name_inv,
mod=self.inv4x,
offset=[xoffset_inv, y_offset],
mirror=mod_dir)
self.connect_inst(["net_block_nor_inv[{0}]".format(i),
self.control_signals[i],
"vdd",
"gnd"])
# nor2 output to inv input
for i in range(self.numb_control_lines - 1):
nor2_inv_connection_height = (self.inv4x.A_position.y
- self.nor2.Z_position.y
+ 0.5 * drc["minwidth_metal1"])
if (i % 2): logic_inst=self.add_inst(name=name_nor,
y_offset = self.min_point + self.inv.height * (i + 1) mod=self.nor2,
mod_dir = "MX" offset=[xoffset_nor, y_offset],
y_correct = (-self.nor2.Z_position.y + 0.5 * drc["minwidth_metal1"] mirror=mirror)
- nor2_inv_connection_height) 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: else:
y_offset = self.min_point + self.inv.height*i logic_inst=self.add_inst(name=name_nand,
mod_dir = "R0" mod=self.nand2,
y_correct = self.nor2.Z_position.y - 0.5 * drc["minwidth_metal1"] offset=[xoffset_nand, y_offset],
# nor2 output to inv input mirror=mirror)
connection = vector(xoffset_inv, y_offset + y_correct) bank_sel_signal = "bank_sel"
self.add_rect(layer="metal1", self.connect_inst([input_name,
offset=connection, "bank_sel",
width=drc["minwidth_metal1"], gated_name+"_temp_bar",
height=nor2_inv_connection_height) "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])
# Connect the inverter output to the central bus
out_pin = inv_inst.get_pin("Z")
bus_pos = vector(self.central_line_xoffset[gated_name], out_pin.rc().y)
self.add_path("metal3",[out_pin.rc(), bus_pos])
self.add_via(layers=("metal2", "via2", "metal3"),
offset=bus_pos - vector(0,0.5*self.m2m3_via.height))
self.add_via(layers=("metal1", "via1", "metal2"),
offset=out_pin.lr() - self.via_shift_offset,
rotate=90)
self.add_via(layers=("metal2", "via2", "metal3"),
offset=out_pin.lr() - self.via_shift_offset,
rotate=90)
# Connect the logic B input to bank_sel/bank_sel_bar
logic_pin = logic_inst.get_pin("B")
input_pos = vector(xoffset_bank_signal,logic_pin.cy())
self.add_path("metal2",[logic_pin.lc(), input_pos])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=logic_pin.ll()-self.via_shift_offset,
rotate=90)
# Connect the logic A input to the input pin
logic_pos = logic_inst.get_pin("A").ll()
input_pos = vector(self.left_vdd_x_offset,logic_pos.y)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=logic_pos-self.via_shift_offset,
rotate=90)
self.add_via(layers=("metal2", "via2", "metal3"),
offset=logic_pos-self.via_shift_offset,
rotate=90)
self.add_layout_pin(text=input_name,
layer="metal3",
offset=vector(self.left_vdd_x_offset,logic_pos.y - self.via_shift_offset.y),
height=self.m3_width,
width=logic_pos.x-self.left_vdd_x_offset)
# Add vdd/gnd supply rails
gnd_pin = inv_inst.get_pin("gnd")
left_gnd_pos = vector(xoffset_bank_sel_inv,gnd_pin.rc().y)
right_gnd_pos = vector(self.gnd_x_offset,gnd_pin.rc().y)
self.add_path("metal1",[left_gnd_pos, right_gnd_pos])
right_via_pos = vector(self.gnd_x_offset,gnd_pin.lr().y)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=right_via_pos + vector(self.m1m2_via.height,0) - self.via_shift_offset,
rotate=90)
vdd_pin = inv_inst.get_pin("vdd")
left_vdd_pos = vector(self.left_vdd_x_offset,vdd_pin.lc().y)
self.add_path("metal1",[left_vdd_pos,vdd_pin.rc()])
def setup_layout_constraints(self): def setup_layout_constraints(self):
""" Calculating layout constraints, width, height etc """ """ Calculating layout constraints, width, height etc """
@ -580,13 +656,17 @@ class bank(design.design):
# Leave room for the output below the tri gate. # Leave room for the output below the tri gate.
tri_gate_min_point = self.tri_gate_array_inst.ll().y - 3*self.m2_pitch tri_gate_min_point = self.tri_gate_array_inst.ll().y - 3*self.m2_pitch
addr_min_point = self.msf_address_inst.ll().y - 2*self.m2_pitch addr_min_point = self.msf_address_inst.ll().y - 2*self.m2_pitch
if self.col_addr_size >1: if self.col_addr_size >1:
decoder_min_point = self.col_decoder_inst.ll().y self.decoder_min_point = self.col_decoder_inst.ll().y
else: else:
decoder_min_point = 0 self.decoder_min_point = addr_min_point
self.min_point = min(tri_gate_min_point, addr_min_point, decoder_min_point)
if self.num_banks>1: if self.num_banks>1:
self.min_point -= self.num_control_lines * self.bitcell.height self.min_point = min(self.decoder_min_point - self.num_control_lines * self.bitcell.height, tri_gate_min_point)
else:
self.min_point = min(addr_min_point, tri_gate_min_point)
# The max point is always the top of the precharge bitlines # The max point is always the top of the precharge bitlines
self.max_point = self.precharge_array_inst.uy() self.max_point = self.precharge_array_inst.uy()
@ -594,7 +674,7 @@ class bank(design.design):
self.height = self.max_point - self.min_point self.height = self.max_point - self.min_point
# Add an extra gap between the bitcell and the rail # Add an extra gap between the bitcell and the rail
self.right_vdd_x_offset = self.bitcell_array_inst.ur().x + 3 * drc["minwidth_metal1"] self.right_vdd_x_offset = self.bitcell_array_inst.ur().x + 3 * self.m1_width
offset = vector(self.right_vdd_x_offset, self.min_point) offset = vector(self.right_vdd_x_offset, self.min_point)
self.add_layout_pin(text="vdd", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
@ -603,7 +683,7 @@ class bank(design.design):
height=self.height) height=self.height)
# from the edge of the decoder is another 2 times minwidth metal1 # from the edge of the decoder is another 2 times minwidth metal1
self.left_vdd_x_offset = min(self.msf_address_inst.ll().x, self.row_decoder_inst.ll().x) - self.vdd_rail_width - 2*drc["minwidth_metal1"] self.left_vdd_x_offset = min(self.msf_address_inst.ll().x, self.row_decoder_inst.ll().x) - self.vdd_rail_width - 2*self.m1_width
offset = vector(self.left_vdd_x_offset, self.min_point) offset = vector(self.left_vdd_x_offset, self.min_point)
self.add_layout_pin(text="vdd", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
@ -640,7 +720,7 @@ class bank(design.design):
height=self.height) height=self.height)
# row address lines (to the left of the column mux or GND rail) # row address lines (to the left of the column mux or GND rail)
# goes from 0 down to the min point # goes from 0 down to the bottom of the address flops
for i in range(self.row_addr_size): for i in range(self.row_addr_size):
x_offset = self.start_of_left_central_bus + i * self.m2_pitch x_offset = self.start_of_left_central_bus + i * self.m2_pitch
name = "A[{}]".format(i) name = "A[{}]".format(i)
@ -648,9 +728,9 @@ class bank(design.design):
# Add a label pin for LVS correspondence and visual help inspecting the rail. # Add a label pin for LVS correspondence and visual help inspecting the rail.
self.add_label_pin(text=name, self.add_label_pin(text=name,
layer="metal2", layer="metal2",
offset=vector(x_offset, self.min_point), offset=vector(x_offset, self.decoder_min_point),
width=self.m2_width, width=self.m2_width,
height=-self.min_point) height=-self.decoder_min_point)
# column mux lines if there is column mux [2 or 4 lines] (to the left of the GND rail) # column mux lines if there is column mux [2 or 4 lines] (to the left of the GND rail)
# goes from 0 down to the min point # goes from 0 down to the min point
@ -662,9 +742,9 @@ class bank(design.design):
# Add a label pin for LVS correspondence # Add a label pin for LVS correspondence
self.add_label_pin(text=name, self.add_label_pin(text=name,
layer="metal2", layer="metal2",
offset=vector(x_offset, self.min_point), offset=vector(x_offset, self.decoder_min_point),
width=self.m2_width, width=self.m2_width,
height=-self.min_point) height=-self.decoder_min_point)
@ -691,7 +771,7 @@ class bank(design.design):
tri_gate_in = self.tri_gate_array_inst.get_pin("in[{}]".format(i)).bc() tri_gate_in = self.tri_gate_array_inst.get_pin("in[{}]".format(i)).bc()
sa_data_out = self.sense_amp_array_inst.get_pin("data[{}]".format(i)).rc() # rc to get enough overlap sa_data_out = self.sense_amp_array_inst.get_pin("data[{}]".format(i)).rc() # rc to get enough overlap
startY = self.tri_gate_array_inst.ll().y - 2*drc["minwidth_metal3"] + 0.5*drc["minwidth_metal1"] startY = self.tri_gate_array_inst.ll().y - 2*drc["minwidth_metal3"] + 0.5*self.m1_width
start = vector(tri_gate_in.x - 3 * drc["minwidth_metal3"], startY) start = vector(tri_gate_in.x - 3 * drc["minwidth_metal3"], startY)
m3_min = vector([drc["minwidth_metal3"]] * 2) m3_min = vector([drc["minwidth_metal3"]] * 2)
@ -734,7 +814,7 @@ class bank(design.design):
# Connect the address rails to the decoder # Connect the address rails to the decoder
# Note that the decoder inputs are long vertical rails so spread out the connections vertically. # Note that the decoder inputs are long vertical rails so spread out the connections vertically.
decoder_in_position = self.row_decoder_inst.get_pin("A[{}]".format(i)).br() + vector(0,position_heights*self.bitcell.height+self.m2_pitch) decoder_in_position = self.row_decoder_inst.get_pin("A[{}]".format(i)).lr() + vector(0,position_heights*self.bitcell.height+self.m2_pitch)
rail_position = vector(self.central_line_xoffset["A[{}]".format(i)]+drc["minwidth_metal2"],decoder_in_position.y) rail_position = vector(self.central_line_xoffset["A[{}]".format(i)]+drc["minwidth_metal2"],decoder_in_position.y)
self.add_path("metal1",[decoder_in_position,rail_position]) self.add_path("metal1",[decoder_in_position,rail_position])
@ -778,8 +858,8 @@ class bank(design.design):
for i in range(self.num_rows): for i in range(self.num_rows):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs # The pre/post is to access the pin from "outside" the cell to avoid DRCs
pre = self.row_decoder_inst.get_pin("decode[{}]".format(i)).lc() pre = self.row_decoder_inst.get_pin("decode[{}]".format(i)).lc()
decoder_out_position = self.row_decoder_inst.get_pin("decode[{}]".format(i)).rc() + vector(0.5*drc["minwidth_metal1"],0) decoder_out_position = self.row_decoder_inst.get_pin("decode[{}]".format(i)).rc() + vector(0.5*self.m1_width,0)
driver_in_position = self.wordline_driver_inst.get_pin("in[{}]".format(i)).lc() + vector(0.5*drc["minwidth_metal1"],0) driver_in_position = self.wordline_driver_inst.get_pin("in[{}]".format(i)).lc() + vector(0.5*self.m1_width,0)
post = self.wordline_driver_inst.get_pin("in[{}]".format(i)).rc() post = self.wordline_driver_inst.get_pin("in[{}]".format(i)).rc()
self.add_path("metal1", [pre, decoder_out_position, driver_in_position, post]) self.add_path("metal1", [pre, decoder_out_position, driver_in_position, post])
@ -839,7 +919,7 @@ class bank(design.design):
self.add_path("metal1",[decode_out_position, selx_position]) self.add_path("metal1",[decode_out_position, selx_position])
# via on end # via on end
decode_out_via = self.col_decoder_inst.get_pin("out[{}]".format(i)).br() decode_out_via = self.col_decoder_inst.get_pin("out[{}]".format(i)).lr()
selx_via = vector(self.central_line_xoffset[name],decode_out_via.y - drc["minwidth_metal2"]) selx_via = vector(self.central_line_xoffset[name],decode_out_via.y - drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=selx_via) offset=selx_via)
@ -870,7 +950,7 @@ class bank(design.design):
mid_position = vector(in_position.x,dout_position.y) mid_position = vector(in_position.x,dout_position.y)
self.add_path("metal3",[dout_position, mid_position, in_position]) self.add_path("metal3",[dout_position, mid_position, in_position])
dout_via = self.msf_address_inst.get_pin("dout[{}]".format(ff_index)).br() dout_via = self.msf_address_inst.get_pin("dout[{}]".format(ff_index)).lr()
in_via = self.col_decoder_inst.get_pin("in[{}]".format(i)).ul() in_via = self.col_decoder_inst.get_pin("in[{}]".format(i)).ul()
self.add_via(layers=("metal2", "via2", "metal3"), self.add_via(layers=("metal2", "via2", "metal3"),
offset=dout_via, offset=dout_via,
@ -934,7 +1014,7 @@ class bank(design.design):
dout_position = self.msf_address_inst.get_pin("dout[{}]".format(i)).rc() dout_position = self.msf_address_inst.get_pin("dout[{}]".format(i)).rc()
rail_position = vector(self.central_line_xoffset["A[{}]".format(i)]+drc["minwidth_metal2"],dout_position.y) rail_position = vector(self.central_line_xoffset["A[{}]".format(i)]+drc["minwidth_metal2"],dout_position.y)
self.add_path("metal1",[dout_position, rail_position]) self.add_path("metal1",[dout_position, rail_position])
dout_via = self.msf_address_inst.get_pin("dout[{}]".format(i)).br() + vector(self.m1m2_via.height,0) dout_via = self.msf_address_inst.get_pin("dout[{}]".format(i)).lr() + vector(self.m1m2_via.height,0)
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=dout_via, offset=dout_via,
rotate=90) rotate=90)
@ -947,7 +1027,7 @@ class bank(design.design):
for gnd_pin in self.msf_address_inst.get_pins("gnd"): for gnd_pin in self.msf_address_inst.get_pins("gnd"):
if gnd_pin.layer != "metal2": if gnd_pin.layer != "metal2":
continue continue
gnd_via = gnd_pin.br() + vector(self.m1m2_via.height,0) gnd_via = gnd_pin.lr() + vector(self.m1m2_via.height,0)
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=gnd_via, offset=gnd_via,
rotate=90) rotate=90)
@ -1019,16 +1099,12 @@ class bank(design.design):
# Connection from the central bus to the main control block crosses # Connection from the central bus to the main control block crosses
# pre-decoder and this connection is in metal3 # pre-decoder and this connection is in metal3
connection = [] connection = []
if self.num_banks>1: connection.append((self.prefix+"clk_bar", self.msf_data_in_inst.get_pin("clk").lc()))
prefix="gated_" connection.append((self.prefix+"tri_en_bar", self.tri_gate_array_inst.get_pin("en_bar").lc()))
else: connection.append((self.prefix+"tri_en", self.tri_gate_array_inst.get_pin("en").lc()))
prefix="" connection.append((self.prefix+"clk_bar", self.precharge_array_inst.get_pin("en").lc()))
connection.append((prefix+"clk_bar", self.msf_data_in_inst.get_pin("clk").lc())) connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc()))
connection.append((prefix+"tri_en_bar", self.tri_gate_array_inst.get_pin("en_bar").lc())) connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
connection.append((prefix+"tri_en", self.tri_gate_array_inst.get_pin("en").lc()))
connection.append((prefix+"clk_bar", self.precharge_array_inst.get_pin("en").lc()))
connection.append((prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc()))
connection.append((prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
for (control_signal, pin_position) in connection: for (control_signal, pin_position) in connection:
control_x_offset = self.central_line_xoffset[control_signal] + self.m2_width control_x_offset = self.central_line_xoffset[control_signal] + self.m2_width
@ -1040,143 +1116,28 @@ class bank(design.design):
rotate=90) rotate=90)
# clk to msf address # clk to msf address
control_signal = prefix+"clk" control_signal = self.prefix+"clk"
pin_position = self.msf_address_inst.get_pin("clk").uc() pin_position = self.msf_address_inst.get_pin("clk").uc()
mid_position = pin_position + vector(0,self.m1_pitch) mid_position = pin_position + vector(0,self.m1_pitch)
control_x_offset = self.central_line_xoffset[control_signal] control_x_offset = self.central_line_xoffset[control_signal]
control_position = vector(control_x_offset + drc["minwidth_metal1"], mid_position.y) control_position = vector(control_x_offset + self.m1_width, mid_position.y)
self.add_path("metal1",[pin_position, mid_position, control_position]) self.add_path("metal1",[pin_position, mid_position, control_position])
control_via_position = vector(control_x_offset, mid_position.y-0.5*drc["minwidth_metal2"]) control_via_position = vector(control_x_offset, mid_position.y-0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=control_via_position) offset=control_via_position)
# clk to wordline_driver # clk to wordline_driver
control_signal = prefix+"clk" control_signal = self.prefix+"clk"
pin_position = self.wordline_driver_inst.get_pin("en").uc() pin_position = self.wordline_driver_inst.get_pin("en").uc()
mid_position = pin_position + vector(0,self.m1_pitch) mid_position = pin_position + vector(0,self.m1_pitch)
control_x_offset = self.central_line_xoffset[control_signal] control_x_offset = self.central_line_xoffset[control_signal]
control_position = vector(control_x_offset + drc["minwidth_metal1"], mid_position.y) control_position = vector(control_x_offset + self.m1_width, mid_position.y)
self.add_wire(("metal1","via1","metal2"),[pin_position, mid_position, control_position]) self.add_wire(("metal1","via1","metal2"),[pin_position, mid_position, control_position])
control_via_position = vector(control_x_offset, mid_position.y-0.5*drc["minwidth_metal2"]) control_via_position = vector(control_x_offset, mid_position.y-0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=control_via_position) offset=control_via_position)
def route_bank_select(self):
""" Route array of or gates to gate the control signals in case
of multiple banks are created in upper level SRAM module """
return
bank_select_line_xoffset = (self.bank_select_or_position.x
- 3*drc["minwidth_metal2"])
self.add_rect(layer="metal2",
offset=[bank_select_line_xoffset,
self.bank_select_or_position.y],
width=drc["minwidth_metal2"],
height=self.num_control_lines*self.inv.height)
# bank select inverter routing
# output side
start = self.bank_select_inv_position + self.inv4x.Z_position
end = self.bank_select_or_position + self.nor2.B_position
mid = vector(start.x, end.y)
self.add_path("metal1", [start, mid, end])
# input side
start = self.bank_select_inv_position + self.inv4x.A_position
end = vector(self.left_vdd_x_offset, start.y + 3 * drc["minwidth_metal3"])
mid = vector(start.x, end.y)
self.add_wire(("metal2", "via1", "metal1"), [start, mid, end])
# save position
self.bank_select_position = end - vector(0, 0.5 * drc["minwidth_metal2"])
self.add_via(layers=("metal2", "via2", "metal3"),
offset=self.bank_select_position)
x_offset = (self.bank_select_or_position.x + self.nor2.width
+ self.inv4x.width - drc["minwidth_metal1"])
for i in range(self.num_control_lines):
base = self.bank_select_or_position.y + self.inv.height * i
if(i % 2):
Z_y_offset = (base + self.inv.height - self.inv4x.Z_position.y
- drc["minwidth_metal1"])
B_y_offset = (base + self.inv.height - self.nor2.B_position.y
- 0.5 * drc["minwidth_metal1"])
A_y_offset = (base + self.inv.height - self.nor2.A_position.y
- 0.5 * drc["minwidth_metal1"])
else:
Z_y_offset = (base + self.inv4x.Z_position.y)
B_y_offset = (base + self.nor2.B_position.y
- 0.5 * drc["minwidth_metal1"])
A_y_offset = (base + self.nor2.A_position.y
+ 0.5 * drc["minwidth_metal1"]
- self.m1m2_via.width)
# output
self.add_rect(layer="metal3",
offset=[x_offset, Z_y_offset],
width=self.central_line_xoffset[i] - x_offset,
height=drc["minwidth_metal3"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[x_offset, Z_y_offset])
self.add_via(layers=("metal2", "via2", "metal3"),
offset=[x_offset, Z_y_offset])
self.add_via(layers=("metal2", "via2", "metal3"),
offset=[self.central_line_xoffset[i], Z_y_offset])
# B_input
if i != 5:
self.add_rect(layer="metal1",
offset=[bank_select_line_xoffset, B_y_offset],
width=(self.bank_select_or_position.x
- bank_select_line_xoffset),
height=drc["minwidth_metal1"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[bank_select_line_xoffset, B_y_offset])
# A_input
if i != 3:
self.add_rect(layer="metal3",
offset=[self.left_vdd_x_offset, A_y_offset],
width=(self.bank_select_or_position.x
- self.left_vdd_x_offset),
height=drc["minwidth_metal3"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.bank_select_or_position.x
+ drc["minwidth_metal1"],
A_y_offset],
rotate=90)
self.add_via(layers=("metal2", "via2", "metal3"),
offset=[self.bank_select_or_position.x
+ drc["minwidth_metal1"],
A_y_offset],
rotate=90)
else:
# connect A to last A, both are tri_en_bar
via_offset = vector(self.bank_select_or_position.x
+ drc["minwidth_metal1"],
A_y_offset)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=via_offset,
rotate=90)
self.add_via(layers=("metal2", "via2", "metal3"),
offset=via_offset,
rotate=90)
start = via_offset + vector(0, 0.5 * self.m1m2_via.width)
mid = [self.left_vdd_x_offset - self.left_vdd_x_offset
- drc["minwidth_metal2"] - drc["metal2_to_metal2"]
+ bank_select_line_xoffset,
start.y]
correct_y = (2 * self.nor2.A_position.y + drc["minwidth_metal1"]
- self.m1m2_via.width)
end = start + vector(0, correct_y)
self.add_wire(("metal3", "via2", "metal2"), [start, mid, end])
# Save position
setattr(self,"{0}_position".format(self.control_signals[i]),
[self.left_vdd_x_offset, A_y_offset])
def route_vdd_supply(self): def route_vdd_supply(self):
""" Route vdd for the precharge, sense amp, write_driver, data FF, tristate """ """ Route vdd for the precharge, sense amp, write_driver, data FF, tristate """
@ -1187,22 +1148,8 @@ class bank(design.design):
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=vdd_pin.ll(), offset=vdd_pin.ll(),
width=self.right_vdd_x_offset - vdd_pin.lx(), width=self.right_vdd_x_offset - vdd_pin.lx(),
height=drc["minwidth_metal1"]) height=self.m1_width)
return
# Connect bank_select_and2_array vdd
if(self.num_banks > 1):
for i in range(self.num_control_lines):
if(i % 2):
self.add_rect(layer="metal1",
offset=[self.left_vdd_x_offset,
self.bank_select_or_position.y
+ i * self.inv.height
- 0.5 * drc["minwidth_metal1"]],
width=(self.bank_select_or_position.x
- self.left_vdd_x_offset),
height=drc["minwidth_metal1"])
def route_gnd_supply(self): def route_gnd_supply(self):
""" Route gnd for the precharge, sense amp, write_driver, data FF, tristate """ """ Route gnd for the precharge, sense amp, write_driver, data FF, tristate """
@ -1220,49 +1167,24 @@ class bank(design.design):
offset=contact_offset, offset=contact_offset,
rotate=90) rotate=90)
# Connect bank_select_or2_array gnd
if(self.num_banks > 1):
return
self.bank_select_inv_position
self.add_rect(layer="metal1",
offset=(self.bank_select_inv_position
+ self.inv4x.gnd_position),
width=(self.bank_select_or_position.x
- self.bank_select_inv_position.x),
height=drc["minwidth_metal1"])
x_offset = (self.bank_select_or_position.x
+ self.nor2.width + self.inv4x.width)
for i in range(self.num_control_lines):
if(i % 2 == 0):
y_offset = self.bank_select_or_position.y + i*self.inv.height \
- 0.5*drc["minwidth_metal1"]
#both M1 & M2 are horizontal, cannot be replaced with wire
self.add_rect(layer="metal1",
offset=[x_offset, y_offset],
width=drc["minwidth_metal1"],
height=drc["minwidth_metal1"])
self.add_rect(layer="metal2",
offset=[x_offset, y_offset],
width=self.left_gnd_x_offset \
- x_offset + self.power_rail_width,
height=drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[x_offset + drc["minwidth_metal1"],
y_offset],
rotate=90)
def add_control_pins(self): def add_control_pins(self):
""" Add the control signal input pins """ """ Add the control signal input pins """
for ctrl in self.control_signals: for ctrl in self.control_signals:
x_offset = self.central_line_xoffset[ctrl] x_offset = self.central_line_xoffset[ctrl]
self.add_layout_pin(text=ctrl, if self.num_banks > 1:
layer="metal2", # it's not an input pin if we have multiple banks
offset=vector(x_offset, self.min_point), self.add_label_pin(text=ctrl,
width=self.m2_width, layer="metal2",
height=self.height) offset=vector(x_offset, self.min_point),
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin(text=ctrl,
layer="metal2",
offset=vector(x_offset, self.min_point),
width=self.m2_width,
height=self.height)
def connect_rail_from_right(self,inst, pin, rail): def connect_rail_from_right(self,inst, pin, rail):

View File

@ -34,11 +34,10 @@ class control_logic(design.design):
def create_modules(self): def create_modules(self):
""" add all the required modules """ """ add all the required modules """
input_lst =["csb","web","oeb"] input_lst =["csb","web","oeb","clk"]
output_lst = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar"] output_lst = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar", "clk_buf"]
clk =["clk"]
rails = ["vdd", "gnd"] rails = ["vdd", "gnd"]
for pin in input_lst + output_lst + clk + rails: for pin in input_lst + output_lst + rails:
self.add_pin(pin) self.add_pin(pin)
self.nand2 = nand_2() self.nand2 = nand_2()
@ -153,9 +152,10 @@ class control_logic(design.design):
rotate=270) rotate=270)
# don't change this order. This pins are meant for internal connection of msf array inside the control logic. # don't change this order. This pins are meant for internal connection of msf array inside the control logic.
# These pins are connecting the msf_array inside of control_logic. # These pins are connecting the msf_array inside of control_logic.
temp = ["oeb", "oe_bar", "oe", temp = ["oeb", "csb", "web",
"csb", "cs_bar", "cs", "oe_bar", "oe",
"web", "we_bar", "we", "cs_bar", "cs",
"we_bar", "we",
"clk_buf", "vdd", "gnd"] "clk_buf", "vdd", "gnd"]
self.connect_inst(temp) self.connect_inst(temp)
@ -347,7 +347,7 @@ class control_logic(design.design):
for i in range(self.num_rails_1): for i in range(self.num_rails_1):
offset = vector(self.rail_1_start_x + (i+1) * self.m2_pitch,0) offset = vector(self.rail_1_start_x + (i+1) * self.m2_pitch,0)
if self.rail_1_names[i] in ["clk_bar", "vdd", "gnd"]: if self.rail_1_names[i] in ["clk_buf", "clk_bar", "vdd", "gnd"]:
self.add_layout_pin(text=self.rail_1_names[i], self.add_layout_pin(text=self.rail_1_names[i],
layer="metal2", layer="metal2",
offset=offset, offset=offset,
@ -367,8 +367,10 @@ class control_logic(design.design):
self.connect_rail_from_left_m2m3(self.msf,"dout_bar[2]","we") self.connect_rail_from_left_m2m3(self.msf,"dout_bar[2]","we")
# Connect the gnd and vdd of the control # Connect the gnd and vdd of the control
gnd_pins = self.msf.get_pin("gnd") gnd_pins = self.msf.get_pins("gnd")
for p in gnd_pins: for p in gnd_pins:
if p.layer != "metal2":
continue
gnd_pin = p.rc() gnd_pin = p.rc()
gnd_rail_position = vector(self.rail_1_x_offsets["gnd"], gnd_pin.y) gnd_rail_position = vector(self.rail_1_x_offsets["gnd"], gnd_pin.y)
self.add_wire(("metal1","via1","metal2"),[gnd_pin, gnd_rail_position, gnd_rail_position - vector(0,self.m2_pitch)]) self.add_wire(("metal1","via1","metal2"),[gnd_pin, gnd_rail_position, gnd_rail_position - vector(0,self.m2_pitch)])
@ -376,9 +378,12 @@ class control_logic(design.design):
offset=gnd_pin + self.m1m2_via_offset, offset=gnd_pin + self.m1m2_via_offset,
rotate=90) rotate=90)
vdd_pin = self.msf.get_pin("vdd").bc() vdd_pins = self.msf.get_pins("vdd")
clk_vdd_position = vector(vdd_pin.x,self.clk_buf.get_pin("vdd").uy()) for p in vdd_pins:
self.add_path("metal1",[vdd_pin,clk_vdd_position]) if p.layer != "metal1":
continue
clk_vdd_position = vector(p.bc().x,self.clk_buf.get_pin("vdd").uy())
self.add_path("metal1",[p.bc(),clk_vdd_position])
self.rail_2_start_x = max (self.row_1_width, self.row_2_width, self.row_3_width) self.rail_2_start_x = max (self.row_1_width, self.row_2_width, self.row_3_width)
for i in range(self.num_rails_2): for i in range(self.num_rails_2):
@ -589,8 +594,8 @@ class control_logic(design.design):
# Now connect the vdd and gnd rails between the replica bitline and the control logic # Now connect the vdd and gnd rails between the replica bitline and the control logic
(rbl_row3_gnd,rbl_row1_gnd) = self.rbl.get_pin("gnd") (rbl_row3_gnd,rbl_row1_gnd) = self.rbl.get_pins("gnd")
(rbl_row3_vdd,rbl_row1_vdd) = self.rbl.get_pin("vdd") (rbl_row3_vdd,rbl_row1_vdd) = self.rbl.get_pins("vdd")
self.add_path("metal1",[row1_gnd_end_offset,rbl_row1_gnd.lc()]) self.add_path("metal1",[row1_gnd_end_offset,rbl_row1_gnd.lc()])
self.add_path("metal1",[row1_vdd_end_offset,rbl_row1_vdd.lc()]) self.add_path("metal1",[row1_vdd_end_offset,rbl_row1_vdd.lc()])

View File

@ -193,7 +193,7 @@ class delay_chain(design.design):
# Use the right most parts of the gnd rails and add a U connector # Use the right most parts of the gnd rails and add a U connector
# We still have the two gnd pins, but it is an either-or connect # We still have the two gnd pins, but it is an either-or connect
gnd_pins = self.get_pin("gnd") gnd_pins = self.get_pins("gnd")
gnd_start = gnd_pins[0].rc() gnd_start = gnd_pins[0].rc()
gnd_mid1 = gnd_start + vector(2*drc["metal1_to_metal1"],0) gnd_mid1 = gnd_start + vector(2*drc["metal1_to_metal1"],0)
gnd_end = gnd_pins[1].rc() gnd_end = gnd_pins[1].rc()

View File

@ -41,8 +41,7 @@ class instance(geometry):
self.offset = vector(offset).snap_to_grid() self.offset = vector(offset).snap_to_grid()
self.mirror = mirror self.mirror = mirror
self.boundary = [vector(0,0),vector(mod.width,mod.height)] self.compute_boundary(offset,mirror,rotate)
self.transform(offset,mirror,rotate)
debug.info(3, "creating instance: " + self.name) debug.info(3, "creating instance: " + self.name)
@ -65,10 +64,10 @@ class instance(geometry):
ur = vector(max(first[0],second[0]),max(first[1],second[1])) ur = vector(max(first[0],second[0]),max(first[1],second[1]))
self.boundary=[ll,ur] self.boundary=[ll,ur]
def transform(self,offset,mirror,rotate): def compute_boundary(self,offset=vector(0,0),mirror="",rotate=0):
""" Transform with offset, mirror and rotation to get the absolute pin location. """ Transform with offset, mirror and rotation to get the absolute pin location.
We must then re-find the ll and ur. The master is the cell instance. """ We must then re-find the ll and ur. The master is the cell instance. """
(ll,ur) = self.boundary (ll,ur) = [vector(0,0),vector(self.mod.width,self.mod.height)]
if mirror=="MX": if mirror=="MX":
ll=ll.scale(1,-1) ll=ll.scale(1,-1)
ur=ur.scale(1,-1) ur=ur.scale(1,-1)

View File

@ -24,7 +24,7 @@ class layout:
self.height = None self.height = None
self.insts = [] # Holds module/cell layout instances self.insts = [] # Holds module/cell layout instances
self.objs = [] # Holds all other objects (labels, geometries, etc) self.objs = [] # Holds all other objects (labels, geometries, etc)
self.pin_map = {} # Holds name->(vector,layer) map for all pins self.pin_map = {} # Holds name->pin_layout map for all pins
self.visited = False # Flag for traversing the hierarchy self.visited = False # Flag for traversing the hierarchy
self.gds_read() self.gds_read()
@ -35,9 +35,9 @@ class layout:
def offset_all_coordinates(self): def offset_all_coordinates(self):
""" This function is called after everything is placed to """ This function is called after everything is placed to
shift the origin in the lowest left corner """ shift the origin in the lowest left corner """
coordinate = self.find_lowest_coords() offset = self.find_lowest_coords()
self.offset_attributes(coordinate) #self.offset_attributes(offset)
self.translate(coordinate) self.translate_all(offset)
def get_gate_offset(self, x_offset, height, inv_num): def get_gate_offset(self, x_offset, height, inv_num):
"""Gets the base offset and y orientation of stacked rows of gates """Gets the base offset and y orientation of stacked rows of gates
@ -59,8 +59,7 @@ class layout:
def find_lowest_coords(self): def find_lowest_coords(self):
"""Finds the lowest set of 2d cartesian coordinates within """Finds the lowest set of 2d cartesian coordinates within
this layout""" this layout"""
#***1,000,000 number is used to avoid empty sequences errors*** # FIXME: don't depend on 1e9
# FIXME Is this hard coded value ok??
try: try:
lowestx1 = min(rect.offset.x for rect in self.objs) lowestx1 = min(rect.offset.x for rect in self.objs)
lowesty1 = min(rect.offset.y for rect in self.objs) lowesty1 = min(rect.offset.y for rect in self.objs)
@ -73,47 +72,24 @@ class layout:
[lowestx2, lowesty2] = [1000000.0, 1000000.0] [lowestx2, lowesty2] = [1000000.0, 1000000.0]
return vector(min(lowestx1, lowestx2), min(lowesty1, lowesty2)) return vector(min(lowestx1, lowestx2), min(lowesty1, lowesty2))
def offset_attributes(self, coordinate):
"""Translates all stored 2d cartesian coordinates within the
attr dictionary"""
# FIXME: This is dangerous. I think we should not do this, but explicitly
# offset the necessary coordinates.
#for attr_key, attr_val in self.attr.items(): def translate_all(self, offset):
for attr_key in dir(self): """
attr_val = getattr(self,attr_key) Translates all objects, instances, and pins by the given (x,y) offset
"""
# skip the list of things as these will be offset separately
if (attr_key in ['objs','insts','mods','pins','conns','name_map']): continue
# if is a list
if isinstance(attr_val, list):
for i in range(len(attr_val)):
# each unit in the list is a list coordinates
if isinstance(attr_val[i], (list,vector)):
attr_val[i] = vector(attr_val[i] - coordinate)
# the list itself is a coordinate
else:
if len(attr_val)!=2: continue
for val in attr_val:
if not isinstance(val, (int, long, float)): continue
setattr(self,attr_key, vector(attr_val - coordinate))
break
# if is a vector coordinate
if isinstance(attr_val, vector):
setattr(self, attr_key, vector(attr_val - coordinate))
def translate(self, coordinate):
"""Translates all 2d cartesian coordinates in a layout given
the (x,y) offset"""
for obj in self.objs: for obj in self.objs:
obj.offset = vector(obj.offset - coordinate) obj.offset = vector(obj.offset - offset)
for inst in self.insts: for inst in self.insts:
inst.offset = vector(inst.offset - coordinate) inst.offset = vector(inst.offset - offset)
# The instances have a precomputed boundary that we need to update.
if inst.__class__.__name__ == "instance":
inst.compute_boundary(offset.scale(-1,-1))
for pin_name in self.pin_map.keys():
# All the pins are absolute coordinates that need to be updated.
pin_list = self.pin_map[pin_name]
for pin in pin_list:
pin.rect = [pin.ll() - offset, pin.ur() - offset]
def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0): def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0):
"""Adds an instance of a mod to this module""" """Adds an instance of a mod to this module"""
@ -149,6 +125,18 @@ class layout:
""" Return a pin list (instead of a single pin) """ """ Return a pin list (instead of a single pin) """
return self.pin_map[text] return self.pin_map[text]
def copy_layout_pin(self, instance, pin_name, new_name=""):
"""
Create a copied version of the layout pin at the current level.
You can optionally rename the pin to a new name.
"""
pins=instance.get_pins(pin_name)
for pin in pins:
if new_name=="":
new_name = pin.name
self.add_layout_pin(new_name, pin.layer, pin.ll(), pin.width(), pin.height())
def add_layout_pin(self, text, layer, offset, width=None, height=None): def add_layout_pin(self, text, layer, offset, width=None, height=None):
"""Create a labeled pin """ """Create a labeled pin """
if width==None: if width==None:

View File

@ -340,21 +340,21 @@ class nor_2(design.design):
- self.nmos1.height) - self.nmos1.height)
self.A_loc = vector(xoffset, yoffset) self.A_loc = vector(xoffset, yoffset)
# gate input # gate input
offset = self.A_loc - vector(0, 0.5 * self.poly_contact.width) offset = self.A_loc - vector(0, self.poly_contact.width)
self.add_contact(layers=("poly", "contact", "metal1"), self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset, offset=offset,
rotate=90) rotate=90)
# connect gate input to tx gate # connect gate input to tx gate
offset = self.A_loc - vector(self.poly_contact.first_layer_position.y, offset = self.A_loc - vector(self.poly_contact.first_layer_position.y,
0.5 * self.poly_contact.width) self.poly_contact.width)
self.add_rect(layer="poly", self.add_rect(layer="poly",
offset=offset, offset=offset,
width=self.poly_contact.first_layer_position.y + drc["minwidth_poly"], width=self.poly_contact.first_layer_position.y + drc["minwidth_poly"],
height=self.poly_contact.first_layer_width) height=self.poly_contact.first_layer_width)
# extend the metal to the boundary of the cell # extend the metal to the boundary of the cell
input_length = self.A_loc.x input_length = self.A_loc.x
offset = [0, self.A_loc.y - 0.5 * drc["minwidth_metal1"]] offset = [0, self.A_loc.y - drc["minwidth_metal1"]]
self.add_layout_pin(text="A", self.add_layout_pin(text="A",
layer="metal1", layer="metal1",
offset=offset, offset=offset,

View File

@ -97,8 +97,8 @@ class pin_layout:
""" Upper left point """ """ Upper left point """
return vector(self.rect[0].x,self.rect[1].y) return vector(self.rect[0].x,self.rect[1].y)
def br(self): def lr(self):
""" Bottom right point """ """ Lower right point """
return vector(self.rect[1].x,self.rect[0].y) return vector(self.rect[1].x,self.rect[0].y)
def ur(self): def ur(self):

View File

@ -50,10 +50,41 @@ class ptx(design.design):
# self.connect_fingered_poly() # self.connect_fingered_poly()
self.offset_all_coordinates() self.offset_all_coordinates()
def offset_attributes(self, coordinate):
"""Translates all stored 2d cartesian coordinates within the
attr dictionary"""
# FIXME: This is dangerous. I think we should not do this, but explicitly
# offset the necessary coordinates. It is only used in ptx for now!
for attr_key in dir(self):
attr_val = getattr(self,attr_key)
# skip the list of things as these will be offset separately
if (attr_key in ['objs','insts','mods','pins','conns','name_map']): continue
# if is a list
if isinstance(attr_val, list):
for i in range(len(attr_val)):
# each unit in the list is a list coordinates
if isinstance(attr_val[i], (list,vector)):
attr_val[i] = vector(attr_val[i] - coordinate)
# the list itself is a coordinate
else:
if len(attr_val)!=2: continue
for val in attr_val:
if not isinstance(val, (int, long, float)): continue
setattr(self,attr_key, vector(attr_val - coordinate))
break
# if is a vector coordinate
if isinstance(attr_val, vector):
setattr(self, attr_key, vector(attr_val - coordinate))
def offset_all_coordinates(self): def offset_all_coordinates(self):
coordinate = self.find_lowest_coords() offset = self.find_lowest_coords()
self.offset_attributes(coordinate) self.offset_attributes(offset)
self.translate(coordinate) self.translate_all(offset)
# We can do this in ptx because we have offset all modules it uses. # We can do this in ptx because we have offset all modules it uses.
# Is this really true considering the paths that connect the src/drain? # Is this really true considering the paths that connect the src/drain?

View File

@ -219,7 +219,7 @@ class replica_bitline(design.design):
height=self.rbl.height+self.bitcell.height+2*self.inv.width+0.5*drc["minwidth_metal1"]) height=self.rbl.height+self.bitcell.height+2*self.inv.width+0.5*drc["minwidth_metal1"])
# Connect the vdd pins of the bitcell load directly to vdd # Connect the vdd pins of the bitcell load directly to vdd
vdd_pins = self.rbl_inst.get_pin("vdd") vdd_pins = self.rbl_inst.get_pins("vdd")
for pin in vdd_pins: for pin in vdd_pins:
offset = vector(vdd_start.x,pin.by()) offset = vector(vdd_start.x,pin.by())
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
@ -286,9 +286,9 @@ class replica_bitline(design.design):
offset=offset) offset=offset)
# Connect the bitcell gnd pin to the rail # Connect the bitcell gnd pin to the rail
gnd_pins = self.get_pin("gnd") gnd_pins = self.get_pins("gnd")
gnd_start = self.get_pin("gnd").uc() gnd_start = gnd_pins[0].uc()
rbl_gnd_pins = self.rbl_inst.get_pin("gnd") rbl_gnd_pins = self.rbl_inst.get_pins("gnd")
# Find the left most rail on M2 # Find the left most rail on M2
gnd_pin = None gnd_pin = None
for pin in rbl_gnd_pins: for pin in rbl_gnd_pins:
@ -302,7 +302,7 @@ class replica_bitline(design.design):
# Add a second gnd pin to the second delay chain rail. No need for full length. # Add a second gnd pin to the second delay chain rail. No need for full length.
dc_gnd_offset = self.dc_inst.get_pin("gnd")[1].ll() dc_gnd_offset = self.dc_inst.get_pins("gnd")[1].ll()
self.add_layout_pin(text="gnd", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=dc_gnd_offset.scale(1,0), offset=dc_gnd_offset.scale(1,0),

View File

@ -43,7 +43,17 @@ class sram(design.design):
self.num_words)) self.num_words))
design.design.__init__(self, name) design.design.__init__(self, name)
# These aren't for instantiating, but we use them to get the dimensions
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
self.poly_contact_offset = vector(0.5*self.poly_contact.width,0.5*self.poly_contact.height)
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
# M1/M2 routing pitch is based on contacted pitch
self.m1_pitch = max(self.m1m2_via.width,self.m1m2_via.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m2_pitch = max(self.m2m3_via.width,self.m2m3_via.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
self.control_size = 6 self.control_size = 6
self.bank_to_bus_distance = 5*drc["minwidth_metal3"] self.bank_to_bus_distance = 5*drc["minwidth_metal3"]
@ -54,6 +64,7 @@ class sram(design.design):
def compute_sizes(self): def compute_sizes(self):
""" Computes the organization of the memory using bitcell size by trying to make it square.""" """ Computes the organization of the memory using bitcell size by trying to make it square."""
debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.") debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.")
self.num_words_per_bank = self.num_words/self.num_banks self.num_words_per_bank = self.num_words/self.num_banks
@ -109,33 +120,34 @@ class sram(design.design):
return words_per_row return words_per_row
def add_pins(self): def add_pins(self):
""" app pins """ """ Add pins for entire SRAM. """
for i in range(self.word_size): for i in range(self.word_size):
self.add_pin("DATA[{0}]".format(i)) self.add_pin("DATA[{0}]".format(i))
for i in range(self.addr_size): for i in range(self.addr_size):
self.add_pin("ADDR[{0}]".format(i)) self.add_pin("ADDR[{0}]".format(i))
for pin in ["CSb","WEb","OEb", for pin in ["CSb","WEb","OEb","clk","vdd","gnd"]:
"clk","vdd","gnd"]:
self.add_pin(pin) self.add_pin(pin)
def create_layout(self): def create_layout(self):
""" Layout creation """ """ Layout creation """
self.create_modules() self.create_modules()
self.add_modules()
self.add_routing()
def add_routing(self): if self.num_banks == 1:
""" Route all of the modules """ self.add_single_bank_modules()
self.add_single_bank_pins()
if (self.num_banks == 2 or self.num_banks == 4): self.route_single_bank()
self.route_2or4_banks() else:
if (self.num_banks == 4): self.add_multi_bank_modules()
self.route_4_banks() self.route_top_banks()
self.route_bank_and_control() if self.num_banks > 2:
self.route_supplies() self.route_bottom_banks()
def create_multibank_modules(self):
def create_multi_bank_modules(self):
""" Add the multibank address flops and bank decoder """ """ Add the multibank address flops and bank decoder """
self.msf_msb_address = self.mod_ms_flop_array(name="msf_msb_address", self.msf_msb_address = self.mod_ms_flop_array(name="msf_msb_address",
@ -165,63 +177,15 @@ class sram(design.design):
if(self.num_banks > 1): if(self.num_banks > 1):
self.create_multibank_modules() self.create_multibank_modules()
# These aren't for instantiating, but we use them to get the dimensions
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
self.bank_count = 0 self.bank_count = 0
self.sram_property = ["bank_clk_positions", self.power_rail_width = self.bank.vdd_rail_width
"bank_clk_bar_positions", self.sram_power_rail_gap = 4*self.bank.vdd_rail_width
"bank_tri_en_positions",
"bank_tri_en_bar_positions",
"bank_w_en_positions",
"bank_s_en_positions"]
self.bank_property = ["clk_position",
"clk_bar_position",
"tri_en_position",
"tri_en_bar_position",
"w_en_position",
"s_en_position", ]
self.bank_positions = []
self.bank_clk_positions = []
self.bank_clk_bar_positions = []
self.bank_tri_en_positions = []
self.bank_tri_en_bar_positions = []
self.bank_w_en_positions = []
self.bank_s_en_positions = []
# SRAM bank address 3D array
# 2 keys will return a x,y position pair
# example key1 = bank_index , key2 = addr_line_index will return [x,y]
self.sram_bank_adress_positions = []
# SRAM data lines 3D array
# 2 keys will return a x,y position pair
# example key1 = bank_index , key2 = data_line_index will return [x,y]
self.sram_bank_data_positions = []
# 2D array for bank_select position of banks
self.sram_bank_select_positions = []
# Bank power rail positions
self.sram_bank_right_vdd_positions = []
self.sram_bank_left_vdd_positions = []
self.sram_bank_left_gnd_positions = []
self.power_rail_width = self.bank.power_rail_width
self.sram_power_rail_gap = 4*self.power_rail_width
self.vdd_position = vector(0, 2*self.power_rail_width)
self.gnd_position = vector(0, 0)
def add_bank(self, position, x_flip, y_flip): def add_bank(self, position, x_flip, y_flip):
""" add and place bank. All the pin position is also """ Place a bank at the given position with orientations """
translated and saved for later use"""
# x_flip == 1 --> no flip in x_axis # x_flip == 1 --> no flip in x_axis
# x_flip == -1 --> flip in x_axis # x_flip == -1 --> flip in x_axis
@ -230,26 +194,25 @@ class sram(design.design):
# x_flip and y_flip are used for position translation # x_flip and y_flip are used for position translation
bank_rotation = 180 if (x_flip == -1 and y_flip == -1) else 0 if x_flip == -1 and y_flip == -1:
bank_mirror = "R0" bank_rotation = 180
else:
bank_rotation = 0
if(x_flip == y_flip): if x_flip == y_flip:
bank_mirror = "R0" bank_mirror = "R0"
elif(x_flip == -1): elif x_flip == -1:
bank_mirror = "MX" bank_mirror = "MX"
elif(y_flip == -1): elif y_flip == -1:
bank_mirror = "MY" bank_mirror = "MY"
else:
yMetalShift = drc["minwidth_metal3"] if (x_flip == -1) else 0 bank_mirror = "R0"
xMetalShift = drc["minwidth_metal3"] if (y_flip == -1) else 0
bank_inst=self.add_inst(name="bank{0}".format(self.bank_count),
position=vector(position) mod=self.bank,
self.add_inst(name="bank{0}".format(self.bank_count), offset=position,
mod=self.bank, mirror=bank_mirror,
offset=position, rotate=bank_rotation)
mirror=bank_mirror,
rotate=bank_rotation)
self.bank_positions.append(position)
temp = [] temp = []
for i in range(self.word_size): for i in range(self.word_size):
@ -257,64 +220,14 @@ class sram(design.design):
for i in range(self.bank_addr_size): for i in range(self.bank_addr_size):
temp.append("ADDR[{0}]".format(i)) temp.append("ADDR[{0}]".format(i))
if(self.num_banks > 1): if(self.num_banks > 1):
temp.append("bank_select[{0}]".format(self.bank_count)) temp.append("bank_sel[{0}]".format(self.bank_count))
temp.extend(["s_en", "w_en", "tri_en_bar", "tri_en", temp.extend(["s_en", "w_en", "tri_en_bar", "tri_en",
"clk_bar","clk" , "vdd", "gnd"]) "clk_bar","clk_buf" , "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
# Saving control line properties
for i in range(len(self.sram_property)):
sub_mod_offset = getattr(self.bank,self.bank_property[i])
new=(position + vector(y_flip,x_flip).scale(sub_mod_offset)
- vector(xMetalShift,yMetalShift))
pos_list=getattr(self,self.sram_property[i])
if pos_list is None:
pos_list=[]
pos_list.append(new)
setattr(self,self.sram_property[i],pos_list)
# Address input lines
bank_address_positions = []
for addr_position in self.bank.address_positions:
new=(position + vector(y_flip,x_flip).scale(addr_position)
- vector(xMetalShift,yMetalShift))
bank_address_positions.append(new)
self.sram_bank_adress_positions.append(bank_address_positions)
# Bank select
if (self.num_banks > 1):
new=(position + vector(y_flip,x_flip).scale(self.bank.bank_select_position)
- vector(xMetalShift,yMetalShift))
self.sram_bank_select_positions.append(new)
# Data input lines
bank_data_positions = []
for data_position in self.bank.data_positions:
new=(position + vector(y_flip,x_flip).scale(data_position)
- vector(xMetalShift,yMetalShift))
bank_data_positions.append(new)
self.sram_bank_data_positions.append(bank_data_positions)
# VDD rails
yPowerShift = self.power_rail_width if(x_flip == -1) else 0
xPowerShift = self.power_rail_width if(y_flip == -1) else 0
# Right vdd
new=(position + vector(y_flip,x_flip).scale(self.bank.right_vdd_position)
- vector(xPowerShift,yPowerShift))
self.sram_bank_right_vdd_positions.append(new)
# left vdd
new=(position + vector(y_flip,x_flip).scale(self.bank.left_vdd_position)
- vector(xPowerShift,yPowerShift))
self.sram_bank_left_vdd_positions.append(new)
# left gnd
new=(position + vector(y_flip,x_flip).scale(self.bank.left_gnd_position)
- vector(xPowerShift,yPowerShift))
self.sram_bank_left_gnd_positions.append(new)
self.bank_count = self.bank_count + 1 self.bank_count = self.bank_count + 1
return bank_inst
# FIXME: This should be in geometry.py or it's own class since it is # FIXME: This should be in geometry.py or it's own class since it is
# reusable # reusable
@ -360,42 +273,54 @@ class sram(design.design):
def add_control_logic(self, position, rotate): def add_control_logic(self, position, rotate):
""" Add and place control logic """ """ Add and place control logic """
self.control_position = position self.control_logic_inst=self.add_inst(name="control",
self.add_inst(name="control", mod=self.control,
mod=self.control, offset=position,
offset=self.control_position, rotate=rotate)
rotate=rotate) temp = ["CSb", "WEb", "OEb", "clk", "s_en", "w_en", "tri_en",
temp = ["CSb", "WEb", "OEb", "s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar", "clk_buf", "vdd", "gnd"]
"tri_en_bar", "clk_bar", "clk", "vdd", "gnd"]
self.connect_inst(temp) self.connect_inst(temp)
def add_singlebank_modules(self): def add_single_bank_modules(self):
""" This adds the moduels for a single bank SRAM with control """
logic. """ This adds the moduels for a single bank SRAM with control
self.add_bank([0, 0], 1, 1) logic.
# FIXME: document """
loc = vector(- 2 * drc["minwidth_metal3"],
self.bank_positions[0].y + self.bank.decoder_position.y # No orientation or offset
+ 2 * drc["minwidth_metal3"]) self.bank_inst = self.add_bank([0, 0], 1, 1)
self.add_control_logic(loc, 90)
# Control logic is placed to the left of the blank even with the
# decoder bottom. A small gap is in the x-dimension.
control_gap = 2*drc["minwidth_metal3"]
pos = vector(-control_gap,
self.bank.row_decoder_inst.by() + 2*drc["minwidth_metal3"])
self.add_control_logic(position=pos,
rotate=90)
self.width = self.bank.width + self.control.height + 2*drc["minwidth_metal3"] self.width = self.bank.width + self.control.height + control_gap
self.height = self.bank.height self.height = self.bank.height
self.control.CSb_position.rotate_scale(-1,1)
self.CSb_position = (self.control.CSb_position.rotate_scale(-1,1)
+self.control_position)
self.OEb_position = (self.control.OEb_position.rotate_scale(-1,1)
+self.control_position)
self.WEb_position = (self.control.WEb_position.rotate_scale(-1,1)
+self.control_position)
self.clk_position = (self.control.clk_position.rotate_scale(-1,1)
+self.control_position)
for i in range(0, self.word_size):
self.add_label(text="DATA[{0}]".format(i),
layer="metal3",
offset=self.bank.data_positions[i])
def add_multibank_modules(self): def add_single_bank_pins(self):
"""
Add the top-level pins for a single bank SRAM with control.
"""
for i in range(self.word_size):
self.copy_layout_pin(self.bank_inst, "DATA[{}]".format(i))
for i in range(self.addr_size):
self.copy_layout_pin(self.bank_inst, "ADDR[{}]".format(i))
for (old,new) in zip(["csb","web","oeb","clk"],["CSb","WEb","OEb","clk"]):
self.copy_layout_pin(self.control_logic_inst, old, new)
self.copy_layout_pin(self.bank_inst, "vdd")
self.copy_layout_pin(self.bank_inst, "gnd")
def add_multi_bank_modules(self):
""" This creates either a 2 or 4 bank SRAM with control logic """ This creates either a 2 or 4 bank SRAM with control logic
and bank selection logic.""" and bank selection logic."""
@ -491,14 +416,13 @@ class sram(design.design):
temp.append("msb{0}".format(i)) temp.append("msb{0}".format(i))
temp.append("msb{0}_bar".format(i)) temp.append("msb{0}_bar".format(i))
else: else:
temp.extend(["bank_select[1]", "bank_select[0]"]) temp.extend(["bank_sel[1]", "bank_sel[0]"])
temp.extend(["clk", "vdd", "gnd"]) temp.extend(["clk_buf", "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
self.add_banks_0and1() self.add_top_banks()
if self.num_banks == 4:
if (self.num_banks == 4): self.add_bottom_banks()
self.add_banks_2and3()
# Extension of Vertical Rail # Extension of Vertical Rail
self.create_bus(layer="metal2", self.create_bus(layer="metal2",
@ -522,16 +446,7 @@ class sram(design.design):
offset=[self.vertical_line_positions[self.control_size + i].x, offset=[self.vertical_line_positions[self.control_size + i].x,
self.max_point]) self.max_point])
def add_modules(self): def add_top_banks(self):
""" add all the modules """
if (self.num_banks == 1):
self.add_singlebank_modules()
elif (self.num_banks == 2 or self.num_banks == 4):
self.add_multibank_modules()
self.add_labels()
def add_banks_0and1(self):
# Placement of bank 0 # Placement of bank 0
self.bank_position_0 = vector(self.bank_w, self.bank_position_0 = vector(self.bank_w,
self.bank_h + self.sram_power_rail_gap) self.bank_h + self.sram_power_rail_gap)
@ -542,7 +457,7 @@ class sram(design.design):
self.bank_position_1 = vector(x_off, self.bank_position_0.y) self.bank_position_1 = vector(x_off, self.bank_position_0.y)
self.add_bank(self.bank_position_1, -1, 1) self.add_bank(self.bank_position_1, -1, 1)
def add_banks_2and3(self): def add_bottom_banks(self):
# Placement of bank 2 # Placement of bank 2
y_off = (self.bank_h + self.horizontal_bus_width y_off = (self.bank_h + self.horizontal_bus_width
+2 * self.bank_to_bus_distance +2 * self.bank_to_bus_distance
@ -564,10 +479,10 @@ class sram(design.design):
mod=self.msb_decoder, mod=self.msb_decoder,
offset=self.msb_decoder_position, offset=self.msb_decoder_position,
mirror="MY") mirror="MY")
temp = ["msb0", "msb1", "bank_select[{0}]".format(0), temp = ["msb0", "msb1"]
"bank_select[{0}]".format(1), "bank_select[{0}]".format(2), for i in range(4):
"bank_select[{0}]".format(3), temp.append("bank_sel[{}]".format(i))
"vdd", "gnd"] temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
self.control_position = vector(0, self.msb_decoder_position.y self.control_position = vector(0, self.msb_decoder_position.y
@ -582,23 +497,9 @@ class sram(design.design):
# Max point # Max point
self.max_point = self.msb_decoder_position.y + self.msb_decoder.height self.max_point = self.msb_decoder_position.y + self.msb_decoder.height
def add_labels(self):
""" Add the top-level labels for control and address """
for label in ["CSb", "OEb", "WEb", "clk"]:
offset = getattr(self, label+"_position")
self.add_label(text=label,
layer="metal3",
offset=offset)
# add address label def route_top_banks(self):
for addr_pos_lst in self.sram_bank_adress_positions: """ Routing of top two banks """
for address, address_positions in enumerate(addr_pos_lst):
self.add_label(text="ADDR[{0}]".format(address),
layer="metal3",
offset=address_positions)
def route_2or4_banks(self):
""" Routing between bank 2 or 4 bank modules """
addr_start_index = len(self.sram_property) + (self.num_banks / 2) addr_start_index = len(self.sram_property) + (self.num_banks / 2)
bank_select_index = addr_start_index + self.bank.addr_size bank_select_index = addr_start_index + self.bank.addr_size
@ -652,7 +553,7 @@ class sram(design.design):
offset=contact_offset) offset=contact_offset)
# Data connection on the horizontal bus # Data connection on the horizontal bus
if (self.num_banks == 4): if self.num_banks == 4:
data_connection_top = self.sram_bank_data_positions[2][0].y + self.m2m3_via.height data_connection_top = self.sram_bank_data_positions[2][0].y + self.m2m3_via.height
else: else:
data_connection_top=self.horizontal_bus_offset.y data_connection_top=self.horizontal_bus_offset.y
@ -673,7 +574,7 @@ class sram(design.design):
offset=[self.sram_bank_data_positions[lower_bank_index][data_index].x, offset=[self.sram_bank_data_positions[lower_bank_index][data_index].x,
self.horizontal_line_positions[data_index].y]) self.horizontal_line_positions[data_index].y])
def route_4_banks(self): def route_bottom_banks(self):
for i in range(2): for i in range(2):
lower_bank_index = i lower_bank_index = i
upper_bank_index = i + 2 upper_bank_index = i + 2
@ -695,48 +596,55 @@ class sram(design.design):
height=self.sram_bank_left_gnd_positions[upper_bank_index].y \ height=self.sram_bank_left_gnd_positions[upper_bank_index].y \
- self.sram_bank_left_gnd_positions[lower_bank_index].y) - self.sram_bank_left_gnd_positions[lower_bank_index].y)
def connect_rail_from_left_m2m3(self, src_pin, dest_pin):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = src_pin.rc()
out_pos = vector(dest_pin.cx(), in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)])
self.add_via(layers=("metal2","via2","metal3"),
offset=src_pin.lr(),
rotate=90)
def connect_rail_from_left_m2m1(self, src_pin, dest_pin):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = src_pin.rc()
out_pos = vector(dest_pin.cx(), in_pos.y)
self.add_wire(("metal2","via1","metal1"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)])
def route_single_bank(self):
""" Route a single bank SRAM """
# left pin is on the control logic, right pin is on the bank
connections = [("clk_buf", "clk"),
("tri_en_bar", "tri_en_bar"),
("tri_en", "tri_en"),
("clk_bar", "clk_bar"),
("w_en", "w_en"),
("s_en", "s_en")]
for (src,dest) in connections:
src_pin = self.control_logic_inst.get_pin(src)
dest_pin = self.bank_inst.get_pin(dest)
self.connect_rail_from_left_m2m3(src_pin, dest_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)
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)
def route_bank_and_control(self): def route_bank_and_control(self):
""" Routing between banks and control """ """ Routing between banks and control """
if (self.num_banks == 1): if self.num_banks == 1:
pass
# FIXME what is this? add comments elif self.num_banks == 2 or self.num_banks == 4:
# 5 = clk
# 4 = tri_en_bar
# 3 = tri_en
# 2 = clk_bar
# 1 = w_en
# 0 = s_en
control_side = []
control_side.append(self.control.clk_position.rotate_scale(-1, 1)
+ self.control_position)
control_side.append(self.control.clk_bar_position.rotate_scale(-1, 1)
+ self.control_position)
control_side.append(self.control.tri_en_position.rotate_scale(-1, 1)
+ self.control_position)
control_side.append(self.control.tri_en_bar_position.rotate_scale(-1, 1)
+ self.control_position)
control_side.append(self.control.w_en_position.rotate_scale(-1, 1)
+ self.control_position)
control_side.append(self.control.s_en_position.rotate_scale(-1, 1)
+ self.control_position)
bank_side = []
for attr_name in (self.sram_property):
bank_side.append(getattr(self,attr_name)[0])
for i in range(len(control_side)):
self.add_rect(layer="metal3",
offset=control_side[i],
width=bank_side[i].x - control_side[i].x,
height=drc["minwidth_metal3"])
self.add_via(layers=("metal2", "via2", "metal3"),
offset=[bank_side[i].x + drc["minwidth_metal2"],
control_side[i].y],
rotate=90)
elif (self.num_banks == 2 or self.num_banks == 4):
for i in range(self.control_size): for i in range(self.control_size):
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.vertical_line_positions[i].x + drc["minwidth_metal2"], offset=[self.vertical_line_positions[i].x + drc["minwidth_metal2"],
@ -857,44 +765,8 @@ class sram(design.design):
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=contact_pos) offset=contact_pos)
def route_vdd_singlebank(self):
""" Route the vdd for 1 bank SRAMs """
# left vdd rail of bank
self.vdd_offset = self.bank.left_vdd_position
self.add_label(text="vdd",
layer="metal1",
offset=self.vdd_offset)
# Add label for right vdd rail bank def route_vdd_multi_bank(self):
self.add_label(text="vdd",
layer="metal1",
offset=self.sram_bank_right_vdd_positions[0])
# control logic
self.control_vdd1_position = (self.control_position
+ self.control.vdd1_position.rotate_scale(-1, 1))
self.control_vdd2_position = (self.control_position
+ self.control.vdd2_position.rotate_scale(-1, 1))
self.add_rect(layer="metal1",
offset=self.control_vdd1_position,
width=self.vdd_offset.x
- self.control_vdd1_position.x,
height=drc["minwidth_metal1"])
self.add_rect(layer="metal2",
offset=self.control_vdd2_position,
width=self.vdd_offset.x
- self.control_vdd2_position.x,
height=drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.vdd_offset.x,
self.control_vdd2_position.y],
size=(2, 1))
def route_vdd_multibank(self):
""" Route the vdd for 2 and 4 bank SRAMs """ """ Route the vdd for 2 and 4 bank SRAMs """
# VDD routing between banks # VDD routing between banks
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
@ -1018,35 +890,8 @@ class sram(design.design):
self.vdd_position.y]) self.vdd_position.y])
def route_gnd_singlebank(self):
""" Route the gnd for 1 bank SRAMs """
# left gnd rail of bank def route_gnd_multi_bank(self):
self.gnd_offset = self.bank.left_gnd_position
self.add_label(text="gnd",
layer="metal2",
offset=self.gnd_offset)
self.control_gnd_position = (self.control_position
+ self.control.gnd_position.rotate_scale(-1,1)
+ vector(drc["minwidth_metal2"],0))
self.add_rect(layer="metal3",
offset=self.control_gnd_position,
width=self.gnd_offset.x - self.control_gnd_position.x,
height=drc["minwidth_metal3"])
self.add_via(layers=("metal2", "via2", "metal3"),
offset=[self.gnd_offset.x,
self.control_gnd_position.y],
size=(2,1))
self.add_via(layers=("metal2", "via2", "metal3"),
offset=self.control_gnd_position,
rotate=90)
def route_gnd_multibank(self):
""" Route the gnd for 2 and 4 bank SRAMs """ """ Route the gnd for 2 and 4 bank SRAMs """
self.add_rect(layer="metal2", self.add_rect(layer="metal2",
offset=self.gnd_position, offset=self.gnd_position,
@ -1141,13 +986,12 @@ class sram(design.design):
def route_supplies(self): def route_supplies(self):
""" vdd/gnd routing of all modules """ """ vdd/gnd routing of all modules """
return
if (self.num_banks == 1): if (self.num_banks == 1):
self.route_vdd_singlebank() pass
self.route_gnd_singlebank()
elif (self.num_banks == 2 or self.num_banks == 4): elif (self.num_banks == 2 or self.num_banks == 4):
self.route_vdd_multibank() self.route_vdd_multi_bank()
self.route_gnd_multibank() self.route_gnd_multi_bank()
else: else:
debug.error("Incorrect number of banks.") debug.error("Incorrect number of banks.")

View File

@ -15,7 +15,7 @@ OPTS = globals.get_opts()
class bank_test(unittest.TestCase): class multi_bank_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
@ -25,20 +25,20 @@ class bank_test(unittest.TestCase):
import bank import bank
debug.info(1, "No column mux") debug.info(1, "No column mux")
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=4, name="test_sram1") a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=2, name="test_bank1")
self.local_check(a) self.local_check(a)
debug.info(1, "Two way column mux") debug.info(1, "Two way column mux")
a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=4, name="test_sram2") a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=2, name="test_bank2")
self.local_check(a) self.local_check(a)
debug.info(1, "Four way column mux") debug.info(1, "Four way column mux")
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=4, name="test_sram3") a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=2, name="test_bank3")
self.local_check(a) self.local_check(a)
# Eight way has a short circuit of one column mux select to gnd rail # Eight way has a short circuit of one column mux select to gnd rail
# debug.info(1, "Eight way column mux") # debug.info(1, "Eight way column mux")
# a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="test_sram4") # a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="test_bank4")
# self.local_check(a) # self.local_check(a)
OPTS.check_lvsdrc = True OPTS.check_lvsdrc = True

View File

@ -16,7 +16,7 @@ OPTS = globals.get_opts()
#@unittest.skip("SKIPPING 20_sram_test") #@unittest.skip("SKIPPING 20_sram_test")
class bank_test(unittest.TestCase): class single_bank_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
@ -26,20 +26,20 @@ class bank_test(unittest.TestCase):
import bank import bank
debug.info(1, "No column mux") debug.info(1, "No column mux")
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="test_sram1") a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="test_bank1")
self.local_check(a) self.local_check(a)
debug.info(1, "Two way column mux") debug.info(1, "Two way column mux")
a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="test_sram2") a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="test_bank2")
self.local_check(a) self.local_check(a)
debug.info(1, "Four way column mux") debug.info(1, "Four way column mux")
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="test_sram3") a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="test_bank3")
self.local_check(a) self.local_check(a)
# Eight way has a short circuit of one column mux select to gnd rail # Eight way has a short circuit of one column mux select to gnd rail
# debug.info(1, "Eight way column mux") # debug.info(1, "Eight way column mux")
# a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="test_sram4") # a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="test_bank4")
# self.local_check(a) # self.local_check(a)
OPTS.check_lvsdrc = True OPTS.check_lvsdrc = True

View File

@ -1,69 +0,0 @@
#!/usr/bin/env python2.7
"""
Run a regresion test on various srams
"""
import unittest
from testutils import header
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
import calibre
OPTS = globals.get_opts()
class bank_test(unittest.TestCase):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
# we will manually run lvs/drc
OPTS.check_lvsdrc = False
import bank
debug.info(1, "No column mux")
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=2, name="test_sram1")
self.local_check(a)
debug.info(1, "Two way column mux")
a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=2, name="test_sram2")
self.local_check(a)
debug.info(1, "Four way column mux")
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=2, name="test_sram3")
self.local_check(a)
# Eight way has a short circuit of one column mux select to gnd rail
# debug.info(1, "Eight way column mux")
# a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="test_sram4")
# self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, a):
tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds"
a.sp_write(tempspice)
a.gds_write(tempgds)
self.assertFalse(calibre.run_drc(a.name, tempgds))
self.assertFalse(calibre.run_lvs(a.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()

View File

@ -25,10 +25,22 @@ class sram_1bank_test(unittest.TestCase):
import sram import sram
debug.info(1, "Testing sample 8bit, 64word SRAM, 1 bank") debug.info(1, "Single bank, no column mux with control logic")
a = sram.sram(word_size=8, num_words=128, num_banks=1, name="test_sram1") a = sram.sram(word_size=4, num_words=16, num_banks=1, name="test_sram1")
self.local_check(a) self.local_check(a)
debug.info(1, "Single bank two way column mux with control logic")
a = sram.sram(word_size=4, num_words=32, num_banks=1, name="test_sram2")
self.local_check(a)
debug.info(1, "Single bank, four way column mux with control logic")
a = sram.sram(word_size=4, num_words=64, num_banks=1, name="test_sram3")
self.local_check(a)
# debug.info(1, "Single bank, eight way column mux with control logic")
# a = sram.sram(word_size=2, num_words=128, num_banks=1, name="test_sram4")
# self.local_check(a)
OPTS.check_lvsdrc = True OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -25,10 +25,22 @@ class sram_2bank_test(unittest.TestCase):
import sram import sram
debug.info(1, "Testing sample 8bit, 128word SRAM, 2 banks") debug.info(1, "Two bank, no column mux with control logic")
a = sram.sram(word_size=8, num_words=128, num_banks=2, name="test_sram1") a = sram.sram(word_size=4, num_words=32, num_banks=2, name="test_sram1")
self.local_check(a) self.local_check(a)
debug.info(1, "Two bank two way column mux with control logic")
a = sram.sram(word_size=4, num_words=64, num_banks=2, name="test_sram2")
self.local_check(a)
debug.info(1, "Two bank, four way column mux with control logic")
a = sram.sram(word_size=4, num_words=128, num_banks=2, name="test_sram3")
self.local_check(a)
# debug.info(1, "Two bank, eight way column mux with control logic")
# a = sram.sram(word_size=2, num_words=256 num_banks=2, name="test_sram4")
# self.local_check(a)
OPTS.check_lvsdrc = True OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -25,10 +25,22 @@ class sram_4bank_test(unittest.TestCase):
import sram import sram
debug.info(1, "Testing sample 8bit, 128word SRAM, 4 banks") debug.info(1, "Four bank, no column mux with control logic")
a = sram.sram(word_size=8, num_words=128, num_banks=4, name="test_sram1") a = sram.sram(word_size=4, num_words=32, num_banks=4, name="test_sram1")
self.local_check(a) self.local_check(a)
debug.info(1, "Four bank two way column mux with control logic")
a = sram.sram(word_size=4, num_words=64, num_banks=4, name="test_sram2")
self.local_check(a)
debug.info(1, "Four bank, four way column mux with control logic")
a = sram.sram(word_size=4, num_words=128, num_banks=4, name="test_sram3")
self.local_check(a)
# debug.info(1, "Four bank, eight way column mux with control logic")
# a = sram.sram(word_size=2, num_words=256, num_banks=4, name="test_sram4")
# self.local_check(a)
OPTS.check_lvsdrc = True OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()