Merge branch 'dev' of https://github.com/VLSIDA/PrivateRAM into multiport

This commit is contained in:
Michael Timothy Grimes 2018-07-26 09:04:59 -07:00
commit fb0de710ec
36 changed files with 1614 additions and 1266 deletions

View File

@ -39,6 +39,7 @@ class layout(lef.lef):
shift the origin in the lowest left corner """
offset = self.find_lowest_coords()
self.translate_all(offset)
return offset
def get_gate_offset(self, x_offset, height, inv_num):
"""Gets the base offset and y orientation of stacked rows of gates
@ -110,7 +111,7 @@ class layout(lef.lef):
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))
inst.compute_boundary(inst.offset)
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]
@ -526,25 +527,27 @@ class layout(lef.lef):
def create_horizontal_pin_bus(self, layer, pitch, offset, names, length):
""" Create a horizontal bus of pins. """
self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=True)
return self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=True)
def create_vertical_pin_bus(self, layer, pitch, offset, names, length):
""" Create a horizontal bus of pins. """
self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=True)
return self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=True)
def create_vertical_bus(self, layer, pitch, offset, names, length):
""" Create a horizontal bus. """
self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=False)
return self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=False)
def create_horiontal_bus(self, layer, pitch, offset, names, length):
def create_horizontal_bus(self, layer, pitch, offset, names, length):
""" Create a horizontal bus. """
self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=False)
return self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=False)
def create_bus(self, layer, pitch, offset, names, length, vertical, make_pins):
"""
Create a horizontal or vertical bus. It can be either just rectangles, or actual
layout pins. It returns an map of line center line positions indexed by name.
The other coordinate is a 0 since the bus provides a range.
TODO: combine with channel router.
"""
# half minwidth so we can return the center line offsets
@ -563,7 +566,8 @@ class layout(lef.lef):
self.add_rect(layer=layer,
offset=line_offset,
height=length)
line_positions[names[i]]=line_offset+vector(half_minwidth,0)
# Make this the center of the rail
line_positions[names[i]]=line_offset+vector(half_minwidth,0.5*length)
else:
for i in range(len(names)):
line_offset = offset + vector(0,i*pitch + half_minwidth)
@ -576,10 +580,241 @@ class layout(lef.lef):
self.add_rect(layer=layer,
offset=line_offset,
width=length)
line_positions[names[i]]=line_offset+vector(0,half_minwidth)
# Make this the center of the rail
line_positions[names[i]]=line_offset+vector(0.5*length,half_minwidth)
return line_positions
def connect_horizontal_bus(self, mapping, inst, bus_offsets,
layer_stack=("metal1","via1","metal2")):
""" Horizontal version of connect_bus. """
self.connect_bus(mapping, inst, bus_offsets, layer_stack, True)
def connect_vertical_bus(self, mapping, inst, bus_offsets,
layer_stack=("metal1","via1","metal2")):
""" Vertical version of connect_bus. """
self.connect_bus(mapping, inst, bus_offsets, layer_stack, False)
def connect_bus(self, mapping, inst, bus_offsets, layer_stack, horizontal):
"""
Connect a mapping of pin -> name for a bus. This could be
replaced with a channel router in the future.
"""
(horizontal_layer, via_layer, vertical_layer)=layer_stack
if horizontal:
route_layer = vertical_layer
else:
route_layer = horizontal_layer
for (pin_name, bus_name) in mapping:
pin = inst.get_pin(pin_name)
pin_pos = pin.center()
bus_pos = bus_offsets[bus_name]
if horizontal:
# up/down then left/right
mid_pos = vector(pin_pos.x, bus_pos.y)
else:
# left/right then up/down
mid_pos = vector(bus_pos.x, pin_pos.y)
self.add_wire(layer_stack,[bus_pos, mid_pos, pin_pos])
# Connect to the pin on the instances with a via if it is
# not on the right layer
if pin.layer != route_layer:
self.add_via_center(layers=layer_stack,
offset=pin_pos)
# FIXME: output pins tend to not be rotate, but supply pins are. Make consistent?
# We only need a via if they happened to align perfectly
# so the add_wire didn't add a via
if (horizontal and bus_pos.y == pin_pos.y) or (not horizontal and bus_pos.x == pin_pos.x):
self.add_via_center(layers=layer_stack,
offset=bus_pos,
rotate=90)
def add_horizontal_trunk_route(self, pins, trunk_offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Create a trunk route for all pins with the the trunk located at the given y offset.
"""
if not pitch:
pitch = self.m1_pitch
max_x = max([pin.center().x for pin in pins])
min_x = min([pin.center().x for pin in pins])
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[0])]
# if we are less than a pitch, just create a non-preferred layer jog
if max_x-min_x < pitch:
# Add the horizontal trunk on the vertical layer!
self.add_path(layer_stack[2],[vector(min_x-half_minwidth,trunk_offset.y), vector(max_x+half_minwidth,trunk_offset.y)])
# Route each pin to the trunk
for pin in pins:
# No bend needed here
mid = vector(pin.center().x, trunk_offset.y)
self.add_path(layer_stack[2], [pin.center(), mid])
else:
# Add the horizontal trunk
self.add_path(layer_stack[0],[vector(min_x,trunk_offset.y), vector(max_x,trunk_offset.y)])
trunk_mid = vector(0.5*(max_x+min_x),trunk_offset.y)
# Route each pin to the trunk
for pin in pins:
# Bend to the center of the trunk so it adds a via automatically
mid = vector(pin.center().x, trunk_offset.y)
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid])
def add_vertical_trunk_route(self, pins, trunk_offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Create a trunk route for all pins with the the trunk located at the given x offset.
"""
if not pitch:
pitch = self.m2_pitch
max_y = max([pin.center().y for pin in pins])
min_y = min([pin.center().y for pin in pins])
# Add the vertical trunk
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[2])]
# if we are less than a pitch, just create a non-preferred layer jog
if max_y-min_y < pitch:
# Add the horizontal trunk on the vertical layer!
self.add_path(layer_stack[0],[vector(trunk_offset.x,min_y-half_minwidth), vector(trunk_offset.x,max_y+half_minwidth)])
# Route each pin to the trunk
for pin in pins:
# No bend needed here
mid = vector(trunk_offset.x, pin.center().y)
self.add_path(layer_stack[0], [pin.center(), mid])
else:
# Add the vertical trunk
self.add_path(layer_stack[2],[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)])
trunk_mid = vector(trunk_offset.x,0.5*(max_y+min_y),)
# Route each pin to the trunk
for pin in pins:
# Bend to the center of the trunk so it adds a via automatically
mid = vector(trunk_offset.x, pin.center().y)
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid])
def create_channel_route(self, route_map, top_pins, bottom_pins, offset,
layer_stack=("metal1", "via1", "metal2"), pitch=None,
vertical=False):
"""
This is a simple channel route for one-to-one connections that
will jog the top route whenever there is a conflict. It does NOT
try to minimize the number of tracks -- instead, it picks an order to avoid the vertical
conflicts between pins.
"""
def remove_pin_from_graph(pin, g):
# Remove the pin from the keys
g.pop(pin,None)
# Remove the pin from all conflicts
# This is O(n^2), so maybe optimize it.
for other_pin,conflicts in g.items():
if pin in conflicts:
conflicts.remove(pin)
vcg[other_pin]=conflicts
if not pitch:
pitch = self.m2_pitch
# merge the two dictionaries to easily access all pins
all_pins = {**top_pins, **bottom_pins}
# FIXME: Must extend this to a horizontal conflict graph too if we want to minimize the
# number of tracks!
#hcg = {}
# Initialize the vertical conflict graph (vcg) and make a list of all pins
vcg = {}
for (top_name, bot_name) in route_map:
vcg[top_name] = []
vcg[bot_name] = []
# Find the vertical pin conflicts
# FIXME: O(n^2) but who cares for now
for top_name,top_pin in top_pins.items():
for bot_name,bot_pin in bottom_pins.items():
if not vertical and abs(top_pin.center().x-bot_pin.center().x) < pitch:
# The edges only go from top to bottom
# since we will order tracks bottom up
vcg[top_name].append(bot_name)
elif vertical and abs(top_pin.center().y-bot_pin.center().y) < pitch:
# The edges only go from top to bottom
# since we will order tracks bottom up
vcg[top_name].append(bot_name)
# This is the starting offset of the first trunk
if vertical:
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[2])]
offset = offset + vector(half_minwidth,0)
else:
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[0])]
offset = offset + vector(0,half_minwidth)
# list of routes to do
while vcg:
#print(vcg)
# get a route from conflict graph with empty fanout set
route_pin=None
for route_pin,conflicts in vcg.items():
if len(conflicts)==0:
remove_pin_from_graph(route_pin,vcg)
break
# Get the connected pins from the routing map
for pin_connections in route_map:
if route_pin in pin_connections:
break
#print("Routing:",route_pin,pin_connections)
# Remove the other pins from the conflict graph too
for other_pin in pin_connections:
remove_pin_from_graph(other_pin, vcg)
# Create a list of the pins rather than a list of the names
pin_list = [all_pins[pin_name] for pin_name in pin_connections]
# Add the trunk route and move up to next track
if vertical:
self.add_vertical_trunk_route(pin_list, offset, layer_stack, pitch)
offset += vector(pitch,0)
else:
self.add_horizontal_trunk_route(pin_list, offset, layer_stack, pitch)
offset += vector(0,pitch)
def create_vertical_channel_route(self, route_map, left_inst, right_inst, offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Wrapper to create a vertical channel route
"""
self.create_channel_route(route_map, left_inst, right_inst, offset,
layer_stack, pitch, vertical=True)
def create_horizontal_channel_route(self, route_map, top_pins, bottom_pins, offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Wrapper to create a horizontal channel route
"""
self.create_channel_route(route_map, top_pins, bottom_pins, offset,
layer_stack, pitch, vertical=False)
def add_enclosure(self, insts, layer="nwell"):
""" Add a layer that surrounds the given instances. Useful
for creating wells, for example. Doesn't check for minimum widths or
@ -600,6 +835,20 @@ class layout(lef.lef):
width=xmax-xmin,
height=ymax-ymin)
def add_power_pin(self, name, loc, rotate=True):
"""
Add a single power pin from M3 own to M1
"""
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=loc,
rotate=90 if rotate else 0)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=loc,
rotate=90 if rotate else 0)
self.add_layout_pin_rect_center(text=name,
layer="metal3",
offset=loc)
def add_power_ring(self, bbox):
"""
Create vdd and gnd power rings around an area of the bounding box argument. Must

View File

@ -78,11 +78,7 @@ class delay():
self.sf.write("\n* SRAM output loads\n")
for i in range(self.word_size):
self.sf.write("CD{0} d[{0}] 0 {1}f\n".format(i,self.load))
# add access transistors for data-bus
self.sf.write("\n* Transmission Gates for data-bus and control signals\n")
self.stim.inst_accesstx(dbits=self.word_size)
self.sf.write("CD{0} DOUT[{0}] 0 {1}f\n".format(i,self.load))
def write_delay_stimulus(self):
@ -112,11 +108,11 @@ class delay():
for i in range(self.word_size):
if i == self.probe_data:
self.gen_data(clk_times=self.cycle_times,
sig_name="data[{0}]".format(i))
sig_name="DIN[{0}]".format(i))
else:
self.stim.gen_constant(sig_name="d[{0}]".format(i),
self.stim.gen_constant(sig_name="DIN[{0}]".format(i),
v_val=0)
self.gen_addr(clk_times=self.cycle_times,
@ -172,7 +168,7 @@ class delay():
# generate data and addr signals
self.sf.write("\n* Generation of data and address signals\n")
for i in range(self.word_size):
self.stim.gen_constant(sig_name="d[{0}]".format(i),
self.stim.gen_constant(sig_name="DIN[{0}]".format(i),
v_val=0)
for i in range(self.addr_size):
self.stim.gen_constant(sig_name="A[{0}]".format(i),
@ -208,7 +204,7 @@ class delay():
# Trigger on the clk of the appropriate cycle
trig_name = "clk"
targ_name = "{0}".format("d[{0}]".format(self.probe_data))
targ_name = "{0}".format("DOUT[{0}]".format(self.probe_data))
trig_val = targ_val = 0.5 * self.vdd_voltage
# Delay the target to measure after the negative edge
@ -338,6 +334,7 @@ class delay():
# Checking from not data_value to data_value
self.write_delay_stimulus()
self.stim.run_sim()
delay_hl = parse_spice_list("timing", "delay_hl")
delay_lh = parse_spice_list("timing", "delay_lh")
@ -783,12 +780,6 @@ class delay():
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
self.stim.gen_pwl("web", clk_times, values, self.period, self.slew, 0.05)
# Keep acc_en deasserted in NOP for measuring >1 period
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
self.stim.gen_pwl("acc_en", clk_times, values, self.period, self.slew, 0)
values = [0, 1, 1, 1, 0, 0, 1, 1, 0, 0]
self.stim.gen_pwl("acc_en_inv", clk_times, values, self.period, self.slew, 0)
def gen_oeb(self, clk_times):
""" Generates the PWL WEb signal """
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP

View File

@ -440,9 +440,7 @@ class lib:
def compute_delay(self):
""" Do the analysis if we haven't characterized the SRAM yet """
try:
self.d
except AttributeError:
if not hasattr(self,"d"):
self.d = delay(self.sram, self.sp_file, self.corner)
if self.use_model:
self.char_results = self.d.analytical_delay(self.sram,self.slews,self.loads)
@ -455,9 +453,7 @@ class lib:
def compute_setup_hold(self):
""" Do the analysis if we haven't characterized a FF yet """
# Do the analysis if we haven't characterized a FF yet
try:
self.sh
except AttributeError:
if not hasattr(self,"sh"):
self.sh = setup_hold(self.corner)
if self.use_model:
self.times = self.sh.analytical_setuphold(self.slews,self.loads)

View File

@ -34,12 +34,14 @@ class stimuli():
""" Function to instatiate an SRAM subckt. """
self.sf.write("Xsram ")
for i in range(dbits):
self.sf.write("D[{0}] ".format(i))
self.sf.write("DIN[{0}] ".format(i))
for i in range(abits):
self.sf.write("A[{0}] ".format(i))
for i in tech.spice["control_signals"]:
self.sf.write("{0} ".format(i))
self.sf.write("{0} ".format(tech.spice["clk"]))
for i in range(dbits):
self.sf.write("DOUT[{0}] ".format(i))
self.sf.write("{0} {1} ".format(self.vdd_name, self.gnd_name))
self.sf.write("{0}\n".format(sram_name))
@ -110,23 +112,6 @@ class stimuli():
"test"+self.vdd_name,
"test"+self.gnd_name))
def inst_accesstx(self, dbits):
""" Adds transmission gate for inputs to data-bus (only for sim purposes) """
self.sf.write("* Tx Pin-list: Drain Gate Source Body\n")
for i in range(dbits):
pmos_access_string="mp{0} DATA[{0}] acc_en D[{0}] {1} {2} w={3}u l={4}u\n"
self.sf.write(pmos_access_string.format(i,
"test"+self.vdd_name,
self.pmos_name,
2 * self.tx_width,
self.tx_length))
nmos_access_string="mn{0} DATA[{0}] acc_en_inv D[{0}] {1} {2} w={3}u l={4}u\n"
self.sf.write(nmos_access_string.format(i,
"test"+self.gnd_name,
self.nmos_name,
2 * self.tx_width,
self.tx_length))
def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
"""

View File

@ -227,10 +227,11 @@ def end_openram():
""" Clean up openram for a proper exit """
cleanup_paths()
import verify
verify.print_drc_stats()
verify.print_lvs_stats()
verify.print_pex_stats()
if OPTS.check_lvsdrc:
import verify
verify.print_drc_stats()
verify.print_lvs_stats()
verify.print_pex_stats()

View File

@ -25,7 +25,7 @@ class bank(design.design):
mod_list = ["tri_gate", "bitcell", "decoder", "ms_flop_array", "wordline_driver",
"bitcell_array", "sense_amp_array", "precharge_array",
"column_mux_array", "write_driver_array", "tri_gate_array",
"bank_select"]
"dff", "bank_select"]
from importlib import reload
for mod_name in mod_list:
config_mod_name = getattr(OPTS, mod_name)
@ -66,18 +66,19 @@ class bank(design.design):
# Can remove the following, but it helps for debug!
self.add_lvs_correspondence_points()
self.offset_all_coordinates()
# Remember the bank center for further placement
self.bank_center=self.offset_all_coordinates().scale(-1,-1)
self.DRC_LVS()
def add_pins(self):
""" Adding pins for Bank module"""
for i in range(self.word_size):
self.add_pin("DOUT[{0}]".format(i),"OUT")
self.add_pin("dout[{0}]".format(i),"OUT")
for i in range(self.word_size):
self.add_pin("DIN[{0}]".format(i),"IN")
self.add_pin("din[{0}]".format(i),"IN")
for i in range(self.addr_size):
self.add_pin("A[{0}]".format(i),"INPUT")
self.add_pin("addr[{0}]".format(i),"INPUT")
# For more than one bank, we have a bank select and name
# the signals gated_*.
@ -95,8 +96,9 @@ class bank(design.design):
self.route_precharge_to_bitcell_array()
self.route_col_mux_to_bitcell_array()
self.route_sense_amp_to_col_mux_or_bitcell_array()
self.route_sense_amp_to_trigate()
self.route_tri_gate_out()
#self.route_sense_amp_to_trigate()
#self.route_tri_gate_out()
self.route_sense_amp_out()
self.route_wordline_driver()
self.route_write_driver()
self.route_row_decoder()
@ -119,7 +121,8 @@ class bank(design.design):
self.add_column_mux_array()
self.add_sense_amp_array()
self.add_write_driver_array()
self.add_tri_gate_array()
# Not needed for single bank
#self.add_tri_gate_array()
# To the left of the bitcell array
self.add_row_decoder()
@ -280,7 +283,7 @@ class bank(design.design):
offset=vector(0,y_offset).scale(-1,-1))
temp = []
for i in range(self.word_size):
temp.append("sa_out[{0}]".format(i))
temp.append("dout[{0}]".format(i))
if self.words_per_row == 1:
temp.append("bl[{0}]".format(i))
temp.append("br[{0}]".format(i))
@ -294,14 +297,15 @@ class bank(design.design):
def add_write_driver_array(self):
""" Adding Write Driver """
y_offset = self.sense_amp_array.height + self.column_mux_height + + self.m2_gap + self.write_driver_array.height
y_offset = self.sense_amp_array.height + self.column_mux_height \
+ self.m2_gap + self.write_driver_array.height
self.write_driver_array_inst=self.add_inst(name="write_driver_array",
mod=self.write_driver_array,
offset=vector(0,y_offset).scale(-1,-1))
temp = []
for i in range(self.word_size):
temp.append("DIN[{0}]".format(i))
temp.append("din[{0}]".format(i))
for i in range(self.word_size):
if (self.words_per_row == 1):
temp.append("bl[{0}]".format(i))
@ -315,7 +319,7 @@ class bank(design.design):
def add_tri_gate_array(self):
""" data tri gate to drive the data bus """
y_offset = self.sense_amp_array.height+self.column_mux_height \
+ self.write_driver_array.height + self.m2_gap + self.tri_gate_array.height
+ self.m2_gap + self.tri_gate_array.height
self.tri_gate_array_inst=self.add_inst(name="tri_gate_array",
mod=self.tri_gate_array,
offset=vector(0,y_offset).scale(-1,-1))
@ -324,7 +328,7 @@ class bank(design.design):
for i in range(self.word_size):
temp.append("sa_out[{0}]".format(i))
for i in range(self.word_size):
temp.append("DOUT[{0}]".format(i))
temp.append("dout[{0}]".format(i))
temp.extend([self.prefix+"tri_en", self.prefix+"tri_en_bar", "vdd", "gnd"])
self.connect_inst(temp)
@ -345,7 +349,7 @@ class bank(design.design):
temp = []
for i in range(self.row_addr_size):
temp.append("A[{0}]".format(i+self.col_addr_size))
temp.append("addr[{0}]".format(i+self.col_addr_size))
for j in range(self.num_rows):
temp.append("dec_out[{0}]".format(j))
temp.extend(["vdd", "gnd"])
@ -375,8 +379,8 @@ class bank(design.design):
"""
Create a 2:4 or 3:8 column address decoder.
"""
# Place the col decoder aligned left to row decoder
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
# Place the col decoder right aligned with row decoder
x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width)
y_off = -(self.col_decoder.height + 2*drc["well_to_well"])
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
mod=self.col_decoder,
@ -384,7 +388,7 @@ class bank(design.design):
temp = []
for i in range(self.col_addr_size):
temp.append("A[{0}]".format(i))
temp.append("addr[{0}]".format(i))
for j in range(self.num_col_addr_lines):
temp.append("sel[{0}]".format(j))
temp.extend(["vdd", "gnd"])
@ -398,7 +402,7 @@ class bank(design.design):
if self.col_addr_size == 0:
return
elif self.col_addr_size == 1:
self.col_decoder = pinvbuf()
self.col_decoder = pinvbuf(height=self.mod_dff.height)
self.add_mod(self.col_decoder)
elif self.col_addr_size == 2:
self.col_decoder = self.row_decoder.pre2_4
@ -443,7 +447,7 @@ class bank(design.design):
self.precharge_array_inst,
self.sense_amp_array_inst,
self.write_driver_array_inst,
self.tri_gate_array_inst,
# self.tri_gate_array_inst,
self.row_decoder_inst,
self.wordline_driver_inst]
# Add these if we use the part...
@ -471,7 +475,7 @@ class bank(design.design):
for gated_name in self.control_signals:
# Connect the inverter output to the central bus
out_pos = self.bank_select_inst.get_pin(gated_name).rc()
bus_pos = vector(self.bus_xoffset[gated_name], out_pos.y)
bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y)
self.add_path("metal3",[out_pos, bus_pos])
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=bus_pos,
@ -492,7 +496,8 @@ class bank(design.design):
#the column decoder (if there is one) or the tristate output
#driver.
# Leave room for the output below the tri gate.
tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch
#tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch
write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch
row_decoder_min_y_offset = self.row_decoder_inst.by()
if self.col_addr_size > 0:
col_decoder_min_y_offset = self.col_decoder_inst.by()
@ -502,10 +507,10 @@ class bank(design.design):
if self.num_banks>1:
# The control gating logic is below the decoder
# Min of the control gating logic and tri gate.
self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, tri_gate_min_y_offset)
self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset)
else:
# Just the min of the decoder logic logic and tri gate.
self.min_y_offset = min(col_decoder_min_y_offset, tri_gate_min_y_offset)
self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset)
# The max point is always the top of the precharge bitlines
# Add a vdd and gnd power rail above the array
@ -530,21 +535,13 @@ class bank(design.design):
# and control lines.
# The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs
control_bus_x_offset = -self.m2_pitch * self.num_control_lines - self.m2_width
# Track the bus offsets for other modules to access
self.bus_xoffset = {}
# Control lines
for i in range(self.num_control_lines):
x_offset = control_bus_x_offset + i*self.m2_pitch
# Make the xoffset map the center of the rail
self.bus_xoffset[self.control_signals[i]]=x_offset + 0.5*self.m2_width
# Pins are added later if this is a single bank, so just add rectangle now
self.add_rect(layer="metal2",
offset=vector(x_offset, self.min_y_offset),
width=self.m2_width,
height=self.max_y_offset-self.min_y_offset)
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, 0)
control_bus_length = self.bitcell_array_inst.uy()
self.bus_xoffset = self.create_vertical_bus(layer="metal2",
pitch=self.m2_pitch,
offset=control_bus_offset,
names=self.control_signals,
length=control_bus_length)
@ -620,12 +617,22 @@ class bank(design.design):
offset=sa_data_out)
self.add_path("metal3",[sa_data_out,tri_gate_in])
def route_sense_amp_out(self):
""" Add pins for the sense amp output """
for i in range(self.word_size):
data_pin = self.sense_amp_array_inst.get_pin("data[{}]".format(i))
self.add_layout_pin_rect_center(text="dout[{}]".format(i),
layer=data_pin.layer,
offset=data_pin.center(),
height=data_pin.height(),
width=data_pin.width()),
def route_tri_gate_out(self):
""" Metal 3 routing of tri_gate output data """
for i in range(self.word_size):
data_pin = self.tri_gate_array_inst.get_pin("out[{}]".format(i))
self.add_layout_pin_rect_center(text="DOUT[{}]".format(i),
layer="metal2",
self.add_layout_pin_rect_center(text="dout[{}]".format(i),
layer=data_pin.layer,
offset=data_pin.center(),
height=data_pin.height(),
width=data_pin.width()),
@ -637,8 +644,8 @@ class bank(design.design):
# Create inputs for the row address lines
for i in range(self.row_addr_size):
addr_idx = i + self.col_addr_size
decoder_name = "A[{}]".format(i)
addr_name = "A[{}]".format(addr_idx)
decoder_name = "addr[{}]".format(i)
addr_name = "addr[{}]".format(addr_idx)
self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name)
@ -647,7 +654,7 @@ class bank(design.design):
for i in range(self.word_size):
data_name = "data[{}]".format(i)
din_name = "DIN[{}]".format(i)
din_name = "din[{}]".format(i)
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
@ -686,7 +693,7 @@ class bank(design.design):
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.col_decoder_inst, "A", "A[0]")
self.copy_layout_pin(self.col_decoder_inst, "A", "addr[0]")
elif self.col_addr_size > 1:
decode_names = []
@ -695,7 +702,7 @@ class bank(design.design):
for i in range(self.col_addr_size):
decoder_name = "in[{}]".format(i)
addr_name = "A[{}]".format(i)
addr_name = "addr[{}]".format(i)
self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name)
@ -749,13 +756,13 @@ class bank(design.design):
layer="metal2",
offset=br_pin.center())
# Add the data output names to the sense amp output
for i in range(self.word_size):
data_name = "data[{}]".format(i)
data_pin = self.sense_amp_array_inst.get_pin(data_name)
self.add_label(text="sa_out[{}]".format(i),
layer="metal2",
offset=data_pin.center())
# # Add the data output names to the sense amp output
# for i in range(self.word_size):
# data_name = "data[{}]".format(i)
# data_pin = self.sense_amp_array_inst.get_pin(data_name)
# self.add_label(text="sa_out[{}]".format(i),
# layer="metal2",
# offset=data_pin.center())
# Add labels on the decoder
for i in range(self.word_size):
@ -775,14 +782,14 @@ class bank(design.design):
# Connection from the central bus to the main control block crosses
# pre-decoder and this connection is in metal3
connection = []
connection.append((self.prefix+"tri_en_bar", self.tri_gate_array_inst.get_pin("en_bar").lc()))
connection.append((self.prefix+"tri_en", self.tri_gate_array_inst.get_pin("en").lc()))
#connection.append((self.prefix+"tri_en_bar", self.tri_gate_array_inst.get_pin("en_bar").lc()))
#connection.append((self.prefix+"tri_en", self.tri_gate_array_inst.get_pin("en").lc()))
connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst.get_pin("en").lc()))
connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc()))
connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
for (control_signal, pin_pos) in connection:
control_pos = vector(self.bus_xoffset[control_signal], pin_pos.y)
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
self.add_path("metal1", [control_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_pos,
@ -792,7 +799,7 @@ class bank(design.design):
control_signal = self.prefix+"clk_buf"
pin_pos = self.wordline_driver_inst.get_pin("en").uc()
mid_pos = pin_pos + vector(0,self.m1_pitch)
control_x_offset = self.bus_xoffset[control_signal]
control_x_offset = self.bus_xoffset[control_signal].x
control_pos = vector(control_x_offset + self.m1_width, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
control_via_pos = vector(control_x_offset, mid_pos.y)
@ -805,7 +812,7 @@ class bank(design.design):
for ctrl in self.control_signals:
# xoffsets are the center of the rail
x_offset = self.bus_xoffset[ctrl] - 0.5*self.m2_width
x_offset = self.bus_xoffset[ctrl].x - 0.5*self.m2_width
if self.num_banks > 1:
# it's not an input pin if we have multiple banks
self.add_label_pin(text=ctrl,

View File

@ -80,10 +80,8 @@ class bitcell_array(design.design):
def add_layout_pins(self):
""" Add the layout pins """
row_list = self.cell.list_row_pins()
column_list = self.cell.list_column_pins()
offset = vector(0.0, 0.0)
for col in range(self.column_size):
for cell_column in column_list:
@ -97,6 +95,7 @@ class bitcell_array(design.design):
# increments to the next column width
offset.x += self.cell.width
row_list = self.cell.list_row_pins()
offset.x = 0.0
for row in range(self.row_size):
for cell_row in row_list:

View File

@ -74,8 +74,8 @@ class control_logic(design.design):
c = reload(__import__(OPTS.replica_bitline))
replica_bitline = getattr(c, OPTS.replica_bitline)
# FIXME: These should be tuned according to the size!
delay_stages = 3 # Should be odd due to bug Kevin found
delay_fanout = 3
delay_stages = 4 # Must be non-inverting
delay_fanout = 3 # This can be anything >=2
bitcell_loads = int(math.ceil(self.num_rows / 5.0))
self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads)
self.add_mod(self.replica_bitline)
@ -94,61 +94,61 @@ class control_logic(design.design):
self.input_list =["csb","web","oeb"]
self.dff_output_list =["cs_bar", "cs", "we_bar", "we", "oe_bar", "oe"]
# list of output control signals (for making a vertical bus)
self.internal_list = ["clk_buf", "clk_buf_bar", "we", "cs", "oe"]
self.internal_width = len(self.internal_list)*self.m2_pitch
self.internal_bus_list = ["clk_buf", "clk_buf_bar", "we", "cs", "oe"]
# leave space for the bus plus one extra space
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
# Ooutputs to the bank
self.output_list = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_buf_bar", "clk_buf"]
self.output_list = ["s_en", "w_en", "clk_buf_bar", "clk_buf"]
# # with tri/tri_en
# self.output_list = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_buf_bar", "clk_buf"]
self.supply_list = ["vdd", "gnd"]
self.rail_width = len(self.input_list)*len(self.output_list)*self.m2_pitch
self.rail_x_offsets = {}
# GAP between main control and replica bitline
#self.replica_bitline_gap = 2*self.m2_pitch
def add_rails(self):
""" Add the input signal inverted tracks """
height = 6*self.inv1.height - self.m2_pitch
for i in range(len(self.internal_list)):
name = self.internal_list[i]
offset = vector(i*self.m2_pitch + self.ctrl_dff_array.width, 0)
# just for LVS correspondence...
self.add_label_pin(text=name,
layer="metal2",
offset=offset,
width=drc["minwidth_metal2"],
height=height)
self.rail_x_offsets[name]=offset.x + 0.5*drc["minwidth_metal2"] # center offset
height = 4*self.inv1.height - self.m2_pitch
offset = vector(self.ctrl_dff_array.width,0)
self.rail_offsets = self.create_vertical_bus("metal2", self.m2_pitch, offset, self.internal_bus_list, height)
def add_modules(self):
""" Place all the modules """
# Keep track of all right-most instances to determine row boundary
# and add the vdd/gnd pins
self.row_end_inst = []
# Add the control flops on the left of the bus
self.add_dffs()
self.add_clk_buffer(row=0)
# Add the logic on the right of the bus
self.add_clk_row(row=0) # clk is a double-high cell
self.add_we_row(row=2)
self.add_trien_row(row=3)
self.add_trien_bar_row(row=4)
self.add_rblk_row(row=5)
self.add_sen_row(row=6)
self.add_rbl(row=7)
# self.add_trien_row(row=3)
# self.add_trien_bar_row(row=4)
self.add_rbl_in_row(row=3)
self.add_sen_row(row=4)
self.add_rbl(row=5)
self.add_lvs_correspondence_points()
self.height = self.rbl_inst.uy()
# Find max of logic rows
max_row = max(self.row_rblk_end_x, self.row_trien_end_x, self.row_trien_bar_end_x,
self.row_sen_end_x, self.row_we_end_x, self.row_we_end_x)
# Max of modules or logic rows
self.width = max(self.clkbuf.rx(), self.rbl_inst.rx(), max_row)
# This offset is used for placement of the control logic in
# the SRAM level.
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), self.rbl_inst.by())
self.height = self.rbl_inst.uy()
# Max of modules or logic rows
self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst]))
def add_routing(self):
""" Routing between modules """
self.route_dffs()
self.route_trien()
self.route_trien_bar()
self.route_rblk()
#self.route_trien()
#self.route_trien_bar()
self.route_rbl_in()
self.route_wen()
self.route_sen()
self.route_clk()
@ -158,191 +158,159 @@ class control_logic(design.design):
def add_rbl(self,row):
""" Add the replica bitline """
y_off = row * self.inv1.height + 2*self.m1_pitch
# Add the RBL above the rows
# Add to the right of the control rows and routing channel
self.replica_bitline_offset = vector(0, y_off)
self.rbl_inst=self.add_inst(name="replica_bitline",
mod=self.replica_bitline,
offset=self.replica_bitline_offset)
self.connect_inst(["rblk", "pre_s_en", "vdd", "gnd"])
self.connect_inst(["rbl_in", "pre_s_en", "vdd", "gnd"])
def add_clk_buffer(self,row):
def add_clk_row(self,row):
""" Add the multistage clock buffer below the control flops """
x_off = self.ctrl_dff_array.width + self.internal_width
y_off = row*self.inv1.height
if row % 2:
y_off += self.clkbuf.height
mirror="MX"
else:
mirror="R0"
x_off = self.ctrl_dff_array.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
clkbuf_offset = vector(x_off,y_off)
self.clkbuf = self.add_inst(name="clkbuf",
mod=self.clkbuf,
offset=clkbuf_offset)
self.clkbuf_inst = self.add_inst(name="clkbuf",
mod=self.clkbuf,
offset=clkbuf_offset)
self.connect_inst(["clk","clk_buf_bar","clk_buf","vdd","gnd"])
self.row_end_inst.append(self.clkbuf_inst)
def add_rblk_row(self,row):
x_off = self.ctrl_dff_array.width + self.internal_width
y_off = row*self.inv1.height
if row % 2:
y_off += self.inv1.height
mirror="MX"
else:
mirror="R0"
def add_rbl_in_row(self,row):
x_off = self.ctrl_dff_array.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
# input: OE, clk_buf_bar,CS output: rblk_bar
self.rblk_bar_offset = vector(x_off, y_off)
self.rblk_bar=self.add_inst(name="nand3_rblk_bar",
mod=self.nand3,
offset=self.rblk_bar_offset,
mirror=mirror)
self.connect_inst(["clk_buf_bar", "oe", "cs", "rblk_bar", "vdd", "gnd"])
# input: OE, clk_buf_bar,CS output: rbl_in_bar
self.rbl_in_bar_offset = vector(x_off, y_off)
self.rbl_in_bar_inst=self.add_inst(name="nand3_rbl_in_bar",
mod=self.nand3,
offset=self.rbl_in_bar_offset,
mirror=mirror)
self.connect_inst(["clk_buf_bar", "oe", "cs", "rbl_in_bar", "vdd", "gnd"])
x_off += self.nand3.width
# input: rblk_bar, output: rblk
self.rblk_offset = vector(x_off, y_off)
self.rblk=self.add_inst(name="inv_rblk",
mod=self.inv1,
offset=self.rblk_offset,
mirror=mirror)
self.connect_inst(["rblk_bar", "rblk", "vdd", "gnd"])
x_off += self.inv1.width
# input: rbl_in_bar, output: rbl_in
self.rbl_in_offset = vector(x_off, y_off)
self.rbl_in_inst=self.add_inst(name="inv_rbl_in",
mod=self.inv1,
offset=self.rbl_in_offset,
mirror=mirror)
self.connect_inst(["rbl_in_bar", "rbl_in", "vdd", "gnd"])
self.row_end_inst.append(self.rbl_in_inst)
self.row_rblk_end_x = x_off
def add_sen_row(self,row):
""" The sense enable buffer gets placed to the far right of the
row. """
x_off = self.ctrl_dff_array.width + self.internal_width
y_off = row*self.inv1.height
if row % 2:
y_off += self.inv1.height
mirror="MX"
else:
mirror="R0"
x_off = self.ctrl_dff_array.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
# input: pre_s_en, output: pre_s_en_bar
self.pre_s_en_bar_offset = vector(x_off, y_off)
self.pre_s_en_bar_inst=self.add_inst(name="inv_pre_s_en_bar",
mod=self.inv2,
offset=self.pre_s_en_bar_offset,
mirror=mirror)
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"])
x_off += self.inv2.width
# BUFFER INVERTERS FOR S_EN
# input: input: pre_s_en_bar, output: s_en
self.s_en_offset = vector(x_off, y_off)
self.s_en=self.add_inst(name="inv_s_en",
mod=self.inv8,
offset=self.s_en_offset,
mirror=mirror)
self.s_en_inst=self.add_inst(name="inv_s_en",
mod=self.inv8,
offset=self.s_en_offset,
mirror=mirror)
self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"])
x_off -= self.inv2.width
# input: pre_s_en, output: pre_s_en_bar
self.pre_s_en_bar_offset = vector(x_off, y_off)
self.pre_s_en_bar=self.add_inst(name="inv_pre_s_en_bar",
mod=self.inv2,
offset=self.pre_s_en_bar_offset,
mirror=mirror)
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"])
self.row_sen_end_x = self.replica_bitline.width
self.row_end_inst.append(self.s_en_inst)
def add_trien_row(self, row):
x_off = self.ctrl_dff_array.width + self.internal_width
y_off = row*self.inv1.height
if row % 2:
y_off += self.inv1.height
mirror="MX"
else:
mirror="R0"
x_off = self.ctrl_dff_array.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
x_off += self.nand2.width
# BUFFER INVERTERS FOR TRI_EN
self.tri_en_offset = vector(x_off, y_off)
self.tri_en=self.add_inst(name="inv_tri_en1",
mod=self.inv2,
offset=self.tri_en_offset,
mirror=mirror)
tri_en_offset = vector(x_off, y_off)
self.tri_en_inst=self.add_inst(name="inv_tri_en1",
mod=self.inv2,
offset=tri_en_offset,
mirror=mirror)
self.connect_inst(["pre_tri_en_bar", "pre_tri_en1", "vdd", "gnd"])
x_off += self.inv2.width
self.tri_en_buf1_offset = vector(x_off, y_off)
self.tri_en_buf1=self.add_inst(name="tri_en_buf1",
mod=self.inv2,
offset=self.tri_en_buf1_offset,
mirror=mirror)
tri_en_buf1_offset = vector(x_off, y_off)
self.tri_en_buf1_inst=self.add_inst(name="tri_en_buf1",
mod=self.inv2,
offset=tri_en_buf1_offset,
mirror=mirror)
self.connect_inst(["pre_tri_en1", "pre_tri_en_bar1", "vdd", "gnd"])
x_off += self.inv2.width
self.tri_en_buf2_offset = vector(x_off, y_off)
self.tri_en_buf2=self.add_inst(name="tri_en_buf2",
mod=self.inv8,
offset=self.tri_en_buf2_offset,
mirror=mirror)
tri_en_buf2_offset = vector(x_off, y_off)
self.tri_en_buf2_inst=self.add_inst(name="tri_en_buf2",
mod=self.inv8,
offset=tri_en_buf2_offset,
mirror=mirror)
self.connect_inst(["pre_tri_en_bar1", "tri_en", "vdd", "gnd"])
x_off += self.inv8.width
#x_off += self.inv1.width + self.cell_gap
self.row_trien_end_x = x_off
self.row_end_inst.append(self.tri_en_inst)
def add_trien_bar_row(self, row):
x_off = self.ctrl_dff_array.width + self.internal_width
y_off = row*self.inv1.height
if row % 2:
y_off += self.inv1.height
mirror="MX"
else:
mirror="R0"
x_off = self.ctrl_dff_array.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
# input: OE, clk_buf_bar output: tri_en_bar
self.tri_en_bar_offset = vector(x_off,y_off)
self.tri_en_bar=self.add_inst(name="nand2_tri_en",
mod=self.nand2,
offset=self.tri_en_bar_offset,
mirror=mirror)
tri_en_bar_offset = vector(x_off,y_off)
self.tri_en_bar_inst=self.add_inst(name="nand2_tri_en",
mod=self.nand2,
offset=tri_en_bar_offset,
mirror=mirror)
self.connect_inst(["clk_buf_bar", "oe", "pre_tri_en_bar", "vdd", "gnd"])
x_off += self.nand2.width
# BUFFER INVERTERS FOR TRI_EN
self.tri_en_bar_buf1_offset = vector(x_off, y_off)
self.tri_en_bar_buf1=self.add_inst(name="tri_en_bar_buf1",
mod=self.inv2,
offset=self.tri_en_bar_buf1_offset,
mirror=mirror)
tri_en_bar_buf1_offset = vector(x_off, y_off)
self.tri_en_bar_buf1_inst=self.add_inst(name="tri_en_bar_buf1",
mod=self.inv2,
offset=tri_en_bar_buf1_offset,
mirror=mirror)
self.connect_inst(["pre_tri_en_bar", "pre_tri_en2", "vdd", "gnd"])
x_off += self.inv2.width
self.tri_en_bar_buf2_offset = vector(x_off, y_off)
self.tri_en_bar_buf2=self.add_inst(name="tri_en_bar_buf2",
mod=self.inv8,
offset=self.tri_en_bar_buf2_offset,
mirror=mirror)
tri_en_bar_buf2_offset = vector(x_off, y_off)
self.tri_en_bar_buf2_inst=self.add_inst(name="tri_en_bar_buf2",
mod=self.inv8,
offset=tri_en_bar_buf2_offset,
mirror=mirror)
self.connect_inst(["pre_tri_en2", "tri_en_bar", "vdd", "gnd"])
x_off += self.inv8.width
#x_off += self.inv1.width + self.cell_gap
self.row_end_inst.append(self.tri_en_bar_buf2_inst)
self.row_trien_bar_end_x = x_off
def route_dffs(self):
""" Route the input inverters """
self.connect_rail_from_right(self.ctrl_dff_inst,"dout_bar[0]","cs")
self.connect_rail_from_right(self.ctrl_dff_inst,"dout_bar[1]","we")
self.connect_rail_from_right(self.ctrl_dff_inst,"dout_bar[2]","oe")
dff_out_map = zip(["dout_bar[{}]".format(i) for i in range(3)], ["cs", "we", "oe"])
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets)
# Connect the clock rail to the other clock rail
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
mid_pos = in_pos + vector(0,self.m2_pitch)
rail_pos = vector(self.rail_x_offsets["clk_buf"], mid_pos.y)
rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[in_pos, mid_pos, rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos,
@ -361,9 +329,9 @@ class control_logic(design.design):
self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list)
def add_we_row(self,row):
x_off = self.ctrl_dff_inst.width + self.internal_width
def get_offset(self,row):
""" Compute the y-offset and mirroring """
y_off = row*self.inv1.height
if row % 2:
y_off += self.inv1.height
@ -371,77 +339,80 @@ class control_logic(design.design):
else:
mirror="R0"
return (y_off,mirror)
def add_we_row(self,row):
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
# input: WE, clk_buf_bar, CS output: w_en_bar
self.w_en_bar_offset = vector(x_off, y_off)
self.w_en_bar=self.add_inst(name="nand3_w_en_bar",
mod=self.nand3,
offset=self.w_en_bar_offset,
mirror=mirror)
w_en_bar_offset = vector(x_off, y_off)
self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar",
mod=self.nand3,
offset=w_en_bar_offset,
mirror=mirror)
self.connect_inst(["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"])
x_off += self.nand3.width
# input: w_en_bar, output: pre_w_en
self.pre_w_en_offset = vector(x_off, y_off)
self.pre_w_en=self.add_inst(name="inv_pre_w_en",
mod=self.inv1,
offset=self.pre_w_en_offset,
mirror=mirror)
pre_w_en_offset = vector(x_off, y_off)
self.pre_w_en_inst=self.add_inst(name="inv_pre_w_en",
mod=self.inv1,
offset=pre_w_en_offset,
mirror=mirror)
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
x_off += self.inv1.width
# BUFFER INVERTERS FOR W_EN
self.pre_w_en_bar_offset = vector(x_off, y_off)
self.pre_w_en_bar=self.add_inst(name="inv_pre_w_en_bar",
mod=self.inv2,
offset=self.pre_w_en_bar_offset,
mirror=mirror)
pre_w_en_bar_offset = vector(x_off, y_off)
self.pre_w_en_bar_inst=self.add_inst(name="inv_pre_w_en_bar",
mod=self.inv2,
offset=pre_w_en_bar_offset,
mirror=mirror)
self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"])
x_off += self.inv2.width
self.w_en_offset = vector(x_off, y_off)
self.w_en=self.add_inst(name="inv_w_en2",
mod=self.inv8,
offset=self.w_en_offset,
mirror=mirror)
w_en_offset = vector(x_off, y_off)
self.w_en_inst=self.add_inst(name="inv_w_en2",
mod=self.inv8,
offset=w_en_offset,
mirror=mirror)
self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"])
x_off += self.inv8.width
self.row_we_end_x = x_off
self.row_end_inst.append(self.w_en_inst)
def route_rblk(self):
""" Connect the logic for the rblk generation """
self.connect_rail_from_left(self.rblk_bar,"A","clk_buf_bar")
self.connect_rail_from_left(self.rblk_bar,"B","oe")
self.connect_rail_from_left(self.rblk_bar,"C","cs")
def route_rbl_in(self):
""" Connect the logic for the rbl_in generation """
rbl_in_map = zip(["A", "B", "C"], ["clk_buf_bar", "oe", "cs"])
self.connect_vertical_bus(rbl_in_map, self.rbl_in_bar_inst, self.rail_offsets)
# Connect the NAND3 output to the inverter
# The pins are assumed to extend all the way to the cell edge
rblk_bar_pos = self.rblk_bar.get_pin("Z").center()
inv_in_pos = self.rblk.get_pin("A").center()
mid1 = vector(inv_in_pos.x,rblk_bar_pos.y)
self.add_path("metal1",[rblk_bar_pos,mid1,inv_in_pos])
rbl_in_bar_pos = self.rbl_in_bar_inst.get_pin("Z").center()
inv_in_pos = self.rbl_in_inst.get_pin("A").center()
mid1 = vector(inv_in_pos.x,rbl_in_bar_pos.y)
self.add_path("metal1",[rbl_in_bar_pos,mid1,inv_in_pos])
# Connect the output to the RBL
rblk_pos = self.rblk.get_pin("Z").center()
rbl_out_pos = self.rbl_in_inst.get_pin("Z").center()
rbl_in_pos = self.rbl_inst.get_pin("en").center()
mid1 = vector(rbl_in_pos.x,rblk_pos.y)
self.add_wire(("metal3","via2","metal2"),[rblk_pos,mid1,rbl_in_pos])
mid1 = vector(rbl_in_pos.x,rbl_out_pos.y)
self.add_wire(("metal3","via2","metal2"),[rbl_out_pos,mid1,rbl_in_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rblk_pos,
offset=rbl_out_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=rblk_pos,
offset=rbl_out_pos,
rotate=90)
def connect_rail_from_right(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).center()
rail_pos = vector(self.rail_x_offsets[rail], in_pos.y)
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos,
@ -450,7 +421,7 @@ class control_logic(design.design):
def connect_rail_from_right_m2m3(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).center()
rail_pos = vector(self.rail_x_offsets[rail], in_pos.y)
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
# Bring it up to M2 for M2/M3 routing
self.add_via_center(layers=("metal1","via1","metal2"),
@ -467,7 +438,7 @@ class control_logic(design.design):
def connect_rail_from_left(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).lc()
rail_pos = vector(self.rail_x_offsets[rail], in_pos.y)
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos,
@ -476,7 +447,7 @@ class control_logic(design.design):
def connect_rail_from_left_m2m3(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).lc()
rail_pos = vector(self.rail_x_offsets[rail], in_pos.y)
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=in_pos,
@ -487,84 +458,86 @@ class control_logic(design.design):
def route_wen(self):
self.connect_rail_from_left(self.w_en_bar,"A","clk_buf_bar")
self.connect_rail_from_left(self.w_en_bar,"B","cs")
self.connect_rail_from_left(self.w_en_bar,"C","we")
wen_map = zip(["A", "B", "C"], ["clk_buf_bar", "cs", "we"])
self.connect_vertical_bus(wen_map, self.w_en_bar_inst, self.rail_offsets)
# Connect the NAND3 output to the inverter
# The pins are assumed to extend all the way to the cell edge
w_en_bar_pos = self.w_en_bar.get_pin("Z").center()
inv_in_pos = self.pre_w_en.get_pin("A").center()
w_en_bar_pos = self.w_en_bar_inst.get_pin("Z").center()
inv_in_pos = self.pre_w_en_inst.get_pin("A").center()
mid1 = vector(inv_in_pos.x,w_en_bar_pos.y)
self.add_path("metal1",[w_en_bar_pos,mid1,inv_in_pos])
self.add_path("metal1",[self.pre_w_en.get_pin("Z").center(), self.pre_w_en_bar.get_pin("A").center()])
self.add_path("metal1",[self.pre_w_en_bar.get_pin("Z").center(), self.w_en.get_pin("A").center()])
self.add_path("metal1",[self.pre_w_en_inst.get_pin("Z").center(), self.pre_w_en_bar_inst.get_pin("A").center()])
self.add_path("metal1",[self.pre_w_en_bar_inst.get_pin("Z").center(), self.w_en_inst.get_pin("A").center()])
self.connect_output(self.w_en, "Z", "w_en")
self.connect_output(self.w_en_inst, "Z", "w_en")
def route_trien(self):
# Connect the NAND2 output to the buffer
tri_en_bar_pos = self.tri_en_bar.get_pin("Z").center()
inv_in_pos = self.tri_en.get_pin("A").center()
tri_en_bar_pos = self.tri_en_bar_inst.get_pin("Z").center()
inv_in_pos = self.tri_en_inst.get_pin("A").center()
mid1 = vector(tri_en_bar_pos.x,inv_in_pos.y)
self.add_wire(("metal1","via1","metal2"),[tri_en_bar_pos,mid1,inv_in_pos])
# Connect the INV output to the buffer
tri_en_pos = self.tri_en.get_pin("Z").center()
inv_in_pos = self.tri_en_buf1.get_pin("A").center()
tri_en_pos = self.tri_en_inst.get_pin("Z").center()
inv_in_pos = self.tri_en_buf1_inst.get_pin("A").center()
mid_xoffset = 0.5*(tri_en_pos.x + inv_in_pos.x)
mid1 = vector(mid_xoffset,tri_en_pos.y)
mid2 = vector(mid_xoffset,inv_in_pos.y)
self.add_path("metal1",[tri_en_pos,mid1,mid2,inv_in_pos])
self.add_path("metal1",[self.tri_en_buf1.get_pin("Z").center(), self.tri_en_buf2.get_pin("A").center()])
self.add_path("metal1",[self.tri_en_buf1_ist.get_pin("Z").center(), self.tri_en_buf2_inst.get_pin("A").center()])
self.connect_output(self.tri_en_buf2, "Z", "tri_en")
self.connect_output(self.tri_en_buf2_inst, "Z", "tri_en")
def route_trien_bar(self):
self.connect_rail_from_left(self.tri_en_bar,"A","clk_buf_bar")
self.connect_rail_from_left(self.tri_en_bar,"B","oe")
trien_map = zip(["A", "B"], ["clk_buf_bar", "oe"])
self.connect_vertical_bus(trien_map, self.tri_en_bar_inst, self.rail_offsets)
# Connect the NAND2 output to the buffer
tri_en_bar_pos = self.tri_en_bar.get_pin("Z").center()
inv_in_pos = self.tri_en_bar_buf1.get_pin("A").center()
tri_en_bar_pos = self.tri_en_bar_inst.get_pin("Z").center()
inv_in_pos = self.tri_en_bar_buf1_inst.get_pin("A").center()
mid_xoffset = 0.5*(tri_en_bar_pos.x + inv_in_pos.x)
mid1 = vector(mid_xoffset,tri_en_bar_pos.y)
mid2 = vector(mid_xoffset,inv_in_pos.y)
self.add_path("metal1",[tri_en_bar_pos,mid1,mid2,inv_in_pos])
self.add_path("metal1",[self.tri_en_bar_buf1.get_pin("Z").center(), self.tri_en_bar_buf2.get_pin("A").center()])
self.add_path("metal1",[self.tri_en_bar_buf1_inst.get_pin("Z").center(), self.tri_en_bar_buf2_inst.get_pin("A").center()])
self.connect_output(self.tri_en_bar_buf2, "Z", "tri_en_bar")
self.connect_output(self.tri_en_bar_buf2_inst, "Z", "tri_en_bar")
def route_sen(self):
rbl_out_pos = self.rbl_inst.get_pin("out").bc()
in_pos = self.pre_s_en_bar.get_pin("A").lc()
in_pos = self.pre_s_en_bar_inst.get_pin("A").lc()
mid1 = vector(rbl_out_pos.x,in_pos.y)
self.add_wire(("metal1","via1","metal2"),[rbl_out_pos,mid1,in_pos])
#s_en_pos = self.s_en.get_pin("Z").lc()
self.add_path("metal1",[self.pre_s_en_bar.get_pin("Z").center(), self.s_en.get_pin("A").center()])
self.add_path("metal1",[self.pre_s_en_bar_inst.get_pin("Z").center(), self.s_en_inst.get_pin("A").center()])
self.connect_output(self.s_en, "Z", "s_en")
self.connect_output(self.s_en_inst, "Z", "s_en")
def route_clk(self):
""" Route the clk and clk_buf_bar signal internally """
clk_pin = self.clkbuf.get_pin("A")
clk_pin = self.clkbuf_inst.get_pin("A")
self.add_layout_pin_segment_center(text="clk",
layer="metal2",
start=clk_pin.bc(),
end=clk_pin.bc().scale(1,0))
self.connect_rail_from_right_m2m3(self.clkbuf, "Z", "clk_buf")
self.connect_rail_from_right_m2m3(self.clkbuf, "Zb", "clk_buf_bar")
self.connect_output(self.clkbuf, "Z", "clk_buf")
self.connect_output(self.clkbuf, "Zb", "clk_buf_bar")
clkbuf_map = zip(["Z", "Zb"], ["clk_buf", "clk_buf_bar"])
self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Z", "clk_buf")
# self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Zb", "clk_buf_bar")
self.connect_output(self.clkbuf_inst, "Z", "clk_buf")
self.connect_output(self.clkbuf_inst, "Zb", "clk_buf_bar")
def connect_output(self, inst, pin_name, out_name):
""" Create an output pin on the right side from the pin of a given instance. """
@ -579,41 +552,32 @@ class control_logic(design.design):
def route_supply(self):
""" Route the vdd and gnd for the rows of logic. """
""" Add vdd and gnd to the instance cells """
rows_start = 0
rows_end = self.width
#well_width = drc["minwidth_well"]
for i in range(8):
if i%2:
name = "vdd"
well_type = "nwell"
else:
name = "gnd"
well_type = "pwell"
yoffset = i*self.inv1.height
self.add_layout_pin_segment_center(text=name,
layer="metal1",
start=vector(rows_start,yoffset),
end=vector(rows_end,yoffset))
# # also add a well +- around the rail
# well_offset = vector(rows_start,yoffset-0.5*well_width)
# self.add_rect(layer=well_type,
# offset=well_offset,
# width=rows_end-rows_start,
# height=well_width)
# self.add_rect(layer="vtg",
# offset=well_offset,
# width=rows_end-rows_start,
# height=well_width)
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
for inst in self.row_end_inst:
pins = inst.get_pins("vdd")
for pin in pins:
if pin.layer == "metal1":
row_loc = pin.rc()
pin_loc = vector(max_row_x_loc, pin.rc().y)
self.add_power_pin("vdd", pin_loc)
self.add_path("metal1", [row_loc, pin_loc])
pins = inst.get_pins("gnd")
for pin in pins:
if pin.layer == "metal1":
row_loc = pin.rc()
pin_loc = vector(max_row_x_loc, pin.rc().y)
self.add_power_pin("gnd", pin_loc)
self.add_path("metal1", [row_loc, pin_loc])
self.copy_layout_pin(self.rbl_inst,"gnd")
self.copy_layout_pin(self.rbl_inst,"vdd")
self.copy_layout_pin(self.ctrl_dff_inst,"gnd")
self.copy_layout_pin(self.ctrl_dff_inst,"vdd")

View File

@ -9,24 +9,20 @@ from globals import OPTS
class delay_chain(design.design):
"""
Generate a delay chain with the given number of stages and fanout.
This automatically adds an extra inverter with no load on the input.
Input is a list contains the electrical effort of each stage.
Input is a list contains the electrical effort (fanout) of each stage.
Usually, this will be constant, but it could have varied fanout.
"""
def __init__(self, fanout_list, name="delay_chain"):
"""init function"""
design.design.__init__(self, name)
# FIXME: input should be logic effort value
# and there should be functions to get
# area efficient inverter stage list
# Two fanouts are needed so that we can route the vdd/gnd connections
for f in fanout_list:
debug.check(f>0,"Must have non-zero fanouts for each stage.")
debug.check(f>=2,"Must have >=2 fanouts for each stage.")
# number of inverters including any fanout loads.
self.fanout_list = fanout_list
self.num_inverters = 1 + sum(fanout_list)
self.num_top_half = round(self.num_inverters / 2.0)
from importlib import reload
c = reload(__import__(OPTS.bitcell))
@ -35,6 +31,7 @@ class delay_chain(design.design):
self.add_pins()
self.create_module()
self.add_inverters()
self.route_inverters()
self.add_layout_pins()
self.DRC_LVS()
@ -52,14 +49,11 @@ class delay_chain(design.design):
self.inv = pinv(route_output=False)
self.add_mod(self.inv)
# half chain length is the width of the layout
# invs are stacked into 2 levels so input/output are close
# extra metal is for the gnd connection U
# Each stage is a a row
self.height = len(self.fanout_list)*self.inv.height
# The width is determined by the largest fanout plus the driver
self.width = (max(self.fanout_list)+1) * self.inv.width
self.add_inverters()
def add_inverters(self):
""" Add the inverters and connect them based on the stage list """
@ -164,19 +158,29 @@ class delay_chain(design.design):
""" Add vdd and gnd rails and the input/output. Connect the gnd rails internally on
the top end with no input/output to obstruct. """
for driver in self.driver_inst_list:
vdd_pin = driver.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=self.width,
height=vdd_pin.height())
gnd_pin = driver.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll(),
width=self.width,
height=gnd_pin.height())
# Add power and ground to all the cells except:
# the fanout driver, the right-most load
# The routing to connect the loads is over the first and last cells
# We have an even number of drivers and must only do every other
# supply rail
for i in range(0,len(self.driver_inst_list),2):
inv = self.driver_inst_list[i]
for load in self.load_inst_map[inv]:
if load==self.rightest_load_inst[inv]:
continue
for pin_name in ["vdd", "gnd"]:
pin = load.get_pin(pin_name)
self.add_power_pin(pin_name, pin.rc())
else:
# We have an even number of rows, so need to get the last gnd rail
inv = self.driver_inst_list[-1]
for load in self.load_inst_map[inv]:
if load==self.rightest_load_inst[inv]:
continue
pin_name = "gnd"
pin = load.get_pin(pin_name)
self.add_power_pin(pin_name, pin.rc())
# input is A pin of first inverter
a_pin = self.driver_inst_list[0].get_pin("A")

View File

@ -18,7 +18,7 @@ class dff_array(design.design):
if name=="":
name = "dff_array_{0}x{1}".format(rows, columns)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns))
from importlib import reload
c = reload(__import__(OPTS.dff))
@ -38,33 +38,33 @@ class dff_array(design.design):
self.DRC_LVS()
def add_pins(self):
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_din_name(y,x))
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_dout_name(y,x))
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_din_name(row,col))
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_dout_name(row,col))
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
def create_dff_array(self):
self.dff_insts={}
for y in range(self.rows):
for x in range(self.columns):
name = "Xdff_r{0}_c{1}".format(y,x)
if (y % 2 == 0):
base = vector(x*self.dff.width,y*self.dff.height)
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0"
else:
base = vector(x*self.dff.width,(y+1)*self.dff.height)
base = vector(col*self.dff.width,(row+1)*self.dff.height)
mirror = "MX"
self.dff_insts[x,y]=self.add_inst(name=name,
self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff,
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(y,x),
self.get_dout_name(y,x),
self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(row,col),
"clk",
"vdd",
"gnd"])
@ -91,38 +91,30 @@ class dff_array(design.design):
def add_layout_pins(self):
for y in range(self.rows):
# Continous vdd rail along with label.
vdd_pin=self.dff_insts[0,y].get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=self.width,
height=self.m1_width)
for row in range(self.rows):
for col in range(self.columns):
# Continous vdd rail along with label.
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
self.add_power_pin("vdd", vdd_pin.center())
# Continous gnd rail along with label.
gnd_pin=self.dff_insts[0,y].get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll(),
width=self.width,
height=self.m1_width)
# Continous gnd rail along with label.
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
self.add_power_pin("gnd", gnd_pin.center())
for y in range(self.rows):
for x in range(self.columns):
din_pin = self.dff_insts[x,y].get_pin("D")
for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row,col].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(y,x),
self.add_layout_pin(text=self.get_din_name(row,col),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.dff_insts[x,y].get_pin("Q")
dout_pin = self.dff_insts[row,col].get_pin("Q")
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(y,x),
self.add_layout_pin(text=self.get_dout_name(row,col),
layer=dout_pin.layer,
offset=dout_pin.ll(),
width=dout_pin.width(),
@ -140,22 +132,20 @@ class dff_array(design.design):
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin(text="clk",
layer="metal3",
offset=vector(0,0),
width=self.width,
height=self.m3_width)
for x in range(self.columns):
clk_pin = self.dff_insts[x,0].get_pin("clk")
self.add_layout_pin_segment_center(text="clk",
layer="metal3",
start=vector(0,self.m3_pitch+self.m3_width),
end=vector(self.width,self.m3_pitch+self.m3_width))
for col in range(self.columns):
clk_pin = self.dff_insts[0,col].get_pin("clk")
# Make a vertical strip for each column
self.add_layout_pin(text="clk",
layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
self.add_rect(layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"),
offset=clk_pin.center().scale(1,0))
offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width))

View File

@ -17,7 +17,7 @@ class dff_buf_array(design.design):
self.columns = columns
if name=="":
name = "dff_array_{0}x{1}".format(rows, columns)
name = "dff_buf_array_{0}x{1}".format(rows, columns)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
@ -36,35 +36,35 @@ class dff_buf_array(design.design):
self.DRC_LVS()
def add_pins(self):
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_din_name(y,x))
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_dout_name(y,x))
self.add_pin(self.get_dout_bar_name(y,x))
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_din_name(row,col))
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_dout_name(row,col))
self.add_pin(self.get_dout_bar_name(row,col))
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
def create_dff_array(self):
self.dff_insts={}
for y in range(self.rows):
for x in range(self.columns):
name = "Xdff_r{0}_c{1}".format(y,x)
if (y % 2 == 0):
base = vector(x*self.dff.width,y*self.dff.height)
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0"
else:
base = vector(x*self.dff.width,(y+1)*self.dff.height)
base = vector(col*self.dff.width,(row+1)*self.dff.height)
mirror = "MX"
self.dff_insts[x,y]=self.add_inst(name=name,
mod=self.dff,
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(y,x),
self.get_dout_name(y,x),
self.get_dout_bar_name(y,x),
self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff,
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(row,col),
self.get_dout_bar_name(row,col),
"clk",
"vdd",
"gnd"])
@ -100,58 +100,38 @@ class dff_buf_array(design.design):
return dout_bar_name
def add_layout_pins(self):
for row in range(self.rows):
for col in range(self.columns):
# Continous vdd rail along with label.
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
self.add_power_pin("vdd", vdd_pin.lc())
xoffsets = []
for x in range(self.columns):
xoffsets.append(self.dff_insts[x,0].get_pin("gnd").lx())
for y in range(self.rows):
# Route both supplies
for n in ["vdd", "gnd"]:
supply_pin = self.dff_insts[0,y].get_pin(n)
supply_offset = supply_pin.ll()
self.add_rect(layer="metal1",
offset=supply_offset,
width=self.width)
# Add pins in two locations
for xoffset in xoffsets:
pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin_pos,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos,
rotate=90)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
offset=pin_pos)
# Continous gnd rail along with label.
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
self.add_power_pin("gnd", gnd_pin.lc())
for y in range(self.rows):
for x in range(self.columns):
din_pin = self.dff_insts[x,y].get_pin("D")
for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row,col].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(y,x),
self.add_layout_pin(text=self.get_din_name(row,col),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.dff_insts[x,y].get_pin("Q")
dout_pin = self.dff_insts[row,col].get_pin("Q")
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(y,x),
self.add_layout_pin(text=self.get_dout_name(row,col),
layer=dout_pin.layer,
offset=dout_pin.ll(),
width=dout_pin.width(),
height=dout_pin.height())
dout_bar_pin = self.dff_insts[x,y].get_pin("Qb")
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2")
self.add_layout_pin(text=self.get_dout_bar_name(y,x),
self.add_layout_pin(text=self.get_dout_bar_name(row,col),
layer=dout_bar_pin.layer,
offset=dout_bar_pin.ll(),
width=dout_bar_pin.width(),
@ -168,22 +148,21 @@ class dff_buf_array(design.design):
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin(text="clk",
layer="metal3",
offset=vector(0,2*self.m2_width),
width=self.width,
height=self.m3_width)
for x in range(self.columns):
clk_pin = self.dff_insts[x,0].get_pin("clk")
self.add_layout_pin_segment_center(text="clk",
layer="metal3",
start=vector(0,self.m3_pitch+self.m3_width),
end=vector(self.width,self.m3_pitch+self.m3_width))
for col in range(self.columns):
clk_pin = self.dff_insts[0,col].get_pin("clk")
# Make a vertical strip for each column
self.add_layout_pin(text="clk",
layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
self.add_rect(layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"),
offset=clk_pin.center().scale(1,0) + vector(0,2*self.m2_width))
offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width))

View File

@ -17,7 +17,7 @@ class dff_inv_array(design.design):
self.columns = columns
if name=="":
name = "dff_array_{0}x{1}".format(rows, columns)
name = "dff_inv_array_{0}x{1}".format(rows, columns)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
@ -36,35 +36,35 @@ class dff_inv_array(design.design):
self.DRC_LVS()
def add_pins(self):
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_din_name(y,x))
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_dout_name(y,x))
self.add_pin(self.get_dout_bar_name(y,x))
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_din_name(row,col))
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_dout_name(row,col))
self.add_pin(self.get_dout_bar_name(row,col))
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
def create_dff_array(self):
self.dff_insts={}
for y in range(self.rows):
for x in range(self.columns):
name = "Xdff_r{0}_c{1}".format(y,x)
if (y % 2 == 0):
base = vector(x*self.dff.width,y*self.dff.height)
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0"
else:
base = vector(x*self.dff.width,(y+1)*self.dff.height)
base = vector(col*self.dff.width,(row+1)*self.dff.height)
mirror = "MX"
self.dff_insts[x,y]=self.add_inst(name=name,
mod=self.dff,
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(y,x),
self.get_dout_name(y,x),
self.get_dout_bar_name(y,x),
self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff,
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(row,col),
self.get_dout_bar_name(row,col),
"clk",
"vdd",
"gnd"])
@ -100,47 +100,38 @@ class dff_inv_array(design.design):
return dout_bar_name
def add_layout_pins(self):
for y in range(self.rows):
# Continous vdd rail along with label.
vdd_pin=self.dff_insts[0,y].get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=self.width,
height=self.m1_width)
for row in range(self.rows):
for col in range(self.columns):
# Continous vdd rail along with label.
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
self.add_power_pin("vdd", vdd_pin.lc())
# Continous gnd rail along with label.
gnd_pin=self.dff_insts[0,y].get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll(),
width=self.width,
height=self.m1_width)
# Continous gnd rail along with label.
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
self.add_power_pin("gnd", gnd_pin.lc())
for y in range(self.rows):
for x in range(self.columns):
din_pin = self.dff_insts[x,y].get_pin("D")
for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row,col].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(y,x),
self.add_layout_pin(text=self.get_din_name(row,col),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.dff_insts[x,y].get_pin("Q")
dout_pin = self.dff_insts[row,col].get_pin("Q")
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(y,x),
self.add_layout_pin(text=self.get_dout_name(row,col),
layer=dout_pin.layer,
offset=dout_pin.ll(),
width=dout_pin.width(),
height=dout_pin.height())
dout_bar_pin = self.dff_insts[x,y].get_pin("Qb")
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2")
self.add_layout_pin(text=self.get_dout_bar_name(y,x),
self.add_layout_pin(text=self.get_dout_bar_name(row,col),
layer=dout_bar_pin.layer,
offset=dout_bar_pin.ll(),
width=dout_bar_pin.width(),
@ -157,22 +148,21 @@ class dff_inv_array(design.design):
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin(text="clk",
layer="metal3",
offset=vector(0,0),
width=self.width,
height=self.m3_width)
for x in range(self.columns):
clk_pin = self.dff_insts[x,0].get_pin("clk")
self.add_layout_pin_segment_center(text="clk",
layer="metal3",
start=vector(0,self.m3_pitch+self.m3_width),
end=vector(self.width,self.m3_pitch+self.m3_width))
for col in range(self.columns):
clk_pin = self.dff_insts[0,col].get_pin("clk")
# Make a vertical strip for each column
self.add_layout_pin(text="clk",
layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
self.add_rect(layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"),
offset=clk_pin.center().scale(1,0))
offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width))

View File

@ -127,12 +127,12 @@ class hierarchical_decoder(design.design):
min_x = min(min_x, -self.pre3_8.width)
input_offset=vector(min_x - self.input_routing_width,0)
input_bus_names = ["A[{0}]".format(i) for i in range(self.num_inputs)]
self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
length=input_height)
input_bus_names = ["addr[{0}]".format(i) for i in range(self.num_inputs)]
self.input_rails = self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
length=input_height)
self.connect_input_to_predecodes()
@ -143,14 +143,15 @@ class hierarchical_decoder(design.design):
for i in range(2):
index = pre_num * 2 + i
input_pin = self.get_pin("A[{}]".format(index))
input_pos = self.input_rails["addr[{}]".format(index)]
in_name = "in[{}]".format(i)
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
# Offset each decoder pin up so they don't conflit
decoder_offset = decoder_pin.center() + vector(0,i*self.m2_pitch)
input_offset = input_pin.center().scale(1,0) + decoder_offset.scale(0,1)
# To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
self.connect_input_rail(decoder_offset, input_offset)
@ -159,14 +160,15 @@ class hierarchical_decoder(design.design):
for i in range(3):
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
input_pin = self.get_pin("A[{}]".format(index))
input_pos = self.input_rails["addr[{}]".format(index)]
in_name = "in[{}]".format(i)
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
# Offset each decoder pin up so they don't conflit
decoder_offset = decoder_pin.center() + vector(0,i*self.m2_pitch)
input_offset = input_pin.center().scale(1,0) + decoder_offset.scale(0,1)
# To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
self.connect_input_rail(decoder_offset, input_offset)
@ -187,7 +189,7 @@ class hierarchical_decoder(design.design):
""" Add the module pins """
for i in range(self.num_inputs):
self.add_pin("A[{0}]".format(i))
self.add_pin("addr[{0}]".format(i))
for j in range(self.rows):
self.add_pin("decode[{0}]".format(j))
@ -248,7 +250,7 @@ class hierarchical_decoder(design.design):
pins = []
for input_index in range(2):
pins.append("A[{0}]".format(input_index + index_off1))
pins.append("addr[{0}]".format(input_index + index_off1))
for output_index in range(4):
pins.append("out[{0}]".format(output_index + index_off2))
pins.extend(["vdd", "gnd"])
@ -275,7 +277,7 @@ class hierarchical_decoder(design.design):
pins = []
for input_index in range(3):
pins.append("A[{0}]".format(input_index + in_index_offset))
pins.append("addr[{0}]".format(input_index + in_index_offset))
for output_index in range(8):
pins.append("out[{0}]".format(output_index + out_index_offset))
pins.extend(["vdd", "gnd"])
@ -415,18 +417,14 @@ class hierarchical_decoder(design.design):
# This is not needed for inputs <4 since they have no pre/decode stages.
if (self.num_inputs >= 4):
# Array for saving the X offsets of the vertical rails. These rail
# offsets are accessed with indices.
self.rail_x_offsets = []
for i in range(self.total_number_of_predecoder_outputs):
# The offsets go into the negative x direction
# assuming the predecodes are placed at (self.internal_routing_width,0)
x_offset = self.m2_pitch * i
self.rail_x_offsets.append(x_offset+0.5*self.m2_width)
self.add_rect(layer="metal2",
offset=vector(x_offset,0),
width=drc["minwidth_metal2"],
height=self.height)
input_offset = vector(0.5*self.m2_width,0)
input_bus_names = ["predecode[{0}]".format(i) for i in range(self.total_number_of_predecoder_outputs)]
self.predecode_rails = self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
length=self.height)
self.connect_rails_to_predecodes()
self.connect_rails_to_decoder()
@ -434,20 +432,22 @@ class hierarchical_decoder(design.design):
def connect_rails_to_predecodes(self):
""" Iterates through all of the predecodes and connects to the rails including the offsets """
# FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre2x4):
for i in range(4):
index = pre_num * 4 + i
predecode_name = "predecode[{}]".format(pre_num * 4 + i)
out_name = "out[{}]".format(i)
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
self.connect_predecode_rail_m3(index, pin)
self.connect_predecode_rail_m3(predecode_name, pin)
# FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre3x8):
for i in range(8):
index = pre_num * 8 + i + self.no_of_pre2x4 * 4
predecode_name = "predecode[{}]".format(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_predecode_rail_m3(index, pin)
self.connect_predecode_rail_m3(predecode_name, pin)
@ -463,17 +463,24 @@ class hierarchical_decoder(design.design):
if (self.num_inputs == 4 or self.num_inputs == 5):
for index_A in self.predec_groups[0]:
for index_B in self.predec_groups[1]:
self.connect_predecode_rail(index_A, self.nand_inst[row_index].get_pin("A"))
self.connect_predecode_rail(index_B, self.nand_inst[row_index].get_pin("B"))
# FIXME: convert to connect_bus?
predecode_name = "predecode[{}]".format(index_A)
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
predecode_name = "predecode[{}]".format(index_B)
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
row_index = row_index + 1
elif (self.num_inputs > 5):
for index_A in self.predec_groups[0]:
for index_B in self.predec_groups[1]:
for index_C in self.predec_groups[2]:
self.connect_predecode_rail(index_A, self.nand_inst[row_index].get_pin("A"))
self.connect_predecode_rail(index_B, self.nand_inst[row_index].get_pin("B"))
self.connect_predecode_rail(index_C, self.nand_inst[row_index].get_pin("C"))
# FIXME: convert to connect_bus?
predecode_name = "predecode[{}]".format(index_A)
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
predecode_name = "predecode[{}]".format(index_B)
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
predecode_name = "predecode[{}]".format(index_C)
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C"))
row_index = row_index + 1
def route_vdd_gnd(self):
@ -509,19 +516,21 @@ class hierarchical_decoder(design.design):
self.copy_layout_pin(pre, "gnd")
def connect_predecode_rail(self, rail_index, pin):
def connect_predecode_rail(self, rail_name, pin):
""" Connect the routing rail to the given metal1 pin """
rail_pos = vector(self.rail_x_offsets[rail_index],pin.lc().y)
rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y)
self.add_path("metal1", [rail_pos, pin.lc()])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_pos,
rotate=90)
def connect_predecode_rail_m3(self, rail_index, pin):
def connect_predecode_rail_m3(self, rail_name, pin):
""" Connect the routing rail to the given metal1 pin """
# This routes the pin up to the rail, basically, to avoid conflicts.
# It would be fixed with a channel router.
mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2)
rail_pos = vector(self.rail_x_offsets[rail_index],mid_point.y)
rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin.center(),
rotate=90)

View File

@ -50,52 +50,42 @@ class hierarchical_predecode(design.design):
debug.error("Invalid number of predecode inputs.",-1)
def setup_constraints(self):
# The rail offsets are indexed by the label
self.rails = {}
# Non inverted input rails
for rail_index in range(self.number_of_inputs):
xoffset = rail_index * self.m2_pitch + 0.5*self.m2_width
self.rails["in[{}]".format(rail_index)]=xoffset
self.height = self.number_of_outputs * self.nand.height
# x offset for input inverters
self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch
# Creating the right hand side metal2 rails for output connections
for rail_index in range(2 * self.number_of_inputs):
xoffset = self.x_off_inv_1 + self.inv.width + ((rail_index+1) * self.m2_pitch) + 0.5*self.m2_width
if rail_index < self.number_of_inputs:
self.rails["Abar[{}]".format(rail_index)]=xoffset
else:
self.rails["A[{}]".format(rail_index-self.number_of_inputs)]=xoffset
# x offset to NAND decoder includes the left rails, mid rails and inverters, plus an extra m2 pitch
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (1 + 2*self.number_of_inputs) * self.m2_pitch
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 1) * self.m2_pitch
# x offset to output inverters
self.x_off_inv_2 = self.x_off_nand + self.nand.width
# Height width are computed
self.width = self.x_off_inv_2 + self.inv.width
self.height = self.number_of_outputs * self.nand.height
def create_rails(self):
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
for label in self.rails.keys():
# these are not primary inputs, so they shouldn't have a
# label or LVS complains about different names on one net
if label.startswith("in"):
self.add_layout_pin(text=label,
layer="metal2",
offset=vector(self.rails[label] - 0.5*self.m1_width, self.m1_width),
width=self.m2_width,
height=self.height - 4*self.m1_width)
else:
self.add_rect(layer="metal2",
offset=vector(self.rails[label] - 0.5*self.m1_width, 2*self.m1_width),
width=self.m2_width,
height=self.height - 4*self.m1_width)
input_names = ["in[{}]".format(x) for x in range(self.number_of_inputs)]
offset = vector(0.5*self.m2_width,2*self.m1_width)
self.input_rails = self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch,
offset=offset,
names=input_names,
length=self.height - 2*self.m1_width)
invert_names = ["Abar[{}]".format(x) for x in range(self.number_of_inputs)]
non_invert_names = ["A[{}]".format(x) for x in range(self.number_of_inputs)]
decode_names = invert_names + non_invert_names
offset = vector(self.x_off_inv_1 + self.inv.width + self.m2_pitch, 2*self.m1_width)
self.decode_rails = self.create_vertical_bus(layer="metal2",
pitch=self.m2_pitch,
offset=offset,
names=decode_names,
length=self.height - 2*self.m1_width)
def add_input_inverters(self):
""" Create the input inverters to invert input signals for the decode stage. """
@ -176,14 +166,14 @@ class hierarchical_predecode(design.design):
y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1m2.width + self.m1_space
in_pin = "in[{}]".format(num)
a_pin = "A[{}]".format(num)
in_pos = vector(self.rails[in_pin],y_offset)
a_pos = vector(self.rails[a_pin],y_offset)
in_pos = vector(self.input_rails[in_pin].x,y_offset)
a_pos = vector(self.decode_rails[a_pin].x,y_offset)
self.add_path("metal1",[in_pos, a_pos])
self.add_via_center(layers = ("metal1", "via1", "metal2"),
offset=[self.rails[in_pin], y_offset],
offset=[self.input_rails[in_pin].x, y_offset],
rotate=90)
self.add_via_center(layers = ("metal1", "via1", "metal2"),
offset=[self.rails[a_pin], y_offset],
offset=[self.decode_rails[a_pin].x, y_offset],
rotate=90)
def route_output_inverters(self):
@ -222,7 +212,7 @@ class hierarchical_predecode(design.design):
y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space
inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc()
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0)
rail_pos = vector(self.rails[out_pin],y_offset)
rail_pos = vector(self.decode_rails[out_pin].x,y_offset)
self.add_path("metal1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
self.add_via_center(layers = ("metal1", "via1", "metal2"),
offset=rail_pos,
@ -231,7 +221,7 @@ class hierarchical_predecode(design.design):
#route input
inv_in_pos = self.in_inst[inv_num].get_pin("A").lc()
in_pos = vector(self.rails[in_pin],inv_in_pos.y)
in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y)
self.add_path("metal1", [in_pos, inv_in_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=in_pos,
@ -253,7 +243,7 @@ class hierarchical_predecode(design.design):
# this will connect pins A,B or A,B,C
for rail_pin,gate_pin in zip(index_lst,gate_lst):
pin_pos = self.nand_inst[k].get_pin(gate_pin).lc()
rail_pos = vector(self.rails[rail_pin], pin_pos.y)
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
self.add_path("metal1", [rail_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_pos,

View File

@ -42,8 +42,9 @@ class replica_bitline(design.design):
#self.add_lvs_correspondence_points()
self.width = self.right_gnd_pin.rx() - self.left_gnd_pin.lx()
self.height = self.left_gnd_pin.uy() - self.left_gnd_pin.by()
# Plus a pitch for the WL contacts on the RBL
self.width = self.rbl_inst.rx() - self.dc_inst.lx() + self.m1_pitch
self.height = max(self.rbl_inst.uy(), self.dc_inst.uy())
self.DRC_LVS()
@ -57,15 +58,19 @@ class replica_bitline(design.design):
# away from the delay chain/inverter with space for three M2 tracks
self.bitcell_offset = vector(0,self.replica_bitcell.height)
self.rbl_offset = self.bitcell_offset
# Gap between the delay chain and RBL
gap_width = 2*self.m2_pitch
# Quadrant 4: with some space below it and tracks on the right for vdd/gnd
self.delay_chain_offset = vector(-self.delay_chain.width-4*self.m2_pitch,self.replica_bitcell.height)
self.delay_chain_offset = vector(-self.delay_chain.width-gap_width,self.replica_bitcell.height)
# Will be flipped vertically below the delay chain
self.rbl_inv_offset = self.delay_chain_offset + vector(0.5*self.delay_chain.width, 0)
# Align it with the inverters in the delay chain to simplify supply connections
self.rbl_inv_offset = self.delay_chain_offset + vector(2*self.inv.width, 0)
# Placed next to the replica bitcell
self.access_tx_offset = vector(-4*self.m2_pitch-self.access_tx.width-self.inv.width, 0.5*self.inv.height)
self.access_tx_offset = vector(-gap_width-self.access_tx.width-self.inv.width, 0.5*self.inv.height)
@ -126,25 +131,53 @@ class replica_bitline(design.design):
def route(self):
""" Connect all the signals together """
self.route_vdd()
self.route_gnd()
self.route_vdd_gnd()
self.route_supplies()
self.route_wl()
self.route_access_tx()
def route_vdd_gnd(self):
def route_wl(self):
""" Connect the RBL word lines to gnd """
# Connect the WL and gnd pins directly to the center and right gnd rails
for row in range(self.bitcell_loads):
wl = "wl[{}]".format(row)
pin = self.rbl_inst.get_pin(wl)
# Route the connection to the right so that it doesn't interfere
# with the cells
pin_right = pin.rc()
pin_extension = pin_right + vector(self.m1_pitch,0)
if pin.layer != "metal1":
continue
self.add_path("metal1", [pin_right, pin_extension])
self.add_power_pin("gnd", pin_extension)
def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
# These are the instances that every bank has
top_instances = [self.rbl_inst,
self.rbl_inv_inst,
self.rbc_inst,
self.dc_inst]
self.dc_inst]
for inst in top_instances:
self.copy_layout_pin(inst, "vdd")
self.copy_layout_pin(inst, "gnd")
# Route the inverter supply pin from M1
# Only vdd is needed because gnd shares a rail with the delay chain
pin = self.rbl_inv_inst.get_pin("vdd")
self.add_power_pin("vdd", pin.lc())
# Replica bitcell needs to be routed up to M3
pin=self.rbc_inst.get_pin("vdd")
# Don't rotate this via to vit in FreePDK45
self.add_power_pin("vdd", pin.center(), False)
for pin in self.rbc_inst.get_pins("gnd"):
self.add_power_pin("gnd", pin.center())
def route_access_tx(self):
# GATE ROUTE
# 1. Add the poly contact and nwell enclosure
@ -183,7 +216,7 @@ class replica_bitline(design.design):
# DRAIN ROUTE
# Route the drain to the vdd rail
drain_offset = self.tx_inst.get_pin("D").center()
self.add_path("metal1", [drain_offset, drain_offset.scale(1,0)])
self.add_power_pin("vdd", drain_offset)
# SOURCE ROUTE
# Route the drain to the RBL inverter input
@ -194,13 +227,11 @@ class replica_bitline(design.design):
# Route the connection of the source route to the RBL bitline (left)
# Via will go halfway down from the bitcell
bl_offset = self.rbc_inst.get_pin("BL").bc()
self.add_path("metal3",[source_offset, bl_offset])
# Route down a pitch so we can use M2 routing
bl_down_offset = bl_offset - vector(0, self.m2_pitch)
self.add_path("metal2",[source_offset, bl_down_offset, bl_offset])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=source_offset)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=source_offset)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=bl_offset)
# BODY ROUTE
# Connect it to the inverter well
@ -213,30 +244,10 @@ class replica_bitline(design.design):
def route_vdd(self):
""" Route all signals connected to vdd """
self.copy_layout_pin(self.dc_inst,"vdd")
self.copy_layout_pin(self.rbc_inst,"vdd")
# Route the vdd lines from left to right
# Add via for the delay chain
left_vdd_start = self.dc_inst.ll().scale(1,0) - vector(self.m2_pitch,0)
left_vdd_end = vector(left_vdd_start.x, self.rbl_inst.uy())
self.left_vdd_pin=self.add_segment_center(layer="metal2",
start=left_vdd_start,
end=left_vdd_end)
# Vdd line to the left of the replica bitline
center_vdd_start = self.rbc_inst.ll() - vector(3*self.m2_pitch,0)
center_vdd_end = vector(center_vdd_start.x, self.rbl_inst.uy())
self.center_vdd_pin=self.add_segment_center(layer="metal2",
start=center_vdd_start,
end=center_vdd_end)
# Vdd line to the right of the replica bitline
right_vdd_start = self.rbc_inst.lr() + vector(2*self.m2_pitch,0)
right_vdd_end = vector(right_vdd_start.x, self.rbl_inst.uy())
self.right_vdd_pin=self.add_segment_center(layer="metal2",
start=right_vdd_start,
end=right_vdd_end)
# Connect the WL and vdd pins directly to the center and right vdd rails
@ -260,24 +271,6 @@ class replica_bitline(design.design):
# Connect the vdd pins of the delay chain to the left rails
dc_vdd_pins = self.dc_inst.get_pins("vdd")
for pin in dc_vdd_pins:
if pin.layer != "metal1":
continue
start = vector(self.left_vdd_pin.cx(),pin.cy())
# Note, we don't connect to center because of via conflicts
# with the RBL pins
#end = vector(center_vdd_pin.cx(),pin.cy())
end = pin.rc()
self.add_layout_pin_segment_center(text="vdd",
layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
# Add via for the inverter
pin = self.rbl_inv_inst.get_pin("vdd")

View File

@ -40,7 +40,9 @@ report_status()
import verify
import sram
print("Output files are " + OPTS.output_name + ".(sp|gds|v|lib|lef)")
output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in ["sp","gds","v","lib","lef"]]
print("Output files are: ")
print(*output_files,sep="\n")
# Keep track of running stats
start_time = datetime.datetime.now()
@ -53,7 +55,7 @@ s = sram.sram(word_size=OPTS.word_size,
name=OPTS.output_name)
# Output the files for the resulting SRAM
s.save_output()
s.save()
# Delete temp files etc.
end_openram()

View File

@ -2,477 +2,78 @@ import sys
import datetime
import getpass
import debug
import design
from sram_1bank import sram_1bank
from sram_2bank import sram_2bank
from sram_4bank import sram_4bank
from math import log,sqrt,ceil
from vector import vector
from globals import OPTS, print_time
class sram(sram_1bank,sram_2bank,sram_4bank):
class sram():
"""
Dynamically generated SRAM by connecting banks to control logic. The
number of banks should be 1 , 2 or 4
This is not a design module, but contains an SRAM design instance.
It could later try options of number of banks and oganization to compare
results.
We can later add visualizer and other high-level functions as needed.
"""
def __init__(self, word_size, num_words, num_banks, name):
from importlib import reload
c = reload(__import__(OPTS.control_logic))
self.mod_control_logic = getattr(c, OPTS.control_logic)
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
c = reload(__import__(OPTS.ms_flop))
self.mod_ms_flop = getattr(c, OPTS.ms_flop)
self.ms_flop = self.mod_ms_flop()
# reset the static duplicate name checker for unit tests
# in case we create more than one SRAM
from design import design
design.name_map=[]
self.word_size = word_size
self.num_words = num_words
self.num_banks = num_banks
debug.info(2, "create sram of size {0} with {1} num of words".format(self.word_size,
self.num_words))
debug.info(2, "create sram of size {0} with {1} num of words".format(word_size,
num_words))
start_time = datetime.datetime.now()
self.compute_sizes()
self.name = name
if self.num_banks == 1:
sram_1bank.__init__(self,name)
elif self.num_banks == 2:
sram_2bank.__init__(self,name)
elif self.num_banks == 4:
sram_4bank.__init__(self,name)
if num_banks == 1:
from sram_1bank import sram_1bank
self.s=sram_1bank(word_size, num_words, name)
elif num_banks == 2:
from sram_2bank import sram_2bank
self.s=sram_2bank(word_size, num_words, name)
elif num_banks == 4:
from sram_4bank import sram_4bank
self.s=sram_4bank(word_size, num_words, name)
else:
debug.error("Invalid number of banks.",-1)
self.control_size = 6
self.bank_to_bus_distance = 5*self.m3_width
self.create_modules()
self.add_pins()
self.create_layout()
self.s.compute_sizes()
self.s.create_modules()
self.s.add_pins()
self.s.create_layout()
# Can remove the following, but it helps for debug!
self.add_lvs_correspondence_points()
self.s.add_lvs_correspondence_points()
self.offset_all_coordinates()
sizes = self.find_highest_coords()
self.width = sizes[0]
self.height = sizes[1]
self.s.offset_all_coordinates()
highest_coord = self.s.find_highest_coords()
self.s.width = highest_coord[0]
self.s.height = highest_coord[1]
self.DRC_LVS(final_verification=True)
self.s.DRC_LVS(final_verification=True)
if not OPTS.is_unit_test:
print_time("SRAM creation", datetime.datetime.now(), start_time)
def compute_sizes(self):
""" 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.")
self.num_words_per_bank = self.num_words/self.num_banks
self.num_bits_per_bank = self.word_size*self.num_words_per_bank
# Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank
self.bank_side_length = sqrt(self.bank_area)
# Estimate the words per row given the height of the bitcell and the square side length
self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width)
self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size)
# Estimate the number of rows given the tentative words per row
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size)
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
# Fix the number of columns and rows
self.num_cols = int(self.words_per_row*self.word_size)
self.num_rows = int(self.num_words_per_bank/self.words_per_row)
# Compute the address and bank sizes
self.row_addr_size = int(log(self.num_rows, 2))
self.col_addr_size = int(log(self.words_per_row, 2))
self.bank_addr_size = self.col_addr_size + self.row_addr_size
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
debug.info(1,"Words per row: {}".format(self.words_per_row))
def estimate_words_per_row(self,tentative_num_cols, word_size):
"""
This provides a heuristic rounded estimate for the number of words
per row.
"""
if tentative_num_cols < 1.5*word_size:
return 1
elif tentative_num_cols > 3*word_size:
return 4
else:
return 2
def amend_words_per_row(self,tentative_num_rows, words_per_row):
"""
This picks the number of words per row more accurately by limiting
it to a minimum and maximum.
"""
# Recompute the words per row given a hard max
if(tentative_num_rows > 512):
debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048")
return int(words_per_row*tentative_num_rows/512)
# Recompute the words per row given a hard min
if(tentative_num_rows < 16):
debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows))
return int(words_per_row*tentative_num_rows/16)
return words_per_row
def add_pins(self):
""" Add pins for entire SRAM. """
for i in range(self.word_size):
self.add_pin("DIN[{0}]".format(i),"INPUT")
for i in range(self.addr_size):
self.add_pin("ADDR[{0}]".format(i),"INPUT")
# These are used to create the physical pins too
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,"INPUT")
for i in range(self.word_size):
self.add_pin("DOUT[{0}]".format(i),"OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
def create_layout(self):
""" Layout creation """
self.add_modules()
self.route()
def compute_bus_sizes(self):
""" Compute the independent bus widths shared between two and four bank SRAMs """
# address size + control signals + one-hot bank select signals
self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks,2) + 1
# data bus size
self.num_horizontal_line = self.word_size
self.vertical_bus_width = self.m2_pitch*self.num_vertical_line
# vertical bus height depends on 2 or 4 banks
self.data_bus_height = self.m3_pitch*self.num_horizontal_line
self.data_bus_width = 2*(self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width
self.control_bus_height = self.m1_pitch*(self.control_size+2)
self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width
self.supply_bus_height = self.m1_pitch*2 # 2 for vdd/gnd placed with control bus
self.supply_bus_width = self.data_bus_width
# Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really)
debug.check(self.bank.width + self.vertical_bus_width > 0.9*self.control_logic.width,
"Bank is too small compared to control logic.")
def add_busses(self):
""" Add the horizontal and vertical busses """
# Vertical bus
# The order of the control signals on the control bus:
self.control_bus_names = ["clk_buf", "tri_en_bar", "tri_en", "clk_buf_bar", "w_en", "s_en"]
self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
pitch=self.m2_pitch,
offset=self.vertical_bus_offset,
names=self.control_bus_names,
length=self.vertical_bus_height)
self.addr_bus_names=["A[{}]".format(i) for i in range(self.addr_size)]
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch,
offset=self.addr_bus_offset,
names=self.addr_bus_names,
length=self.addr_bus_height))
self.bank_sel_bus_names = ["bank_sel[{}]".format(i) for i in range(self.num_banks)]
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch,
offset=self.bank_sel_bus_offset,
names=self.bank_sel_bus_names,
length=self.vertical_bus_height))
# Horizontal data bus
self.data_bus_names = ["DATA[{}]".format(i) for i in range(self.word_size)]
self.data_bus_positions = self.create_horizontal_pin_bus(layer="metal3",
pitch=self.m3_pitch,
offset=self.data_bus_offset,
names=self.data_bus_names,
length=self.data_bus_width)
# Horizontal control logic bus
# vdd/gnd in bus go along whole SRAM
# FIXME: Fatten these wires?
self.horz_control_bus_positions = self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch,
offset=self.supply_bus_offset,
names=["vdd"],
length=self.supply_bus_width)
# The gnd rail must not be the entire width since we protrude the right-most vdd rail up for
# the decoder in 4-bank SRAMs
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch,
offset=self.supply_bus_offset+vector(0,self.m1_pitch),
names=["gnd"],
length=self.supply_bus_width))
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch,
offset=self.control_bus_offset,
names=self.control_bus_names,
length=self.control_bus_width))
def route_vdd_gnd(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
# These are the instances that every bank has
top_instances = [self.bitcell_array_inst,
self.precharge_array_inst,
self.sense_amp_array_inst,
self.write_driver_array_inst,
self.tri_gate_array_inst,
self.row_decoder_inst,
self.wordline_driver_inst]
# Add these if we use the part...
if self.col_addr_size > 0:
top_instances.append(self.col_decoder_inst)
top_instances.append(self.col_mux_array_inst)
if self.num_banks > 1:
top_instances.append(self.bank_select_inst)
for inst in top_instances:
# Column mux has no vdd
if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst):
self.copy_layout_pin(inst, "vdd")
# Precharge has no gnd
if inst != self.precharge_array_inst:
self.copy_layout_pin(inst, "gnd")
def create_multi_bank_modules(self):
""" Create the multibank address flops and bank decoder """
from dff_buf_array import dff_buf_array
self.msb_address = dff_buf_array(name="msb_address",
rows=1,
columns=self.num_banks/2)
self.add_mod(self.msb_address)
if self.num_banks>2:
self.msb_decoder = self.bank.decoder.pre2_4
self.add_mod(self.msb_decoder)
def create_modules(self):
""" Create all the modules that will be used """
from control_logic import control_logic
# Create the control logic module
self.control_logic = self.mod_control_logic(num_rows=self.num_rows)
self.add_mod(self.control_logic)
# Create the address and control flops (but not the clk)
dff_size = self.addr_size
from dff_array import dff_array
self.addr_dff = dff_array(name="dff_array", rows=dff_size, columns=1)
self.add_mod(self.addr_dff)
# Create the bank module (up to four are instantiated)
from bank import bank
self.bank = bank(word_size=self.word_size,
num_words=self.num_words_per_bank,
words_per_row=self.words_per_row,
num_banks=self.num_banks,
name="bank")
self.add_mod(self.bank)
# Create bank decoder
if(self.num_banks > 1):
self.create_multi_bank_modules()
self.bank_count = 0
self.supply_rail_width = self.bank.supply_rail_width
self.supply_rail_pitch = self.bank.supply_rail_pitch
def add_bank(self, bank_num, position, x_flip, y_flip):
""" Place a bank at the given position with orientations """
# x_flip == 1 --> no flip in x_axis
# x_flip == -1 --> flip in x_axis
# y_flip == 1 --> no flip in y_axis
# y_flip == -1 --> flip in y_axis
# x_flip and y_flip are used for position translation
if x_flip == -1 and y_flip == -1:
bank_rotation = 180
else:
bank_rotation = 0
if x_flip == y_flip:
bank_mirror = "R0"
elif x_flip == -1:
bank_mirror = "MX"
elif y_flip == -1:
bank_mirror = "MY"
else:
bank_mirror = "R0"
bank_inst=self.add_inst(name="bank{0}".format(bank_num),
mod=self.bank,
offset=position,
mirror=bank_mirror,
rotate=bank_rotation)
temp = []
for i in range(self.word_size):
temp.append("DOUT[{0}]".format(i))
for i in range(self.word_size):
temp.append("DIN[{0}]".format(i))
for i in range(self.bank_addr_size):
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",
"clk_buf_bar","clk_buf" , "vdd", "gnd"])
self.connect_inst(temp)
return bank_inst
def add_addr_dff(self, position):
""" Add and place address and control flops """
self.addr_dff_inst = self.add_inst(name="address",
mod=self.addr_dff,
offset=position)
# inputs, outputs/output/bar
inputs = []
outputs = []
for i in range(self.addr_size):
inputs.append("ADDR[{}]".format(i))
outputs.append("A[{}]".format(i))
self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"])
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(inputs + self.control_logic_outputs + ["vdd", "gnd"])
def sp_write(self,name):
self.s.sp_write(name)
def gds_write(self,name):
self.s.gds_write(name)
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
if self.num_banks==1: return
for n in self.control_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])
for n in self.bank_sel_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])
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_center(layers=("metal2","via2","metal3"),
offset=src_pin.rc(),
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 verilog_write(self,name):
self.s.verilog_write(name)
def sp_write(self, sp_name):
# Write the entire spice of the object to the file
############################################################
# Spice circuit
############################################################
sp = open(sp_name, 'w')
sp.write("**************************************************\n")
sp.write("* OpenRAM generated memory.\n")
sp.write("* Words: {}\n".format(self.num_words))
sp.write("* Data bits: {}\n".format(self.word_size))
sp.write("* Banks: {}\n".format(self.num_banks))
sp.write("* Column mux: {}:1\n".format(self.words_per_row))
sp.write("**************************************************\n")
# This causes unit test mismatch
# sp.write("* Created: {0}\n".format(datetime.datetime.now()))
# sp.write("* User: {0}\n".format(getpass.getuser()))
# sp.write(".global {0} {1}\n".format(spice["vdd_name"],
# spice["gnd_name"]))
usedMODS = list()
self.sp_write_file(sp, usedMODS)
del usedMODS
sp.close()
def analytical_delay(self,slew,load):
""" LH and HL are the same in analytical model. """
return self.bank.analytical_delay(slew,load)
def save_output(self):
def save(self):
""" Save all the output files while reporting time to do it as well. """
# Save the spice file
start_time = datetime.datetime.now()
spname = OPTS.output_path + self.name + ".sp"
spname = OPTS.output_path + self.s.name + ".sp"
print("SP: Writing to {0}".format(spname))
self.sp_write(spname)
self.s.sp_write(spname)
print_time("Spice writing", datetime.datetime.now(), start_time)
# Save the extracted spice file
@ -480,12 +81,12 @@ class sram(sram_1bank,sram_2bank,sram_4bank):
start_time = datetime.datetime.now()
# Output the extracted design if requested
sp_file = OPTS.output_path + "temp_pex.sp"
verify.run_pex(self.name, gdsname, spname, output=sp_file)
verify.run_pex(self.s.name, gdsname, spname, output=sp_file)
print_time("Extraction", datetime.datetime.now(), start_time)
else:
# Use generated spice file for characterization
sp_file = spname
print(sys.path)
# Characterize the design
start_time = datetime.datetime.now()
from characterizer import lib
@ -497,26 +98,26 @@ class sram(sram_1bank,sram_2bank,sram_4bank):
print("Performing simulation-based characterization with {}".format(OPTS.spice_name))
if OPTS.trim_netlist:
print("Trimming netlist to speed up characterization.")
lib(out_dir=OPTS.output_path, sram=self, sp_file=sp_file)
lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file)
print_time("Characterization", datetime.datetime.now(), start_time)
# Write the layout
start_time = datetime.datetime.now()
gdsname = OPTS.output_path + self.name + ".gds"
gdsname = OPTS.output_path + self.s.name + ".gds"
print("GDS: Writing to {0}".format(gdsname))
self.gds_write(gdsname)
self.s.gds_write(gdsname)
print_time("GDS", datetime.datetime.now(), start_time)
# Create a LEF physical model
start_time = datetime.datetime.now()
lefname = OPTS.output_path + self.name + ".lef"
lefname = OPTS.output_path + self.s.name + ".lef"
print("LEF: Writing to {0}".format(lefname))
self.lef_write(lefname)
self.s.lef_write(lefname)
print_time("LEF", datetime.datetime.now(), start_time)
# Write a verilog model
start_time = datetime.datetime.now()
vname = OPTS.output_path + self.name + ".v"
vname = OPTS.output_path + self.s.name + ".v"
print("Verilog: Writing to {0}".format(vname))
self.verilog_write(vname)
self.s.verilog_write(vname)
print_time("Verilog", datetime.datetime.now(), start_time)

