mirror of https://github.com/VLSIDA/OpenRAM.git
Single bank passing DRC and LVS again.
Unfold hierarchical decoder to improve routability.
This commit is contained in:
parent
b867e163a6
commit
1f81b24e96
|
|
@ -178,7 +178,7 @@ class layout(lef.lef):
|
||||||
""" Return the pin or list of pins """
|
""" Return the pin or list of pins """
|
||||||
try:
|
try:
|
||||||
if len(self.pin_map[text])>1:
|
if len(self.pin_map[text])>1:
|
||||||
debug.warning("Should use a pin iterator since more than one pin {}".format(text))
|
debug.error("Should use a pin iterator since more than one pin {}".format(text),-1)
|
||||||
# If we have one pin, return it and not the list.
|
# If we have one pin, return it and not the list.
|
||||||
# Otherwise, should use get_pins()
|
# Otherwise, should use get_pins()
|
||||||
return self.pin_map[text][0]
|
return self.pin_map[text][0]
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,6 @@ class bank(design.design):
|
||||||
self.route_tri_gate_out()
|
self.route_tri_gate_out()
|
||||||
self.route_wordline_driver()
|
self.route_wordline_driver()
|
||||||
self.route_row_decoder()
|
self.route_row_decoder()
|
||||||
self.route_address()
|
|
||||||
self.route_column_address_lines()
|
self.route_column_address_lines()
|
||||||
self.route_control_lines()
|
self.route_control_lines()
|
||||||
self.add_control_pins()
|
self.add_control_pins()
|
||||||
|
|
@ -174,11 +173,6 @@ class bank(design.design):
|
||||||
# The width of this bus is needed to place other modules (e.g. decoder)
|
# The width of this bus is needed to place other modules (e.g. decoder)
|
||||||
self.central_bus_width = self.m2_pitch * (self.num_control_lines + self.num_addr_lines + 1)
|
self.central_bus_width = self.m2_pitch * (self.num_control_lines + self.num_addr_lines + 1)
|
||||||
|
|
||||||
# The width of the bus below the decoder to route to the central bus
|
|
||||||
self.addr_bus_height = self.m1_pitch * (self.addr_size + 1)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -411,7 +405,7 @@ class bank(design.design):
|
||||||
"""
|
"""
|
||||||
# Place the col decoder aligned left to row decoder
|
# Place the col decoder aligned left to row decoder
|
||||||
x_off = -(self.central_bus_width + self.row_decoder.width)
|
x_off = -(self.central_bus_width + self.row_decoder.width)
|
||||||
y_off = -(self.row_decoder.predecoder_height + self.col_decoder.height + self.addr_bus_height)
|
y_off = -(self.row_decoder.predecoder_height + self.col_decoder.height + 2*drc["well_to_well"])
|
||||||
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
|
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
|
||||||
mod=self.col_decoder,
|
mod=self.col_decoder,
|
||||||
offset=vector(x_off,y_off))
|
offset=vector(x_off,y_off))
|
||||||
|
|
@ -460,10 +454,8 @@ class bank(design.design):
|
||||||
offset=self.bank_select_pos)
|
offset=self.bank_select_pos)
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for i in range(self.num_control_lines):
|
temp.extend(self.input_control_signals)
|
||||||
temp.append(self.input_control_signals[i])
|
temp.extend(self.control_signals)
|
||||||
for i in range(self.num_control_lines):
|
|
||||||
temp.append(self.control_signals[i])
|
|
||||||
temp.extend(["vdd", "gnd"])
|
temp.extend(["vdd", "gnd"])
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
|
|
@ -475,7 +467,7 @@ class bank(design.design):
|
||||||
layer="metal3",
|
layer="metal3",
|
||||||
start=vector(self.left_gnd_x_offset,in_pos.y),
|
start=vector(self.left_gnd_x_offset,in_pos.y),
|
||||||
end=in_pos)
|
end=in_pos)
|
||||||
|
|
||||||
for gated_name in self.control_signals:
|
for gated_name in self.control_signals:
|
||||||
# Connect the inverter output to the central bus
|
# Connect the inverter output to the central bus
|
||||||
out_pos = self.bank_select_inst.get_pin(gated_name).rc()
|
out_pos = self.bank_select_inst.get_pin(gated_name).rc()
|
||||||
|
|
@ -505,9 +497,7 @@ class bank(design.design):
|
||||||
if self.col_addr_size > 0:
|
if self.col_addr_size > 0:
|
||||||
col_decoder_min_point = self.col_decoder_inst.by()
|
col_decoder_min_point = self.col_decoder_inst.by()
|
||||||
else:
|
else:
|
||||||
col_decoder_min_point = row_decoder_min_point - self.addr_bus_height
|
col_decoder_min_point = row_decoder_min_point
|
||||||
|
|
||||||
self.addr_min_point = row_decoder_min_point - self.addr_bus_height
|
|
||||||
|
|
||||||
if self.num_banks>1:
|
if self.num_banks>1:
|
||||||
# The control gating logic is below the decoder
|
# The control gating logic is below the decoder
|
||||||
|
|
@ -575,11 +565,11 @@ class bank(design.design):
|
||||||
# Make the xoffset map the center of the rail
|
# Make the xoffset map the center of the rail
|
||||||
self.bus_xoffset[name]=x_offset + 0.5*self.m2_width
|
self.bus_xoffset[name]=x_offset + 0.5*self.m2_width
|
||||||
# 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_layout_pin(text=name,
|
||||||
layer="metal2",
|
layer="metal2",
|
||||||
offset=vector(x_offset, self.addr_min_point),
|
offset=vector(x_offset, self.min_point),
|
||||||
width=self.m2_width,
|
width=self.m2_width,
|
||||||
height=-self.addr_min_point)
|
height=-self.min_point)
|
||||||
|
|
||||||
# Column mux lines if there is column mux
|
# Column mux lines if there is column mux
|
||||||
# goes from bottom of bitcell array down to the bottom of the column decoder/addresses
|
# goes from bottom of bitcell array down to the bottom of the column decoder/addresses
|
||||||
|
|
@ -775,11 +765,11 @@ class bank(design.design):
|
||||||
|
|
||||||
# The Address LSB
|
# The Address LSB
|
||||||
decode_in_pin = self.col_decoder_inst.get_pin("A")
|
decode_in_pin = self.col_decoder_inst.get_pin("A")
|
||||||
pin_pos = vector(self.left_gnd_x_offset, decode_in_pin.cy())
|
pin_pos = vector(decode_in_pin.cx(), self.min_point)
|
||||||
self.add_layout_pin_center_segment(text="A[0]",
|
self.add_layout_pin_center_segment(text="A[0]",
|
||||||
layer="metal1",
|
layer="metal2",
|
||||||
start=pin_pos,
|
start=pin_pos,
|
||||||
end=decode_in_pin.lc())
|
end=decode_in_pin.bc())
|
||||||
|
|
||||||
elif self.col_addr_size > 1:
|
elif self.col_addr_size > 1:
|
||||||
# Route the col decoder outputs to the col select bus
|
# Route the col decoder outputs to the col select bus
|
||||||
|
|
@ -794,14 +784,14 @@ class bank(design.design):
|
||||||
|
|
||||||
# Route from the col decoder up to the address bus
|
# Route from the col decoder up to the address bus
|
||||||
for i in range(self.col_addr_size):
|
for i in range(self.col_addr_size):
|
||||||
name = "A[{}]".format(i)
|
decoder_name = "in[{}]".format(i)
|
||||||
decode_in_pin = self.col_decoder_inst.get_pin("in[{}]".format(i))
|
addr_name = "A[{}]".format(i)
|
||||||
addr_pin = self.get_pin(name)
|
decode_in_pin = self.col_decoder_inst.get_pin(decoder_name)
|
||||||
addr_pos = vector(decode_in_pin.cx(), addr_pin.by() + 0.5*self.m1_width)
|
pin_pos = vector(decode_in_pin.cx(), self.min_point)
|
||||||
self.add_path("metal2", [addr_pos, decode_in_pin.uc()])
|
self.add_layout_pin_center_segment(text=addr_name,
|
||||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
layer="metal2",
|
||||||
offset=addr_pos,
|
start=pin_pos,
|
||||||
rotate=90)
|
end=decode_in_pin.bc())
|
||||||
|
|
||||||
|
|
||||||
# route the gnd rails, add contact to rail as well
|
# route the gnd rails, add contact to rail as well
|
||||||
|
|
@ -823,26 +813,6 @@ class bank(design.design):
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
|
|
||||||
def route_address(self):
|
|
||||||
""" Routing the row address lines from the address ms-flop array to the row-decoder """
|
|
||||||
|
|
||||||
|
|
||||||
for i in range(self.addr_size):
|
|
||||||
name = "A[{}]".format(i)
|
|
||||||
address_y_offset = self.addr_min_point + (i+1)*self.m1_pitch
|
|
||||||
pin_pos = vector(self.left_gnd_x_offset, address_y_offset)
|
|
||||||
if name in self.bus_xoffset:
|
|
||||||
rail_pos = vector(self.bus_xoffset[name],pin_pos.y)
|
|
||||||
else:
|
|
||||||
# Route to right of col decoder if it's not part of central bus
|
|
||||||
rail_pos = vector(self.col_decoder_inst.rx(),pin_pos.y)
|
|
||||||
self.add_layout_pin_center_segment(text=name,
|
|
||||||
layer="metal1",
|
|
||||||
start=pin_pos,
|
|
||||||
end=rail_pos)
|
|
||||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
||||||
offset=rail_pos,
|
|
||||||
rotate=90)
|
|
||||||
|
|
||||||
|
|
||||||
def add_lvs_correspondence_points(self):
|
def add_lvs_correspondence_points(self):
|
||||||
|
|
|
||||||
|
|
@ -21,18 +21,14 @@ class bank_select(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.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_bar", "w_en", "s_en"]
|
self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_buf_bar", "w_en", "s_en"]
|
||||||
# These will be outputs of the gaters if this is multibank
|
# These will be outputs of the gaters if this is multibank
|
||||||
self.control_signals = ["gated_"+str for str in self.input_control_signals]
|
self.control_signals = ["gated_"+str for str in self.input_control_signals]
|
||||||
|
|
||||||
for i in range(self.num_control_lines):
|
self.add_pin_list(self.input_control_signals, "INPUT")
|
||||||
input_name = self.input_control_signals[i]
|
self.add_pin_list(self.control_signals, "OUTPUT")
|
||||||
self.add_pin(input_name)
|
self.add_pin("vdd","POWER")
|
||||||
for i in range(self.num_control_lines):
|
self.add_pin("gnd","GROUND")
|
||||||
gated_name = self.control_signals[i]
|
|
||||||
self.add_pin(gated_name)
|
|
||||||
self.add_pin("vdd")
|
|
||||||
self.add_pin("gnd")
|
|
||||||
|
|
||||||
self.create_modules()
|
self.create_modules()
|
||||||
self.calculate_module_offsets()
|
self.calculate_module_offsets()
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class control_logic(design.design):
|
||||||
# Some cells may have pwell/nwell spacing problems too when the wells are different heights.
|
# Some cells may have pwell/nwell spacing problems too when the wells are different heights.
|
||||||
#self.cell_gap = max(self.m2_pitch,drc["pwell_to_nwell"])
|
#self.cell_gap = max(self.m2_pitch,drc["pwell_to_nwell"])
|
||||||
|
|
||||||
self.input_list =["web","csb","oeb"]
|
self.input_list =["csb","web","oeb"]
|
||||||
self.input_width = len(self.input_list)*self.m2_pitch
|
self.input_width = len(self.input_list)*self.m2_pitch
|
||||||
self.input_bar_list = ["clk_buf_bar", "we", "cs", "oe"]
|
self.input_bar_list = ["clk_buf_bar", "we", "cs", "oe"]
|
||||||
self.input_bar_width = len(self.input_bar_list)*self.m2_pitch
|
self.input_bar_width = len(self.input_bar_list)*self.m2_pitch
|
||||||
|
|
@ -602,7 +602,7 @@ class control_logic(design.design):
|
||||||
rows_end = self.width
|
rows_end = self.width
|
||||||
well_width = drc["minwidth_well"]
|
well_width = drc["minwidth_well"]
|
||||||
|
|
||||||
for i in range(7):
|
for i in range(8):
|
||||||
if i%2:
|
if i%2:
|
||||||
name = "vdd"
|
name = "vdd"
|
||||||
well_type = "nwell"
|
well_type = "nwell"
|
||||||
|
|
@ -648,6 +648,24 @@ class control_logic(design.design):
|
||||||
start=left,
|
start=left,
|
||||||
end=right)
|
end=right)
|
||||||
|
|
||||||
|
for vdd_pin in self.rbl_inst.get_pins("vdd"):
|
||||||
|
if vdd_pin.layer != "metal2":
|
||||||
|
continue
|
||||||
|
self.add_layout_pin(text="vdd",
|
||||||
|
layer="metal2",
|
||||||
|
offset=vdd_pin.ll(),
|
||||||
|
height=vdd_pin.height(),
|
||||||
|
width=vdd_pin.width())
|
||||||
|
|
||||||
|
for gnd_pin in self.rbl_inst.get_pins("gnd"):
|
||||||
|
if gnd_pin.layer != "metal2":
|
||||||
|
continue
|
||||||
|
self.add_layout_pin(text="gnd",
|
||||||
|
layer="metal2",
|
||||||
|
offset=gnd_pin.ll(),
|
||||||
|
height=gnd_pin.height(),
|
||||||
|
width=gnd_pin.width())
|
||||||
|
|
||||||
|
|
||||||
def add_lvs_correspondence_points(self):
|
def add_lvs_correspondence_points(self):
|
||||||
""" This adds some points for easier debugging if LVS goes wrong.
|
""" This adds some points for easier debugging if LVS goes wrong.
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,9 @@ class hierarchical_decoder(design.design):
|
||||||
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||||
|
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
||||||
|
self.offset_all_coordinates()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
|
|
@ -155,8 +158,8 @@ class hierarchical_decoder(design.design):
|
||||||
self.row_decoder_height = self.inv.height * self.rows
|
self.row_decoder_height = self.inv.height * self.rows
|
||||||
|
|
||||||
# Calculates height and width of hierarchical decoder
|
# Calculates height and width of hierarchical decoder
|
||||||
self.height = self.predecoder_height + self.row_decoder_height
|
self.height = self.row_decoder_height
|
||||||
self.width = self.predecoder_width + self.routing_width
|
self.width = self.predecoder_width + self.row_decoder_width
|
||||||
|
|
||||||
def create_pre_decoder(self):
|
def create_pre_decoder(self):
|
||||||
""" Creates pre-decoder and places labels input address [A] """
|
""" Creates pre-decoder and places labels input address [A] """
|
||||||
|
|
@ -171,12 +174,10 @@ class hierarchical_decoder(design.design):
|
||||||
""" Add a 2x4 predecoder """
|
""" Add a 2x4 predecoder """
|
||||||
|
|
||||||
if (self.num_inputs == 2):
|
if (self.num_inputs == 2):
|
||||||
base = vector(self.routing_width,0)
|
base = vector(-self.pre2_4.width,0)
|
||||||
mirror = "RO"
|
|
||||||
index_off1 = index_off2 = 0
|
index_off1 = index_off2 = 0
|
||||||
else:
|
else:
|
||||||
base= vector(self.routing_width+self.pre2_4.width, num * self.pre2_4.height)
|
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
||||||
mirror = "MY"
|
|
||||||
index_off1 = num * 2
|
index_off1 = num * 2
|
||||||
index_off2 = num * 4
|
index_off2 = num * 4
|
||||||
|
|
||||||
|
|
@ -189,8 +190,7 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num),
|
self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num),
|
||||||
mod=self.pre2_4,
|
mod=self.pre2_4,
|
||||||
offset=base,
|
offset=base))
|
||||||
mirror=mirror))
|
|
||||||
self.connect_inst(pins)
|
self.connect_inst(pins)
|
||||||
|
|
||||||
self.add_pre2x4_pins(num)
|
self.add_pre2x4_pins(num)
|
||||||
|
|
@ -215,12 +215,11 @@ class hierarchical_decoder(design.design):
|
||||||
def add_pre3x8(self,num):
|
def add_pre3x8(self,num):
|
||||||
""" Add 3x8 numbered predecoder """
|
""" Add 3x8 numbered predecoder """
|
||||||
if (self.num_inputs == 3):
|
if (self.num_inputs == 3):
|
||||||
offset = vector(self.routing_width,0)
|
offset = vector(-self.pre_3_8.width,0)
|
||||||
mirror ="R0"
|
mirror ="R0"
|
||||||
else:
|
else:
|
||||||
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
|
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
|
||||||
offset = vector(self.routing_width+self.pre3_8.width, height)
|
offset = vector(-self.pre3_8.width, height)
|
||||||
mirror="MY"
|
|
||||||
|
|
||||||
# If we had 2x4 predecodes, those are used as the lower
|
# If we had 2x4 predecodes, those are used as the lower
|
||||||
# decode output bits
|
# decode output bits
|
||||||
|
|
@ -236,8 +235,7 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num),
|
self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num),
|
||||||
mod=self.pre3_8,
|
mod=self.pre3_8,
|
||||||
offset=offset,
|
offset=offset))
|
||||||
mirror=mirror))
|
|
||||||
self.connect_inst(pins)
|
self.connect_inst(pins)
|
||||||
|
|
||||||
# The 3x8 predecoders will be stacked, so use yoffset
|
# The 3x8 predecoders will be stacked, so use yoffset
|
||||||
|
|
@ -305,11 +303,11 @@ class hierarchical_decoder(design.design):
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
name = "DEC_NAND[{0}]".format(row)
|
name = "DEC_NAND[{0}]".format(row)
|
||||||
if ((row % 2) == 0):
|
if ((row % 2) == 0):
|
||||||
y_off = self.predecoder_height + nand_mod.height*row
|
y_off = nand_mod.height*row
|
||||||
y_dir = 1
|
y_dir = 1
|
||||||
mirror = "R0"
|
mirror = "R0"
|
||||||
else:
|
else:
|
||||||
y_off = self.predecoder_height + nand_mod.height*(row + 1)
|
y_off = nand_mod.height*(row + 1)
|
||||||
y_dir = -1
|
y_dir = -1
|
||||||
mirror = "MX"
|
mirror = "MX"
|
||||||
|
|
||||||
|
|
@ -342,7 +340,7 @@ class hierarchical_decoder(design.design):
|
||||||
inv_row_height = self.inv.height * (row + 1)
|
inv_row_height = self.inv.height * (row + 1)
|
||||||
mirror = "MX"
|
mirror = "MX"
|
||||||
y_dir = -1
|
y_dir = -1
|
||||||
y_off = self.predecoder_height + inv_row_height
|
y_off = inv_row_height
|
||||||
offset = vector(x_off,y_off)
|
offset = vector(x_off,y_off)
|
||||||
|
|
||||||
self.inv_inst.append(self.add_inst(name=name,
|
self.inv_inst.append(self.add_inst(name=name,
|
||||||
|
|
@ -408,7 +406,7 @@ class hierarchical_decoder(design.design):
|
||||||
index = pre_num * 4 + i
|
index = pre_num * 4 + i
|
||||||
out_name = "out[{}]".format(i)
|
out_name = "out[{}]".format(i)
|
||||||
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
||||||
self.connect_rail(index, pin)
|
self.connect_rail_m3(index, pin)
|
||||||
|
|
||||||
|
|
||||||
for pre_num in range(self.no_of_pre3x8):
|
for pre_num in range(self.no_of_pre3x8):
|
||||||
|
|
@ -416,7 +414,7 @@ class hierarchical_decoder(design.design):
|
||||||
index = pre_num * 8 + i + self.no_of_pre2x4 * 4
|
index = pre_num * 8 + i + self.no_of_pre2x4 * 4
|
||||||
out_name = "out[{}]".format(i)
|
out_name = "out[{}]".format(i)
|
||||||
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
||||||
self.connect_rail(index, pin)
|
self.connect_rail_m3(index, pin)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -448,11 +446,11 @@ class hierarchical_decoder(design.design):
|
||||||
def route_vdd_gnd(self):
|
def route_vdd_gnd(self):
|
||||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||||
|
|
||||||
for num in range(0,self.total_number_of_predecoder_outputs + self.rows):
|
for num in range(0,self.rows):
|
||||||
# this will result in duplicate polygons for rails, but who cares
|
# this will result in duplicate polygons for rails, but who cares
|
||||||
|
|
||||||
# use the inverter offset even though it will be the nand's too
|
# use the inverter offset even though it will be the nand's too
|
||||||
(gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
|
(gate_offset, y_dir) = self.get_gate_offset(-self.predecoder_width, self.inv.height, num)
|
||||||
# route vdd
|
# route vdd
|
||||||
vdd_offset = gate_offset + self.inv.get_pin("vdd").ll().scale(1,y_dir)
|
vdd_offset = gate_offset + self.inv.get_pin("vdd").ll().scale(1,y_dir)
|
||||||
self.add_layout_pin(text="vdd",
|
self.add_layout_pin(text="vdd",
|
||||||
|
|
@ -478,6 +476,19 @@ class hierarchical_decoder(design.design):
|
||||||
offset=rail_pos,
|
offset=rail_pos,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
|
|
||||||
|
def connect_rail_m3(self, rail_index, pin):
|
||||||
|
""" Connect the routing rail to the given metal1 pin """
|
||||||
|
mid_point = vector(pin.cx(), pin.cy()-self.inv.height/2)
|
||||||
|
rail_pos = vector(self.rail_x_offsets[rail_index],mid_point.y)
|
||||||
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||||
|
offset=pin.center(),
|
||||||
|
rotate=90)
|
||||||
|
self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()])
|
||||||
|
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||||
|
offset=rail_pos,
|
||||||
|
rotate=90)
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, slew, load = 0.0):
|
def analytical_delay(self, slew, load = 0.0):
|
||||||
# A -> out
|
# A -> out
|
||||||
|
|
|
||||||
|
|
@ -49,9 +49,8 @@ class hierarchical_predecode(design.design):
|
||||||
debug.error("Invalid number of predecode inputs.",-1)
|
debug.error("Invalid number of predecode inputs.",-1)
|
||||||
|
|
||||||
def setup_constraints(self):
|
def setup_constraints(self):
|
||||||
# we are going to use horizontal vias, so use the via height
|
|
||||||
# use a conservative douple spacing just to get rid of annoying via DRCs
|
# use a conservative douple spacing just to get rid of annoying via DRCs
|
||||||
self.m2_pitch = contact.m1m2.height + 2*self.m2_space
|
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space)
|
||||||
|
|
||||||
# The rail offsets are indexed by the label
|
# The rail offsets are indexed by the label
|
||||||
self.rails = {}
|
self.rails = {}
|
||||||
|
|
@ -203,11 +202,12 @@ class hierarchical_predecode(design.design):
|
||||||
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
|
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
|
||||||
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
|
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
|
||||||
|
|
||||||
z_pos = self.inv_inst[num].get_pin("Z").rc()
|
z_pin = self.inv_inst[num].get_pin("Z")
|
||||||
self.add_layout_pin_center_segment(text="out[{}]".format(num),
|
self.add_layout_pin(text="out[{}]".format(num),
|
||||||
layer="metal1",
|
layer="metal1",
|
||||||
start=z_pos,
|
offset=z_pin.ll(),
|
||||||
end=z_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(),0))
|
height=z_pin.height(),
|
||||||
|
width=z_pin.width())
|
||||||
|
|
||||||
|
|
||||||
def route_input_inverters(self):
|
def route_input_inverters(self):
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,6 @@ class sram(design.design):
|
||||||
|
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
# For different layer width vias
|
|
||||||
self.m2m3_offset_fix = vector(0,0.5*(self.m3_width-self.m2_width))
|
|
||||||
|
|
||||||
# M1/M2 routing pitch is based on contacted pitch of the biggest layer
|
# M1/M2 routing pitch is based on contacted pitch of the biggest layer
|
||||||
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space,self.m2_space)
|
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space,self.m2_space)
|
||||||
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space)
|
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space)
|
||||||
|
|
@ -149,7 +146,7 @@ class sram(design.design):
|
||||||
self.control_logic_inputs=self.control_logic.get_inputs()
|
self.control_logic_inputs=self.control_logic.get_inputs()
|
||||||
self.control_logic_outputs=self.control_logic.get_outputs()
|
self.control_logic_outputs=self.control_logic.get_outputs()
|
||||||
|
|
||||||
self.add_pin_list(self.control_logic_inputs + ["clk"],"INPUT")
|
self.add_pin_list(self.control_logic_inputs,"INPUT")
|
||||||
self.add_pin("vdd","POWER")
|
self.add_pin("vdd","POWER")
|
||||||
self.add_pin("gnd","GROUND")
|
self.add_pin("gnd","GROUND")
|
||||||
|
|
||||||
|
|
@ -168,7 +165,7 @@ class sram(design.design):
|
||||||
self.route_four_banks()
|
self.route_four_banks()
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid number of banks.",-1)
|
debug.error("Invalid number of banks.",-1)
|
||||||
|
|
||||||
|
|
||||||
def add_four_bank_modules(self):
|
def add_four_bank_modules(self):
|
||||||
""" Adds the modules and the buses to the top level """
|
""" Adds the modules and the buses to the top level """
|
||||||
|
|
@ -388,7 +385,8 @@ class sram(design.design):
|
||||||
offset=self.bank_sel_bus_offset,
|
offset=self.bank_sel_bus_offset,
|
||||||
names=self.bank_sel_bus_names,
|
names=self.bank_sel_bus_names,
|
||||||
length=self.vertical_bus_height,
|
length=self.vertical_bus_height,
|
||||||
vertical=True))
|
vertical=True,
|
||||||
|
make_pins=True))
|
||||||
|
|
||||||
|
|
||||||
# Horizontal data bus
|
# Horizontal data bus
|
||||||
|
|
@ -797,7 +795,7 @@ class sram(design.design):
|
||||||
|
|
||||||
# Create the address and control flops (but not the clk)
|
# Create the address and control flops (but not the clk)
|
||||||
dff_size = self.addr_size + len(self.control_logic.get_inputs())-1
|
dff_size = self.addr_size + len(self.control_logic.get_inputs())-1
|
||||||
self.addr_ctrl_dff = self.mod_dff_array(rows=dff_size, columns=1)
|
self.addr_ctrl_dff = self.mod_dff_array(name="dff_array", rows=dff_size, columns=1)
|
||||||
self.add_mod(self.addr_ctrl_dff)
|
self.add_mod(self.addr_ctrl_dff)
|
||||||
|
|
||||||
# Create the bank module (up to four are instantiated)
|
# Create the bank module (up to four are instantiated)
|
||||||
|
|
@ -853,7 +851,7 @@ class sram(design.design):
|
||||||
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))
|
||||||
for i in range(self.bank_addr_size):
|
for i in range(self.bank_addr_size):
|
||||||
temp.append("ADDR[{0}]".format(i))
|
temp.append("A[{0}]".format(i))
|
||||||
if(self.num_banks > 1):
|
if(self.num_banks > 1):
|
||||||
temp.append("bank_sel[{0}]".format(bank_num))
|
temp.append("bank_sel[{0}]".format(bank_num))
|
||||||
temp.extend(["s_en", "w_en", "tri_en_bar", "tri_en",
|
temp.extend(["s_en", "w_en", "tri_en_bar", "tri_en",
|
||||||
|
|
@ -924,10 +922,17 @@ class sram(design.design):
|
||||||
|
|
||||||
def add_control_logic(self, position):
|
def add_control_logic(self, position):
|
||||||
""" Add and place control logic """
|
""" Add and place control logic """
|
||||||
|
inputs = []
|
||||||
|
for i in self.control_logic_inputs:
|
||||||
|
if i != "clk":
|
||||||
|
inputs.append(i+"_s")
|
||||||
|
else:
|
||||||
|
inputs.append(i)
|
||||||
|
|
||||||
self.control_logic_inst=self.add_inst(name="control",
|
self.control_logic_inst=self.add_inst(name="control",
|
||||||
mod=self.control_logic,
|
mod=self.control_logic,
|
||||||
offset=position)
|
offset=position)
|
||||||
self.connect_inst(self.control_logic_inputs + self.control_logic_outputs + ["vdd", "gnd"])
|
self.connect_inst(inputs + self.control_logic_outputs + ["vdd", "gnd"])
|
||||||
|
|
||||||
|
|
||||||
def add_lvs_correspondence_points(self):
|
def add_lvs_correspondence_points(self):
|
||||||
|
|
@ -986,10 +991,9 @@ class sram(design.design):
|
||||||
for (old,new) in zip(ctrl_flops,["CSb","WEb","OEb"]):
|
for (old,new) in zip(ctrl_flops,["CSb","WEb","OEb"]):
|
||||||
self.copy_layout_pin(self.addr_ctrl_dff_inst, old, new)
|
self.copy_layout_pin(self.addr_ctrl_dff_inst, old, new)
|
||||||
|
|
||||||
self.copy_layout_pin(self.control_logic_inst, "clk")
|
self.copy_layout_pin(self.addr_ctrl_dff_inst, "clk")
|
||||||
self.copy_layout_pin(self.bank_inst, "vdd")
|
|
||||||
self.copy_layout_pin(self.bank_inst, "gnd")
|
# Power ring contains the power pins
|
||||||
|
|
||||||
|
|
||||||
def add_two_banks(self):
|
def add_two_banks(self):
|
||||||
# Placement of bank 0 (left)
|
# Placement of bank 0 (left)
|
||||||
|
|
@ -1030,9 +1034,9 @@ class sram(design.design):
|
||||||
in_pos = src_pin.rc()
|
in_pos = src_pin.rc()
|
||||||
out_pos = vector(dest_pin.cx(), in_pos.y)
|
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_wire(("metal3","via2","metal2"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)])
|
||||||
self.add_via(layers=("metal2","via2","metal3"),
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||||
offset=src_pin.lr() - self.m2m3_offset_fix,
|
offset=src_pin.rc(),
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
def connect_rail_from_left_m2m1(self, src_pin, dest_pin):
|
def connect_rail_from_left_m2m1(self, src_pin, dest_pin):
|
||||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
||||||
|
|
@ -1042,34 +1046,41 @@ class sram(design.design):
|
||||||
|
|
||||||
def route_single_bank(self):
|
def route_single_bank(self):
|
||||||
""" Route a single bank SRAM """
|
""" Route a single bank SRAM """
|
||||||
|
|
||||||
|
# Route the outputs from the control logic module
|
||||||
for n in self.control_logic_outputs:
|
for n in self.control_logic_outputs:
|
||||||
src_pin = self.control_logic_inst.get_pin(n)
|
src_pin = self.control_logic_inst.get_pin(n)
|
||||||
dest_pin = self.bank_inst.get_pin(n)
|
dest_pin = self.bank_inst.get_pin(n)
|
||||||
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
|
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
|
||||||
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
offset=src_pin.rc(),
|
||||||
|
rotate=90)
|
||||||
|
|
||||||
|
|
||||||
# Expand the ring around the bank
|
# Expand the ring around the bank to include flops and control logic
|
||||||
bbox_lr = vector(self.control_logic_inst.lx(), self.bank_inst.by() + 2*self.supply_rail_pitch)
|
bbox_lr = vector(self.control_logic_inst.lx(), self.bank_inst.by() + 2*self.supply_rail_pitch)
|
||||||
bbox_ur = self.bank_inst.ur() - vector(2*self.supply_rail_pitch, 2*self.supply_rail_pitch)
|
bbox_ur = self.bank_inst.ur() - vector(2*self.supply_rail_pitch, 2*self.supply_rail_pitch)
|
||||||
self.add_power_ring([bbox_lr, bbox_ur])
|
self.add_power_ring([bbox_lr, bbox_ur])
|
||||||
self.route_single_bank_vdd()
|
self.route_single_bank_vdd()
|
||||||
self.route_single_bank_gnd()
|
self.route_single_bank_gnd()
|
||||||
|
|
||||||
|
# Connect the output of the flops to the bank pins
|
||||||
for i in range(self.addr_size):
|
for i in range(self.addr_size):
|
||||||
flop_name = "dout[{}]".format(i)
|
flop_name = "dout[{}]".format(i)
|
||||||
bank_name = "A[{}]".format(i)
|
bank_name = "A[{}]".format(i)
|
||||||
flop_pin = self.addr_ctrl_dff_inst.get_pin(flop_name)
|
flop_pin = self.addr_ctrl_dff_inst.get_pin(flop_name)
|
||||||
bank_pin = self.bank_inst.get_pin(bank_name)
|
bank_pin = self.bank_inst.get_pin(bank_name)
|
||||||
flop_pos = flop_pin.center()
|
flop_pos = flop_pin.center()
|
||||||
bank_pos = bank_pin.lc()
|
bank_pos = vector(bank_pin.cx(),flop_pos.y)
|
||||||
mid_x_pos = 0.5*(flop_pos.x + bank_pos.x)
|
self.add_path("metal3",[flop_pos, bank_pos])
|
||||||
mid_pos = vector(mid_x_pos - i*self.m2_pitch, flop_pos.y)
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||||
self.add_wire(("metal1","via1","metal2"),[flop_pos, mid_pos, bank_pos])
|
|
||||||
# There should be M1 in the flop already, but just in case
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=flop_pos,
|
offset=flop_pos,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||||
|
offset=bank_pos,
|
||||||
|
rotate=90)
|
||||||
|
|
||||||
|
# Connect the output of the flops to the control pins
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
flop_name = "dout[{}]".format(self.addr_size+i)
|
flop_name = "dout[{}]".format(self.addr_size+i)
|
||||||
ctrl_name = ["csb","web","oeb"][i]
|
ctrl_name = ["csb","web","oeb"][i]
|
||||||
|
|
@ -1082,7 +1093,20 @@ class sram(design.design):
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||||
offset=flop_pos,
|
offset=flop_pos,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
|
# Connect the clock between the flops and control module
|
||||||
|
# FIXME: Buffered clock should drive the flops, but then
|
||||||
|
# it would change the setup time...
|
||||||
|
flop_pin = self.addr_ctrl_dff_inst.get_pin("clk")
|
||||||
|
ctrl_pin = self.control_logic_inst.get_pin("clk")
|
||||||
|
flop_pos = flop_pin.uc()
|
||||||
|
ctrl_pos = ctrl_pin.bc()
|
||||||
|
mid_ypos = 0.5*(ctrl_pos.y+flop_pos.y)
|
||||||
|
mid1_pos = vector(flop_pos.x, mid_ypos)
|
||||||
|
mid2_pos = vector(ctrl_pos.x, mid_ypos)
|
||||||
|
self.add_wire(("metal1","via1","metal2"),[flop_pin.uc(), mid1_pos, mid2_pos, ctrl_pin.bc()])
|
||||||
|
|
||||||
|
|
||||||
def route_single_bank_vdd(self):
|
def route_single_bank_vdd(self):
|
||||||
""" Route vdd for the control and dff array """
|
""" Route vdd for the control and dff array """
|
||||||
|
|
||||||
|
|
@ -1100,6 +1124,17 @@ class sram(design.design):
|
||||||
size = (1,3),
|
size = (1,3),
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
|
# Route the vdd rails to the TOP
|
||||||
|
for vdd_pin in self.control_logic_inst.get_pins("vdd"):
|
||||||
|
if vdd_pin.layer != "metal2":
|
||||||
|
continue
|
||||||
|
vdd_pos = vdd_pin.uc()
|
||||||
|
top_rail_pos = vector(vdd_pos.x, self.top_vdd_y_center)
|
||||||
|
self.add_path("metal2", [top_rail_pos, vdd_pos])
|
||||||
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||||
|
offset=top_rail_pos,
|
||||||
|
size = (1,3))
|
||||||
|
|
||||||
|
|
||||||
def route_single_bank_gnd(self):
|
def route_single_bank_gnd(self):
|
||||||
""" Route gnd for the control and dff array """
|
""" Route gnd for the control and dff array """
|
||||||
|
|
@ -1118,6 +1153,16 @@ class sram(design.design):
|
||||||
size = (1,3),
|
size = (1,3),
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
|
# Route the vdd rails to the TOP
|
||||||
|
for gnd_pin in self.control_logic_inst.get_pins("gnd"):
|
||||||
|
if gnd_pin.layer != "metal2":
|
||||||
|
continue
|
||||||
|
gnd_pos = gnd_pin.uc()
|
||||||
|
top_rail_pos = vector(gnd_pos.x, self.top_gnd_y_center)
|
||||||
|
self.add_path("metal2", [top_rail_pos, gnd_pos])
|
||||||
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||||
|
offset=top_rail_pos,
|
||||||
|
size = (1,3))
|
||||||
|
|
||||||
|
|
||||||
def sp_write(self, sp_name):
|
def sp_write(self, sp_name):
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,7 @@ def write_netgen_script(cell_name, sp_name):
|
||||||
# This circuit has symmetries and needs to be flattened to resolve them or the banks won't pass
|
# This circuit has symmetries and needs to be flattened to resolve them or the banks won't pass
|
||||||
# Is there a more elegant way to add this when needed?
|
# Is there a more elegant way to add this when needed?
|
||||||
f.write("flatten class {{{0}.spice precharge_array}}\n".format(cell_name))
|
f.write("flatten class {{{0}.spice precharge_array}}\n".format(cell_name))
|
||||||
|
f.write("flatten class {{{0}.spice dff_array}}\n".format(cell_name))
|
||||||
f.write("property {{nfet {0}.spice}} remove as ad ps pd\n".format(cell_name))
|
f.write("property {{nfet {0}.spice}} remove as ad ps pd\n".format(cell_name))
|
||||||
f.write("property {{pfet {0}.spice}} remove as ad ps pd\n".format(cell_name))
|
f.write("property {{pfet {0}.spice}} remove as ad ps pd\n".format(cell_name))
|
||||||
f.write("property {{n {0}}} remove as ad ps pd\n".format(sp_name))
|
f.write("property {{n {0}}} remove as ad ps pd\n".format(sp_name))
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,10 @@ drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/freepdk45/layers.map"
|
||||||
drc["minwidth_tx"]=0.09
|
drc["minwidth_tx"]=0.09
|
||||||
drc["minlength_channel"] = 0.05
|
drc["minlength_channel"] = 0.05
|
||||||
|
|
||||||
# WELL.1 Minimum spacing of nwell/pwell at different potential
|
# WELL.2 Minimum spacing of nwell/pwell at different potential
|
||||||
drc["pwell_to_nwell"] = 0.225
|
drc["pwell_to_nwell"] = 0.225
|
||||||
|
# WELL.3 Minimum spacing of nwell/pwell at the same potential
|
||||||
|
drc["well_to_well"] = 0.135
|
||||||
# WELL.4 Minimum width of nwell/pwell
|
# WELL.4 Minimum width of nwell/pwell
|
||||||
drc["minwidth_well"] = 0.2
|
drc["minwidth_well"] = 0.2
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,10 +70,13 @@ drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/scn3me_subm/layers.map"
|
||||||
drc["minwidth_tx"] = 1.2
|
drc["minwidth_tx"] = 1.2
|
||||||
drc["minlength_channel"] = 0.6
|
drc["minlength_channel"] = 0.6
|
||||||
|
|
||||||
|
# 1.3 Minimum spacing between wells of same type (if both are drawn)
|
||||||
|
drc["well_to_well"] = 1.8
|
||||||
# 1.4 Minimum spacing between wells of different type (if both are drawn)
|
# 1.4 Minimum spacing between wells of different type (if both are drawn)
|
||||||
drc["pwell_to_nwell"] = 0
|
drc["pwell_to_nwell"] = 0
|
||||||
# 1.1 Minimum width
|
# 1.1 Minimum width
|
||||||
drc["minwidth_well"] = 3.6
|
drc["minwidth_well"] = 3.6
|
||||||
|
|
||||||
# 3.1 Minimum width
|
# 3.1 Minimum width
|
||||||
drc["minwidth_poly"] = 0.6
|
drc["minwidth_poly"] = 0.6
|
||||||
# 3.2 Minimum spacing over active
|
# 3.2 Minimum spacing over active
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue