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 """ shift the origin in the lowest left corner """
offset = self.find_lowest_coords() offset = self.find_lowest_coords()
self.translate_all(offset) self.translate_all(offset)
return offset
def get_gate_offset(self, x_offset, height, inv_num): def get_gate_offset(self, x_offset, height, inv_num):
"""Gets the base offset and y orientation of stacked rows of gates """Gets the base offset and y orientation of stacked rows of gates
@ -110,7 +111,7 @@ class layout(lef.lef):
inst.offset = vector(inst.offset - offset) inst.offset = vector(inst.offset - offset)
# The instances have a precomputed boundary that we need to update. # The instances have a precomputed boundary that we need to update.
if inst.__class__.__name__ == "instance": 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(): for pin_name in self.pin_map.keys():
# All the pins are absolute coordinates that need to be updated. # All the pins are absolute coordinates that need to be updated.
pin_list = self.pin_map[pin_name] 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): def create_horizontal_pin_bus(self, layer, pitch, offset, names, length):
""" Create a horizontal bus of pins. """ """ 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): def create_vertical_pin_bus(self, layer, pitch, offset, names, length):
""" Create a horizontal bus of pins. """ """ 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): def create_vertical_bus(self, layer, pitch, offset, names, length):
""" Create a horizontal bus. """ """ 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. """ """ 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): 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 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. 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 # half minwidth so we can return the center line offsets
@ -563,7 +566,8 @@ class layout(lef.lef):
self.add_rect(layer=layer, self.add_rect(layer=layer,
offset=line_offset, offset=line_offset,
height=length) 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: else:
for i in range(len(names)): for i in range(len(names)):
line_offset = offset + vector(0,i*pitch + half_minwidth) line_offset = offset + vector(0,i*pitch + half_minwidth)
@ -576,10 +580,241 @@ class layout(lef.lef):
self.add_rect(layer=layer, self.add_rect(layer=layer,
offset=line_offset, offset=line_offset,
width=length) 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 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"): def add_enclosure(self, insts, layer="nwell"):
""" Add a layer that surrounds the given instances. Useful """ Add a layer that surrounds the given instances. Useful
for creating wells, for example. Doesn't check for minimum widths or for creating wells, for example. Doesn't check for minimum widths or
@ -600,6 +835,20 @@ class layout(lef.lef):
width=xmax-xmin, width=xmax-xmin,
height=ymax-ymin) 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): def add_power_ring(self, bbox):
""" """
Create vdd and gnd power rings around an area of the bounding box argument. Must 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") self.sf.write("\n* SRAM output loads\n")
for i in range(self.word_size): for i in range(self.word_size):
self.sf.write("CD{0} d[{0}] 0 {1}f\n".format(i,self.load)) self.sf.write("CD{0} DOUT[{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)
def write_delay_stimulus(self): def write_delay_stimulus(self):
@ -112,11 +108,11 @@ class delay():
for i in range(self.word_size): for i in range(self.word_size):
if i == self.probe_data: if i == self.probe_data:
self.gen_data(clk_times=self.cycle_times, self.gen_data(clk_times=self.cycle_times,
sig_name="data[{0}]".format(i)) sig_name="DIN[{0}]".format(i))
else: else:
self.stim.gen_constant(sig_name="d[{0}]".format(i), self.stim.gen_constant(sig_name="DIN[{0}]".format(i),
v_val=0) v_val=0)
self.gen_addr(clk_times=self.cycle_times, self.gen_addr(clk_times=self.cycle_times,
@ -172,7 +168,7 @@ class delay():
# generate data and addr signals # generate data and addr signals
self.sf.write("\n* Generation of data and address signals\n") self.sf.write("\n* Generation of data and address signals\n")
for i in range(self.word_size): 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) v_val=0)
for i in range(self.addr_size): for i in range(self.addr_size):
self.stim.gen_constant(sig_name="A[{0}]".format(i), self.stim.gen_constant(sig_name="A[{0}]".format(i),
@ -208,7 +204,7 @@ class delay():
# Trigger on the clk of the appropriate cycle # Trigger on the clk of the appropriate cycle
trig_name = "clk" 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 trig_val = targ_val = 0.5 * self.vdd_voltage
# Delay the target to measure after the negative edge # Delay the target to measure after the negative edge
@ -338,6 +334,7 @@ class delay():
# Checking from not data_value to data_value # Checking from not data_value to data_value
self.write_delay_stimulus() self.write_delay_stimulus()
self.stim.run_sim() self.stim.run_sim()
delay_hl = parse_spice_list("timing", "delay_hl") delay_hl = parse_spice_list("timing", "delay_hl")
delay_lh = parse_spice_list("timing", "delay_lh") 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] 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) 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): def gen_oeb(self, clk_times):
""" Generates the PWL WEb signal """ """ Generates the PWL WEb signal """
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP # values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP

View File

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

View File

@ -34,12 +34,14 @@ class stimuli():
""" Function to instatiate an SRAM subckt. """ """ Function to instatiate an SRAM subckt. """
self.sf.write("Xsram ") self.sf.write("Xsram ")
for i in range(dbits): for i in range(dbits):
self.sf.write("D[{0}] ".format(i)) self.sf.write("DIN[{0}] ".format(i))
for i in range(abits): for i in range(abits):
self.sf.write("A[{0}] ".format(i)) self.sf.write("A[{0}] ".format(i))
for i in tech.spice["control_signals"]: for i in tech.spice["control_signals"]:
self.sf.write("{0} ".format(i)) self.sf.write("{0} ".format(i))
self.sf.write("{0} ".format(tech.spice["clk"])) 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} {1} ".format(self.vdd_name, self.gnd_name))
self.sf.write("{0}\n".format(sram_name)) self.sf.write("{0}\n".format(sram_name))
@ -111,23 +113,6 @@ class stimuli():
"test"+self.gnd_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): def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
""" """
Generates a periodic signal with 50% duty cycle and slew rates. Period is measured Generates a periodic signal with 50% duty cycle and slew rates. Period is measured

View File

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

View File

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

View File

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

View File

@ -9,24 +9,20 @@ from globals import OPTS
class delay_chain(design.design): class delay_chain(design.design):
""" """
Generate a delay chain with the given number of stages and fanout. 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 (fanout) of each stage.
Input is a list contains the electrical effort of each stage. Usually, this will be constant, but it could have varied fanout.
""" """
def __init__(self, fanout_list, name="delay_chain"): def __init__(self, fanout_list, name="delay_chain"):
"""init function""" """init function"""
design.design.__init__(self, name) 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: 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. # number of inverters including any fanout loads.
self.fanout_list = fanout_list 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 from importlib import reload
c = reload(__import__(OPTS.bitcell)) c = reload(__import__(OPTS.bitcell))
@ -35,6 +31,7 @@ class delay_chain(design.design):
self.add_pins() self.add_pins()
self.create_module() self.create_module()
self.add_inverters()
self.route_inverters() self.route_inverters()
self.add_layout_pins() self.add_layout_pins()
self.DRC_LVS() self.DRC_LVS()
@ -52,14 +49,11 @@ class delay_chain(design.design):
self.inv = pinv(route_output=False) self.inv = pinv(route_output=False)
self.add_mod(self.inv) self.add_mod(self.inv)
# half chain length is the width of the layout # Each stage is a a row
# invs are stacked into 2 levels so input/output are close
# extra metal is for the gnd connection U
self.height = len(self.fanout_list)*self.inv.height 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.width = (max(self.fanout_list)+1) * self.inv.width
self.add_inverters()
def add_inverters(self): def add_inverters(self):
""" Add the inverters and connect them based on the stage list """ """ 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 """ Add vdd and gnd rails and the input/output. Connect the gnd rails internally on
the top end with no input/output to obstruct. """ the top end with no input/output to obstruct. """
for driver in self.driver_inst_list: # Add power and ground to all the cells except:
vdd_pin = driver.get_pin("vdd") # the fanout driver, the right-most load
self.add_layout_pin(text="vdd", # The routing to connect the loads is over the first and last cells
layer="metal1", # We have an even number of drivers and must only do every other
offset=vdd_pin.ll(), # supply rail
width=self.width, for i in range(0,len(self.driver_inst_list),2):
height=vdd_pin.height()) inv = self.driver_inst_list[i]
gnd_pin = driver.get_pin("gnd") for load in self.load_inst_map[inv]:
self.add_layout_pin(text="gnd", if load==self.rightest_load_inst[inv]:
layer="metal1", continue
offset=gnd_pin.ll(), for pin_name in ["vdd", "gnd"]:
width=self.width, pin = load.get_pin(pin_name)
height=gnd_pin.height()) 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 # input is A pin of first inverter
a_pin = self.driver_inst_list[0].get_pin("A") a_pin = self.driver_inst_list[0].get_pin("A")

View File

@ -18,7 +18,7 @@ class dff_array(design.design):
if name=="": if name=="":
name = "dff_array_{0}x{1}".format(rows, columns) name = "dff_array_{0}x{1}".format(rows, columns)
design.design.__init__(self, name) 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 from importlib import reload
c = reload(__import__(OPTS.dff)) c = reload(__import__(OPTS.dff))
@ -38,33 +38,33 @@ class dff_array(design.design):
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
for y in range(self.rows): for row in range(self.rows):
for x in range(self.columns): for col in range(self.columns):
self.add_pin(self.get_din_name(y,x)) self.add_pin(self.get_din_name(row,col))
for y in range(self.rows): for row in range(self.rows):
for x in range(self.columns): for col in range(self.columns):
self.add_pin(self.get_dout_name(y,x)) self.add_pin(self.get_dout_name(row,col))
self.add_pin("clk") self.add_pin("clk")
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def create_dff_array(self): def create_dff_array(self):
self.dff_insts={} self.dff_insts={}
for y in range(self.rows): for row in range(self.rows):
for x in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(y,x) name = "Xdff_r{0}_c{1}".format(row,col)
if (y % 2 == 0): if (row % 2 == 0):
base = vector(x*self.dff.width,y*self.dff.height) base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0" mirror = "R0"
else: 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" 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, mod=self.dff,
offset=base, offset=base,
mirror=mirror) mirror=mirror)
self.connect_inst([self.get_din_name(y,x), self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(y,x), self.get_dout_name(row,col),
"clk", "clk",
"vdd", "vdd",
"gnd"]) "gnd"])
@ -91,38 +91,30 @@ class dff_array(design.design):
def add_layout_pins(self): 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.center())
for y in range(self.rows): # Continous gnd rail along with label.
# Continous vdd rail along with label. gnd_pin=self.dff_insts[row,col].get_pin("gnd")
vdd_pin=self.dff_insts[0,y].get_pin("vdd") self.add_power_pin("gnd", gnd_pin.center())
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=self.width,
height=self.m1_width)
# 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)
for y in range(self.rows): for row in range(self.rows):
for x in range(self.columns): for col in range(self.columns):
din_pin = self.dff_insts[x,y].get_pin("D") din_pin = self.dff_insts[row,col].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2") 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, layer=din_pin.layer,
offset=din_pin.ll(), offset=din_pin.ll(),
width=din_pin.width(), width=din_pin.width(),
height=din_pin.height()) 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") 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, layer=dout_pin.layer,
offset=dout_pin.ll(), offset=dout_pin.ll(),
width=dout_pin.width(), width=dout_pin.width(),
@ -140,22 +132,20 @@ class dff_array(design.design):
width=self.m2_width, width=self.m2_width,
height=self.height) height=self.height)
else: else:
self.add_layout_pin(text="clk", self.add_layout_pin_segment_center(text="clk",
layer="metal3", layer="metal3",
offset=vector(0,0), start=vector(0,self.m3_pitch+self.m3_width),
width=self.width, end=vector(self.width,self.m3_pitch+self.m3_width))
height=self.m3_width) for col in range(self.columns):
for x in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk")
clk_pin = self.dff_insts[x,0].get_pin("clk")
# Make a vertical strip for each column # Make a vertical strip for each column
self.add_layout_pin(text="clk", self.add_rect(layer="metal2",
layer="metal2", offset=clk_pin.ll().scale(1,0),
offset=clk_pin.ll().scale(1,0), width=self.m2_width,
width=self.m2_width, height=self.height)
height=self.height)
# Drop a via to the M3 pin # Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"), 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 self.columns = columns
if name=="": 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) design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
@ -36,35 +36,35 @@ class dff_buf_array(design.design):
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
for y in range(self.rows): for row in range(self.rows):
for x in range(self.columns): for col in range(self.columns):
self.add_pin(self.get_din_name(y,x)) self.add_pin(self.get_din_name(row,col))
for y in range(self.rows): for row in range(self.rows):
for x in range(self.columns): for col in range(self.columns):
self.add_pin(self.get_dout_name(y,x)) self.add_pin(self.get_dout_name(row,col))
self.add_pin(self.get_dout_bar_name(y,x)) self.add_pin(self.get_dout_bar_name(row,col))
self.add_pin("clk") self.add_pin("clk")
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def create_dff_array(self): def create_dff_array(self):
self.dff_insts={} self.dff_insts={}
for y in range(self.rows): for row in range(self.rows):
for x in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(y,x) name = "Xdff_r{0}_c{1}".format(row,col)
if (y % 2 == 0): if (row % 2 == 0):
base = vector(x*self.dff.width,y*self.dff.height) base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0" mirror = "R0"
else: 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" 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, mod=self.dff,
offset=base, offset=base,
mirror=mirror) mirror=mirror)
self.connect_inst([self.get_din_name(y,x), self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(y,x), self.get_dout_name(row,col),
self.get_dout_bar_name(y,x), self.get_dout_bar_name(row,col),
"clk", "clk",
"vdd", "vdd",
"gnd"]) "gnd"])
@ -100,58 +100,38 @@ class dff_buf_array(design.design):
return dout_bar_name return dout_bar_name
def add_layout_pins(self): 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 = [] # Continous gnd rail along with label.
for x in range(self.columns): gnd_pin=self.dff_insts[row,col].get_pin("gnd")
xoffsets.append(self.dff_insts[x,0].get_pin("gnd").lx()) self.add_power_pin("gnd", gnd_pin.lc())
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)
for row in range(self.rows):
for y in range(self.rows): for col in range(self.columns):
for x in range(self.columns): din_pin = self.dff_insts[row,col].get_pin("D")
din_pin = self.dff_insts[x,y].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2") 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, layer=din_pin.layer,
offset=din_pin.ll(), offset=din_pin.ll(),
width=din_pin.width(), width=din_pin.width(),
height=din_pin.height()) 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") 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, layer=dout_pin.layer,
offset=dout_pin.ll(), offset=dout_pin.ll(),
width=dout_pin.width(), width=dout_pin.width(),
height=dout_pin.height()) height=dout_pin.height())
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
dout_bar_pin = self.dff_insts[x,y].get_pin("Qb")
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2") 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, layer=dout_bar_pin.layer,
offset=dout_bar_pin.ll(), offset=dout_bar_pin.ll(),
width=dout_bar_pin.width(), width=dout_bar_pin.width(),
@ -168,22 +148,21 @@ class dff_buf_array(design.design):
width=self.m2_width, width=self.m2_width,
height=self.height) height=self.height)
else: else:
self.add_layout_pin(text="clk", self.add_layout_pin_segment_center(text="clk",
layer="metal3", layer="metal3",
offset=vector(0,2*self.m2_width), start=vector(0,self.m3_pitch+self.m3_width),
width=self.width, end=vector(self.width,self.m3_pitch+self.m3_width))
height=self.m3_width) for col in range(self.columns):
for x in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk")
clk_pin = self.dff_insts[x,0].get_pin("clk")
# Make a vertical strip for each column # Make a vertical strip for each column
self.add_layout_pin(text="clk", self.add_rect(layer="metal2",
layer="metal2", offset=clk_pin.ll().scale(1,0),
offset=clk_pin.ll().scale(1,0), width=self.m2_width,
width=self.m2_width, height=self.height)
height=self.height)
# Drop a via to the M3 pin # Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"), 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 self.columns = columns
if name=="": 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) design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
@ -36,35 +36,35 @@ class dff_inv_array(design.design):
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
for y in range(self.rows): for row in range(self.rows):
for x in range(self.columns): for col in range(self.columns):
self.add_pin(self.get_din_name(y,x)) self.add_pin(self.get_din_name(row,col))
for y in range(self.rows): for row in range(self.rows):
for x in range(self.columns): for col in range(self.columns):
self.add_pin(self.get_dout_name(y,x)) self.add_pin(self.get_dout_name(row,col))
self.add_pin(self.get_dout_bar_name(y,x)) self.add_pin(self.get_dout_bar_name(row,col))
self.add_pin("clk") self.add_pin("clk")
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def create_dff_array(self): def create_dff_array(self):
self.dff_insts={} self.dff_insts={}
for y in range(self.rows): for row in range(self.rows):
for x in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(y,x) name = "Xdff_r{0}_c{1}".format(row,col)
if (y % 2 == 0): if (row % 2 == 0):
base = vector(x*self.dff.width,y*self.dff.height) base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0" mirror = "R0"
else: 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" 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, mod=self.dff,
offset=base, offset=base,
mirror=mirror) mirror=mirror)
self.connect_inst([self.get_din_name(y,x), self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(y,x), self.get_dout_name(row,col),
self.get_dout_bar_name(y,x), self.get_dout_bar_name(row,col),
"clk", "clk",
"vdd", "vdd",
"gnd"]) "gnd"])
@ -100,47 +100,38 @@ class dff_inv_array(design.design):
return dout_bar_name return dout_bar_name
def add_layout_pins(self): 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())
for y in range(self.rows): # Continous gnd rail along with label.
# Continous vdd rail along with label. gnd_pin=self.dff_insts[row,col].get_pin("gnd")
vdd_pin=self.dff_insts[0,y].get_pin("vdd") self.add_power_pin("gnd", gnd_pin.lc())
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=self.width,
height=self.m1_width)
# 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)
for y in range(self.rows): for row in range(self.rows):
for x in range(self.columns): for col in range(self.columns):
din_pin = self.dff_insts[x,y].get_pin("D") din_pin = self.dff_insts[row,col].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2") 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, layer=din_pin.layer,
offset=din_pin.ll(), offset=din_pin.ll(),
width=din_pin.width(), width=din_pin.width(),
height=din_pin.height()) 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") 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, layer=dout_pin.layer,
offset=dout_pin.ll(), offset=dout_pin.ll(),
width=dout_pin.width(), width=dout_pin.width(),
height=dout_pin.height()) height=dout_pin.height())
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
dout_bar_pin = self.dff_insts[x,y].get_pin("Qb")
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2") 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, layer=dout_bar_pin.layer,
offset=dout_bar_pin.ll(), offset=dout_bar_pin.ll(),
width=dout_bar_pin.width(), width=dout_bar_pin.width(),
@ -157,22 +148,21 @@ class dff_inv_array(design.design):
width=self.m2_width, width=self.m2_width,
height=self.height) height=self.height)
else: else:
self.add_layout_pin(text="clk", self.add_layout_pin_segment_center(text="clk",
layer="metal3", layer="metal3",
offset=vector(0,0), start=vector(0,self.m3_pitch+self.m3_width),
width=self.width, end=vector(self.width,self.m3_pitch+self.m3_width))
height=self.m3_width) for col in range(self.columns):
for x in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk")
clk_pin = self.dff_insts[x,0].get_pin("clk")
# Make a vertical strip for each column # Make a vertical strip for each column
self.add_layout_pin(text="clk", self.add_rect(layer="metal2",
layer="metal2", offset=clk_pin.ll().scale(1,0),
offset=clk_pin.ll().scale(1,0), width=self.m2_width,
width=self.m2_width, height=self.height)
height=self.height)
# Drop a via to the M3 pin # Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"), 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) min_x = min(min_x, -self.pre3_8.width)
input_offset=vector(min_x - self.input_routing_width,0) input_offset=vector(min_x - self.input_routing_width,0)
input_bus_names = ["A[{0}]".format(i) for i in range(self.num_inputs)] input_bus_names = ["addr[{0}]".format(i) for i in range(self.num_inputs)]
self.create_vertical_pin_bus(layer="metal2", self.input_rails = self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=input_offset, offset=input_offset,
names=input_bus_names, names=input_bus_names,
length=input_height) length=input_height)
self.connect_input_to_predecodes() self.connect_input_to_predecodes()
@ -143,14 +143,15 @@ class hierarchical_decoder(design.design):
for i in range(2): for i in range(2):
index = pre_num * 2 + i 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) in_name = "in[{}]".format(i)
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
# Offset each decoder pin up so they don't conflit # To prevent conflicts, we will offset each input connect so
decoder_offset = decoder_pin.center() + vector(0,i*self.m2_pitch) # that it aligns with the vdd/gnd rails
input_offset = input_pin.center().scale(1,0) + decoder_offset.scale(0,1) 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) self.connect_input_rail(decoder_offset, input_offset)
@ -159,14 +160,15 @@ class hierarchical_decoder(design.design):
for i in range(3): for i in range(3):
index = pre_num * 3 + i + self.no_of_pre2x4 * 2 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) in_name = "in[{}]".format(i)
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
# Offset each decoder pin up so they don't conflit # To prevent conflicts, we will offset each input connect so
decoder_offset = decoder_pin.center() + vector(0,i*self.m2_pitch) # that it aligns with the vdd/gnd rails
input_offset = input_pin.center().scale(1,0) + decoder_offset.scale(0,1) 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) self.connect_input_rail(decoder_offset, input_offset)
@ -187,7 +189,7 @@ class hierarchical_decoder(design.design):
""" Add the module pins """ """ Add the module pins """
for i in range(self.num_inputs): 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): for j in range(self.rows):
self.add_pin("decode[{0}]".format(j)) self.add_pin("decode[{0}]".format(j))
@ -248,7 +250,7 @@ class hierarchical_decoder(design.design):
pins = [] pins = []
for input_index in range(2): 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): for output_index in range(4):
pins.append("out[{0}]".format(output_index + index_off2)) pins.append("out[{0}]".format(output_index + index_off2))
pins.extend(["vdd", "gnd"]) pins.extend(["vdd", "gnd"])
@ -275,7 +277,7 @@ class hierarchical_decoder(design.design):
pins = [] pins = []
for input_index in range(3): 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): for output_index in range(8):
pins.append("out[{0}]".format(output_index + out_index_offset)) pins.append("out[{0}]".format(output_index + out_index_offset))
pins.extend(["vdd", "gnd"]) 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. # This is not needed for inputs <4 since they have no pre/decode stages.
if (self.num_inputs >= 4): if (self.num_inputs >= 4):
# Array for saving the X offsets of the vertical rails. These rail input_offset = vector(0.5*self.m2_width,0)
# offsets are accessed with indices. input_bus_names = ["predecode[{0}]".format(i) for i in range(self.total_number_of_predecoder_outputs)]
self.rail_x_offsets = [] self.predecode_rails = self.create_vertical_pin_bus(layer="metal2",
for i in range(self.total_number_of_predecoder_outputs): pitch=self.m2_pitch,
# The offsets go into the negative x direction offset=input_offset,
# assuming the predecodes are placed at (self.internal_routing_width,0) names=input_bus_names,
x_offset = self.m2_pitch * i length=self.height)
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)
self.connect_rails_to_predecodes() self.connect_rails_to_predecodes()
self.connect_rails_to_decoder() self.connect_rails_to_decoder()
@ -434,20 +432,22 @@ class hierarchical_decoder(design.design):
def connect_rails_to_predecodes(self): def connect_rails_to_predecodes(self):
""" Iterates through all of the predecodes and connects to the rails including the offsets """ """ 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 pre_num in range(self.no_of_pre2x4):
for i in range(4): for i in range(4):
index = pre_num * 4 + i predecode_name = "predecode[{}]".format(pre_num * 4 + i)
out_name = "out[{}]".format(i) out_name = "out[{}]".format(i)
pin = self.pre2x4_inst[pre_num].get_pin(out_name) pin = self.pre2x4_inst[pre_num].get_pin(out_name)
self.connect_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 pre_num in range(self.no_of_pre3x8):
for i in range(8): 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) out_name = "out[{}]".format(i)
pin = self.pre3x8_inst[pre_num].get_pin(out_name) pin = self.pre3x8_inst[pre_num].get_pin(out_name)
self.connect_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): if (self.num_inputs == 4 or self.num_inputs == 5):
for index_A in self.predec_groups[0]: for index_A in self.predec_groups[0]:
for index_B in self.predec_groups[1]: for index_B in self.predec_groups[1]:
self.connect_predecode_rail(index_A, self.nand_inst[row_index].get_pin("A")) # FIXME: convert to connect_bus?
self.connect_predecode_rail(index_B, self.nand_inst[row_index].get_pin("B")) 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 row_index = row_index + 1
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
for index_A in self.predec_groups[0]: for index_A in self.predec_groups[0]:
for index_B in self.predec_groups[1]: for index_B in self.predec_groups[1]:
for index_C in self.predec_groups[2]: for index_C in self.predec_groups[2]:
self.connect_predecode_rail(index_A, self.nand_inst[row_index].get_pin("A")) # FIXME: convert to connect_bus?
self.connect_predecode_rail(index_B, self.nand_inst[row_index].get_pin("B")) predecode_name = "predecode[{}]".format(index_A)
self.connect_predecode_rail(index_C, self.nand_inst[row_index].get_pin("C")) 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 row_index = row_index + 1
def route_vdd_gnd(self): def route_vdd_gnd(self):
@ -509,19 +516,21 @@ class hierarchical_decoder(design.design):
self.copy_layout_pin(pre, "gnd") 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 """ """ 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_path("metal1", [rail_pos, pin.lc()])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_pos, offset=rail_pos,
rotate=90) 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 """ """ 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) 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"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin.center(), offset=pin.center(),
rotate=90) rotate=90)

View File

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

View File

@ -42,8 +42,9 @@ class replica_bitline(design.design):
#self.add_lvs_correspondence_points() #self.add_lvs_correspondence_points()
self.width = self.right_gnd_pin.rx() - self.left_gnd_pin.lx() # Plus a pitch for the WL contacts on the RBL
self.height = self.left_gnd_pin.uy() - self.left_gnd_pin.by() 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() self.DRC_LVS()
@ -58,14 +59,18 @@ class replica_bitline(design.design):
self.bitcell_offset = vector(0,self.replica_bitcell.height) self.bitcell_offset = vector(0,self.replica_bitcell.height)
self.rbl_offset = self.bitcell_offset 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 # 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 # 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 # 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): def route(self):
""" Connect all the signals together """ """ Connect all the signals together """
self.route_vdd() self.route_supplies()
self.route_gnd() self.route_wl()
self.route_vdd_gnd()
self.route_access_tx() 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 """ """ Propagate all vdd/gnd pins up to this level for all modules """
# These are the instances that every bank has # These are the instances that every bank has
top_instances = [self.rbl_inst, top_instances = [self.rbl_inst,
self.rbl_inv_inst,
self.rbc_inst,
self.dc_inst] self.dc_inst]
for inst in top_instances: for inst in top_instances:
self.copy_layout_pin(inst, "vdd") self.copy_layout_pin(inst, "vdd")
self.copy_layout_pin(inst, "gnd") 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): def route_access_tx(self):
# GATE ROUTE # GATE ROUTE
# 1. Add the poly contact and nwell enclosure # 1. Add the poly contact and nwell enclosure
@ -183,7 +216,7 @@ class replica_bitline(design.design):
# DRAIN ROUTE # DRAIN ROUTE
# Route the drain to the vdd rail # Route the drain to the vdd rail
drain_offset = self.tx_inst.get_pin("D").center() 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 # SOURCE ROUTE
# Route the drain to the RBL inverter input # 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) # Route the connection of the source route to the RBL bitline (left)
# Via will go halfway down from the bitcell # Via will go halfway down from the bitcell
bl_offset = self.rbc_inst.get_pin("BL").bc() 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"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=source_offset) 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 # BODY ROUTE
# Connect it to the inverter well # Connect it to the inverter well
@ -214,28 +245,8 @@ class replica_bitline(design.design):
def route_vdd(self): def route_vdd(self):
""" Route all signals connected to vdd """ """ Route all signals connected to vdd """
# Route the vdd lines from left to right self.copy_layout_pin(self.dc_inst,"vdd")
self.copy_layout_pin(self.rbc_inst,"vdd")
# 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)
@ -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 # Add via for the inverter
pin = self.rbl_inv_inst.get_pin("vdd") pin = self.rbl_inv_inst.get_pin("vdd")

View File

@ -40,7 +40,9 @@ report_status()
import verify import verify
import sram 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 # Keep track of running stats
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
@ -53,7 +55,7 @@ s = sram.sram(word_size=OPTS.word_size,
name=OPTS.output_name) name=OPTS.output_name)
# Output the files for the resulting SRAM # Output the files for the resulting SRAM
s.save_output() s.save()
# Delete temp files etc. # Delete temp files etc.
end_openram() end_openram()

View File

@ -2,477 +2,78 @@ import sys
import datetime import datetime
import getpass import getpass
import debug 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 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 This is not a design module, but contains an SRAM design instance.
number of banks should be 1 , 2 or 4 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): 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 # reset the static duplicate name checker for unit tests
# in case we create more than one SRAM # in case we create more than one SRAM
from design import design from design import design
design.name_map=[] design.name_map=[]
self.word_size = word_size debug.info(2, "create sram of size {0} with {1} num of words".format(word_size,
self.num_words = 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))
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
self.compute_sizes() self.name = name
if self.num_banks == 1: if num_banks == 1:
sram_1bank.__init__(self,name) from sram_1bank import sram_1bank
elif self.num_banks == 2: self.s=sram_1bank(word_size, num_words, name)
sram_2bank.__init__(self,name) elif num_banks == 2:
elif self.num_banks == 4: from sram_2bank import sram_2bank
sram_4bank.__init__(self,name) 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: else:
debug.error("Invalid number of banks.",-1) debug.error("Invalid number of banks.",-1)
self.control_size = 6 self.s.compute_sizes()
self.bank_to_bus_distance = 5*self.m3_width self.s.create_modules()
self.s.add_pins()
self.create_modules() self.s.create_layout()
self.add_pins()
self.create_layout()
# Can remove the following, but it helps for debug! # Can remove the following, but it helps for debug!
self.add_lvs_correspondence_points() self.s.add_lvs_correspondence_points()
self.offset_all_coordinates() self.s.offset_all_coordinates()
sizes = self.find_highest_coords() highest_coord = self.s.find_highest_coords()
self.width = sizes[0] self.s.width = highest_coord[0]
self.height = sizes[1] 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: if not OPTS.is_unit_test:
print_time("SRAM creation", datetime.datetime.now(), start_time) print_time("SRAM creation", datetime.datetime.now(), start_time)
def compute_sizes(self): def sp_write(self,name):
""" Computes the organization of the memory using bitcell size by trying to make it square.""" self.s.sp_write(name)
debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.") def gds_write(self,name):
self.s.gds_write(name)
self.num_words_per_bank = self.num_words/self.num_banks def verilog_write(self,name):
self.num_bits_per_bank = self.word_size*self.num_words_per_bank self.s.verilog_write(name)
# 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 def save(self):
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 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 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):
""" Save all the output files while reporting time to do it as well. """ """ Save all the output files while reporting time to do it as well. """
# Save the spice file # Save the spice file
start_time = datetime.datetime.now() 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)) 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) print_time("Spice writing", datetime.datetime.now(), start_time)
# Save the extracted spice file # Save the extracted spice file
@ -480,12 +81,12 @@ class sram(sram_1bank,sram_2bank,sram_4bank):
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
# Output the extracted design if requested # Output the extracted design if requested
sp_file = OPTS.output_path + "temp_pex.sp" 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) print_time("Extraction", datetime.datetime.now(), start_time)
else: else:
# Use generated spice file for characterization # Use generated spice file for characterization
sp_file = spname sp_file = spname
print(sys.path)
# Characterize the design # Characterize the design
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
from characterizer import lib 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)) print("Performing simulation-based characterization with {}".format(OPTS.spice_name))
if OPTS.trim_netlist: if OPTS.trim_netlist:
print("Trimming netlist to speed up characterization.") 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) print_time("Characterization", datetime.datetime.now(), start_time)
# Write the layout # Write the layout
start_time = datetime.datetime.now() 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)) print("GDS: Writing to {0}".format(gdsname))
self.gds_write(gdsname) self.s.gds_write(gdsname)
print_time("GDS", datetime.datetime.now(), start_time) print_time("GDS", datetime.datetime.now(), start_time)
# Create a LEF physical model # Create a LEF physical model
start_time = datetime.datetime.now() 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)) print("LEF: Writing to {0}".format(lefname))
self.lef_write(lefname) self.s.lef_write(lefname)
print_time("LEF", datetime.datetime.now(), start_time) print_time("LEF", datetime.datetime.now(), start_time)
# Write a verilog model # Write a verilog model
start_time = datetime.datetime.now() 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)) print("Verilog: Writing to {0}".format(vname))
self.verilog_write(vname) self.s.verilog_write(vname)
print_time("Verilog", datetime.datetime.now(), start_time) print_time("Verilog", datetime.datetime.now(), start_time)

View File

@ -7,18 +7,18 @@ import getpass
from vector import vector from vector import vector
from globals import OPTS, print_time from globals import OPTS, print_time
from design import design from sram_base import sram_base
from bank import bank from bank import bank
from dff_buf_array import dff_buf_array from dff_buf_array import dff_buf_array
from dff_array import dff_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): def __init__(self, word_size, num_words, name):
design.__init__(self, name) sram_base.__init__(self, word_size, num_words, 1, name)
def add_modules(self): def add_modules(self):
""" """
@ -29,37 +29,107 @@ class sram_1bank(design):
# No orientation or offset # No orientation or offset
self.bank_inst = self.add_bank(0, [0, 0], 1, 1) 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, 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) self.add_control_logic(position=control_pos)
# Leave room for the control routes to the left of the flops # 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) 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. # 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.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
self.height = self.bank.height 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. 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): 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())
for i in range(self.addr_size): # Lower address bits
self.copy_layout_pin(self.addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i)) 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())
def route(self): def route(self):
""" Route a single bank SRAM """ """ 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: for n in self.control_logic_outputs:
src_pin = self.control_logic_inst.get_pin(n) src_pin = self.control_logic_inst.get_pin(n)
dest_pin = self.bank_inst.get_pin(n) dest_pin = self.bank_inst.get_pin(n)
@ -69,33 +139,64 @@ class sram_1bank(design):
rotate=90) rotate=90)
# Connect the output of the flops to the bank pins def route_row_addr_dff(self):
for i in range(self.addr_size): """ Connect the output of the row flops to the bank pins """
for i in range(self.row_addr_size):
flop_name = "dout[{}]".format(i) flop_name = "dout[{}]".format(i)
bank_name = "A[{}]".format(i) bank_name = "addr[{}]".format(i+self.col_addr_size)
flop_pin = self.addr_dff_inst.get_pin(flop_name) flop_pin = self.row_addr_dff_inst.get_pin(flop_name)
bank_pin = self.bank_inst.get_pin(bank_name) bank_pin = self.bank_inst.get_pin(bank_name)
flop_pos = flop_pin.center() flop_pos = flop_pin.center()
bank_pos = vector(bank_pin.cx(),flop_pos.y) bank_pos = bank_pin.center()
self.add_path("metal3",[flop_pos, bank_pos]) 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"), self.add_via_center(layers=("metal2","via2","metal3"),
offset=flop_pos, offset=flop_pos,
rotate=90) rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=bank_pos,
rotate=90)
# Connect the control pins as inputs def route_col_addr_dff(self):
for n in self.control_logic_inputs + ["clk"]: """ Connect the output of the row flops to the bank pins """
self.copy_layout_pin(self.control_logic_inst, n)
# Connect the clock between the flops and control module bus_names = ["addr[{}]".format(x) for x in range(self.col_addr_size)]
flop_pin = self.addr_dff_inst.get_pin("clk") col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1",
ctrl_pin = self.control_logic_inst.get_pin("clk_buf") pitch=self.m1_pitch,
flop_pos = flop_pin.uc() offset=self.col_addr_dff_inst.ul() + vector(0, self.m1_pitch),
ctrl_pos = ctrl_pin.bc() names=bus_names,
mid_ypos = 0.5*(ctrl_pos.y+flop_pos.y) length=self.col_addr_dff_inst.width)
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()])
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 import sys
from tech import drc, spice from tech import drc, spice
import debug import debug
import design
from math import log,sqrt,ceil 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 datetime
import getpass import getpass
from vector import vector from vector import vector
from globals import OPTS, print_time 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. Procedures specific to a two bank SRAM.
""" """
def __init__(self, name): def __init__(self, word_size, num_words, name):
design.__init__(self, name) sram_base.__init__(self, word_size, num_words, 2, name)
def compute_bank_offsets(self): def compute_bank_offsets(self):
""" Compute the overall offsets for a two bank SRAM """ """ 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,23 +1,23 @@
import sys import sys
from tech import drc, spice from tech import drc, spice
import debug import debug
import design
from math import log,sqrt,ceil 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 datetime
import getpass import getpass
from vector import vector from vector import vector
from globals import OPTS, print_time 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. Procedures specific to a four bank SRAM.
""" """
def __init__(self, name): def __init__(self, word_size, num_words, name):
design.__init__(self, name) sram_base.__init__(self, word_size, num_words, 4, name)
def compute_bank_offsets(self): def compute_bank_offsets(self):
""" Compute the overall offsets for a four bank SRAM """ """ 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]) 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 global verify
import verify import verify
import bank from bank import bank
debug.info(1, "No column mux") debug.info(1, "No column mux")
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=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) self.local_check(a)
debug.info(1, "Two way column mux") debug.info(1, "Two way column mux")
a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=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) self.local_check(a)
debug.info(1, "Four way column mux") debug.info(1, "Four way column mux")
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=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) self.local_check(a)
debug.info(1, "Eight way column mux") debug.info(1, "Eight way column mux")
a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4_multi") a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4_multi")
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -18,23 +18,23 @@ class single_bank_test(openram_test):
global verify global verify
import verify import verify
import bank from bank import bank
debug.info(1, "No column mux") debug.info(1, "No column mux")
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_single") a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_single")
self.local_check(a) self.local_check(a)
debug.info(1, "Two way column mux") debug.info(1, "Two way column mux")
a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single") a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single")
self.local_check(a) self.local_check(a)
debug.info(1, "Four way column mux") debug.info(1, "Four way column mux")
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3_single") a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3_single")
self.local_check(a) self.local_check(a)
# Eight way has a short circuit of one column mux select to gnd rail # Eight way has a short circuit of one column mux select to gnd rail
debug.info(1, "Eight way column mux") debug.info(1, "Eight way column mux")
a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single") a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single")
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,7 +28,7 @@ class lib_test(openram_test):
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) 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 # get all of the .lib files generated
files = os.listdir(OPTS.openram_temp) files = os.listdir(OPTS.openram_temp)

