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 """
|
||||
try:
|
||||
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.
|
||||
# Otherwise, should use get_pins()
|
||||
return self.pin_map[text][0]
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ class bank(design.design):
|
|||
self.route_tri_gate_out()
|
||||
self.route_wordline_driver()
|
||||
self.route_row_decoder()
|
||||
self.route_address()
|
||||
self.route_column_address_lines()
|
||||
self.route_control_lines()
|
||||
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)
|
||||
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
|
||||
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",
|
||||
mod=self.col_decoder,
|
||||
offset=vector(x_off,y_off))
|
||||
|
|
@ -460,10 +454,8 @@ class bank(design.design):
|
|||
offset=self.bank_select_pos)
|
||||
|
||||
temp = []
|
||||
for i in range(self.num_control_lines):
|
||||
temp.append(self.input_control_signals[i])
|
||||
for i in range(self.num_control_lines):
|
||||
temp.append(self.control_signals[i])
|
||||
temp.extend(self.input_control_signals)
|
||||
temp.extend(self.control_signals)
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
|
@ -505,9 +497,7 @@ class bank(design.design):
|
|||
if self.col_addr_size > 0:
|
||||
col_decoder_min_point = self.col_decoder_inst.by()
|
||||
else:
|
||||
col_decoder_min_point = row_decoder_min_point - self.addr_bus_height
|
||||
|
||||
self.addr_min_point = row_decoder_min_point - self.addr_bus_height
|
||||
col_decoder_min_point = row_decoder_min_point
|
||||
|
||||
if self.num_banks>1:
|
||||
# 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
|
||||
self.bus_xoffset[name]=x_offset + 0.5*self.m2_width
|
||||
# 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",
|
||||
offset=vector(x_offset, self.addr_min_point),
|
||||
offset=vector(x_offset, self.min_point),
|
||||
width=self.m2_width,
|
||||
height=-self.addr_min_point)
|
||||
height=-self.min_point)
|
||||
|
||||
# Column mux lines if there is column mux
|
||||
# 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
|
||||
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]",
|
||||
layer="metal1",
|
||||
layer="metal2",
|
||||
start=pin_pos,
|
||||
end=decode_in_pin.lc())
|
||||
end=decode_in_pin.bc())
|
||||
|
||||
elif self.col_addr_size > 1:
|
||||
# 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
|
||||
for i in range(self.col_addr_size):
|
||||
name = "A[{}]".format(i)
|
||||
decode_in_pin = self.col_decoder_inst.get_pin("in[{}]".format(i))
|
||||
addr_pin = self.get_pin(name)
|
||||
addr_pos = vector(decode_in_pin.cx(), addr_pin.by() + 0.5*self.m1_width)
|
||||
self.add_path("metal2", [addr_pos, decode_in_pin.uc()])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=addr_pos,
|
||||
rotate=90)
|
||||
decoder_name = "in[{}]".format(i)
|
||||
addr_name = "A[{}]".format(i)
|
||||
decode_in_pin = self.col_decoder_inst.get_pin(decoder_name)
|
||||
pin_pos = vector(decode_in_pin.cx(), self.min_point)
|
||||
self.add_layout_pin_center_segment(text=addr_name,
|
||||
layer="metal2",
|
||||
start=pin_pos,
|
||||
end=decode_in_pin.bc())
|
||||
|
||||
|
||||
# route the gnd rails, add contact to rail as well
|
||||
|
|
@ -823,26 +813,6 @@ class bank(design.design):
|
|||
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):
|
||||
|
|
|
|||
|
|
@ -21,18 +21,14 @@ class bank_select(design.design):
|
|||
# Number of control lines in the bus
|
||||
self.num_control_lines = 6
|
||||
# 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
|
||||
self.control_signals = ["gated_"+str for str in self.input_control_signals]
|
||||
|
||||
for i in range(self.num_control_lines):
|
||||
input_name = self.input_control_signals[i]
|
||||
self.add_pin(input_name)
|
||||
for i in range(self.num_control_lines):
|
||||
gated_name = self.control_signals[i]
|
||||
self.add_pin(gated_name)
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
self.add_pin_list(self.input_control_signals, "INPUT")
|
||||
self.add_pin_list(self.control_signals, "OUTPUT")
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
self.create_modules()
|
||||
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.
|
||||
#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_bar_list = ["clk_buf_bar", "we", "cs", "oe"]
|
||||
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
|
||||
well_width = drc["minwidth_well"]
|
||||
|
||||
for i in range(7):
|
||||
for i in range(8):
|
||||
if i%2:
|
||||
name = "vdd"
|
||||
well_type = "nwell"
|
||||
|
|
@ -648,6 +648,24 @@ class control_logic(design.design):
|
|||
start=left,
|
||||
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):
|
||||
""" 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.create_layout()
|
||||
|
||||
self.offset_all_coordinates()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_layout(self):
|
||||
|
|
@ -155,8 +158,8 @@ class hierarchical_decoder(design.design):
|
|||
self.row_decoder_height = self.inv.height * self.rows
|
||||
|
||||
# Calculates height and width of hierarchical decoder
|
||||
self.height = self.predecoder_height + self.row_decoder_height
|
||||
self.width = self.predecoder_width + self.routing_width
|
||||
self.height = self.row_decoder_height
|
||||
self.width = self.predecoder_width + self.row_decoder_width
|
||||
|
||||
def create_pre_decoder(self):
|
||||
""" Creates pre-decoder and places labels input address [A] """
|
||||
|
|
@ -171,12 +174,10 @@ class hierarchical_decoder(design.design):
|
|||
""" Add a 2x4 predecoder """
|
||||
|
||||
if (self.num_inputs == 2):
|
||||
base = vector(self.routing_width,0)
|
||||
mirror = "RO"
|
||||
base = vector(-self.pre2_4.width,0)
|
||||
index_off1 = index_off2 = 0
|
||||
else:
|
||||
base= vector(self.routing_width+self.pre2_4.width, num * self.pre2_4.height)
|
||||
mirror = "MY"
|
||||
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
||||
index_off1 = num * 2
|
||||
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),
|
||||
mod=self.pre2_4,
|
||||
offset=base,
|
||||
mirror=mirror))
|
||||
offset=base))
|
||||
self.connect_inst(pins)
|
||||
|
||||
self.add_pre2x4_pins(num)
|
||||
|
|
@ -215,12 +215,11 @@ class hierarchical_decoder(design.design):
|
|||
def add_pre3x8(self,num):
|
||||
""" Add 3x8 numbered predecoder """
|
||||
if (self.num_inputs == 3):
|
||||
offset = vector(self.routing_width,0)
|
||||
offset = vector(-self.pre_3_8.width,0)
|
||||
mirror ="R0"
|
||||
else:
|
||||
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
|
||||
offset = vector(self.routing_width+self.pre3_8.width, height)
|
||||
mirror="MY"
|
||||
offset = vector(-self.pre3_8.width, height)
|
||||
|
||||
# If we had 2x4 predecodes, those are used as the lower
|
||||
# decode output bits
|
||||
|
|
@ -236,8 +235,7 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num),
|
||||
mod=self.pre3_8,
|
||||
offset=offset,
|
||||
mirror=mirror))
|
||||
offset=offset))
|
||||
self.connect_inst(pins)
|
||||
|
||||
# The 3x8 predecoders will be stacked, so use yoffset
|
||||
|
|
@ -305,11 +303,11 @@ class hierarchical_decoder(design.design):
|
|||
for row in range(self.rows):
|
||||
name = "DEC_NAND[{0}]".format(row)
|
||||
if ((row % 2) == 0):
|
||||
y_off = self.predecoder_height + nand_mod.height*row
|
||||
y_off = nand_mod.height*row
|
||||
y_dir = 1
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off = self.predecoder_height + nand_mod.height*(row + 1)
|
||||
y_off = nand_mod.height*(row + 1)
|
||||
y_dir = -1
|
||||
mirror = "MX"
|
||||
|
||||
|
|
@ -342,7 +340,7 @@ class hierarchical_decoder(design.design):
|
|||
inv_row_height = self.inv.height * (row + 1)
|
||||
mirror = "MX"
|
||||
y_dir = -1
|
||||
y_off = self.predecoder_height + inv_row_height
|
||||
y_off = inv_row_height
|
||||
offset = vector(x_off,y_off)
|
||||
|
||||
self.inv_inst.append(self.add_inst(name=name,
|
||||
|
|
@ -408,7 +406,7 @@ class hierarchical_decoder(design.design):
|
|||
index = pre_num * 4 + i
|
||||
out_name = "out[{}]".format(i)
|
||||
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):
|
||||
|
|
@ -416,7 +414,7 @@ class hierarchical_decoder(design.design):
|
|||
index = pre_num * 8 + i + self.no_of_pre2x4 * 4
|
||||
out_name = "out[{}]".format(i)
|
||||
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):
|
||||
""" 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
|
||||
|
||||
# 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
|
||||
vdd_offset = gate_offset + self.inv.get_pin("vdd").ll().scale(1,y_dir)
|
||||
self.add_layout_pin(text="vdd",
|
||||
|
|
@ -479,6 +477,19 @@ class hierarchical_decoder(design.design):
|
|||
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):
|
||||
# A -> out
|
||||
if self.determine_predecodes(self.num_inputs)[1]==0:
|
||||
|
|
|
|||
|
|
@ -49,9 +49,8 @@ class hierarchical_predecode(design.design):
|
|||
debug.error("Invalid number of predecode inputs.",-1)
|
||||
|
||||
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
|
||||
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
|
||||
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)
|
||||
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
|
||||
|
||||
z_pos = self.inv_inst[num].get_pin("Z").rc()
|
||||
self.add_layout_pin_center_segment(text="out[{}]".format(num),
|
||||
z_pin = self.inv_inst[num].get_pin("Z")
|
||||
self.add_layout_pin(text="out[{}]".format(num),
|
||||
layer="metal1",
|
||||
start=z_pos,
|
||||
end=z_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(),0))
|
||||
offset=z_pin.ll(),
|
||||
height=z_pin.height(),
|
||||
width=z_pin.width())
|
||||
|
||||
|
||||
def route_input_inverters(self):
|
||||
|
|
|
|||
|
|
@ -48,9 +48,6 @@ class sram(design.design):
|
|||
|
||||
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
|
||||
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)
|
||||
|
|
@ -149,7 +146,7 @@ class sram(design.design):
|
|||
self.control_logic_inputs=self.control_logic.get_inputs()
|
||||
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("gnd","GROUND")
|
||||
|
||||
|
|
@ -388,7 +385,8 @@ class sram(design.design):
|
|||
offset=self.bank_sel_bus_offset,
|
||||
names=self.bank_sel_bus_names,
|
||||
length=self.vertical_bus_height,
|
||||
vertical=True))
|
||||
vertical=True,
|
||||
make_pins=True))
|
||||
|
||||
|
||||
# Horizontal data bus
|
||||
|
|
@ -797,7 +795,7 @@ class sram(design.design):
|
|||
|
||||
# Create the address and control flops (but not the clk)
|
||||
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)
|
||||
|
||||
# Create the bank module (up to four are instantiated)
|
||||
|
|
@ -853,7 +851,7 @@ class sram(design.design):
|
|||
for i in range(self.word_size):
|
||||
temp.append("DATA[{0}]".format(i))
|
||||
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):
|
||||
temp.append("bank_sel[{0}]".format(bank_num))
|
||||
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):
|
||||
""" 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",
|
||||
mod=self.control_logic,
|
||||
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):
|
||||
|
|
@ -986,10 +991,9 @@ class sram(design.design):
|
|||
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.control_logic_inst, "clk")
|
||||
self.copy_layout_pin(self.bank_inst, "vdd")
|
||||
self.copy_layout_pin(self.bank_inst, "gnd")
|
||||
self.copy_layout_pin(self.addr_ctrl_dff_inst, "clk")
|
||||
|
||||
# Power ring contains the power pins
|
||||
|
||||
def add_two_banks(self):
|
||||
# Placement of bank 0 (left)
|
||||
|
|
@ -1030,8 +1034,8 @@ class sram(design.design):
|
|||
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() - self.m2m3_offset_fix,
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=src_pin.rc(),
|
||||
rotate=90)
|
||||
|
||||
def connect_rail_from_left_m2m1(self, src_pin, dest_pin):
|
||||
|
|
@ -1042,34 +1046,41 @@ class sram(design.design):
|
|||
|
||||
def route_single_bank(self):
|
||||
""" Route a single bank SRAM """
|
||||
|
||||
# Route the outputs from the control logic module
|
||||
for n in self.control_logic_outputs:
|
||||
src_pin = self.control_logic_inst.get_pin(n)
|
||||
dest_pin = self.bank_inst.get_pin(n)
|
||||
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_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.route_single_bank_vdd()
|
||||
self.route_single_bank_gnd()
|
||||
|
||||
# Connect the output of the flops to the bank pins
|
||||
for i in range(self.addr_size):
|
||||
flop_name = "dout[{}]".format(i)
|
||||
bank_name = "A[{}]".format(i)
|
||||
flop_pin = self.addr_ctrl_dff_inst.get_pin(flop_name)
|
||||
bank_pin = self.bank_inst.get_pin(bank_name)
|
||||
flop_pos = flop_pin.center()
|
||||
bank_pos = bank_pin.lc()
|
||||
mid_x_pos = 0.5*(flop_pos.x + bank_pos.x)
|
||||
mid_pos = vector(mid_x_pos - i*self.m2_pitch, flop_pos.y)
|
||||
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"),
|
||||
bank_pos = vector(bank_pin.cx(),flop_pos.y)
|
||||
self.add_path("metal3",[flop_pos, bank_pos])
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=flop_pos,
|
||||
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):
|
||||
flop_name = "dout[{}]".format(self.addr_size+i)
|
||||
ctrl_name = ["csb","web","oeb"][i]
|
||||
|
|
@ -1083,6 +1094,19 @@ class sram(design.design):
|
|||
offset=flop_pos,
|
||||
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):
|
||||
""" Route vdd for the control and dff array """
|
||||
|
||||
|
|
@ -1100,6 +1124,17 @@ class sram(design.design):
|
|||
size = (1,3),
|
||||
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):
|
||||
""" Route gnd for the control and dff array """
|
||||
|
|
@ -1118,6 +1153,16 @@ class sram(design.design):
|
|||
size = (1,3),
|
||||
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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
# 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 dff_array}}\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 {{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["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
|
||||
# WELL.3 Minimum spacing of nwell/pwell at the same potential
|
||||
drc["well_to_well"] = 0.135
|
||||
# WELL.4 Minimum width of nwell/pwell
|
||||
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["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)
|
||||
drc["pwell_to_nwell"] = 0
|
||||
# 1.1 Minimum width
|
||||
drc["minwidth_well"] = 3.6
|
||||
|
||||
# 3.1 Minimum width
|
||||
drc["minwidth_poly"] = 0.6
|
||||
# 3.2 Minimum spacing over active
|
||||
|
|
|
|||
Loading…
Reference in New Issue