View File

@ -7,18 +7,18 @@ import getpass
from vector import vector
from globals import OPTS, print_time
from design import design
from sram_base import sram_base
from bank import bank
from dff_buf_array import dff_buf_array
from dff_array import dff_array
class sram_1bank(design):
class sram_1bank(sram_base):
"""
Procedures specific to a two bank SRAM.
Procedures specific to a one bank SRAM.
"""
def __init__(self, name):
design.__init__(self, name)
def __init__(self, word_size, num_words, name):
sram_base.__init__(self, word_size, num_words, 1, name)
def add_modules(self):
"""
@ -29,37 +29,107 @@ class sram_1bank(design):
# No orientation or offset
self.bank_inst = self.add_bank(0, [0, 0], 1, 1)
# 3/5/18 MRG: Cannot reference positions inside submodules because boundaries
# are not recomputed using instance placement. So, place the control logic such that it aligns
# with the top of the SRAM.
control_pos = vector(-self.control_logic.width - self.m3_pitch,
3*self.supply_rail_width)
self.bank.bank_center.y - self.control_logic.control_logic_center.y)
self.add_control_logic(position=control_pos)
# Leave room for the control routes to the left of the flops
addr_pos = vector(self.control_logic_inst.lx() + 4*self.m2_pitch,
row_addr_pos = vector(self.control_logic_inst.rx() - self.row_addr_dff.width,
control_pos.y + self.control_logic.height + self.m1_pitch)
self.add_addr_dff(addr_pos)
self.add_row_addr_dff(row_addr_pos)
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
data_gap = -self.m2_pitch*(self.word_size+1)
# Add the column address below the bank under the control
# Keep it aligned with the data flops
if self.col_addr_dff:
col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width,
data_gap - self.col_addr_dff.height)
self.add_col_addr_dff(col_addr_pos)
# Add the data flops below the bank
# This relies on the center point of the bank:
# decoder in upper left, bank in upper right, sensing in lower right.
# These flops go below the sensing and leave a gap to channel route to the
# sense amps.
data_pos = vector(self.bank.bank_center.x,
data_gap - self.data_dff.height)
self.add_data_dff(data_pos)
# two supply rails are already included in the bank, so just 2 here.
self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
self.height = self.bank.height
def add_pins(self):
def add_layout_pins(self):
"""
Add the top-level pins for a single bank SRAM with control.
"""
# Connect the control pins as inputs
for n in self.control_logic_inputs + ["clk"]:
self.copy_layout_pin(self.control_logic_inst, n)
for i in range(self.word_size):
self.copy_layout_pin(self.bank_inst, "DOUT[{}]".format(i))
dout_name = "dout[{}]".format(i)
self.copy_layout_pin(self.bank_inst, dout_name, dout_name.upper())
# Lower address bits
for i in range(self.col_addr_size):
self.copy_layout_pin(self.col_addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i))
# Upper address bits
for i in range(self.row_addr_size):
self.copy_layout_pin(self.row_addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i+self.col_addr_size))
for i in range(self.word_size):
din_name = "din[{}]".format(i)
self.copy_layout_pin(self.data_dff_inst, din_name, din_name.upper())
for i in range(self.addr_size):
self.copy_layout_pin(self.addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i))
def route(self):
""" Route a single bank SRAM """
# Route the outputs from the control logic module
self.add_layout_pins()
self.route_vdd_gnd()
self.route_clk()
self.route_control_logic()
self.route_row_addr_dff()
if self.col_addr_dff:
self.route_col_addr_dff()
self.route_data_dff()
def route_clk(self):
""" Route the clock network """
debug.warning("Clock is top-level must connect.")
# For now, just have four clock pins for the address (x2), data, and control
if self.col_addr_dff:
self.copy_layout_pin(self.col_addr_dff_inst, "clk")
self.copy_layout_pin(self.row_addr_dff_inst, "clk")
self.copy_layout_pin(self.data_dff_inst, "clk")
self.copy_layout_pin(self.control_logic_inst, "clk")
def route_vdd_gnd(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
# These are the instances that every bank has
top_instances = [self.bank_inst,
self.row_addr_dff_inst,
self.data_dff_inst,
self.control_logic_inst]
if self.col_addr_dff:
top_instances.append(self.col_addr_dff_inst)
for inst in top_instances:
self.copy_layout_pin(inst, "vdd")
self.copy_layout_pin(inst, "gnd")
def route_control_logic(self):
""" 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)
@ -69,33 +139,64 @@ class sram_1bank(design):
rotate=90)
# Connect the output of the flops to the bank pins
for i in range(self.addr_size):
def route_row_addr_dff(self):
""" Connect the output of the row flops to the bank pins """
for i in range(self.row_addr_size):
flop_name = "dout[{}]".format(i)
bank_name = "A[{}]".format(i)
flop_pin = self.addr_dff_inst.get_pin(flop_name)
bank_name = "addr[{}]".format(i+self.col_addr_size)
flop_pin = self.row_addr_dff_inst.get_pin(flop_name)
bank_pin = self.bank_inst.get_pin(bank_name)
flop_pos = flop_pin.center()
bank_pos = vector(bank_pin.cx(),flop_pos.y)
self.add_path("metal3",[flop_pos, bank_pos])
bank_pos = bank_pin.center()
mid_pos = vector(bank_pos.x,flop_pos.y)
self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_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 control pins as inputs
for n in self.control_logic_inputs + ["clk"]:
self.copy_layout_pin(self.control_logic_inst, n)
def route_col_addr_dff(self):
""" Connect the output of the row flops to the bank pins """
# Connect the clock between the flops and control module
flop_pin = self.addr_dff_inst.get_pin("clk")
ctrl_pin = self.control_logic_inst.get_pin("clk_buf")
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()])
bus_names = ["addr[{}]".format(x) for x in range(self.col_addr_size)]
col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch,
offset=self.col_addr_dff_inst.ul() + vector(0, self.m1_pitch),
names=bus_names,
length=self.col_addr_dff_inst.width)
dff_names = ["dout[{}]".format(x) for x in range(self.col_addr_size)]
data_dff_map = zip(dff_names, bus_names)
self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_inst, col_addr_bus_offsets)
bank_names = ["addr[{}]".format(x) for x in range(self.col_addr_size)]
data_bank_map = zip(bank_names, bus_names)
self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets)
def route_data_dff(self):
""" Connect the output of the data flops to the write driver """
# This is where the channel will start (y-dimension at least)
offset = self.data_dff_inst.ul() + vector(0, self.m1_pitch)
dff_names = ["dout[{}]".format(x) for x in range(self.word_size)]
bank_names = ["din[{}]".format(x) for x in range(self.word_size)]
route_map = list(zip(bank_names, dff_names))
dff_pins = {key: self.data_dff_inst.get_pin(key) for key in dff_names }
bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names }
self.create_horizontal_channel_route(route_map, dff_pins, bank_pins, offset)
def add_lvs_correspondence_points(self):
"""
This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
for n in self.control_logic_outputs:
pin = self.control_logic_inst.get_pin(n)
self.add_label(text=n,
layer=pin.layer,
offset=pin.center())

View File

@ -1,23 +1,23 @@
import sys
from tech import drc, spice
import debug
import design
from math import log,sqrt,ceil
import contact
from bank import bank
from dff_buf_array import dff_buf_array
from dff_array import dff_array
import datetime
import getpass
from vector import vector
from globals import OPTS, print_time
class sram_2bank(design.design):
from sram_base import sram_base
from bank import bank
from dff_buf_array import dff_buf_array
from dff_array import dff_array
class sram_2bank(sram_base):
"""
Procedures specific to a two bank SRAM.
"""
def __init__(self, name):
design.__init__(self, name)
def __init__(self, word_size, num_words, name):
sram_base.__init__(self, word_size, num_words, 2, name)
def compute_bank_offsets(self):
""" Compute the overall offsets for a two bank SRAM """
@ -214,3 +214,20 @@ class sram_2bank(design.design):
def add_lvs_correspondence_points(self):
"""
This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
if self.num_banks==1: return
for n in self.control_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])
for n in self.bank_sel_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])