View File

@ -37,7 +37,7 @@ class lib_test(openram_test):
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) 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 # get all of the .lib files generated
files = os.listdir(OPTS.openram_temp) files = os.listdir(OPTS.openram_temp)

View File

@ -37,7 +37,7 @@ class lib_test(openram_test):
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) 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 # get all of the .lib files generated
files = os.listdir(OPTS.openram_temp) files = os.listdir(OPTS.openram_temp)

View File

@ -11,6 +11,7 @@ import globals
from globals import OPTS from globals import OPTS
import debug import debug
@unittest.skip("SKIPPING 24_lef_sram_test")
class lef_test(openram_test): class lef_test(openram_test):
def runTest(self): 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) result=verify.run_drc(w.name, tempgds)
if result != 0: if result != 0:
self.fail("DRC failed: {}".format(a.name)) self.fail("DRC failed: {}".format(w.name))
self.cleanup() self.cleanup()
@ -163,6 +163,11 @@ class openram_test(unittest.TestCase):
def header(filename, technology): def header(filename, technology):
# Skip the header for gitlab regression
import getpass
if getpass.getuser() == "gitlab-runner":
return
tst = "Running Test for:" tst = "Running Test for:"
print("\n") print("\n")
print(" ______________________________________________________________________________ ") 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", 'lvsMaskDBFile': OPTS.openram_temp + cell_name + ".maskdb",
'cmnFDILayerMapFile': drc["layer_map"], 'cmnFDILayerMapFile': drc["layer_map"],
'cmnFDIUseLayerMap': 1, 'cmnFDIUseLayerMap': 1,
'lvsRecognizeGates': 'NONE' 'cmnTranscriptFile': './lvs.log',
#'cmnVConnectNamesState' : 'ALL', #connects all nets with the same name '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 # 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' lvs_runset['cmnVConnectNames']='vdd gnd'
# write the runset file # write the runset file
f = open(OPTS.openram_temp + "lvs_runset", "w") f = open(OPTS.openram_temp + "lvs_runset", "w")
for k in sorted(iter(lvs_runset.keys())): 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")) debug.error(e.strip("\n"))
out_errors = len(stdouterrors) out_errors = len(stdouterrors)
total_errors = summary_errors + out_errors + ext_errors 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 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. # Just print out the whole file, it is short.
for e in results: for e in results:
debug.info(1,e.strip("\n")) 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 return total_errors