View File

@ -1,24 +1,24 @@
import sys
from tech import drc, spice
import debug
import design
from math import log,sqrt,ceil
import contact
from bank import bank
from dff_buf_array import dff_buf_array
from dff_array import dff_array
import datetime
import getpass
from vector import vector
from globals import OPTS, print_time
class sram_4bank(design.design):
from sram_base import sram_base
from bank import bank
from dff_buf_array import dff_buf_array
from dff_array import dff_array
class sram_4bank(sram_base):
"""
Procedures specific to a four bank SRAM.
"""
def __init__(self, name):
design.__init__(self, name)
def __init__(self, word_size, num_words, name):
sram_base.__init__(self, word_size, num_words, 4, name)
def compute_bank_offsets(self):
""" Compute the overall offsets for a four bank SRAM """
@ -312,3 +312,20 @@ class sram_4bank(design.design):
self.route_bank_supply_rails(left_banks=[0,2], bottom_banks=[2,3])
def add_lvs_correspondence_points(self):
"""
This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
if self.num_banks==1: return
for n in self.control_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])
for n in self.bank_sel_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])

443
compiler/sram_base.py Normal file
View File

@ -0,0 +1,443 @@
import sys
import datetime
import getpass
import debug
from math import log,sqrt,ceil
from vector import vector
from globals import OPTS, print_time
from design import design
class sram_base(design):
"""
Dynamically generated SRAM by connecting banks to control logic. The
number of banks should be 1 , 2 or 4
"""
def __init__(self, word_size, num_words, num_banks, name):
design.__init__(self, name)
from importlib import reload
c = reload(__import__(OPTS.control_logic))
self.mod_control_logic = getattr(c, OPTS.control_logic)
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
c = reload(__import__(OPTS.ms_flop))
self.mod_ms_flop = getattr(c, OPTS.ms_flop)
self.ms_flop = self.mod_ms_flop()
self.word_size = word_size
self.num_words = num_words
self.num_banks = num_banks
def compute_sizes(self):
""" 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.")
self.num_words_per_bank = self.num_words/self.num_banks
self.num_bits_per_bank = self.word_size*self.num_words_per_bank
# Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank
self.bank_side_length = sqrt(self.bank_area)
# Estimate the words per row given the height of the bitcell and the square side length
self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width)
self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size)
# Estimate the number of rows given the tentative words per row
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size)
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
# Fix the number of columns and rows
self.num_cols = int(self.words_per_row*self.word_size)
self.num_rows = int(self.num_words_per_bank/self.words_per_row)
# Compute the address and bank sizes
self.row_addr_size = int(log(self.num_rows, 2))
self.col_addr_size = int(log(self.words_per_row, 2))
self.bank_addr_size = self.col_addr_size + self.row_addr_size
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
debug.info(1,"Words per row: {}".format(self.words_per_row))
def estimate_words_per_row(self,tentative_num_cols, word_size):
"""
This provides a heuristic rounded estimate for the number of words
per row.
"""
if tentative_num_cols < 1.5*word_size:
return 1
elif tentative_num_cols > 3*word_size:
return 4
else:
return 2
def amend_words_per_row(self,tentative_num_rows, words_per_row):
"""
This picks the number of words per row more accurately by limiting
it to a minimum and maximum.
"""
# Recompute the words per row given a hard max
if(tentative_num_rows > 512):
debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048")
return int(words_per_row*tentative_num_rows/512)
# Recompute the words per row given a hard min
if(tentative_num_rows < 16):
debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows))
return int(words_per_row*tentative_num_rows/16)
return words_per_row
def add_pins(self):
""" Add pins for entire SRAM. """
for i in range(self.word_size):
self.add_pin("DIN[{0}]".format(i),"INPUT")
for i in range(self.addr_size):
self.add_pin("ADDR[{0}]".format(i),"INPUT")
# These are used to create the physical pins too
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,"INPUT")
for i in range(self.word_size):
self.add_pin("DOUT[{0}]".format(i),"OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
def create_layout(self):
""" Layout creation """
self.add_modules()
self.route()
self.add_lvs_correspondence_points()
def compute_bus_sizes(self):
""" Compute the independent bus widths shared between two and four bank SRAMs """
# address size + control signals + one-hot bank select signals
self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks,2) + 1
# data bus size
self.num_horizontal_line = self.word_size
self.vertical_bus_width = self.m2_pitch*self.num_vertical_line
# vertical bus height depends on 2 or 4 banks
self.data_bus_height = self.m3_pitch*self.num_horizontal_line
self.data_bus_width = 2*(self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width
self.control_bus_height = self.m1_pitch*(self.control_size+2)
self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width
self.supply_bus_height = self.m1_pitch*2 # 2 for vdd/gnd placed with control bus
self.supply_bus_width = self.data_bus_width
# Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really)
debug.check(self.bank.width + self.vertical_bus_width > 0.9*self.control_logic.width,
"Bank is too small compared to control logic.")
def add_busses(self):
""" Add the horizontal and vertical busses """
# Vertical bus
# The order of the control signals on the control bus:
self.control_bus_names = ["clk_buf", "tri_en_bar", "tri_en", "clk_buf_bar", "w_en", "s_en"]
self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
pitch=self.m2_pitch,
offset=self.vertical_bus_offset,
names=self.control_bus_names,
length=self.vertical_bus_height)
self.addr_bus_names=["A[{}]".format(i) for i in range(self.addr_size)]
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch,
offset=self.addr_bus_offset,
names=self.addr_bus_names,
length=self.addr_bus_height))
self.bank_sel_bus_names = ["bank_sel[{}]".format(i) for i in range(self.num_banks)]
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch,
offset=self.bank_sel_bus_offset,
names=self.bank_sel_bus_names,
length=self.vertical_bus_height))
# Horizontal data bus
self.data_bus_names = ["DATA[{}]".format(i) for i in range(self.word_size)]
self.data_bus_positions = self.create_horizontal_pin_bus(layer="metal3",
pitch=self.m3_pitch,
offset=self.data_bus_offset,
names=self.data_bus_names,
length=self.data_bus_width)
# Horizontal control logic bus
# vdd/gnd in bus go along whole SRAM
# FIXME: Fatten these wires?
self.horz_control_bus_positions = self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch,
offset=self.supply_bus_offset,
names=["vdd"],
length=self.supply_bus_width)
# The gnd rail must not be the entire width since we protrude the right-most vdd rail up for
# the decoder in 4-bank SRAMs
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch,
offset=self.supply_bus_offset+vector(0,self.m1_pitch),
names=["gnd"],
length=self.supply_bus_width))
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch,
offset=self.control_bus_offset,
names=self.control_bus_names,
length=self.control_bus_width))
def route_vdd_gnd(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
# These are the instances that every bank has
top_instances = [self.bitcell_array_inst,
self.precharge_array_inst,
self.sense_amp_array_inst,
self.write_driver_array_inst,
self.tri_gate_array_inst,
self.row_decoder_inst,
self.wordline_driver_inst]
# Add these if we use the part...
if self.col_addr_size > 0:
top_instances.append(self.col_decoder_inst)
top_instances.append(self.col_mux_array_inst)
if self.num_banks > 1:
top_instances.append(self.bank_select_inst)
for inst in top_instances:
# Column mux has no vdd
if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst):
self.copy_layout_pin(inst, "vdd")
# Precharge has no gnd
if inst != self.precharge_array_inst:
self.copy_layout_pin(inst, "gnd")
def create_multi_bank_modules(self):
""" Create the multibank address flops and bank decoder """
from dff_buf_array import dff_buf_array
self.msb_address = dff_buf_array(name="msb_address",
rows=1,
columns=self.num_banks/2)
self.add_mod(self.msb_address)
if self.num_banks>2:
self.msb_decoder = self.bank.decoder.pre2_4
self.add_mod(self.msb_decoder)
def create_modules(self):
""" Create all the modules that will be used """
from control_logic import control_logic
# Create the control logic module
self.control_logic = self.mod_control_logic(num_rows=self.num_rows)
self.add_mod(self.control_logic)
# Create the address and control flops (but not the clk)
from dff_array import dff_array
self.row_addr_dff = dff_array(name="row_addr_dff", rows=self.row_addr_size, columns=1)
self.add_mod(self.row_addr_dff)
if self.col_addr_size > 0:
self.col_addr_dff = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size)
self.add_mod(self.col_addr_dff)
else:
self.col_addr_dff = None
self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size)
self.add_mod(self.data_dff)
# Create the bank module (up to four are instantiated)
from bank import bank
self.bank = bank(word_size=self.word_size,
num_words=self.num_words_per_bank,
words_per_row=self.words_per_row,
num_banks=self.num_banks,
name="bank")
self.add_mod(self.bank)
# Create bank decoder
if(self.num_banks > 1):
self.create_multi_bank_modules()
self.bank_count = 0
self.supply_rail_width = self.bank.supply_rail_width
self.supply_rail_pitch = self.bank.supply_rail_pitch
def add_bank(self, bank_num, position, x_flip, y_flip):
""" Place a bank at the given position with orientations """
# x_flip == 1 --> no flip in x_axis
# x_flip == -1 --> flip in x_axis
# y_flip == 1 --> no flip in y_axis
# y_flip == -1 --> flip in y_axis
# x_flip and y_flip are used for position translation
if x_flip == -1 and y_flip == -1:
bank_rotation = 180
else:
bank_rotation = 0
if x_flip == y_flip:
bank_mirror = "R0"
elif x_flip == -1:
bank_mirror = "MX"
elif y_flip == -1:
bank_mirror = "MY"
else:
bank_mirror = "R0"
bank_inst=self.add_inst(name="bank{0}".format(bank_num),
mod=self.bank,
offset=position,
mirror=bank_mirror,
rotate=bank_rotation)
temp = []
for i in range(self.word_size):
temp.append("DOUT[{0}]".format(i))
for i in range(self.word_size):
temp.append("BANK_DIN[{0}]".format(i))
for i in range(self.bank_addr_size):
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",
"clk_buf_bar","clk_buf" , "vdd", "gnd"])
self.connect_inst(temp)
return bank_inst
def add_row_addr_dff(self, position):
""" Add and place all address flops for the main decoder """
self.row_addr_dff_inst = self.add_inst(name="row_address",
mod=self.row_addr_dff,
offset=position)
# inputs, outputs/output/bar
inputs = []
outputs = []
for i in range(self.row_addr_size):
inputs.append("ADDR[{}]".format(i+self.col_addr_size))
outputs.append("A[{}]".format(i+self.col_addr_size))
# FIXME clk->clk_buf
self.connect_inst(inputs + outputs + ["clk", "vdd", "gnd"])
def add_col_addr_dff(self, position):
""" Add and place all address flops for the column decoder """
self.col_addr_dff_inst = self.add_inst(name="row_address",
mod=self.col_addr_dff,
offset=position)
# inputs, outputs/output/bar
inputs = []
outputs = []
for i in range(self.col_addr_size):
inputs.append("ADDR[{}]".format(i))
outputs.append("A[{}]".format(i))
# FIXME clk->clk_buf
self.connect_inst(inputs + outputs + ["clk", "vdd", "gnd"])
def add_data_dff(self, position):
""" Add and place all data flops """
self.data_dff_inst = self.add_inst(name="data_dff",
mod=self.data_dff,
offset=position)
# inputs, outputs/output/bar
inputs = []
outputs = []
for i in range(self.word_size):
inputs.append("DIN[{}]".format(i))
outputs.append("BANK_DIN[{}]".format(i))
# FIXME clk->clk_buf_bar
self.connect_inst(inputs + outputs + ["clk", "vdd", "gnd"])
def add_control_logic(self, position):
""" Add and place control logic """
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"])
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 = dest_pin.center()
self.add_wire(("metal3","via2","metal2"),[in_pos, vector(out_pos.x,in_pos.y),out_pos])
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):
""" 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 sp_write(self, sp_name):
# Write the entire spice of the object to the file
############################################################
# Spice circuit
############################################################
sp = open(sp_name, 'w')
sp.write("**************************************************\n")
sp.write("* OpenRAM generated memory.\n")
sp.write("* Words: {}\n".format(self.num_words))
sp.write("* Data bits: {}\n".format(self.word_size))
sp.write("* Banks: {}\n".format(self.num_banks))
sp.write("* Column mux: {}:1\n".format(self.words_per_row))
sp.write("**************************************************\n")
# This causes unit test mismatch
# sp.write("* Created: {0}\n".format(datetime.datetime.now()))
# sp.write("* User: {0}\n".format(getpass.getuser()))
# sp.write(".global {0} {1}\n".format(spice["vdd_name"],
# spice["gnd_name"]))
usedMODS = list()
self.sp_write_file(sp, usedMODS)
del usedMODS
sp.close()
def analytical_delay(self,slew,load):
""" LH and HL are the same in analytical model. """
return self.bank.analytical_delay(slew,load)

View File

@ -18,22 +18,22 @@ class multi_bank_test(openram_test):
global verify
import verify
import bank
from bank 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="bank1_multi")
a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=2, name="bank1_multi")
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="bank2_multi")
a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=2, name="bank2_multi")
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="bank3_multi")
a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=2, name="bank3_multi")
self.local_check(a)
debug.info(1, "Eight way column mux")
a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4_multi")
a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4_multi")
self.local_check(a)
globals.end_openram()

View File

@ -18,23 +18,23 @@ class single_bank_test(openram_test):
global verify
import verify
import bank
from bank import bank
debug.info(1, "No column mux")
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_single")
a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_single")
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=1, name="bank2_single")
a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single")
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=1, name="bank3_single")
a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3_single")
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=1, name="bank4_single")
a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single")
self.local_check(a)
globals.end_openram()

View File

@ -18,23 +18,23 @@ class sram_1bank_test(openram_test):
global verify
import verify
import sram
from sram import sram
debug.info(1, "Single bank, no column mux with control logic")
a = sram.sram(word_size=4, num_words=16, num_banks=1, name="sram1")
a = sram(word_size=4, num_words=16, num_banks=1, name="sram1")
self.local_check(a, final_verification=True)
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="sram2")
a = sram(word_size=4, num_words=32, num_banks=1, name="sram2")
self.local_check(a, final_verification=True)
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="sram3")
a = sram(word_size=4, num_words=64, num_banks=1, name="sram3")
self.local_check(a, final_verification=True)
# 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="sram4")
# self.local_check(a, final_verification=True)
debug.info(1, "Single bank, eight way column mux with control logic")
a = sram(word_size=2, num_words=128, num_banks=1, name="sram4")
self.local_check(a, final_verification=True)
globals.end_openram()

View File

@ -19,23 +19,23 @@ class sram_2bank_test(openram_test):
global verify
import verify
import sram
from sram import sram
debug.info(1, "Two bank, no column mux with control logic")
a = sram.sram(word_size=16, num_words=32, num_banks=2, name="sram1")
a = sram(word_size=16, num_words=32, num_banks=2, name="sram1")
self.local_check(a, final_verification=True)
debug.info(1, "Two bank two way column mux with control logic")
a = sram.sram(word_size=16, num_words=64, num_banks=2, name="sram2")
a = sram(word_size=16, num_words=64, num_banks=2, name="sram2")
self.local_check(a, final_verification=True)
debug.info(1, "Two bank, four way column mux with control logic")
a = sram.sram(word_size=16, num_words=128, num_banks=2, name="sram3")
a = sram(word_size=16, num_words=128, num_banks=2, name="sram3")
self.local_check(a, final_verification=True)
# 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="sram4")
# self.local_check(a, final_verification=True)
debug.info(1, "Two bank, eight way column mux with control logic")
a = sram(word_size=2, num_words=256, num_banks=2, name="sram4")
self.local_check(a, final_verification=True)
globals.end_openram()

View File

@ -19,23 +19,23 @@ class sram_4bank_test(openram_test):
global verify
import verify
import sram
from sram import sram
debug.info(1, "Four bank, no column mux with control logic")
a = sram.sram(word_size=16, num_words=64, num_banks=4, name="sram1")
a = sram(word_size=16, num_words=64, num_banks=4, name="sram1")
self.local_check(a, final_verification=True)
debug.info(1, "Four bank two way column mux with control logic")
a = sram.sram(word_size=16, num_words=128, num_banks=4, name="sram2")
a = sram(word_size=16, num_words=128, num_banks=4, name="sram2")
self.local_check(a, final_verification=True)
debug.info(1, "Four bank, four way column mux with control logic")
a = sram.sram(word_size=16, num_words=256, num_banks=4, name="sram3")
a = sram(word_size=16, num_words=256, num_banks=4, name="sram3")
self.local_check(a, final_verification=True)
# 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="sram4")
# self.local_check(a, final_verification=True)
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="sram4")
self.local_check(a, final_verification=True)
globals.end_openram()

View File

@ -39,16 +39,16 @@ class timing_sram_test(openram_test):
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
probe_address = "1" * s.addr_size
probe_data = s.word_size - 1
probe_address = "1" * s.s.addr_size
probe_data = s.s.word_size - 1
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay(s,tempspice,corner)
d = delay(s.s, tempspice, corner)
import tech
loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data,slews,loads)
data = d.analyze(probe_address, probe_data, slews, loads)
#print data
if OPTS.tech_name == "freepdk45":
golden_data = {'leakage_power': 0.0006964536000000001,

View File

@ -37,16 +37,16 @@ class timing_sram_test(openram_test):
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
probe_address = "1" * s.addr_size
probe_data = s.word_size - 1
probe_address = "1" * s.s.addr_size
probe_data = s.s.word_size - 1
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay(s,tempspice,corner)
d = delay(s.s, tempspice, corner)
import tech
loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data,slews,loads)
data = d.analyze(probe_address, probe_data, slews, loads)
#print data
if OPTS.tech_name == "freepdk45":
golden_data = {'leakage_power': 0.0007348262,

View File

@ -28,7 +28,7 @@ class lib_test(openram_test):
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=True)
lib(out_dir=OPTS.openram_temp, sram=s.s, sp_file=tempspice, use_model=True)
# get all of the .lib files generated
files = os.listdir(OPTS.openram_temp)

View File

@ -37,7 +37,7 @@ class lib_test(openram_test):
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False)
lib(out_dir=OPTS.openram_temp, sram=s.s, sp_file=tempspice, use_model=False)
# get all of the .lib files generated
files = os.listdir(OPTS.openram_temp)

View File

@ -37,7 +37,7 @@ class lib_test(openram_test):
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False)
lib(out_dir=OPTS.openram_temp, sram=s.s, sp_file=tempspice, use_model=False)
# get all of the .lib files generated
files = os.listdir(OPTS.openram_temp)

View File

@ -11,6 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 24_lef_sram_test")
class lef_test(openram_test):
def runTest(self):

View File

@ -1,3 +0,0 @@
Note that the tests turn off DRC/LVS when they perform their own check
for performance improvement. However, it must be turned back on before
the test runs an assert.

View File

@ -17,7 +17,7 @@ class openram_test(unittest.TestCase):
result=verify.run_drc(w.name, tempgds)
if result != 0:
self.fail("DRC failed: {}".format(a.name))
self.fail("DRC failed: {}".format(w.name))
self.cleanup()
@ -163,6 +163,11 @@ class openram_test(unittest.TestCase):
def header(filename, technology):
# Skip the header for gitlab regression
import getpass
if getpass.getuser() == "gitlab-runner":
return
tst = "Running Test for:"
print("\n")
print(" ______________________________________________________________________________ ")

View File

@ -173,8 +173,13 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
'lvsMaskDBFile': OPTS.openram_temp + cell_name + ".maskdb",
'cmnFDILayerMapFile': drc["layer_map"],
'cmnFDIUseLayerMap': 1,
'lvsRecognizeGates': 'NONE'
#'cmnVConnectNamesState' : 'ALL', #connects all nets with the same name
'cmnTranscriptFile': './lvs.log',
'cmnTranscriptEchoToFile': 1,
'lvsRecognizeGates': 'NONE',
# FIXME: Remove when vdd/gnd connected
'cmnVConnectNamesState' : 'ALL', #connects all nets with the same namee
# FIXME: Remove when vdd/gnd connected
'lvsAbortOnSupplyError' : 0
}
# This should be removed for final verification
@ -184,7 +189,6 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
lvs_runset['cmnVConnectNames']='vdd gnd'
# write the runset file
f = open(OPTS.openram_temp + "lvs_runset", "w")
for k in sorted(iter(lvs_runset.keys())):
@ -260,8 +264,19 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
debug.error(e.strip("\n"))
out_errors = len(stdouterrors)
total_errors = summary_errors + out_errors + ext_errors
if total_errors > 0:
debug.error("{0}\tSummary: {1}\tOutput: {2}\tExtraction: {3}".format(cell_name,
summary_errors,
out_errors,
ext_errors))
else:
debug.info(1, "{0}\tSummary: {1}\tOutput: {2}\tExtraction: {3}".format(cell_name,
summary_errors,
out_errors,
ext_errors))
return total_errors

View File

@ -272,7 +272,9 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
# Just print out the whole file, it is short.
for e in results:
debug.info(1,e.strip("\n"))
debug.error("LVS mismatch (results in {})".format(resultsfile))
debug.error("{0}\tLVS mismatch (results in {1})".format(cell_name,resultsfile))
else:
debug.info(1, "{0}\tLVS matches".format(cell_name))
return total_errors