mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' of https://github.com/VLSIDA/PrivateRAM into multiport
This commit is contained in:
commit
fb0de710ec
|
|
@ -39,6 +39,7 @@ class layout(lef.lef):
|
|||
shift the origin in the lowest left corner """
|
||||
offset = self.find_lowest_coords()
|
||||
self.translate_all(offset)
|
||||
return offset
|
||||
|
||||
def get_gate_offset(self, x_offset, height, inv_num):
|
||||
"""Gets the base offset and y orientation of stacked rows of gates
|
||||
|
|
@ -110,7 +111,7 @@ class layout(lef.lef):
|
|||
inst.offset = vector(inst.offset - offset)
|
||||
# The instances have a precomputed boundary that we need to update.
|
||||
if inst.__class__.__name__ == "instance":
|
||||
inst.compute_boundary(offset.scale(-1,-1))
|
||||
inst.compute_boundary(inst.offset)
|
||||
for pin_name in self.pin_map.keys():
|
||||
# All the pins are absolute coordinates that need to be updated.
|
||||
pin_list = self.pin_map[pin_name]
|
||||
|
|
@ -526,25 +527,27 @@ class layout(lef.lef):
|
|||
|
||||
def create_horizontal_pin_bus(self, layer, pitch, offset, names, length):
|
||||
""" Create a horizontal bus of pins. """
|
||||
self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=True)
|
||||
return self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=True)
|
||||
|
||||
def create_vertical_pin_bus(self, layer, pitch, offset, names, length):
|
||||
""" Create a horizontal bus of pins. """
|
||||
self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=True)
|
||||
return self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=True)
|
||||
|
||||
def create_vertical_bus(self, layer, pitch, offset, names, length):
|
||||
""" Create a horizontal bus. """
|
||||
self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=False)
|
||||
return self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=False)
|
||||
|
||||
def create_horiontal_bus(self, layer, pitch, offset, names, length):
|
||||
def create_horizontal_bus(self, layer, pitch, offset, names, length):
|
||||
""" Create a horizontal bus. """
|
||||
self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=False)
|
||||
return self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=False)
|
||||
|
||||
|
||||
def create_bus(self, layer, pitch, offset, names, length, vertical, make_pins):
|
||||
"""
|
||||
Create a horizontal or vertical bus. It can be either just rectangles, or actual
|
||||
layout pins. It returns an map of line center line positions indexed by name.
|
||||
The other coordinate is a 0 since the bus provides a range.
|
||||
TODO: combine with channel router.
|
||||
"""
|
||||
|
||||
# half minwidth so we can return the center line offsets
|
||||
|
|
@ -563,7 +566,8 @@ class layout(lef.lef):
|
|||
self.add_rect(layer=layer,
|
||||
offset=line_offset,
|
||||
height=length)
|
||||
line_positions[names[i]]=line_offset+vector(half_minwidth,0)
|
||||
# Make this the center of the rail
|
||||
line_positions[names[i]]=line_offset+vector(half_minwidth,0.5*length)
|
||||
else:
|
||||
for i in range(len(names)):
|
||||
line_offset = offset + vector(0,i*pitch + half_minwidth)
|
||||
|
|
@ -576,10 +580,241 @@ class layout(lef.lef):
|
|||
self.add_rect(layer=layer,
|
||||
offset=line_offset,
|
||||
width=length)
|
||||
line_positions[names[i]]=line_offset+vector(0,half_minwidth)
|
||||
# Make this the center of the rail
|
||||
line_positions[names[i]]=line_offset+vector(0.5*length,half_minwidth)
|
||||
|
||||
return line_positions
|
||||
|
||||
def connect_horizontal_bus(self, mapping, inst, bus_offsets,
|
||||
layer_stack=("metal1","via1","metal2")):
|
||||
""" Horizontal version of connect_bus. """
|
||||
self.connect_bus(mapping, inst, bus_offsets, layer_stack, True)
|
||||
|
||||
def connect_vertical_bus(self, mapping, inst, bus_offsets,
|
||||
layer_stack=("metal1","via1","metal2")):
|
||||
""" Vertical version of connect_bus. """
|
||||
self.connect_bus(mapping, inst, bus_offsets, layer_stack, False)
|
||||
|
||||
def connect_bus(self, mapping, inst, bus_offsets, layer_stack, horizontal):
|
||||
"""
|
||||
Connect a mapping of pin -> name for a bus. This could be
|
||||
replaced with a channel router in the future.
|
||||
"""
|
||||
(horizontal_layer, via_layer, vertical_layer)=layer_stack
|
||||
if horizontal:
|
||||
route_layer = vertical_layer
|
||||
else:
|
||||
route_layer = horizontal_layer
|
||||
|
||||
for (pin_name, bus_name) in mapping:
|
||||
pin = inst.get_pin(pin_name)
|
||||
pin_pos = pin.center()
|
||||
bus_pos = bus_offsets[bus_name]
|
||||
|
||||
if horizontal:
|
||||
# up/down then left/right
|
||||
mid_pos = vector(pin_pos.x, bus_pos.y)
|
||||
else:
|
||||
# left/right then up/down
|
||||
mid_pos = vector(bus_pos.x, pin_pos.y)
|
||||
|
||||
self.add_wire(layer_stack,[bus_pos, mid_pos, pin_pos])
|
||||
|
||||
# Connect to the pin on the instances with a via if it is
|
||||
# not on the right layer
|
||||
if pin.layer != route_layer:
|
||||
self.add_via_center(layers=layer_stack,
|
||||
offset=pin_pos)
|
||||
# FIXME: output pins tend to not be rotate, but supply pins are. Make consistent?
|
||||
|
||||
|
||||
|
||||
# We only need a via if they happened to align perfectly
|
||||
# so the add_wire didn't add a via
|
||||
if (horizontal and bus_pos.y == pin_pos.y) or (not horizontal and bus_pos.x == pin_pos.x):
|
||||
self.add_via_center(layers=layer_stack,
|
||||
offset=bus_pos,
|
||||
rotate=90)
|
||||
|
||||
def add_horizontal_trunk_route(self, pins, trunk_offset,
|
||||
layer_stack=("metal1", "via1", "metal2"),
|
||||
pitch=None):
|
||||
"""
|
||||
Create a trunk route for all pins with the the trunk located at the given y offset.
|
||||
"""
|
||||
if not pitch:
|
||||
pitch = self.m1_pitch
|
||||
|
||||
max_x = max([pin.center().x for pin in pins])
|
||||
min_x = min([pin.center().x for pin in pins])
|
||||
|
||||
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[0])]
|
||||
|
||||
# if we are less than a pitch, just create a non-preferred layer jog
|
||||
if max_x-min_x < pitch:
|
||||
# Add the horizontal trunk on the vertical layer!
|
||||
self.add_path(layer_stack[2],[vector(min_x-half_minwidth,trunk_offset.y), vector(max_x+half_minwidth,trunk_offset.y)])
|
||||
|
||||
# Route each pin to the trunk
|
||||
for pin in pins:
|
||||
# No bend needed here
|
||||
mid = vector(pin.center().x, trunk_offset.y)
|
||||
self.add_path(layer_stack[2], [pin.center(), mid])
|
||||
else:
|
||||
# Add the horizontal trunk
|
||||
self.add_path(layer_stack[0],[vector(min_x,trunk_offset.y), vector(max_x,trunk_offset.y)])
|
||||
trunk_mid = vector(0.5*(max_x+min_x),trunk_offset.y)
|
||||
|
||||
# Route each pin to the trunk
|
||||
for pin in pins:
|
||||
# Bend to the center of the trunk so it adds a via automatically
|
||||
mid = vector(pin.center().x, trunk_offset.y)
|
||||
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid])
|
||||
|
||||
def add_vertical_trunk_route(self, pins, trunk_offset,
|
||||
layer_stack=("metal1", "via1", "metal2"),
|
||||
pitch=None):
|
||||
"""
|
||||
Create a trunk route for all pins with the the trunk located at the given x offset.
|
||||
"""
|
||||
if not pitch:
|
||||
pitch = self.m2_pitch
|
||||
|
||||
max_y = max([pin.center().y for pin in pins])
|
||||
min_y = min([pin.center().y for pin in pins])
|
||||
|
||||
# Add the vertical trunk
|
||||
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[2])]
|
||||
|
||||
# if we are less than a pitch, just create a non-preferred layer jog
|
||||
if max_y-min_y < pitch:
|
||||
# Add the horizontal trunk on the vertical layer!
|
||||
self.add_path(layer_stack[0],[vector(trunk_offset.x,min_y-half_minwidth), vector(trunk_offset.x,max_y+half_minwidth)])
|
||||
|
||||
# Route each pin to the trunk
|
||||
for pin in pins:
|
||||
# No bend needed here
|
||||
mid = vector(trunk_offset.x, pin.center().y)
|
||||
self.add_path(layer_stack[0], [pin.center(), mid])
|
||||
else:
|
||||
# Add the vertical trunk
|
||||
self.add_path(layer_stack[2],[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)])
|
||||
trunk_mid = vector(trunk_offset.x,0.5*(max_y+min_y),)
|
||||
|
||||
# Route each pin to the trunk
|
||||
for pin in pins:
|
||||
# Bend to the center of the trunk so it adds a via automatically
|
||||
mid = vector(trunk_offset.x, pin.center().y)
|
||||
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid])
|
||||
|
||||
|
||||
def create_channel_route(self, route_map, top_pins, bottom_pins, offset,
|
||||
layer_stack=("metal1", "via1", "metal2"), pitch=None,
|
||||
vertical=False):
|
||||
"""
|
||||
This is a simple channel route for one-to-one connections that
|
||||
will jog the top route whenever there is a conflict. It does NOT
|
||||
try to minimize the number of tracks -- instead, it picks an order to avoid the vertical
|
||||
conflicts between pins.
|
||||
"""
|
||||
|
||||
def remove_pin_from_graph(pin, g):
|
||||
# Remove the pin from the keys
|
||||
g.pop(pin,None)
|
||||
# Remove the pin from all conflicts
|
||||
# This is O(n^2), so maybe optimize it.
|
||||
for other_pin,conflicts in g.items():
|
||||
if pin in conflicts:
|
||||
conflicts.remove(pin)
|
||||
vcg[other_pin]=conflicts
|
||||
|
||||
if not pitch:
|
||||
pitch = self.m2_pitch
|
||||
|
||||
# merge the two dictionaries to easily access all pins
|
||||
all_pins = {**top_pins, **bottom_pins}
|
||||
|
||||
# FIXME: Must extend this to a horizontal conflict graph too if we want to minimize the
|
||||
# number of tracks!
|
||||
#hcg = {}
|
||||
|
||||
# Initialize the vertical conflict graph (vcg) and make a list of all pins
|
||||
vcg = {}
|
||||
for (top_name, bot_name) in route_map:
|
||||
vcg[top_name] = []
|
||||
vcg[bot_name] = []
|
||||
|
||||
# Find the vertical pin conflicts
|
||||
# FIXME: O(n^2) but who cares for now
|
||||
for top_name,top_pin in top_pins.items():
|
||||
for bot_name,bot_pin in bottom_pins.items():
|
||||
if not vertical and abs(top_pin.center().x-bot_pin.center().x) < pitch:
|
||||
# The edges only go from top to bottom
|
||||
# since we will order tracks bottom up
|
||||
vcg[top_name].append(bot_name)
|
||||
elif vertical and abs(top_pin.center().y-bot_pin.center().y) < pitch:
|
||||
# The edges only go from top to bottom
|
||||
# since we will order tracks bottom up
|
||||
vcg[top_name].append(bot_name)
|
||||
|
||||
# This is the starting offset of the first trunk
|
||||
if vertical:
|
||||
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[2])]
|
||||
offset = offset + vector(half_minwidth,0)
|
||||
else:
|
||||
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[0])]
|
||||
offset = offset + vector(0,half_minwidth)
|
||||
|
||||
# list of routes to do
|
||||
while vcg:
|
||||
#print(vcg)
|
||||
# get a route from conflict graph with empty fanout set
|
||||
route_pin=None
|
||||
for route_pin,conflicts in vcg.items():
|
||||
if len(conflicts)==0:
|
||||
remove_pin_from_graph(route_pin,vcg)
|
||||
break
|
||||
|
||||
# Get the connected pins from the routing map
|
||||
for pin_connections in route_map:
|
||||
if route_pin in pin_connections:
|
||||
break
|
||||
#print("Routing:",route_pin,pin_connections)
|
||||
|
||||
# Remove the other pins from the conflict graph too
|
||||
for other_pin in pin_connections:
|
||||
remove_pin_from_graph(other_pin, vcg)
|
||||
|
||||
# Create a list of the pins rather than a list of the names
|
||||
pin_list = [all_pins[pin_name] for pin_name in pin_connections]
|
||||
|
||||
# Add the trunk route and move up to next track
|
||||
if vertical:
|
||||
self.add_vertical_trunk_route(pin_list, offset, layer_stack, pitch)
|
||||
offset += vector(pitch,0)
|
||||
else:
|
||||
self.add_horizontal_trunk_route(pin_list, offset, layer_stack, pitch)
|
||||
offset += vector(0,pitch)
|
||||
|
||||
|
||||
def create_vertical_channel_route(self, route_map, left_inst, right_inst, offset,
|
||||
layer_stack=("metal1", "via1", "metal2"),
|
||||
pitch=None):
|
||||
"""
|
||||
Wrapper to create a vertical channel route
|
||||
"""
|
||||
self.create_channel_route(route_map, left_inst, right_inst, offset,
|
||||
layer_stack, pitch, vertical=True)
|
||||
|
||||
def create_horizontal_channel_route(self, route_map, top_pins, bottom_pins, offset,
|
||||
layer_stack=("metal1", "via1", "metal2"),
|
||||
pitch=None):
|
||||
"""
|
||||
Wrapper to create a horizontal channel route
|
||||
"""
|
||||
self.create_channel_route(route_map, top_pins, bottom_pins, offset,
|
||||
layer_stack, pitch, vertical=False)
|
||||
|
||||
def add_enclosure(self, insts, layer="nwell"):
|
||||
""" Add a layer that surrounds the given instances. Useful
|
||||
for creating wells, for example. Doesn't check for minimum widths or
|
||||
|
|
@ -600,6 +835,20 @@ class layout(lef.lef):
|
|||
width=xmax-xmin,
|
||||
height=ymax-ymin)
|
||||
|
||||
def add_power_pin(self, name, loc, rotate=True):
|
||||
"""
|
||||
Add a single power pin from M3 own to M1
|
||||
"""
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=loc,
|
||||
rotate=90 if rotate else 0)
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=loc,
|
||||
rotate=90 if rotate else 0)
|
||||
self.add_layout_pin_rect_center(text=name,
|
||||
layer="metal3",
|
||||
offset=loc)
|
||||
|
||||
def add_power_ring(self, bbox):
|
||||
"""
|
||||
Create vdd and gnd power rings around an area of the bounding box argument. Must
|
||||
|
|
|
|||
|
|
@ -78,11 +78,7 @@ class delay():
|
|||
|
||||
self.sf.write("\n* SRAM output loads\n")
|
||||
for i in range(self.word_size):
|
||||
self.sf.write("CD{0} d[{0}] 0 {1}f\n".format(i,self.load))
|
||||
|
||||
# add access transistors for data-bus
|
||||
self.sf.write("\n* Transmission Gates for data-bus and control signals\n")
|
||||
self.stim.inst_accesstx(dbits=self.word_size)
|
||||
self.sf.write("CD{0} DOUT[{0}] 0 {1}f\n".format(i,self.load))
|
||||
|
||||
|
||||
def write_delay_stimulus(self):
|
||||
|
|
@ -112,11 +108,11 @@ class delay():
|
|||
for i in range(self.word_size):
|
||||
if i == self.probe_data:
|
||||
self.gen_data(clk_times=self.cycle_times,
|
||||
sig_name="data[{0}]".format(i))
|
||||
sig_name="DIN[{0}]".format(i))
|
||||
|
||||
|
||||
else:
|
||||
self.stim.gen_constant(sig_name="d[{0}]".format(i),
|
||||
self.stim.gen_constant(sig_name="DIN[{0}]".format(i),
|
||||
v_val=0)
|
||||
|
||||
self.gen_addr(clk_times=self.cycle_times,
|
||||
|
|
@ -172,7 +168,7 @@ class delay():
|
|||
# generate data and addr signals
|
||||
self.sf.write("\n* Generation of data and address signals\n")
|
||||
for i in range(self.word_size):
|
||||
self.stim.gen_constant(sig_name="d[{0}]".format(i),
|
||||
self.stim.gen_constant(sig_name="DIN[{0}]".format(i),
|
||||
v_val=0)
|
||||
for i in range(self.addr_size):
|
||||
self.stim.gen_constant(sig_name="A[{0}]".format(i),
|
||||
|
|
@ -208,7 +204,7 @@ class delay():
|
|||
|
||||
# Trigger on the clk of the appropriate cycle
|
||||
trig_name = "clk"
|
||||
targ_name = "{0}".format("d[{0}]".format(self.probe_data))
|
||||
targ_name = "{0}".format("DOUT[{0}]".format(self.probe_data))
|
||||
trig_val = targ_val = 0.5 * self.vdd_voltage
|
||||
|
||||
# Delay the target to measure after the negative edge
|
||||
|
|
@ -338,6 +334,7 @@ class delay():
|
|||
|
||||
# Checking from not data_value to data_value
|
||||
self.write_delay_stimulus()
|
||||
|
||||
self.stim.run_sim()
|
||||
delay_hl = parse_spice_list("timing", "delay_hl")
|
||||
delay_lh = parse_spice_list("timing", "delay_lh")
|
||||
|
|
@ -783,12 +780,6 @@ class delay():
|
|||
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
|
||||
self.stim.gen_pwl("web", clk_times, values, self.period, self.slew, 0.05)
|
||||
|
||||
# Keep acc_en deasserted in NOP for measuring >1 period
|
||||
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
|
||||
self.stim.gen_pwl("acc_en", clk_times, values, self.period, self.slew, 0)
|
||||
values = [0, 1, 1, 1, 0, 0, 1, 1, 0, 0]
|
||||
self.stim.gen_pwl("acc_en_inv", clk_times, values, self.period, self.slew, 0)
|
||||
|
||||
def gen_oeb(self, clk_times):
|
||||
""" Generates the PWL WEb signal """
|
||||
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
|
||||
|
|
|
|||
|
|
@ -440,9 +440,7 @@ class lib:
|
|||
|
||||
def compute_delay(self):
|
||||
""" Do the analysis if we haven't characterized the SRAM yet """
|
||||
try:
|
||||
self.d
|
||||
except AttributeError:
|
||||
if not hasattr(self,"d"):
|
||||
self.d = delay(self.sram, self.sp_file, self.corner)
|
||||
if self.use_model:
|
||||
self.char_results = self.d.analytical_delay(self.sram,self.slews,self.loads)
|
||||
|
|
@ -455,9 +453,7 @@ class lib:
|
|||
def compute_setup_hold(self):
|
||||
""" Do the analysis if we haven't characterized a FF yet """
|
||||
# Do the analysis if we haven't characterized a FF yet
|
||||
try:
|
||||
self.sh
|
||||
except AttributeError:
|
||||
if not hasattr(self,"sh"):
|
||||
self.sh = setup_hold(self.corner)
|
||||
if self.use_model:
|
||||
self.times = self.sh.analytical_setuphold(self.slews,self.loads)
|
||||
|
|
|
|||
|
|
@ -34,12 +34,14 @@ class stimuli():
|
|||
""" Function to instatiate an SRAM subckt. """
|
||||
self.sf.write("Xsram ")
|
||||
for i in range(dbits):
|
||||
self.sf.write("D[{0}] ".format(i))
|
||||
self.sf.write("DIN[{0}] ".format(i))
|
||||
for i in range(abits):
|
||||
self.sf.write("A[{0}] ".format(i))
|
||||
for i in tech.spice["control_signals"]:
|
||||
self.sf.write("{0} ".format(i))
|
||||
self.sf.write("{0} ".format(tech.spice["clk"]))
|
||||
for i in range(dbits):
|
||||
self.sf.write("DOUT[{0}] ".format(i))
|
||||
self.sf.write("{0} {1} ".format(self.vdd_name, self.gnd_name))
|
||||
self.sf.write("{0}\n".format(sram_name))
|
||||
|
||||
|
|
@ -110,23 +112,6 @@ class stimuli():
|
|||
"test"+self.vdd_name,
|
||||
"test"+self.gnd_name))
|
||||
|
||||
|
||||
def inst_accesstx(self, dbits):
|
||||
""" Adds transmission gate for inputs to data-bus (only for sim purposes) """
|
||||
self.sf.write("* Tx Pin-list: Drain Gate Source Body\n")
|
||||
for i in range(dbits):
|
||||
pmos_access_string="mp{0} DATA[{0}] acc_en D[{0}] {1} {2} w={3}u l={4}u\n"
|
||||
self.sf.write(pmos_access_string.format(i,
|
||||
"test"+self.vdd_name,
|
||||
self.pmos_name,
|
||||
2 * self.tx_width,
|
||||
self.tx_length))
|
||||
nmos_access_string="mn{0} DATA[{0}] acc_en_inv D[{0}] {1} {2} w={3}u l={4}u\n"
|
||||
self.sf.write(nmos_access_string.format(i,
|
||||
"test"+self.gnd_name,
|
||||
self.nmos_name,
|
||||
2 * self.tx_width,
|
||||
self.tx_length))
|
||||
|
||||
def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -227,10 +227,11 @@ def end_openram():
|
|||
""" Clean up openram for a proper exit """
|
||||
cleanup_paths()
|
||||
|
||||
import verify
|
||||
verify.print_drc_stats()
|
||||
verify.print_lvs_stats()
|
||||
verify.print_pex_stats()
|
||||
if OPTS.check_lvsdrc:
|
||||
import verify
|
||||
verify.print_drc_stats()
|
||||
verify.print_lvs_stats()
|
||||
verify.print_pex_stats()
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class bank(design.design):
|
|||
mod_list = ["tri_gate", "bitcell", "decoder", "ms_flop_array", "wordline_driver",
|
||||
"bitcell_array", "sense_amp_array", "precharge_array",
|
||||
"column_mux_array", "write_driver_array", "tri_gate_array",
|
||||
"bank_select"]
|
||||
"dff", "bank_select"]
|
||||
from importlib import reload
|
||||
for mod_name in mod_list:
|
||||
config_mod_name = getattr(OPTS, mod_name)
|
||||
|
|
@ -66,18 +66,19 @@ class bank(design.design):
|
|||
# Can remove the following, but it helps for debug!
|
||||
self.add_lvs_correspondence_points()
|
||||
|
||||
self.offset_all_coordinates()
|
||||
# Remember the bank center for further placement
|
||||
self.bank_center=self.offset_all_coordinates().scale(-1,-1)
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
""" Adding pins for Bank module"""
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("DOUT[{0}]".format(i),"OUT")
|
||||
self.add_pin("dout[{0}]".format(i),"OUT")
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("DIN[{0}]".format(i),"IN")
|
||||
self.add_pin("din[{0}]".format(i),"IN")
|
||||
for i in range(self.addr_size):
|
||||
self.add_pin("A[{0}]".format(i),"INPUT")
|
||||
self.add_pin("addr[{0}]".format(i),"INPUT")
|
||||
|
||||
# For more than one bank, we have a bank select and name
|
||||
# the signals gated_*.
|
||||
|
|
@ -95,8 +96,9 @@ class bank(design.design):
|
|||
self.route_precharge_to_bitcell_array()
|
||||
self.route_col_mux_to_bitcell_array()
|
||||
self.route_sense_amp_to_col_mux_or_bitcell_array()
|
||||
self.route_sense_amp_to_trigate()
|
||||
self.route_tri_gate_out()
|
||||
#self.route_sense_amp_to_trigate()
|
||||
#self.route_tri_gate_out()
|
||||
self.route_sense_amp_out()
|
||||
self.route_wordline_driver()
|
||||
self.route_write_driver()
|
||||
self.route_row_decoder()
|
||||
|
|
@ -119,7 +121,8 @@ class bank(design.design):
|
|||
self.add_column_mux_array()
|
||||
self.add_sense_amp_array()
|
||||
self.add_write_driver_array()
|
||||
self.add_tri_gate_array()
|
||||
# Not needed for single bank
|
||||
#self.add_tri_gate_array()
|
||||
|
||||
# To the left of the bitcell array
|
||||
self.add_row_decoder()
|
||||
|
|
@ -280,7 +283,7 @@ class bank(design.design):
|
|||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
temp = []
|
||||
for i in range(self.word_size):
|
||||
temp.append("sa_out[{0}]".format(i))
|
||||
temp.append("dout[{0}]".format(i))
|
||||
if self.words_per_row == 1:
|
||||
temp.append("bl[{0}]".format(i))
|
||||
temp.append("br[{0}]".format(i))
|
||||
|
|
@ -294,14 +297,15 @@ class bank(design.design):
|
|||
def add_write_driver_array(self):
|
||||
""" Adding Write Driver """
|
||||
|
||||
y_offset = self.sense_amp_array.height + self.column_mux_height + + self.m2_gap + self.write_driver_array.height
|
||||
y_offset = self.sense_amp_array.height + self.column_mux_height \
|
||||
+ self.m2_gap + self.write_driver_array.height
|
||||
self.write_driver_array_inst=self.add_inst(name="write_driver_array",
|
||||
mod=self.write_driver_array,
|
||||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
|
||||
temp = []
|
||||
for i in range(self.word_size):
|
||||
temp.append("DIN[{0}]".format(i))
|
||||
temp.append("din[{0}]".format(i))
|
||||
for i in range(self.word_size):
|
||||
if (self.words_per_row == 1):
|
||||
temp.append("bl[{0}]".format(i))
|
||||
|
|
@ -315,7 +319,7 @@ class bank(design.design):
|
|||
def add_tri_gate_array(self):
|
||||
""" data tri gate to drive the data bus """
|
||||
y_offset = self.sense_amp_array.height+self.column_mux_height \
|
||||
+ self.write_driver_array.height + self.m2_gap + self.tri_gate_array.height
|
||||
+ self.m2_gap + self.tri_gate_array.height
|
||||
self.tri_gate_array_inst=self.add_inst(name="tri_gate_array",
|
||||
mod=self.tri_gate_array,
|
||||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
|
|
@ -324,7 +328,7 @@ class bank(design.design):
|
|||
for i in range(self.word_size):
|
||||
temp.append("sa_out[{0}]".format(i))
|
||||
for i in range(self.word_size):
|
||||
temp.append("DOUT[{0}]".format(i))
|
||||
temp.append("dout[{0}]".format(i))
|
||||
temp.extend([self.prefix+"tri_en", self.prefix+"tri_en_bar", "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
|
@ -345,7 +349,7 @@ class bank(design.design):
|
|||
|
||||
temp = []
|
||||
for i in range(self.row_addr_size):
|
||||
temp.append("A[{0}]".format(i+self.col_addr_size))
|
||||
temp.append("addr[{0}]".format(i+self.col_addr_size))
|
||||
for j in range(self.num_rows):
|
||||
temp.append("dec_out[{0}]".format(j))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
|
|
@ -375,8 +379,8 @@ class bank(design.design):
|
|||
"""
|
||||
Create a 2:4 or 3:8 column address decoder.
|
||||
"""
|
||||
# Place the col decoder aligned left to row decoder
|
||||
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
||||
# Place the col decoder right aligned with row decoder
|
||||
x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width)
|
||||
y_off = -(self.col_decoder.height + 2*drc["well_to_well"])
|
||||
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
|
||||
mod=self.col_decoder,
|
||||
|
|
@ -384,7 +388,7 @@ class bank(design.design):
|
|||
|
||||
temp = []
|
||||
for i in range(self.col_addr_size):
|
||||
temp.append("A[{0}]".format(i))
|
||||
temp.append("addr[{0}]".format(i))
|
||||
for j in range(self.num_col_addr_lines):
|
||||
temp.append("sel[{0}]".format(j))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
|
|
@ -398,7 +402,7 @@ class bank(design.design):
|
|||
if self.col_addr_size == 0:
|
||||
return
|
||||
elif self.col_addr_size == 1:
|
||||
self.col_decoder = pinvbuf()
|
||||
self.col_decoder = pinvbuf(height=self.mod_dff.height)
|
||||
self.add_mod(self.col_decoder)
|
||||
elif self.col_addr_size == 2:
|
||||
self.col_decoder = self.row_decoder.pre2_4
|
||||
|
|
@ -443,7 +447,7 @@ class bank(design.design):
|
|||
self.precharge_array_inst,
|
||||
self.sense_amp_array_inst,
|
||||
self.write_driver_array_inst,
|
||||
self.tri_gate_array_inst,
|
||||
# self.tri_gate_array_inst,
|
||||
self.row_decoder_inst,
|
||||
self.wordline_driver_inst]
|
||||
# Add these if we use the part...
|
||||
|
|
@ -471,7 +475,7 @@ class bank(design.design):
|
|||
for gated_name in self.control_signals:
|
||||
# Connect the inverter output to the central bus
|
||||
out_pos = self.bank_select_inst.get_pin(gated_name).rc()
|
||||
bus_pos = vector(self.bus_xoffset[gated_name], out_pos.y)
|
||||
bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y)
|
||||
self.add_path("metal3",[out_pos, bus_pos])
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=bus_pos,
|
||||
|
|
@ -492,7 +496,8 @@ class bank(design.design):
|
|||
#the column decoder (if there is one) or the tristate output
|
||||
#driver.
|
||||
# Leave room for the output below the tri gate.
|
||||
tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch
|
||||
#tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch
|
||||
write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch
|
||||
row_decoder_min_y_offset = self.row_decoder_inst.by()
|
||||
if self.col_addr_size > 0:
|
||||
col_decoder_min_y_offset = self.col_decoder_inst.by()
|
||||
|
|
@ -502,10 +507,10 @@ class bank(design.design):
|
|||
if self.num_banks>1:
|
||||
# The control gating logic is below the decoder
|
||||
# Min of the control gating logic and tri gate.
|
||||
self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, tri_gate_min_y_offset)
|
||||
self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset)
|
||||
else:
|
||||
# Just the min of the decoder logic logic and tri gate.
|
||||
self.min_y_offset = min(col_decoder_min_y_offset, tri_gate_min_y_offset)
|
||||
self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset)
|
||||
|
||||
# The max point is always the top of the precharge bitlines
|
||||
# Add a vdd and gnd power rail above the array
|
||||
|
|
@ -530,21 +535,13 @@ class bank(design.design):
|
|||
# and control lines.
|
||||
# The bank is at (0,0), so this is to the left of the y-axis.
|
||||
# 2 pitches on the right for vias/jogs to access the inputs
|
||||
control_bus_x_offset = -self.m2_pitch * self.num_control_lines - self.m2_width
|
||||
|
||||
# Track the bus offsets for other modules to access
|
||||
self.bus_xoffset = {}
|
||||
|
||||
# Control lines
|
||||
for i in range(self.num_control_lines):
|
||||
x_offset = control_bus_x_offset + i*self.m2_pitch
|
||||
# Make the xoffset map the center of the rail
|
||||
self.bus_xoffset[self.control_signals[i]]=x_offset + 0.5*self.m2_width
|
||||
# Pins are added later if this is a single bank, so just add rectangle now
|
||||
self.add_rect(layer="metal2",
|
||||
offset=vector(x_offset, self.min_y_offset),
|
||||
width=self.m2_width,
|
||||
height=self.max_y_offset-self.min_y_offset)
|
||||
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, 0)
|
||||
control_bus_length = self.bitcell_array_inst.uy()
|
||||
self.bus_xoffset = self.create_vertical_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=control_bus_offset,
|
||||
names=self.control_signals,
|
||||
length=control_bus_length)
|
||||
|
||||
|
||||
|
||||
|
|
@ -620,12 +617,22 @@ class bank(design.design):
|
|||
offset=sa_data_out)
|
||||
self.add_path("metal3",[sa_data_out,tri_gate_in])
|
||||
|
||||
def route_sense_amp_out(self):
|
||||
""" Add pins for the sense amp output """
|
||||
for i in range(self.word_size):
|
||||
data_pin = self.sense_amp_array_inst.get_pin("data[{}]".format(i))
|
||||
self.add_layout_pin_rect_center(text="dout[{}]".format(i),
|
||||
layer=data_pin.layer,
|
||||
offset=data_pin.center(),
|
||||
height=data_pin.height(),
|
||||
width=data_pin.width()),
|
||||
|
||||
def route_tri_gate_out(self):
|
||||
""" Metal 3 routing of tri_gate output data """
|
||||
for i in range(self.word_size):
|
||||
data_pin = self.tri_gate_array_inst.get_pin("out[{}]".format(i))
|
||||
self.add_layout_pin_rect_center(text="DOUT[{}]".format(i),
|
||||
layer="metal2",
|
||||
self.add_layout_pin_rect_center(text="dout[{}]".format(i),
|
||||
layer=data_pin.layer,
|
||||
offset=data_pin.center(),
|
||||
height=data_pin.height(),
|
||||
width=data_pin.width()),
|
||||
|
|
@ -637,8 +644,8 @@ class bank(design.design):
|
|||
# Create inputs for the row address lines
|
||||
for i in range(self.row_addr_size):
|
||||
addr_idx = i + self.col_addr_size
|
||||
decoder_name = "A[{}]".format(i)
|
||||
addr_name = "A[{}]".format(addr_idx)
|
||||
decoder_name = "addr[{}]".format(i)
|
||||
addr_name = "addr[{}]".format(addr_idx)
|
||||
self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name)
|
||||
|
||||
|
||||
|
|
@ -647,7 +654,7 @@ class bank(design.design):
|
|||
|
||||
for i in range(self.word_size):
|
||||
data_name = "data[{}]".format(i)
|
||||
din_name = "DIN[{}]".format(i)
|
||||
din_name = "din[{}]".format(i)
|
||||
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
|
||||
|
||||
|
||||
|
|
@ -686,7 +693,7 @@ class bank(design.design):
|
|||
decode_names = ["Zb", "Z"]
|
||||
|
||||
# The Address LSB
|
||||
self.copy_layout_pin(self.col_decoder_inst, "A", "A[0]")
|
||||
self.copy_layout_pin(self.col_decoder_inst, "A", "addr[0]")
|
||||
|
||||
elif self.col_addr_size > 1:
|
||||
decode_names = []
|
||||
|
|
@ -695,7 +702,7 @@ class bank(design.design):
|
|||
|
||||
for i in range(self.col_addr_size):
|
||||
decoder_name = "in[{}]".format(i)
|
||||
addr_name = "A[{}]".format(i)
|
||||
addr_name = "addr[{}]".format(i)
|
||||
self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name)
|
||||
|
||||
|
||||
|
|
@ -749,13 +756,13 @@ class bank(design.design):
|
|||
layer="metal2",
|
||||
offset=br_pin.center())
|
||||
|
||||
# Add the data output names to the sense amp output
|
||||
for i in range(self.word_size):
|
||||
data_name = "data[{}]".format(i)
|
||||
data_pin = self.sense_amp_array_inst.get_pin(data_name)
|
||||
self.add_label(text="sa_out[{}]".format(i),
|
||||
layer="metal2",
|
||||
offset=data_pin.center())
|
||||
# # Add the data output names to the sense amp output
|
||||
# for i in range(self.word_size):
|
||||
# data_name = "data[{}]".format(i)
|
||||
# data_pin = self.sense_amp_array_inst.get_pin(data_name)
|
||||
# self.add_label(text="sa_out[{}]".format(i),
|
||||
# layer="metal2",
|
||||
# offset=data_pin.center())
|
||||
|
||||
# Add labels on the decoder
|
||||
for i in range(self.word_size):
|
||||
|
|
@ -775,14 +782,14 @@ class bank(design.design):
|
|||
# Connection from the central bus to the main control block crosses
|
||||
# pre-decoder and this connection is in metal3
|
||||
connection = []
|
||||
connection.append((self.prefix+"tri_en_bar", self.tri_gate_array_inst.get_pin("en_bar").lc()))
|
||||
connection.append((self.prefix+"tri_en", self.tri_gate_array_inst.get_pin("en").lc()))
|
||||
#connection.append((self.prefix+"tri_en_bar", self.tri_gate_array_inst.get_pin("en_bar").lc()))
|
||||
#connection.append((self.prefix+"tri_en", self.tri_gate_array_inst.get_pin("en").lc()))
|
||||
connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst.get_pin("en").lc()))
|
||||
connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc()))
|
||||
connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
|
||||
|
||||
for (control_signal, pin_pos) in connection:
|
||||
control_pos = vector(self.bus_xoffset[control_signal], pin_pos.y)
|
||||
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
|
||||
self.add_path("metal1", [control_pos, pin_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=control_pos,
|
||||
|
|
@ -792,7 +799,7 @@ class bank(design.design):
|
|||
control_signal = self.prefix+"clk_buf"
|
||||
pin_pos = self.wordline_driver_inst.get_pin("en").uc()
|
||||
mid_pos = pin_pos + vector(0,self.m1_pitch)
|
||||
control_x_offset = self.bus_xoffset[control_signal]
|
||||
control_x_offset = self.bus_xoffset[control_signal].x
|
||||
control_pos = vector(control_x_offset + self.m1_width, mid_pos.y)
|
||||
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
|
||||
control_via_pos = vector(control_x_offset, mid_pos.y)
|
||||
|
|
@ -805,7 +812,7 @@ class bank(design.design):
|
|||
|
||||
for ctrl in self.control_signals:
|
||||
# xoffsets are the center of the rail
|
||||
x_offset = self.bus_xoffset[ctrl] - 0.5*self.m2_width
|
||||
x_offset = self.bus_xoffset[ctrl].x - 0.5*self.m2_width
|
||||
if self.num_banks > 1:
|
||||
# it's not an input pin if we have multiple banks
|
||||
self.add_label_pin(text=ctrl,
|
||||
|
|
|
|||
|
|
@ -80,10 +80,8 @@ class bitcell_array(design.design):
|
|||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
|
||||
row_list = self.cell.list_row_pins()
|
||||
|
||||
column_list = self.cell.list_column_pins()
|
||||
|
||||
offset = vector(0.0, 0.0)
|
||||
for col in range(self.column_size):
|
||||
for cell_column in column_list:
|
||||
|
|
@ -97,6 +95,7 @@ class bitcell_array(design.design):
|
|||
# increments to the next column width
|
||||
offset.x += self.cell.width
|
||||
|
||||
row_list = self.cell.list_row_pins()
|
||||
offset.x = 0.0
|
||||
for row in range(self.row_size):
|
||||
for cell_row in row_list:
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ class control_logic(design.design):
|
|||
c = reload(__import__(OPTS.replica_bitline))
|
||||
replica_bitline = getattr(c, OPTS.replica_bitline)
|
||||
# FIXME: These should be tuned according to the size!
|
||||
delay_stages = 3 # Should be odd due to bug Kevin found
|
||||
delay_fanout = 3
|
||||
delay_stages = 4 # Must be non-inverting
|
||||
delay_fanout = 3 # This can be anything >=2
|
||||
bitcell_loads = int(math.ceil(self.num_rows / 5.0))
|
||||
self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads)
|
||||
self.add_mod(self.replica_bitline)
|
||||
|
|
@ -94,61 +94,61 @@ class control_logic(design.design):
|
|||
self.input_list =["csb","web","oeb"]
|
||||
self.dff_output_list =["cs_bar", "cs", "we_bar", "we", "oe_bar", "oe"]
|
||||
# list of output control signals (for making a vertical bus)
|
||||
self.internal_list = ["clk_buf", "clk_buf_bar", "we", "cs", "oe"]
|
||||
self.internal_width = len(self.internal_list)*self.m2_pitch
|
||||
self.internal_bus_list = ["clk_buf", "clk_buf_bar", "we", "cs", "oe"]
|
||||
# leave space for the bus plus one extra space
|
||||
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
|
||||
# Ooutputs to the bank
|
||||
self.output_list = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_buf_bar", "clk_buf"]
|
||||
self.output_list = ["s_en", "w_en", "clk_buf_bar", "clk_buf"]
|
||||
# # with tri/tri_en
|
||||
# self.output_list = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_buf_bar", "clk_buf"]
|
||||
self.supply_list = ["vdd", "gnd"]
|
||||
self.rail_width = len(self.input_list)*len(self.output_list)*self.m2_pitch
|
||||
self.rail_x_offsets = {}
|
||||
|
||||
# GAP between main control and replica bitline
|
||||
#self.replica_bitline_gap = 2*self.m2_pitch
|
||||
|
||||
def add_rails(self):
|
||||
""" Add the input signal inverted tracks """
|
||||
height = 6*self.inv1.height - self.m2_pitch
|
||||
for i in range(len(self.internal_list)):
|
||||
name = self.internal_list[i]
|
||||
offset = vector(i*self.m2_pitch + self.ctrl_dff_array.width, 0)
|
||||
# just for LVS correspondence...
|
||||
self.add_label_pin(text=name,
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=height)
|
||||
self.rail_x_offsets[name]=offset.x + 0.5*drc["minwidth_metal2"] # center offset
|
||||
height = 4*self.inv1.height - self.m2_pitch
|
||||
offset = vector(self.ctrl_dff_array.width,0)
|
||||
|
||||
self.rail_offsets = self.create_vertical_bus("metal2", self.m2_pitch, offset, self.internal_bus_list, height)
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
""" Place all the modules """
|
||||
# Keep track of all right-most instances to determine row boundary
|
||||
# and add the vdd/gnd pins
|
||||
self.row_end_inst = []
|
||||
|
||||
|
||||
# Add the control flops on the left of the bus
|
||||
self.add_dffs()
|
||||
self.add_clk_buffer(row=0)
|
||||
|
||||
# Add the logic on the right of the bus
|
||||
self.add_clk_row(row=0) # clk is a double-high cell
|
||||
self.add_we_row(row=2)
|
||||
self.add_trien_row(row=3)
|
||||
self.add_trien_bar_row(row=4)
|
||||
self.add_rblk_row(row=5)
|
||||
self.add_sen_row(row=6)
|
||||
self.add_rbl(row=7)
|
||||
# self.add_trien_row(row=3)
|
||||
# self.add_trien_bar_row(row=4)
|
||||
self.add_rbl_in_row(row=3)
|
||||
self.add_sen_row(row=4)
|
||||
self.add_rbl(row=5)
|
||||
|
||||
|
||||
self.add_lvs_correspondence_points()
|
||||
|
||||
self.height = self.rbl_inst.uy()
|
||||
# Find max of logic rows
|
||||
max_row = max(self.row_rblk_end_x, self.row_trien_end_x, self.row_trien_bar_end_x,
|
||||
self.row_sen_end_x, self.row_we_end_x, self.row_we_end_x)
|
||||
# Max of modules or logic rows
|
||||
self.width = max(self.clkbuf.rx(), self.rbl_inst.rx(), max_row)
|
||||
# This offset is used for placement of the control logic in
|
||||
# the SRAM level.
|
||||
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), self.rbl_inst.by())
|
||||
|
||||
self.height = self.rbl_inst.uy()
|
||||
# Max of modules or logic rows
|
||||
self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst]))
|
||||
|
||||
|
||||
def add_routing(self):
|
||||
""" Routing between modules """
|
||||
self.route_dffs()
|
||||
self.route_trien()
|
||||
self.route_trien_bar()
|
||||
self.route_rblk()
|
||||
#self.route_trien()
|
||||
#self.route_trien_bar()
|
||||
self.route_rbl_in()
|
||||
self.route_wen()
|
||||
self.route_sen()
|
||||
self.route_clk()
|
||||
|
|
@ -158,191 +158,159 @@ class control_logic(design.design):
|
|||
def add_rbl(self,row):
|
||||
""" Add the replica bitline """
|
||||
y_off = row * self.inv1.height + 2*self.m1_pitch
|
||||
|
||||
|
||||
# Add the RBL above the rows
|
||||
# Add to the right of the control rows and routing channel
|
||||
self.replica_bitline_offset = vector(0, y_off)
|
||||
self.rbl_inst=self.add_inst(name="replica_bitline",
|
||||
mod=self.replica_bitline,
|
||||
offset=self.replica_bitline_offset)
|
||||
self.connect_inst(["rblk", "pre_s_en", "vdd", "gnd"])
|
||||
self.connect_inst(["rbl_in", "pre_s_en", "vdd", "gnd"])
|
||||
|
||||
|
||||
def add_clk_buffer(self,row):
|
||||
def add_clk_row(self,row):
|
||||
""" Add the multistage clock buffer below the control flops """
|
||||
x_off = self.ctrl_dff_array.width + self.internal_width
|
||||
y_off = row*self.inv1.height
|
||||
if row % 2:
|
||||
y_off += self.clkbuf.height
|
||||
mirror="MX"
|
||||
else:
|
||||
mirror="R0"
|
||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
|
||||
clkbuf_offset = vector(x_off,y_off)
|
||||
self.clkbuf = self.add_inst(name="clkbuf",
|
||||
mod=self.clkbuf,
|
||||
offset=clkbuf_offset)
|
||||
self.clkbuf_inst = self.add_inst(name="clkbuf",
|
||||
mod=self.clkbuf,
|
||||
offset=clkbuf_offset)
|
||||
|
||||
self.connect_inst(["clk","clk_buf_bar","clk_buf","vdd","gnd"])
|
||||
|
||||
|
||||
self.row_end_inst.append(self.clkbuf_inst)
|
||||
|
||||
|
||||
def add_rblk_row(self,row):
|
||||
x_off = self.ctrl_dff_array.width + self.internal_width
|
||||
y_off = row*self.inv1.height
|
||||
if row % 2:
|
||||
y_off += self.inv1.height
|
||||
mirror="MX"
|
||||
else:
|
||||
mirror="R0"
|
||||
def add_rbl_in_row(self,row):
|
||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
|
||||
|
||||
# input: OE, clk_buf_bar,CS output: rblk_bar
|
||||
self.rblk_bar_offset = vector(x_off, y_off)
|
||||
self.rblk_bar=self.add_inst(name="nand3_rblk_bar",
|
||||
mod=self.nand3,
|
||||
offset=self.rblk_bar_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["clk_buf_bar", "oe", "cs", "rblk_bar", "vdd", "gnd"])
|
||||
# input: OE, clk_buf_bar,CS output: rbl_in_bar
|
||||
self.rbl_in_bar_offset = vector(x_off, y_off)
|
||||
self.rbl_in_bar_inst=self.add_inst(name="nand3_rbl_in_bar",
|
||||
mod=self.nand3,
|
||||
offset=self.rbl_in_bar_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["clk_buf_bar", "oe", "cs", "rbl_in_bar", "vdd", "gnd"])
|
||||
x_off += self.nand3.width
|
||||
|
||||
# input: rblk_bar, output: rblk
|
||||
self.rblk_offset = vector(x_off, y_off)
|
||||
self.rblk=self.add_inst(name="inv_rblk",
|
||||
mod=self.inv1,
|
||||
offset=self.rblk_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["rblk_bar", "rblk", "vdd", "gnd"])
|
||||
x_off += self.inv1.width
|
||||
# input: rbl_in_bar, output: rbl_in
|
||||
self.rbl_in_offset = vector(x_off, y_off)
|
||||
self.rbl_in_inst=self.add_inst(name="inv_rbl_in",
|
||||
mod=self.inv1,
|
||||
offset=self.rbl_in_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["rbl_in_bar", "rbl_in", "vdd", "gnd"])
|
||||
|
||||
self.row_end_inst.append(self.rbl_in_inst)
|
||||
|
||||
self.row_rblk_end_x = x_off
|
||||
|
||||
def add_sen_row(self,row):
|
||||
""" The sense enable buffer gets placed to the far right of the
|
||||
row. """
|
||||
x_off = self.ctrl_dff_array.width + self.internal_width
|
||||
y_off = row*self.inv1.height
|
||||
if row % 2:
|
||||
y_off += self.inv1.height
|
||||
mirror="MX"
|
||||
else:
|
||||
mirror="R0"
|
||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
|
||||
# input: pre_s_en, output: pre_s_en_bar
|
||||
self.pre_s_en_bar_offset = vector(x_off, y_off)
|
||||
self.pre_s_en_bar_inst=self.add_inst(name="inv_pre_s_en_bar",
|
||||
mod=self.inv2,
|
||||
offset=self.pre_s_en_bar_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"])
|
||||
|
||||
x_off += self.inv2.width
|
||||
|
||||
# BUFFER INVERTERS FOR S_EN
|
||||
# input: input: pre_s_en_bar, output: s_en
|
||||
self.s_en_offset = vector(x_off, y_off)
|
||||
self.s_en=self.add_inst(name="inv_s_en",
|
||||
mod=self.inv8,
|
||||
offset=self.s_en_offset,
|
||||
mirror=mirror)
|
||||
self.s_en_inst=self.add_inst(name="inv_s_en",
|
||||
mod=self.inv8,
|
||||
offset=self.s_en_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"])
|
||||
x_off -= self.inv2.width
|
||||
# input: pre_s_en, output: pre_s_en_bar
|
||||
self.pre_s_en_bar_offset = vector(x_off, y_off)
|
||||
self.pre_s_en_bar=self.add_inst(name="inv_pre_s_en_bar",
|
||||
mod=self.inv2,
|
||||
offset=self.pre_s_en_bar_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"])
|
||||
|
||||
self.row_sen_end_x = self.replica_bitline.width
|
||||
|
||||
|
||||
self.row_end_inst.append(self.s_en_inst)
|
||||
|
||||
def add_trien_row(self, row):
|
||||
x_off = self.ctrl_dff_array.width + self.internal_width
|
||||
y_off = row*self.inv1.height
|
||||
if row % 2:
|
||||
y_off += self.inv1.height
|
||||
mirror="MX"
|
||||
else:
|
||||
mirror="R0"
|
||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
|
||||
|
||||
x_off += self.nand2.width
|
||||
|
||||
# BUFFER INVERTERS FOR TRI_EN
|
||||
self.tri_en_offset = vector(x_off, y_off)
|
||||
self.tri_en=self.add_inst(name="inv_tri_en1",
|
||||
mod=self.inv2,
|
||||
offset=self.tri_en_offset,
|
||||
mirror=mirror)
|
||||
tri_en_offset = vector(x_off, y_off)
|
||||
self.tri_en_inst=self.add_inst(name="inv_tri_en1",
|
||||
mod=self.inv2,
|
||||
offset=tri_en_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_tri_en_bar", "pre_tri_en1", "vdd", "gnd"])
|
||||
x_off += self.inv2.width
|
||||
|
||||
self.tri_en_buf1_offset = vector(x_off, y_off)
|
||||
self.tri_en_buf1=self.add_inst(name="tri_en_buf1",
|
||||
mod=self.inv2,
|
||||
offset=self.tri_en_buf1_offset,
|
||||
mirror=mirror)
|
||||
tri_en_buf1_offset = vector(x_off, y_off)
|
||||
self.tri_en_buf1_inst=self.add_inst(name="tri_en_buf1",
|
||||
mod=self.inv2,
|
||||
offset=tri_en_buf1_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_tri_en1", "pre_tri_en_bar1", "vdd", "gnd"])
|
||||
x_off += self.inv2.width
|
||||
|
||||
self.tri_en_buf2_offset = vector(x_off, y_off)
|
||||
self.tri_en_buf2=self.add_inst(name="tri_en_buf2",
|
||||
mod=self.inv8,
|
||||
offset=self.tri_en_buf2_offset,
|
||||
mirror=mirror)
|
||||
tri_en_buf2_offset = vector(x_off, y_off)
|
||||
self.tri_en_buf2_inst=self.add_inst(name="tri_en_buf2",
|
||||
mod=self.inv8,
|
||||
offset=tri_en_buf2_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_tri_en_bar1", "tri_en", "vdd", "gnd"])
|
||||
x_off += self.inv8.width
|
||||
|
||||
#x_off += self.inv1.width + self.cell_gap
|
||||
|
||||
|
||||
|
||||
self.row_trien_end_x = x_off
|
||||
|
||||
self.row_end_inst.append(self.tri_en_inst)
|
||||
|
||||
def add_trien_bar_row(self, row):
|
||||
x_off = self.ctrl_dff_array.width + self.internal_width
|
||||
y_off = row*self.inv1.height
|
||||
if row % 2:
|
||||
y_off += self.inv1.height
|
||||
mirror="MX"
|
||||
else:
|
||||
mirror="R0"
|
||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
|
||||
|
||||
# input: OE, clk_buf_bar output: tri_en_bar
|
||||
self.tri_en_bar_offset = vector(x_off,y_off)
|
||||
self.tri_en_bar=self.add_inst(name="nand2_tri_en",
|
||||
mod=self.nand2,
|
||||
offset=self.tri_en_bar_offset,
|
||||
mirror=mirror)
|
||||
tri_en_bar_offset = vector(x_off,y_off)
|
||||
self.tri_en_bar_inst=self.add_inst(name="nand2_tri_en",
|
||||
mod=self.nand2,
|
||||
offset=tri_en_bar_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["clk_buf_bar", "oe", "pre_tri_en_bar", "vdd", "gnd"])
|
||||
x_off += self.nand2.width
|
||||
|
||||
# BUFFER INVERTERS FOR TRI_EN
|
||||
self.tri_en_bar_buf1_offset = vector(x_off, y_off)
|
||||
self.tri_en_bar_buf1=self.add_inst(name="tri_en_bar_buf1",
|
||||
mod=self.inv2,
|
||||
offset=self.tri_en_bar_buf1_offset,
|
||||
mirror=mirror)
|
||||
tri_en_bar_buf1_offset = vector(x_off, y_off)
|
||||
self.tri_en_bar_buf1_inst=self.add_inst(name="tri_en_bar_buf1",
|
||||
mod=self.inv2,
|
||||
offset=tri_en_bar_buf1_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_tri_en_bar", "pre_tri_en2", "vdd", "gnd"])
|
||||
x_off += self.inv2.width
|
||||
|
||||
self.tri_en_bar_buf2_offset = vector(x_off, y_off)
|
||||
self.tri_en_bar_buf2=self.add_inst(name="tri_en_bar_buf2",
|
||||
mod=self.inv8,
|
||||
offset=self.tri_en_bar_buf2_offset,
|
||||
mirror=mirror)
|
||||
tri_en_bar_buf2_offset = vector(x_off, y_off)
|
||||
self.tri_en_bar_buf2_inst=self.add_inst(name="tri_en_bar_buf2",
|
||||
mod=self.inv8,
|
||||
offset=tri_en_bar_buf2_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_tri_en2", "tri_en_bar", "vdd", "gnd"])
|
||||
x_off += self.inv8.width
|
||||
|
||||
#x_off += self.inv1.width + self.cell_gap
|
||||
self.row_end_inst.append(self.tri_en_bar_buf2_inst)
|
||||
|
||||
|
||||
|
||||
self.row_trien_bar_end_x = x_off
|
||||
|
||||
def route_dffs(self):
|
||||
""" Route the input inverters """
|
||||
self.connect_rail_from_right(self.ctrl_dff_inst,"dout_bar[0]","cs")
|
||||
self.connect_rail_from_right(self.ctrl_dff_inst,"dout_bar[1]","we")
|
||||
self.connect_rail_from_right(self.ctrl_dff_inst,"dout_bar[2]","oe")
|
||||
|
||||
dff_out_map = zip(["dout_bar[{}]".format(i) for i in range(3)], ["cs", "we", "oe"])
|
||||
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets)
|
||||
|
||||
# Connect the clock rail to the other clock rail
|
||||
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
|
||||
mid_pos = in_pos + vector(0,self.m2_pitch)
|
||||
rail_pos = vector(self.rail_x_offsets["clk_buf"], mid_pos.y)
|
||||
rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y)
|
||||
self.add_wire(("metal1","via1","metal2"),[in_pos, mid_pos, rail_pos])
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=rail_pos,
|
||||
|
|
@ -361,9 +329,9 @@ class control_logic(design.design):
|
|||
|
||||
self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list)
|
||||
|
||||
|
||||
def add_we_row(self,row):
|
||||
x_off = self.ctrl_dff_inst.width + self.internal_width
|
||||
|
||||
def get_offset(self,row):
|
||||
""" Compute the y-offset and mirroring """
|
||||
y_off = row*self.inv1.height
|
||||
if row % 2:
|
||||
y_off += self.inv1.height
|
||||
|
|
@ -371,77 +339,80 @@ class control_logic(design.design):
|
|||
else:
|
||||
mirror="R0"
|
||||
|
||||
return (y_off,mirror)
|
||||
|
||||
def add_we_row(self,row):
|
||||
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
|
||||
# input: WE, clk_buf_bar, CS output: w_en_bar
|
||||
self.w_en_bar_offset = vector(x_off, y_off)
|
||||
self.w_en_bar=self.add_inst(name="nand3_w_en_bar",
|
||||
mod=self.nand3,
|
||||
offset=self.w_en_bar_offset,
|
||||
mirror=mirror)
|
||||
w_en_bar_offset = vector(x_off, y_off)
|
||||
self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar",
|
||||
mod=self.nand3,
|
||||
offset=w_en_bar_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"])
|
||||
x_off += self.nand3.width
|
||||
|
||||
# input: w_en_bar, output: pre_w_en
|
||||
self.pre_w_en_offset = vector(x_off, y_off)
|
||||
self.pre_w_en=self.add_inst(name="inv_pre_w_en",
|
||||
mod=self.inv1,
|
||||
offset=self.pre_w_en_offset,
|
||||
mirror=mirror)
|
||||
pre_w_en_offset = vector(x_off, y_off)
|
||||
self.pre_w_en_inst=self.add_inst(name="inv_pre_w_en",
|
||||
mod=self.inv1,
|
||||
offset=pre_w_en_offset,
|
||||
mirror=mirror)
|
||||
|
||||
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
|
||||
x_off += self.inv1.width
|
||||
|
||||
# BUFFER INVERTERS FOR W_EN
|
||||
self.pre_w_en_bar_offset = vector(x_off, y_off)
|
||||
self.pre_w_en_bar=self.add_inst(name="inv_pre_w_en_bar",
|
||||
mod=self.inv2,
|
||||
offset=self.pre_w_en_bar_offset,
|
||||
mirror=mirror)
|
||||
pre_w_en_bar_offset = vector(x_off, y_off)
|
||||
self.pre_w_en_bar_inst=self.add_inst(name="inv_pre_w_en_bar",
|
||||
mod=self.inv2,
|
||||
offset=pre_w_en_bar_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"])
|
||||
x_off += self.inv2.width
|
||||
|
||||
self.w_en_offset = vector(x_off, y_off)
|
||||
self.w_en=self.add_inst(name="inv_w_en2",
|
||||
mod=self.inv8,
|
||||
offset=self.w_en_offset,
|
||||
mirror=mirror)
|
||||
w_en_offset = vector(x_off, y_off)
|
||||
self.w_en_inst=self.add_inst(name="inv_w_en2",
|
||||
mod=self.inv8,
|
||||
offset=w_en_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"])
|
||||
x_off += self.inv8.width
|
||||
|
||||
self.row_we_end_x = x_off
|
||||
self.row_end_inst.append(self.w_en_inst)
|
||||
|
||||
|
||||
def route_rblk(self):
|
||||
""" Connect the logic for the rblk generation """
|
||||
self.connect_rail_from_left(self.rblk_bar,"A","clk_buf_bar")
|
||||
self.connect_rail_from_left(self.rblk_bar,"B","oe")
|
||||
self.connect_rail_from_left(self.rblk_bar,"C","cs")
|
||||
|
||||
def route_rbl_in(self):
|
||||
""" Connect the logic for the rbl_in generation """
|
||||
rbl_in_map = zip(["A", "B", "C"], ["clk_buf_bar", "oe", "cs"])
|
||||
self.connect_vertical_bus(rbl_in_map, self.rbl_in_bar_inst, self.rail_offsets)
|
||||
|
||||
# Connect the NAND3 output to the inverter
|
||||
# The pins are assumed to extend all the way to the cell edge
|
||||
rblk_bar_pos = self.rblk_bar.get_pin("Z").center()
|
||||
inv_in_pos = self.rblk.get_pin("A").center()
|
||||
mid1 = vector(inv_in_pos.x,rblk_bar_pos.y)
|
||||
self.add_path("metal1",[rblk_bar_pos,mid1,inv_in_pos])
|
||||
rbl_in_bar_pos = self.rbl_in_bar_inst.get_pin("Z").center()
|
||||
inv_in_pos = self.rbl_in_inst.get_pin("A").center()
|
||||
mid1 = vector(inv_in_pos.x,rbl_in_bar_pos.y)
|
||||
self.add_path("metal1",[rbl_in_bar_pos,mid1,inv_in_pos])
|
||||
|
||||
# Connect the output to the RBL
|
||||
rblk_pos = self.rblk.get_pin("Z").center()
|
||||
rbl_out_pos = self.rbl_in_inst.get_pin("Z").center()
|
||||
rbl_in_pos = self.rbl_inst.get_pin("en").center()
|
||||
mid1 = vector(rbl_in_pos.x,rblk_pos.y)
|
||||
self.add_wire(("metal3","via2","metal2"),[rblk_pos,mid1,rbl_in_pos])
|
||||
mid1 = vector(rbl_in_pos.x,rbl_out_pos.y)
|
||||
self.add_wire(("metal3","via2","metal2"),[rbl_out_pos,mid1,rbl_in_pos])
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=rblk_pos,
|
||||
offset=rbl_out_pos,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=rblk_pos,
|
||||
offset=rbl_out_pos,
|
||||
rotate=90)
|
||||
|
||||
|
||||
|
||||
def connect_rail_from_right(self,inst, pin, rail):
|
||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
||||
in_pos = inst.get_pin(pin).center()
|
||||
rail_pos = vector(self.rail_x_offsets[rail], in_pos.y)
|
||||
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
|
||||
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=rail_pos,
|
||||
|
|
@ -450,7 +421,7 @@ class control_logic(design.design):
|
|||
def connect_rail_from_right_m2m3(self,inst, pin, rail):
|
||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
||||
in_pos = inst.get_pin(pin).center()
|
||||
rail_pos = vector(self.rail_x_offsets[rail], in_pos.y)
|
||||
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
|
||||
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
|
||||
# Bring it up to M2 for M2/M3 routing
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
|
|
@ -467,7 +438,7 @@ class control_logic(design.design):
|
|||
def connect_rail_from_left(self,inst, pin, rail):
|
||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
||||
in_pos = inst.get_pin(pin).lc()
|
||||
rail_pos = vector(self.rail_x_offsets[rail], in_pos.y)
|
||||
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
|
||||
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=rail_pos,
|
||||
|
|
@ -476,7 +447,7 @@ class control_logic(design.design):
|
|||
def connect_rail_from_left_m2m3(self,inst, pin, rail):
|
||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
||||
in_pos = inst.get_pin(pin).lc()
|
||||
rail_pos = vector(self.rail_x_offsets[rail], in_pos.y)
|
||||
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
|
||||
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=in_pos,
|
||||
|
|
@ -487,84 +458,86 @@ class control_logic(design.design):
|
|||
|
||||
|
||||
def route_wen(self):
|
||||
self.connect_rail_from_left(self.w_en_bar,"A","clk_buf_bar")
|
||||
self.connect_rail_from_left(self.w_en_bar,"B","cs")
|
||||
self.connect_rail_from_left(self.w_en_bar,"C","we")
|
||||
wen_map = zip(["A", "B", "C"], ["clk_buf_bar", "cs", "we"])
|
||||
self.connect_vertical_bus(wen_map, self.w_en_bar_inst, self.rail_offsets)
|
||||
|
||||
# Connect the NAND3 output to the inverter
|
||||
# The pins are assumed to extend all the way to the cell edge
|
||||
w_en_bar_pos = self.w_en_bar.get_pin("Z").center()
|
||||
inv_in_pos = self.pre_w_en.get_pin("A").center()
|
||||
w_en_bar_pos = self.w_en_bar_inst.get_pin("Z").center()
|
||||
inv_in_pos = self.pre_w_en_inst.get_pin("A").center()
|
||||
mid1 = vector(inv_in_pos.x,w_en_bar_pos.y)
|
||||
self.add_path("metal1",[w_en_bar_pos,mid1,inv_in_pos])
|
||||
|
||||
self.add_path("metal1",[self.pre_w_en.get_pin("Z").center(), self.pre_w_en_bar.get_pin("A").center()])
|
||||
self.add_path("metal1",[self.pre_w_en_bar.get_pin("Z").center(), self.w_en.get_pin("A").center()])
|
||||
self.add_path("metal1",[self.pre_w_en_inst.get_pin("Z").center(), self.pre_w_en_bar_inst.get_pin("A").center()])
|
||||
self.add_path("metal1",[self.pre_w_en_bar_inst.get_pin("Z").center(), self.w_en_inst.get_pin("A").center()])
|
||||
|
||||
self.connect_output(self.w_en, "Z", "w_en")
|
||||
self.connect_output(self.w_en_inst, "Z", "w_en")
|
||||
|
||||
def route_trien(self):
|
||||
|
||||
# Connect the NAND2 output to the buffer
|
||||
tri_en_bar_pos = self.tri_en_bar.get_pin("Z").center()
|
||||
inv_in_pos = self.tri_en.get_pin("A").center()
|
||||
tri_en_bar_pos = self.tri_en_bar_inst.get_pin("Z").center()
|
||||
inv_in_pos = self.tri_en_inst.get_pin("A").center()
|
||||
mid1 = vector(tri_en_bar_pos.x,inv_in_pos.y)
|
||||
self.add_wire(("metal1","via1","metal2"),[tri_en_bar_pos,mid1,inv_in_pos])
|
||||
|
||||
# Connect the INV output to the buffer
|
||||
tri_en_pos = self.tri_en.get_pin("Z").center()
|
||||
inv_in_pos = self.tri_en_buf1.get_pin("A").center()
|
||||
tri_en_pos = self.tri_en_inst.get_pin("Z").center()
|
||||
inv_in_pos = self.tri_en_buf1_inst.get_pin("A").center()
|
||||
mid_xoffset = 0.5*(tri_en_pos.x + inv_in_pos.x)
|
||||
mid1 = vector(mid_xoffset,tri_en_pos.y)
|
||||
mid2 = vector(mid_xoffset,inv_in_pos.y)
|
||||
self.add_path("metal1",[tri_en_pos,mid1,mid2,inv_in_pos])
|
||||
|
||||
self.add_path("metal1",[self.tri_en_buf1.get_pin("Z").center(), self.tri_en_buf2.get_pin("A").center()])
|
||||
self.add_path("metal1",[self.tri_en_buf1_ist.get_pin("Z").center(), self.tri_en_buf2_inst.get_pin("A").center()])
|
||||
|
||||
self.connect_output(self.tri_en_buf2, "Z", "tri_en")
|
||||
self.connect_output(self.tri_en_buf2_inst, "Z", "tri_en")
|
||||
|
||||
def route_trien_bar(self):
|
||||
|
||||
self.connect_rail_from_left(self.tri_en_bar,"A","clk_buf_bar")
|
||||
self.connect_rail_from_left(self.tri_en_bar,"B","oe")
|
||||
trien_map = zip(["A", "B"], ["clk_buf_bar", "oe"])
|
||||
self.connect_vertical_bus(trien_map, self.tri_en_bar_inst, self.rail_offsets)
|
||||
|
||||
# Connect the NAND2 output to the buffer
|
||||
tri_en_bar_pos = self.tri_en_bar.get_pin("Z").center()
|
||||
inv_in_pos = self.tri_en_bar_buf1.get_pin("A").center()
|
||||
tri_en_bar_pos = self.tri_en_bar_inst.get_pin("Z").center()
|
||||
inv_in_pos = self.tri_en_bar_buf1_inst.get_pin("A").center()
|
||||
mid_xoffset = 0.5*(tri_en_bar_pos.x + inv_in_pos.x)
|
||||
mid1 = vector(mid_xoffset,tri_en_bar_pos.y)
|
||||
mid2 = vector(mid_xoffset,inv_in_pos.y)
|
||||
self.add_path("metal1",[tri_en_bar_pos,mid1,mid2,inv_in_pos])
|
||||
|
||||
self.add_path("metal1",[self.tri_en_bar_buf1.get_pin("Z").center(), self.tri_en_bar_buf2.get_pin("A").center()])
|
||||
self.add_path("metal1",[self.tri_en_bar_buf1_inst.get_pin("Z").center(), self.tri_en_bar_buf2_inst.get_pin("A").center()])
|
||||
|
||||
self.connect_output(self.tri_en_bar_buf2, "Z", "tri_en_bar")
|
||||
self.connect_output(self.tri_en_bar_buf2_inst, "Z", "tri_en_bar")
|
||||
|
||||
|
||||
def route_sen(self):
|
||||
rbl_out_pos = self.rbl_inst.get_pin("out").bc()
|
||||
in_pos = self.pre_s_en_bar.get_pin("A").lc()
|
||||
in_pos = self.pre_s_en_bar_inst.get_pin("A").lc()
|
||||
mid1 = vector(rbl_out_pos.x,in_pos.y)
|
||||
self.add_wire(("metal1","via1","metal2"),[rbl_out_pos,mid1,in_pos])
|
||||
#s_en_pos = self.s_en.get_pin("Z").lc()
|
||||
|
||||
self.add_path("metal1",[self.pre_s_en_bar.get_pin("Z").center(), self.s_en.get_pin("A").center()])
|
||||
self.add_path("metal1",[self.pre_s_en_bar_inst.get_pin("Z").center(), self.s_en_inst.get_pin("A").center()])
|
||||
|
||||
self.connect_output(self.s_en, "Z", "s_en")
|
||||
self.connect_output(self.s_en_inst, "Z", "s_en")
|
||||
|
||||
def route_clk(self):
|
||||
""" Route the clk and clk_buf_bar signal internally """
|
||||
|
||||
clk_pin = self.clkbuf.get_pin("A")
|
||||
clk_pin = self.clkbuf_inst.get_pin("A")
|
||||
self.add_layout_pin_segment_center(text="clk",
|
||||
layer="metal2",
|
||||
start=clk_pin.bc(),
|
||||
end=clk_pin.bc().scale(1,0))
|
||||
|
||||
self.connect_rail_from_right_m2m3(self.clkbuf, "Z", "clk_buf")
|
||||
self.connect_rail_from_right_m2m3(self.clkbuf, "Zb", "clk_buf_bar")
|
||||
self.connect_output(self.clkbuf, "Z", "clk_buf")
|
||||
self.connect_output(self.clkbuf, "Zb", "clk_buf_bar")
|
||||
clkbuf_map = zip(["Z", "Zb"], ["clk_buf", "clk_buf_bar"])
|
||||
self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
|
||||
|
||||
# self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Z", "clk_buf")
|
||||
# self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Zb", "clk_buf_bar")
|
||||
self.connect_output(self.clkbuf_inst, "Z", "clk_buf")
|
||||
self.connect_output(self.clkbuf_inst, "Zb", "clk_buf_bar")
|
||||
|
||||
def connect_output(self, inst, pin_name, out_name):
|
||||
""" Create an output pin on the right side from the pin of a given instance. """
|
||||
|
|
@ -579,41 +552,32 @@ class control_logic(design.design):
|
|||
|
||||
|
||||
def route_supply(self):
|
||||
""" Route the vdd and gnd for the rows of logic. """
|
||||
""" Add vdd and gnd to the instance cells """
|
||||
|
||||
rows_start = 0
|
||||
rows_end = self.width
|
||||
#well_width = drc["minwidth_well"]
|
||||
|
||||
for i in range(8):
|
||||
if i%2:
|
||||
name = "vdd"
|
||||
well_type = "nwell"
|
||||
else:
|
||||
name = "gnd"
|
||||
well_type = "pwell"
|
||||
|
||||
yoffset = i*self.inv1.height
|
||||
|
||||
self.add_layout_pin_segment_center(text=name,
|
||||
layer="metal1",
|
||||
start=vector(rows_start,yoffset),
|
||||
end=vector(rows_end,yoffset))
|
||||
|
||||
# # also add a well +- around the rail
|
||||
# well_offset = vector(rows_start,yoffset-0.5*well_width)
|
||||
# self.add_rect(layer=well_type,
|
||||
# offset=well_offset,
|
||||
# width=rows_end-rows_start,
|
||||
# height=well_width)
|
||||
# self.add_rect(layer="vtg",
|
||||
# offset=well_offset,
|
||||
# width=rows_end-rows_start,
|
||||
# height=well_width)
|
||||
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
|
||||
for inst in self.row_end_inst:
|
||||
pins = inst.get_pins("vdd")
|
||||
for pin in pins:
|
||||
if pin.layer == "metal1":
|
||||
row_loc = pin.rc()
|
||||
pin_loc = vector(max_row_x_loc, pin.rc().y)
|
||||
self.add_power_pin("vdd", pin_loc)
|
||||
self.add_path("metal1", [row_loc, pin_loc])
|
||||
|
||||
pins = inst.get_pins("gnd")
|
||||
for pin in pins:
|
||||
if pin.layer == "metal1":
|
||||
row_loc = pin.rc()
|
||||
pin_loc = vector(max_row_x_loc, pin.rc().y)
|
||||
self.add_power_pin("gnd", pin_loc)
|
||||
self.add_path("metal1", [row_loc, pin_loc])
|
||||
|
||||
|
||||
self.copy_layout_pin(self.rbl_inst,"gnd")
|
||||
self.copy_layout_pin(self.rbl_inst,"vdd")
|
||||
|
||||
self.copy_layout_pin(self.ctrl_dff_inst,"gnd")
|
||||
self.copy_layout_pin(self.ctrl_dff_inst,"vdd")
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,24 +9,20 @@ from globals import OPTS
|
|||
class delay_chain(design.design):
|
||||
"""
|
||||
Generate a delay chain with the given number of stages and fanout.
|
||||
This automatically adds an extra inverter with no load on the input.
|
||||
Input is a list contains the electrical effort of each stage.
|
||||
Input is a list contains the electrical effort (fanout) of each stage.
|
||||
Usually, this will be constant, but it could have varied fanout.
|
||||
"""
|
||||
|
||||
def __init__(self, fanout_list, name="delay_chain"):
|
||||
"""init function"""
|
||||
design.design.__init__(self, name)
|
||||
# FIXME: input should be logic effort value
|
||||
# and there should be functions to get
|
||||
# area efficient inverter stage list
|
||||
|
||||
# Two fanouts are needed so that we can route the vdd/gnd connections
|
||||
for f in fanout_list:
|
||||
debug.check(f>0,"Must have non-zero fanouts for each stage.")
|
||||
debug.check(f>=2,"Must have >=2 fanouts for each stage.")
|
||||
|
||||
# number of inverters including any fanout loads.
|
||||
self.fanout_list = fanout_list
|
||||
self.num_inverters = 1 + sum(fanout_list)
|
||||
self.num_top_half = round(self.num_inverters / 2.0)
|
||||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
|
|
@ -35,6 +31,7 @@ class delay_chain(design.design):
|
|||
|
||||
self.add_pins()
|
||||
self.create_module()
|
||||
self.add_inverters()
|
||||
self.route_inverters()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
|
@ -52,14 +49,11 @@ class delay_chain(design.design):
|
|||
self.inv = pinv(route_output=False)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
# half chain length is the width of the layout
|
||||
# invs are stacked into 2 levels so input/output are close
|
||||
# extra metal is for the gnd connection U
|
||||
# Each stage is a a row
|
||||
self.height = len(self.fanout_list)*self.inv.height
|
||||
# The width is determined by the largest fanout plus the driver
|
||||
self.width = (max(self.fanout_list)+1) * self.inv.width
|
||||
|
||||
self.add_inverters()
|
||||
|
||||
|
||||
def add_inverters(self):
|
||||
""" Add the inverters and connect them based on the stage list """
|
||||
|
|
@ -164,19 +158,29 @@ class delay_chain(design.design):
|
|||
""" Add vdd and gnd rails and the input/output. Connect the gnd rails internally on
|
||||
the top end with no input/output to obstruct. """
|
||||
|
||||
for driver in self.driver_inst_list:
|
||||
vdd_pin = driver.get_pin("vdd")
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_pin.ll(),
|
||||
width=self.width,
|
||||
height=vdd_pin.height())
|
||||
gnd_pin = driver.get_pin("gnd")
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_pin.ll(),
|
||||
width=self.width,
|
||||
height=gnd_pin.height())
|
||||
# Add power and ground to all the cells except:
|
||||
# the fanout driver, the right-most load
|
||||
# The routing to connect the loads is over the first and last cells
|
||||
# We have an even number of drivers and must only do every other
|
||||
# supply rail
|
||||
for i in range(0,len(self.driver_inst_list),2):
|
||||
inv = self.driver_inst_list[i]
|
||||
for load in self.load_inst_map[inv]:
|
||||
if load==self.rightest_load_inst[inv]:
|
||||
continue
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
pin = load.get_pin(pin_name)
|
||||
self.add_power_pin(pin_name, pin.rc())
|
||||
else:
|
||||
# We have an even number of rows, so need to get the last gnd rail
|
||||
inv = self.driver_inst_list[-1]
|
||||
for load in self.load_inst_map[inv]:
|
||||
if load==self.rightest_load_inst[inv]:
|
||||
continue
|
||||
pin_name = "gnd"
|
||||
pin = load.get_pin(pin_name)
|
||||
self.add_power_pin(pin_name, pin.rc())
|
||||
|
||||
|
||||
# input is A pin of first inverter
|
||||
a_pin = self.driver_inst_list[0].get_pin("A")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class dff_array(design.design):
|
|||
if name=="":
|
||||
name = "dff_array_{0}x{1}".format(rows, columns)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns))
|
||||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.dff))
|
||||
|
|
@ -38,33 +38,33 @@ class dff_array(design.design):
|
|||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
self.add_pin(self.get_din_name(y,x))
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
self.add_pin(self.get_dout_name(y,x))
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
self.add_pin(self.get_din_name(row,col))
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
self.add_pin(self.get_dout_name(row,col))
|
||||
self.add_pin("clk")
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_dff_array(self):
|
||||
self.dff_insts={}
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(y,x)
|
||||
if (y % 2 == 0):
|
||||
base = vector(x*self.dff.width,y*self.dff.height)
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||
if (row % 2 == 0):
|
||||
base = vector(col*self.dff.width,row*self.dff.height)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector(x*self.dff.width,(y+1)*self.dff.height)
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.dff_insts[x,y]=self.add_inst(name=name,
|
||||
self.dff_insts[row,col]=self.add_inst(name=name,
|
||||
mod=self.dff,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.connect_inst([self.get_din_name(y,x),
|
||||
self.get_dout_name(y,x),
|
||||
self.connect_inst([self.get_din_name(row,col),
|
||||
self.get_dout_name(row,col),
|
||||
"clk",
|
||||
"vdd",
|
||||
"gnd"])
|
||||
|
|
@ -91,38 +91,30 @@ class dff_array(design.design):
|
|||
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
for y in range(self.rows):
|
||||
# Continous vdd rail along with label.
|
||||
vdd_pin=self.dff_insts[0,y].get_pin("vdd")
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_pin.ll(),
|
||||
width=self.width,
|
||||
height=self.m1_width)
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
# Continous vdd rail along with label.
|
||||
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
|
||||
self.add_power_pin("vdd", vdd_pin.center())
|
||||
|
||||
# Continous gnd rail along with label.
|
||||
gnd_pin=self.dff_insts[0,y].get_pin("gnd")
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_pin.ll(),
|
||||
width=self.width,
|
||||
height=self.m1_width)
|
||||
# Continous gnd rail along with label.
|
||||
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
|
||||
self.add_power_pin("gnd", gnd_pin.center())
|
||||
|
||||
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
din_pin = self.dff_insts[x,y].get_pin("D")
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
din_pin = self.dff_insts[row,col].get_pin("D")
|
||||
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_din_name(y,x),
|
||||
self.add_layout_pin(text=self.get_din_name(row,col),
|
||||
layer=din_pin.layer,
|
||||
offset=din_pin.ll(),
|
||||
width=din_pin.width(),
|
||||
height=din_pin.height())
|
||||
|
||||
dout_pin = self.dff_insts[x,y].get_pin("Q")
|
||||
dout_pin = self.dff_insts[row,col].get_pin("Q")
|
||||
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_dout_name(y,x),
|
||||
self.add_layout_pin(text=self.get_dout_name(row,col),
|
||||
layer=dout_pin.layer,
|
||||
offset=dout_pin.ll(),
|
||||
width=dout_pin.width(),
|
||||
|
|
@ -140,22 +132,20 @@ class dff_array(design.design):
|
|||
width=self.m2_width,
|
||||
height=self.height)
|
||||
else:
|
||||
self.add_layout_pin(text="clk",
|
||||
layer="metal3",
|
||||
offset=vector(0,0),
|
||||
width=self.width,
|
||||
height=self.m3_width)
|
||||
for x in range(self.columns):
|
||||
clk_pin = self.dff_insts[x,0].get_pin("clk")
|
||||
self.add_layout_pin_segment_center(text="clk",
|
||||
layer="metal3",
|
||||
start=vector(0,self.m3_pitch+self.m3_width),
|
||||
end=vector(self.width,self.m3_pitch+self.m3_width))
|
||||
for col in range(self.columns):
|
||||
clk_pin = self.dff_insts[0,col].get_pin("clk")
|
||||
# Make a vertical strip for each column
|
||||
self.add_layout_pin(text="clk",
|
||||
layer="metal2",
|
||||
offset=clk_pin.ll().scale(1,0),
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=clk_pin.ll().scale(1,0),
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
# Drop a via to the M3 pin
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=clk_pin.center().scale(1,0))
|
||||
offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width))
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class dff_buf_array(design.design):
|
|||
self.columns = columns
|
||||
|
||||
if name=="":
|
||||
name = "dff_array_{0}x{1}".format(rows, columns)
|
||||
name = "dff_buf_array_{0}x{1}".format(rows, columns)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
|
||||
|
|
@ -36,35 +36,35 @@ class dff_buf_array(design.design):
|
|||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
self.add_pin(self.get_din_name(y,x))
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
self.add_pin(self.get_dout_name(y,x))
|
||||
self.add_pin(self.get_dout_bar_name(y,x))
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
self.add_pin(self.get_din_name(row,col))
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
self.add_pin(self.get_dout_name(row,col))
|
||||
self.add_pin(self.get_dout_bar_name(row,col))
|
||||
self.add_pin("clk")
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_dff_array(self):
|
||||
self.dff_insts={}
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(y,x)
|
||||
if (y % 2 == 0):
|
||||
base = vector(x*self.dff.width,y*self.dff.height)
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||
if (row % 2 == 0):
|
||||
base = vector(col*self.dff.width,row*self.dff.height)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector(x*self.dff.width,(y+1)*self.dff.height)
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.dff_insts[x,y]=self.add_inst(name=name,
|
||||
mod=self.dff,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.connect_inst([self.get_din_name(y,x),
|
||||
self.get_dout_name(y,x),
|
||||
self.get_dout_bar_name(y,x),
|
||||
self.dff_insts[row,col]=self.add_inst(name=name,
|
||||
mod=self.dff,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.connect_inst([self.get_din_name(row,col),
|
||||
self.get_dout_name(row,col),
|
||||
self.get_dout_bar_name(row,col),
|
||||
"clk",
|
||||
"vdd",
|
||||
"gnd"])
|
||||
|
|
@ -100,58 +100,38 @@ class dff_buf_array(design.design):
|
|||
return dout_bar_name
|
||||
|
||||
def add_layout_pins(self):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
# Continous vdd rail along with label.
|
||||
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
|
||||
self.add_power_pin("vdd", vdd_pin.lc())
|
||||
|
||||
xoffsets = []
|
||||
for x in range(self.columns):
|
||||
xoffsets.append(self.dff_insts[x,0].get_pin("gnd").lx())
|
||||
|
||||
for y in range(self.rows):
|
||||
|
||||
# Route both supplies
|
||||
for n in ["vdd", "gnd"]:
|
||||
supply_pin = self.dff_insts[0,y].get_pin(n)
|
||||
supply_offset = supply_pin.ll()
|
||||
self.add_rect(layer="metal1",
|
||||
offset=supply_offset,
|
||||
width=self.width)
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in xoffsets:
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pin_pos,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=pin_pos,
|
||||
rotate=90)
|
||||
self.add_layout_pin_rect_center(text=n,
|
||||
layer="metal3",
|
||||
offset=pin_pos)
|
||||
|
||||
# Continous gnd rail along with label.
|
||||
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
|
||||
self.add_power_pin("gnd", gnd_pin.lc())
|
||||
|
||||
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
din_pin = self.dff_insts[x,y].get_pin("D")
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
din_pin = self.dff_insts[row,col].get_pin("D")
|
||||
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_din_name(y,x),
|
||||
self.add_layout_pin(text=self.get_din_name(row,col),
|
||||
layer=din_pin.layer,
|
||||
offset=din_pin.ll(),
|
||||
width=din_pin.width(),
|
||||
height=din_pin.height())
|
||||
|
||||
dout_pin = self.dff_insts[x,y].get_pin("Q")
|
||||
dout_pin = self.dff_insts[row,col].get_pin("Q")
|
||||
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_dout_name(y,x),
|
||||
self.add_layout_pin(text=self.get_dout_name(row,col),
|
||||
layer=dout_pin.layer,
|
||||
offset=dout_pin.ll(),
|
||||
width=dout_pin.width(),
|
||||
height=dout_pin.height())
|
||||
|
||||
|
||||
dout_bar_pin = self.dff_insts[x,y].get_pin("Qb")
|
||||
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
|
||||
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_dout_bar_name(y,x),
|
||||
self.add_layout_pin(text=self.get_dout_bar_name(row,col),
|
||||
layer=dout_bar_pin.layer,
|
||||
offset=dout_bar_pin.ll(),
|
||||
width=dout_bar_pin.width(),
|
||||
|
|
@ -168,22 +148,21 @@ class dff_buf_array(design.design):
|
|||
width=self.m2_width,
|
||||
height=self.height)
|
||||
else:
|
||||
self.add_layout_pin(text="clk",
|
||||
layer="metal3",
|
||||
offset=vector(0,2*self.m2_width),
|
||||
width=self.width,
|
||||
height=self.m3_width)
|
||||
for x in range(self.columns):
|
||||
clk_pin = self.dff_insts[x,0].get_pin("clk")
|
||||
self.add_layout_pin_segment_center(text="clk",
|
||||
layer="metal3",
|
||||
start=vector(0,self.m3_pitch+self.m3_width),
|
||||
end=vector(self.width,self.m3_pitch+self.m3_width))
|
||||
for col in range(self.columns):
|
||||
clk_pin = self.dff_insts[0,col].get_pin("clk")
|
||||
|
||||
# Make a vertical strip for each column
|
||||
self.add_layout_pin(text="clk",
|
||||
layer="metal2",
|
||||
offset=clk_pin.ll().scale(1,0),
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=clk_pin.ll().scale(1,0),
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
# Drop a via to the M3 pin
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=clk_pin.center().scale(1,0) + vector(0,2*self.m2_width))
|
||||
offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width))
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class dff_inv_array(design.design):
|
|||
self.columns = columns
|
||||
|
||||
if name=="":
|
||||
name = "dff_array_{0}x{1}".format(rows, columns)
|
||||
name = "dff_inv_array_{0}x{1}".format(rows, columns)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
|
||||
|
|
@ -36,35 +36,35 @@ class dff_inv_array(design.design):
|
|||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
self.add_pin(self.get_din_name(y,x))
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
self.add_pin(self.get_dout_name(y,x))
|
||||
self.add_pin(self.get_dout_bar_name(y,x))
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
self.add_pin(self.get_din_name(row,col))
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
self.add_pin(self.get_dout_name(row,col))
|
||||
self.add_pin(self.get_dout_bar_name(row,col))
|
||||
self.add_pin("clk")
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_dff_array(self):
|
||||
self.dff_insts={}
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(y,x)
|
||||
if (y % 2 == 0):
|
||||
base = vector(x*self.dff.width,y*self.dff.height)
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||
if (row % 2 == 0):
|
||||
base = vector(col*self.dff.width,row*self.dff.height)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector(x*self.dff.width,(y+1)*self.dff.height)
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.dff_insts[x,y]=self.add_inst(name=name,
|
||||
mod=self.dff,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.connect_inst([self.get_din_name(y,x),
|
||||
self.get_dout_name(y,x),
|
||||
self.get_dout_bar_name(y,x),
|
||||
self.dff_insts[row,col]=self.add_inst(name=name,
|
||||
mod=self.dff,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.connect_inst([self.get_din_name(row,col),
|
||||
self.get_dout_name(row,col),
|
||||
self.get_dout_bar_name(row,col),
|
||||
"clk",
|
||||
"vdd",
|
||||
"gnd"])
|
||||
|
|
@ -100,47 +100,38 @@ class dff_inv_array(design.design):
|
|||
return dout_bar_name
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
for y in range(self.rows):
|
||||
# Continous vdd rail along with label.
|
||||
vdd_pin=self.dff_insts[0,y].get_pin("vdd")
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_pin.ll(),
|
||||
width=self.width,
|
||||
height=self.m1_width)
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
# Continous vdd rail along with label.
|
||||
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
|
||||
self.add_power_pin("vdd", vdd_pin.lc())
|
||||
|
||||
# Continous gnd rail along with label.
|
||||
gnd_pin=self.dff_insts[0,y].get_pin("gnd")
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_pin.ll(),
|
||||
width=self.width,
|
||||
height=self.m1_width)
|
||||
# Continous gnd rail along with label.
|
||||
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
|
||||
self.add_power_pin("gnd", gnd_pin.lc())
|
||||
|
||||
|
||||
for y in range(self.rows):
|
||||
for x in range(self.columns):
|
||||
din_pin = self.dff_insts[x,y].get_pin("D")
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
din_pin = self.dff_insts[row,col].get_pin("D")
|
||||
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_din_name(y,x),
|
||||
self.add_layout_pin(text=self.get_din_name(row,col),
|
||||
layer=din_pin.layer,
|
||||
offset=din_pin.ll(),
|
||||
width=din_pin.width(),
|
||||
height=din_pin.height())
|
||||
|
||||
dout_pin = self.dff_insts[x,y].get_pin("Q")
|
||||
dout_pin = self.dff_insts[row,col].get_pin("Q")
|
||||
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_dout_name(y,x),
|
||||
self.add_layout_pin(text=self.get_dout_name(row,col),
|
||||
layer=dout_pin.layer,
|
||||
offset=dout_pin.ll(),
|
||||
width=dout_pin.width(),
|
||||
height=dout_pin.height())
|
||||
|
||||
|
||||
dout_bar_pin = self.dff_insts[x,y].get_pin("Qb")
|
||||
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
|
||||
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_dout_bar_name(y,x),
|
||||
self.add_layout_pin(text=self.get_dout_bar_name(row,col),
|
||||
layer=dout_bar_pin.layer,
|
||||
offset=dout_bar_pin.ll(),
|
||||
width=dout_bar_pin.width(),
|
||||
|
|
@ -157,22 +148,21 @@ class dff_inv_array(design.design):
|
|||
width=self.m2_width,
|
||||
height=self.height)
|
||||
else:
|
||||
self.add_layout_pin(text="clk",
|
||||
layer="metal3",
|
||||
offset=vector(0,0),
|
||||
width=self.width,
|
||||
height=self.m3_width)
|
||||
for x in range(self.columns):
|
||||
clk_pin = self.dff_insts[x,0].get_pin("clk")
|
||||
self.add_layout_pin_segment_center(text="clk",
|
||||
layer="metal3",
|
||||
start=vector(0,self.m3_pitch+self.m3_width),
|
||||
end=vector(self.width,self.m3_pitch+self.m3_width))
|
||||
for col in range(self.columns):
|
||||
clk_pin = self.dff_insts[0,col].get_pin("clk")
|
||||
# Make a vertical strip for each column
|
||||
self.add_layout_pin(text="clk",
|
||||
layer="metal2",
|
||||
offset=clk_pin.ll().scale(1,0),
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=clk_pin.ll().scale(1,0),
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
# Drop a via to the M3 pin
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=clk_pin.center().scale(1,0))
|
||||
offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width))
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -127,12 +127,12 @@ class hierarchical_decoder(design.design):
|
|||
min_x = min(min_x, -self.pre3_8.width)
|
||||
input_offset=vector(min_x - self.input_routing_width,0)
|
||||
|
||||
input_bus_names = ["A[{0}]".format(i) for i in range(self.num_inputs)]
|
||||
self.create_vertical_pin_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=input_offset,
|
||||
names=input_bus_names,
|
||||
length=input_height)
|
||||
input_bus_names = ["addr[{0}]".format(i) for i in range(self.num_inputs)]
|
||||
self.input_rails = self.create_vertical_pin_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=input_offset,
|
||||
names=input_bus_names,
|
||||
length=input_height)
|
||||
|
||||
self.connect_input_to_predecodes()
|
||||
|
||||
|
|
@ -143,14 +143,15 @@ class hierarchical_decoder(design.design):
|
|||
for i in range(2):
|
||||
index = pre_num * 2 + i
|
||||
|
||||
input_pin = self.get_pin("A[{}]".format(index))
|
||||
input_pos = self.input_rails["addr[{}]".format(index)]
|
||||
|
||||
in_name = "in[{}]".format(i)
|
||||
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
|
||||
|
||||
# Offset each decoder pin up so they don't conflit
|
||||
decoder_offset = decoder_pin.center() + vector(0,i*self.m2_pitch)
|
||||
input_offset = input_pin.center().scale(1,0) + decoder_offset.scale(0,1)
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
|
||||
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
|
||||
|
||||
self.connect_input_rail(decoder_offset, input_offset)
|
||||
|
||||
|
|
@ -159,14 +160,15 @@ class hierarchical_decoder(design.design):
|
|||
for i in range(3):
|
||||
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
|
||||
|
||||
input_pin = self.get_pin("A[{}]".format(index))
|
||||
input_pos = self.input_rails["addr[{}]".format(index)]
|
||||
|
||||
in_name = "in[{}]".format(i)
|
||||
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
|
||||
|
||||
# Offset each decoder pin up so they don't conflit
|
||||
decoder_offset = decoder_pin.center() + vector(0,i*self.m2_pitch)
|
||||
input_offset = input_pin.center().scale(1,0) + decoder_offset.scale(0,1)
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
|
||||
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
|
||||
|
||||
self.connect_input_rail(decoder_offset, input_offset)
|
||||
|
||||
|
|
@ -187,7 +189,7 @@ class hierarchical_decoder(design.design):
|
|||
""" Add the module pins """
|
||||
|
||||
for i in range(self.num_inputs):
|
||||
self.add_pin("A[{0}]".format(i))
|
||||
self.add_pin("addr[{0}]".format(i))
|
||||
|
||||
for j in range(self.rows):
|
||||
self.add_pin("decode[{0}]".format(j))
|
||||
|
|
@ -248,7 +250,7 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
pins = []
|
||||
for input_index in range(2):
|
||||
pins.append("A[{0}]".format(input_index + index_off1))
|
||||
pins.append("addr[{0}]".format(input_index + index_off1))
|
||||
for output_index in range(4):
|
||||
pins.append("out[{0}]".format(output_index + index_off2))
|
||||
pins.extend(["vdd", "gnd"])
|
||||
|
|
@ -275,7 +277,7 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
pins = []
|
||||
for input_index in range(3):
|
||||
pins.append("A[{0}]".format(input_index + in_index_offset))
|
||||
pins.append("addr[{0}]".format(input_index + in_index_offset))
|
||||
for output_index in range(8):
|
||||
pins.append("out[{0}]".format(output_index + out_index_offset))
|
||||
pins.extend(["vdd", "gnd"])
|
||||
|
|
@ -415,18 +417,14 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
# This is not needed for inputs <4 since they have no pre/decode stages.
|
||||
if (self.num_inputs >= 4):
|
||||
# Array for saving the X offsets of the vertical rails. These rail
|
||||
# offsets are accessed with indices.
|
||||
self.rail_x_offsets = []
|
||||
for i in range(self.total_number_of_predecoder_outputs):
|
||||
# The offsets go into the negative x direction
|
||||
# assuming the predecodes are placed at (self.internal_routing_width,0)
|
||||
x_offset = self.m2_pitch * i
|
||||
self.rail_x_offsets.append(x_offset+0.5*self.m2_width)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=vector(x_offset,0),
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
input_offset = vector(0.5*self.m2_width,0)
|
||||
input_bus_names = ["predecode[{0}]".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
||||
self.predecode_rails = self.create_vertical_pin_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=input_offset,
|
||||
names=input_bus_names,
|
||||
length=self.height)
|
||||
|
||||
|
||||
self.connect_rails_to_predecodes()
|
||||
self.connect_rails_to_decoder()
|
||||
|
|
@ -434,20 +432,22 @@ class hierarchical_decoder(design.design):
|
|||
def connect_rails_to_predecodes(self):
|
||||
""" Iterates through all of the predecodes and connects to the rails including the offsets """
|
||||
|
||||
# FIXME: convert to connect_bus
|
||||
for pre_num in range(self.no_of_pre2x4):
|
||||
for i in range(4):
|
||||
index = pre_num * 4 + i
|
||||
predecode_name = "predecode[{}]".format(pre_num * 4 + i)
|
||||
out_name = "out[{}]".format(i)
|
||||
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
||||
self.connect_predecode_rail_m3(index, pin)
|
||||
self.connect_predecode_rail_m3(predecode_name, pin)
|
||||
|
||||
|
||||
# FIXME: convert to connect_bus
|
||||
for pre_num in range(self.no_of_pre3x8):
|
||||
for i in range(8):
|
||||
index = pre_num * 8 + i + self.no_of_pre2x4 * 4
|
||||
predecode_name = "predecode[{}]".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
|
||||
out_name = "out[{}]".format(i)
|
||||
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
||||
self.connect_predecode_rail_m3(index, pin)
|
||||
self.connect_predecode_rail_m3(predecode_name, pin)
|
||||
|
||||
|
||||
|
||||
|
|
@ -463,17 +463,24 @@ class hierarchical_decoder(design.design):
|
|||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
for index_A in self.predec_groups[0]:
|
||||
for index_B in self.predec_groups[1]:
|
||||
self.connect_predecode_rail(index_A, self.nand_inst[row_index].get_pin("A"))
|
||||
self.connect_predecode_rail(index_B, self.nand_inst[row_index].get_pin("B"))
|
||||
# FIXME: convert to connect_bus?
|
||||
predecode_name = "predecode[{}]".format(index_A)
|
||||
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode[{}]".format(index_B)
|
||||
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
row_index = row_index + 1
|
||||
|
||||
elif (self.num_inputs > 5):
|
||||
for index_A in self.predec_groups[0]:
|
||||
for index_B in self.predec_groups[1]:
|
||||
for index_C in self.predec_groups[2]:
|
||||
self.connect_predecode_rail(index_A, self.nand_inst[row_index].get_pin("A"))
|
||||
self.connect_predecode_rail(index_B, self.nand_inst[row_index].get_pin("B"))
|
||||
self.connect_predecode_rail(index_C, self.nand_inst[row_index].get_pin("C"))
|
||||
# FIXME: convert to connect_bus?
|
||||
predecode_name = "predecode[{}]".format(index_A)
|
||||
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode[{}]".format(index_B)
|
||||
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
predecode_name = "predecode[{}]".format(index_C)
|
||||
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C"))
|
||||
row_index = row_index + 1
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
|
|
@ -509,19 +516,21 @@ class hierarchical_decoder(design.design):
|
|||
self.copy_layout_pin(pre, "gnd")
|
||||
|
||||
|
||||
def connect_predecode_rail(self, rail_index, pin):
|
||||
def connect_predecode_rail(self, rail_name, pin):
|
||||
""" Connect the routing rail to the given metal1 pin """
|
||||
rail_pos = vector(self.rail_x_offsets[rail_index],pin.lc().y)
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y)
|
||||
self.add_path("metal1", [rail_pos, pin.lc()])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=rail_pos,
|
||||
rotate=90)
|
||||
|
||||
|
||||
def connect_predecode_rail_m3(self, rail_index, pin):
|
||||
def connect_predecode_rail_m3(self, rail_name, pin):
|
||||
""" Connect the routing rail to the given metal1 pin """
|
||||
# This routes the pin up to the rail, basically, to avoid conflicts.
|
||||
# It would be fixed with a channel router.
|
||||
mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2)
|
||||
rail_pos = vector(self.rail_x_offsets[rail_index],mid_point.y)
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pin.center(),
|
||||
rotate=90)
|
||||
|
|
|
|||
|
|
@ -50,52 +50,42 @@ class hierarchical_predecode(design.design):
|
|||
debug.error("Invalid number of predecode inputs.",-1)
|
||||
|
||||
def setup_constraints(self):
|
||||
# The rail offsets are indexed by the label
|
||||
self.rails = {}
|
||||
|
||||
# Non inverted input rails
|
||||
for rail_index in range(self.number_of_inputs):
|
||||
xoffset = rail_index * self.m2_pitch + 0.5*self.m2_width
|
||||
self.rails["in[{}]".format(rail_index)]=xoffset
|
||||
self.height = self.number_of_outputs * self.nand.height
|
||||
|
||||
# x offset for input inverters
|
||||
self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch
|
||||
|
||||
# Creating the right hand side metal2 rails for output connections
|
||||
for rail_index in range(2 * self.number_of_inputs):
|
||||
xoffset = self.x_off_inv_1 + self.inv.width + ((rail_index+1) * self.m2_pitch) + 0.5*self.m2_width
|
||||
if rail_index < self.number_of_inputs:
|
||||
self.rails["Abar[{}]".format(rail_index)]=xoffset
|
||||
else:
|
||||
self.rails["A[{}]".format(rail_index-self.number_of_inputs)]=xoffset
|
||||
|
||||
# x offset to NAND decoder includes the left rails, mid rails and inverters, plus an extra m2 pitch
|
||||
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (1 + 2*self.number_of_inputs) * self.m2_pitch
|
||||
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 1) * self.m2_pitch
|
||||
|
||||
|
||||
# x offset to output inverters
|
||||
self.x_off_inv_2 = self.x_off_nand + self.nand.width
|
||||
|
||||
# Height width are computed
|
||||
self.width = self.x_off_inv_2 + self.inv.width
|
||||
self.height = self.number_of_outputs * self.nand.height
|
||||
|
||||
def create_rails(self):
|
||||
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
|
||||
for label in self.rails.keys():
|
||||
# these are not primary inputs, so they shouldn't have a
|
||||
# label or LVS complains about different names on one net
|
||||
if label.startswith("in"):
|
||||
self.add_layout_pin(text=label,
|
||||
layer="metal2",
|
||||
offset=vector(self.rails[label] - 0.5*self.m1_width, self.m1_width),
|
||||
width=self.m2_width,
|
||||
height=self.height - 4*self.m1_width)
|
||||
else:
|
||||
self.add_rect(layer="metal2",
|
||||
offset=vector(self.rails[label] - 0.5*self.m1_width, 2*self.m1_width),
|
||||
width=self.m2_width,
|
||||
height=self.height - 4*self.m1_width)
|
||||
input_names = ["in[{}]".format(x) for x in range(self.number_of_inputs)]
|
||||
offset = vector(0.5*self.m2_width,2*self.m1_width)
|
||||
self.input_rails = self.create_vertical_pin_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=offset,
|
||||
names=input_names,
|
||||
length=self.height - 2*self.m1_width)
|
||||
|
||||
invert_names = ["Abar[{}]".format(x) for x in range(self.number_of_inputs)]
|
||||
non_invert_names = ["A[{}]".format(x) for x in range(self.number_of_inputs)]
|
||||
decode_names = invert_names + non_invert_names
|
||||
offset = vector(self.x_off_inv_1 + self.inv.width + self.m2_pitch, 2*self.m1_width)
|
||||
self.decode_rails = self.create_vertical_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=offset,
|
||||
names=decode_names,
|
||||
length=self.height - 2*self.m1_width)
|
||||
|
||||
|
||||
def add_input_inverters(self):
|
||||
""" Create the input inverters to invert input signals for the decode stage. """
|
||||
|
||||
|
|
@ -176,14 +166,14 @@ class hierarchical_predecode(design.design):
|
|||
y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1m2.width + self.m1_space
|
||||
in_pin = "in[{}]".format(num)
|
||||
a_pin = "A[{}]".format(num)
|
||||
in_pos = vector(self.rails[in_pin],y_offset)
|
||||
a_pos = vector(self.rails[a_pin],y_offset)
|
||||
in_pos = vector(self.input_rails[in_pin].x,y_offset)
|
||||
a_pos = vector(self.decode_rails[a_pin].x,y_offset)
|
||||
self.add_path("metal1",[in_pos, a_pos])
|
||||
self.add_via_center(layers = ("metal1", "via1", "metal2"),
|
||||
offset=[self.rails[in_pin], y_offset],
|
||||
offset=[self.input_rails[in_pin].x, y_offset],
|
||||
rotate=90)
|
||||
self.add_via_center(layers = ("metal1", "via1", "metal2"),
|
||||
offset=[self.rails[a_pin], y_offset],
|
||||
offset=[self.decode_rails[a_pin].x, y_offset],
|
||||
rotate=90)
|
||||
|
||||
def route_output_inverters(self):
|
||||
|
|
@ -222,7 +212,7 @@ class hierarchical_predecode(design.design):
|
|||
y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space
|
||||
inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc()
|
||||
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0)
|
||||
rail_pos = vector(self.rails[out_pin],y_offset)
|
||||
rail_pos = vector(self.decode_rails[out_pin].x,y_offset)
|
||||
self.add_path("metal1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
|
||||
self.add_via_center(layers = ("metal1", "via1", "metal2"),
|
||||
offset=rail_pos,
|
||||
|
|
@ -231,7 +221,7 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
#route input
|
||||
inv_in_pos = self.in_inst[inv_num].get_pin("A").lc()
|
||||
in_pos = vector(self.rails[in_pin],inv_in_pos.y)
|
||||
in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y)
|
||||
self.add_path("metal1", [in_pos, inv_in_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=in_pos,
|
||||
|
|
@ -253,7 +243,7 @@ class hierarchical_predecode(design.design):
|
|||
# this will connect pins A,B or A,B,C
|
||||
for rail_pin,gate_pin in zip(index_lst,gate_lst):
|
||||
pin_pos = self.nand_inst[k].get_pin(gate_pin).lc()
|
||||
rail_pos = vector(self.rails[rail_pin], pin_pos.y)
|
||||
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
|
||||
self.add_path("metal1", [rail_pos, pin_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=rail_pos,
|
||||
|
|
|
|||
|
|
@ -42,8 +42,9 @@ class replica_bitline(design.design):
|
|||
|
||||
#self.add_lvs_correspondence_points()
|
||||
|
||||
self.width = self.right_gnd_pin.rx() - self.left_gnd_pin.lx()
|
||||
self.height = self.left_gnd_pin.uy() - self.left_gnd_pin.by()
|
||||
# Plus a pitch for the WL contacts on the RBL
|
||||
self.width = self.rbl_inst.rx() - self.dc_inst.lx() + self.m1_pitch
|
||||
self.height = max(self.rbl_inst.uy(), self.dc_inst.uy())
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
|
|
@ -57,15 +58,19 @@ class replica_bitline(design.design):
|
|||
# away from the delay chain/inverter with space for three M2 tracks
|
||||
self.bitcell_offset = vector(0,self.replica_bitcell.height)
|
||||
self.rbl_offset = self.bitcell_offset
|
||||
|
||||
# Gap between the delay chain and RBL
|
||||
gap_width = 2*self.m2_pitch
|
||||
|
||||
# Quadrant 4: with some space below it and tracks on the right for vdd/gnd
|
||||
self.delay_chain_offset = vector(-self.delay_chain.width-4*self.m2_pitch,self.replica_bitcell.height)
|
||||
self.delay_chain_offset = vector(-self.delay_chain.width-gap_width,self.replica_bitcell.height)
|
||||
|
||||
# Will be flipped vertically below the delay chain
|
||||
self.rbl_inv_offset = self.delay_chain_offset + vector(0.5*self.delay_chain.width, 0)
|
||||
# Align it with the inverters in the delay chain to simplify supply connections
|
||||
self.rbl_inv_offset = self.delay_chain_offset + vector(2*self.inv.width, 0)
|
||||
|
||||
# Placed next to the replica bitcell
|
||||
self.access_tx_offset = vector(-4*self.m2_pitch-self.access_tx.width-self.inv.width, 0.5*self.inv.height)
|
||||
self.access_tx_offset = vector(-gap_width-self.access_tx.width-self.inv.width, 0.5*self.inv.height)
|
||||
|
||||
|
||||
|
||||
|
|
@ -126,25 +131,53 @@ class replica_bitline(design.design):
|
|||
|
||||
def route(self):
|
||||
""" Connect all the signals together """
|
||||
self.route_vdd()
|
||||
self.route_gnd()
|
||||
self.route_vdd_gnd()
|
||||
self.route_supplies()
|
||||
self.route_wl()
|
||||
self.route_access_tx()
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
def route_wl(self):
|
||||
""" Connect the RBL word lines to gnd """
|
||||
# Connect the WL and gnd pins directly to the center and right gnd rails
|
||||
for row in range(self.bitcell_loads):
|
||||
wl = "wl[{}]".format(row)
|
||||
pin = self.rbl_inst.get_pin(wl)
|
||||
|
||||
# Route the connection to the right so that it doesn't interfere
|
||||
# with the cells
|
||||
pin_right = pin.rc()
|
||||
pin_extension = pin_right + vector(self.m1_pitch,0)
|
||||
if pin.layer != "metal1":
|
||||
continue
|
||||
self.add_path("metal1", [pin_right, pin_extension])
|
||||
self.add_power_pin("gnd", pin_extension)
|
||||
|
||||
|
||||
def route_supplies(self):
|
||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||
|
||||
# These are the instances that every bank has
|
||||
top_instances = [self.rbl_inst,
|
||||
self.rbl_inv_inst,
|
||||
self.rbc_inst,
|
||||
self.dc_inst]
|
||||
|
||||
self.dc_inst]
|
||||
for inst in top_instances:
|
||||
self.copy_layout_pin(inst, "vdd")
|
||||
self.copy_layout_pin(inst, "gnd")
|
||||
|
||||
|
||||
# Route the inverter supply pin from M1
|
||||
# Only vdd is needed because gnd shares a rail with the delay chain
|
||||
pin = self.rbl_inv_inst.get_pin("vdd")
|
||||
self.add_power_pin("vdd", pin.lc())
|
||||
|
||||
# Replica bitcell needs to be routed up to M3
|
||||
pin=self.rbc_inst.get_pin("vdd")
|
||||
# Don't rotate this via to vit in FreePDK45
|
||||
self.add_power_pin("vdd", pin.center(), False)
|
||||
|
||||
for pin in self.rbc_inst.get_pins("gnd"):
|
||||
self.add_power_pin("gnd", pin.center())
|
||||
|
||||
|
||||
|
||||
def route_access_tx(self):
|
||||
# GATE ROUTE
|
||||
# 1. Add the poly contact and nwell enclosure
|
||||
|
|
@ -183,7 +216,7 @@ class replica_bitline(design.design):
|
|||
# DRAIN ROUTE
|
||||
# Route the drain to the vdd rail
|
||||
drain_offset = self.tx_inst.get_pin("D").center()
|
||||
self.add_path("metal1", [drain_offset, drain_offset.scale(1,0)])
|
||||
self.add_power_pin("vdd", drain_offset)
|
||||
|
||||
# SOURCE ROUTE
|
||||
# Route the drain to the RBL inverter input
|
||||
|
|
@ -194,13 +227,11 @@ class replica_bitline(design.design):
|
|||
# Route the connection of the source route to the RBL bitline (left)
|
||||
# Via will go halfway down from the bitcell
|
||||
bl_offset = self.rbc_inst.get_pin("BL").bc()
|
||||
self.add_path("metal3",[source_offset, bl_offset])
|
||||
# Route down a pitch so we can use M2 routing
|
||||
bl_down_offset = bl_offset - vector(0, self.m2_pitch)
|
||||
self.add_path("metal2",[source_offset, bl_down_offset, bl_offset])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=source_offset)
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=source_offset)
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=bl_offset)
|
||||
|
||||
# BODY ROUTE
|
||||
# Connect it to the inverter well
|
||||
|
|
@ -213,30 +244,10 @@ class replica_bitline(design.design):
|
|||
|
||||
def route_vdd(self):
|
||||
""" Route all signals connected to vdd """
|
||||
|
||||
self.copy_layout_pin(self.dc_inst,"vdd")
|
||||
self.copy_layout_pin(self.rbc_inst,"vdd")
|
||||
|
||||
# Route the vdd lines from left to right
|
||||
|
||||
# Add via for the delay chain
|
||||
left_vdd_start = self.dc_inst.ll().scale(1,0) - vector(self.m2_pitch,0)
|
||||
left_vdd_end = vector(left_vdd_start.x, self.rbl_inst.uy())
|
||||
self.left_vdd_pin=self.add_segment_center(layer="metal2",
|
||||
start=left_vdd_start,
|
||||
end=left_vdd_end)
|
||||
|
||||
# Vdd line to the left of the replica bitline
|
||||
center_vdd_start = self.rbc_inst.ll() - vector(3*self.m2_pitch,0)
|
||||
center_vdd_end = vector(center_vdd_start.x, self.rbl_inst.uy())
|
||||
self.center_vdd_pin=self.add_segment_center(layer="metal2",
|
||||
start=center_vdd_start,
|
||||
end=center_vdd_end)
|
||||
|
||||
# Vdd line to the right of the replica bitline
|
||||
right_vdd_start = self.rbc_inst.lr() + vector(2*self.m2_pitch,0)
|
||||
right_vdd_end = vector(right_vdd_start.x, self.rbl_inst.uy())
|
||||
self.right_vdd_pin=self.add_segment_center(layer="metal2",
|
||||
start=right_vdd_start,
|
||||
end=right_vdd_end)
|
||||
|
||||
|
||||
|
||||
# Connect the WL and vdd pins directly to the center and right vdd rails
|
||||
|
|
@ -260,24 +271,6 @@ class replica_bitline(design.design):
|
|||
|
||||
|
||||
|
||||
# Connect the vdd pins of the delay chain to the left rails
|
||||
dc_vdd_pins = self.dc_inst.get_pins("vdd")
|
||||
for pin in dc_vdd_pins:
|
||||
if pin.layer != "metal1":
|
||||
continue
|
||||
start = vector(self.left_vdd_pin.cx(),pin.cy())
|
||||
# Note, we don't connect to center because of via conflicts
|
||||
# with the RBL pins
|
||||
#end = vector(center_vdd_pin.cx(),pin.cy())
|
||||
end = pin.rc()
|
||||
self.add_layout_pin_segment_center(text="vdd",
|
||||
layer="metal1",
|
||||
start=start,
|
||||
end=end)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=start,
|
||||
rotate=90)
|
||||
|
||||
|
||||
# Add via for the inverter
|
||||
pin = self.rbl_inv_inst.get_pin("vdd")
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ report_status()
|
|||
import verify
|
||||
import sram
|
||||
|
||||
print("Output files are " + OPTS.output_name + ".(sp|gds|v|lib|lef)")
|
||||
output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in ["sp","gds","v","lib","lef"]]
|
||||
print("Output files are: ")
|
||||
print(*output_files,sep="\n")
|
||||
|
||||
# Keep track of running stats
|
||||
start_time = datetime.datetime.now()
|
||||
|
|
@ -53,7 +55,7 @@ s = sram.sram(word_size=OPTS.word_size,
|
|||
name=OPTS.output_name)
|
||||
|
||||
# Output the files for the resulting SRAM
|
||||
s.save_output()
|
||||
s.save()
|
||||
|
||||
# Delete temp files etc.
|
||||
end_openram()
|
||||
|
|
|
|||
489
compiler/sram.py
489
compiler/sram.py
|
|
@ -2,477 +2,78 @@ import sys
|
|||
import datetime
|
||||
import getpass
|
||||
import debug
|
||||
import design
|
||||
from sram_1bank import sram_1bank
|
||||
from sram_2bank import sram_2bank
|
||||
from sram_4bank import sram_4bank
|
||||
from math import log,sqrt,ceil
|
||||
from vector import vector
|
||||
from globals import OPTS, print_time
|
||||
|
||||
|
||||
class sram(sram_1bank,sram_2bank,sram_4bank):
|
||||
class sram():
|
||||
"""
|
||||
Dynamically generated SRAM by connecting banks to control logic. The
|
||||
number of banks should be 1 , 2 or 4
|
||||
This is not a design module, but contains an SRAM design instance.
|
||||
It could later try options of number of banks and oganization to compare
|
||||
results.
|
||||
We can later add visualizer and other high-level functions as needed.
|
||||
"""
|
||||
def __init__(self, word_size, num_words, num_banks, name):
|
||||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.control_logic))
|
||||
self.mod_control_logic = getattr(c, OPTS.control_logic)
|
||||
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
c = reload(__import__(OPTS.ms_flop))
|
||||
self.mod_ms_flop = getattr(c, OPTS.ms_flop)
|
||||
self.ms_flop = self.mod_ms_flop()
|
||||
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
# in case we create more than one SRAM
|
||||
from design import design
|
||||
design.name_map=[]
|
||||
|
||||
self.word_size = word_size
|
||||
self.num_words = num_words
|
||||
self.num_banks = num_banks
|
||||
|
||||
debug.info(2, "create sram of size {0} with {1} num of words".format(self.word_size,
|
||||
self.num_words))
|
||||
debug.info(2, "create sram of size {0} with {1} num of words".format(word_size,
|
||||
num_words))
|
||||
start_time = datetime.datetime.now()
|
||||
|
||||
self.compute_sizes()
|
||||
self.name = name
|
||||
|
||||
if self.num_banks == 1:
|
||||
sram_1bank.__init__(self,name)
|
||||
elif self.num_banks == 2:
|
||||
sram_2bank.__init__(self,name)
|
||||
elif self.num_banks == 4:
|
||||
sram_4bank.__init__(self,name)
|
||||
if num_banks == 1:
|
||||
from sram_1bank import sram_1bank
|
||||
self.s=sram_1bank(word_size, num_words, name)
|
||||
elif num_banks == 2:
|
||||
from sram_2bank import sram_2bank
|
||||
self.s=sram_2bank(word_size, num_words, name)
|
||||
elif num_banks == 4:
|
||||
from sram_4bank import sram_4bank
|
||||
self.s=sram_4bank(word_size, num_words, name)
|
||||
else:
|
||||
debug.error("Invalid number of banks.",-1)
|
||||
|
||||
self.control_size = 6
|
||||
self.bank_to_bus_distance = 5*self.m3_width
|
||||
|
||||
self.create_modules()
|
||||
self.add_pins()
|
||||
self.create_layout()
|
||||
self.s.compute_sizes()
|
||||
self.s.create_modules()
|
||||
self.s.add_pins()
|
||||
self.s.create_layout()
|
||||
|
||||
# Can remove the following, but it helps for debug!
|
||||
self.add_lvs_correspondence_points()
|
||||
self.s.add_lvs_correspondence_points()
|
||||
|
||||
self.offset_all_coordinates()
|
||||
sizes = self.find_highest_coords()
|
||||
self.width = sizes[0]
|
||||
self.height = sizes[1]
|
||||
self.s.offset_all_coordinates()
|
||||
highest_coord = self.s.find_highest_coords()
|
||||
self.s.width = highest_coord[0]
|
||||
self.s.height = highest_coord[1]
|
||||
|
||||
self.DRC_LVS(final_verification=True)
|
||||
self.s.DRC_LVS(final_verification=True)
|
||||
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("SRAM creation", datetime.datetime.now(), start_time)
|
||||
|
||||
|
||||
def compute_sizes(self):
|
||||
""" Computes the organization of the memory using bitcell size by trying to make it square."""
|
||||
|
||||
debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.")
|
||||
|
||||
self.num_words_per_bank = self.num_words/self.num_banks
|
||||
self.num_bits_per_bank = self.word_size*self.num_words_per_bank
|
||||
|
||||
# Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
|
||||
self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank
|
||||
self.bank_side_length = sqrt(self.bank_area)
|
||||
|
||||
# Estimate the words per row given the height of the bitcell and the square side length
|
||||
self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width)
|
||||
self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size)
|
||||
|
||||
# Estimate the number of rows given the tentative words per row
|
||||
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size)
|
||||
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
|
||||
|
||||
# Fix the number of columns and rows
|
||||
self.num_cols = int(self.words_per_row*self.word_size)
|
||||
self.num_rows = int(self.num_words_per_bank/self.words_per_row)
|
||||
|
||||
# Compute the address and bank sizes
|
||||
self.row_addr_size = int(log(self.num_rows, 2))
|
||||
self.col_addr_size = int(log(self.words_per_row, 2))
|
||||
self.bank_addr_size = self.col_addr_size + self.row_addr_size
|
||||
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
|
||||
|
||||
debug.info(1,"Words per row: {}".format(self.words_per_row))
|
||||
|
||||
def estimate_words_per_row(self,tentative_num_cols, word_size):
|
||||
"""
|
||||
This provides a heuristic rounded estimate for the number of words
|
||||
per row.
|
||||
"""
|
||||
|
||||
if tentative_num_cols < 1.5*word_size:
|
||||
return 1
|
||||
elif tentative_num_cols > 3*word_size:
|
||||
return 4
|
||||
else:
|
||||
return 2
|
||||
|
||||
def amend_words_per_row(self,tentative_num_rows, words_per_row):
|
||||
"""
|
||||
This picks the number of words per row more accurately by limiting
|
||||
it to a minimum and maximum.
|
||||
"""
|
||||
# Recompute the words per row given a hard max
|
||||
if(tentative_num_rows > 512):
|
||||
debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048")
|
||||
return int(words_per_row*tentative_num_rows/512)
|
||||
# Recompute the words per row given a hard min
|
||||
if(tentative_num_rows < 16):
|
||||
debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows))
|
||||
return int(words_per_row*tentative_num_rows/16)
|
||||
|
||||
return words_per_row
|
||||
|
||||
def add_pins(self):
|
||||
""" Add pins for entire SRAM. """
|
||||
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("DIN[{0}]".format(i),"INPUT")
|
||||
for i in range(self.addr_size):
|
||||
self.add_pin("ADDR[{0}]".format(i),"INPUT")
|
||||
|
||||
# These are used to create the physical pins too
|
||||
self.control_logic_inputs=self.control_logic.get_inputs()
|
||||
self.control_logic_outputs=self.control_logic.get_outputs()
|
||||
|
||||
self.add_pin_list(self.control_logic_inputs,"INPUT")
|
||||
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("DOUT[{0}]".format(i),"OUTPUT")
|
||||
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
|
||||
def create_layout(self):
|
||||
""" Layout creation """
|
||||
self.add_modules()
|
||||
self.route()
|
||||
|
||||
def compute_bus_sizes(self):
|
||||
""" Compute the independent bus widths shared between two and four bank SRAMs """
|
||||
|
||||
# address size + control signals + one-hot bank select signals
|
||||
self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks,2) + 1
|
||||
# data bus size
|
||||
self.num_horizontal_line = self.word_size
|
||||
|
||||
self.vertical_bus_width = self.m2_pitch*self.num_vertical_line
|
||||
# vertical bus height depends on 2 or 4 banks
|
||||
|
||||
self.data_bus_height = self.m3_pitch*self.num_horizontal_line
|
||||
self.data_bus_width = 2*(self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width
|
||||
|
||||
self.control_bus_height = self.m1_pitch*(self.control_size+2)
|
||||
self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width
|
||||
|
||||
self.supply_bus_height = self.m1_pitch*2 # 2 for vdd/gnd placed with control bus
|
||||
self.supply_bus_width = self.data_bus_width
|
||||
|
||||
# Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really)
|
||||
debug.check(self.bank.width + self.vertical_bus_width > 0.9*self.control_logic.width,
|
||||
"Bank is too small compared to control logic.")
|
||||
|
||||
|
||||
|
||||
def add_busses(self):
|
||||
""" Add the horizontal and vertical busses """
|
||||
# Vertical bus
|
||||
# The order of the control signals on the control bus:
|
||||
self.control_bus_names = ["clk_buf", "tri_en_bar", "tri_en", "clk_buf_bar", "w_en", "s_en"]
|
||||
self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=self.vertical_bus_offset,
|
||||
names=self.control_bus_names,
|
||||
length=self.vertical_bus_height)
|
||||
|
||||
self.addr_bus_names=["A[{}]".format(i) for i in range(self.addr_size)]
|
||||
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=self.addr_bus_offset,
|
||||
names=self.addr_bus_names,
|
||||
length=self.addr_bus_height))
|
||||
|
||||
|
||||
self.bank_sel_bus_names = ["bank_sel[{}]".format(i) for i in range(self.num_banks)]
|
||||
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=self.bank_sel_bus_offset,
|
||||
names=self.bank_sel_bus_names,
|
||||
length=self.vertical_bus_height))
|
||||
|
||||
|
||||
# Horizontal data bus
|
||||
self.data_bus_names = ["DATA[{}]".format(i) for i in range(self.word_size)]
|
||||
self.data_bus_positions = self.create_horizontal_pin_bus(layer="metal3",
|
||||
pitch=self.m3_pitch,
|
||||
offset=self.data_bus_offset,
|
||||
names=self.data_bus_names,
|
||||
length=self.data_bus_width)
|
||||
|
||||
# Horizontal control logic bus
|
||||
# vdd/gnd in bus go along whole SRAM
|
||||
# FIXME: Fatten these wires?
|
||||
self.horz_control_bus_positions = self.create_horizontal_bus(layer="metal1",
|
||||
pitch=self.m1_pitch,
|
||||
offset=self.supply_bus_offset,
|
||||
names=["vdd"],
|
||||
length=self.supply_bus_width)
|
||||
# The gnd rail must not be the entire width since we protrude the right-most vdd rail up for
|
||||
# the decoder in 4-bank SRAMs
|
||||
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
|
||||
pitch=self.m1_pitch,
|
||||
offset=self.supply_bus_offset+vector(0,self.m1_pitch),
|
||||
names=["gnd"],
|
||||
length=self.supply_bus_width))
|
||||
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
|
||||
pitch=self.m1_pitch,
|
||||
offset=self.control_bus_offset,
|
||||
names=self.control_bus_names,
|
||||
length=self.control_bus_width))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||
|
||||
# These are the instances that every bank has
|
||||
top_instances = [self.bitcell_array_inst,
|
||||
self.precharge_array_inst,
|
||||
self.sense_amp_array_inst,
|
||||
self.write_driver_array_inst,
|
||||
self.tri_gate_array_inst,
|
||||
self.row_decoder_inst,
|
||||
self.wordline_driver_inst]
|
||||
# Add these if we use the part...
|
||||
if self.col_addr_size > 0:
|
||||
top_instances.append(self.col_decoder_inst)
|
||||
top_instances.append(self.col_mux_array_inst)
|
||||
|
||||
if self.num_banks > 1:
|
||||
top_instances.append(self.bank_select_inst)
|
||||
|
||||
|
||||
for inst in top_instances:
|
||||
# Column mux has no vdd
|
||||
if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst):
|
||||
self.copy_layout_pin(inst, "vdd")
|
||||
# Precharge has no gnd
|
||||
if inst != self.precharge_array_inst:
|
||||
self.copy_layout_pin(inst, "gnd")
|
||||
|
||||
|
||||
def create_multi_bank_modules(self):
|
||||
""" Create the multibank address flops and bank decoder """
|
||||
from dff_buf_array import dff_buf_array
|
||||
self.msb_address = dff_buf_array(name="msb_address",
|
||||
rows=1,
|
||||
columns=self.num_banks/2)
|
||||
self.add_mod(self.msb_address)
|
||||
|
||||
if self.num_banks>2:
|
||||
self.msb_decoder = self.bank.decoder.pre2_4
|
||||
self.add_mod(self.msb_decoder)
|
||||
|
||||
def create_modules(self):
|
||||
""" Create all the modules that will be used """
|
||||
|
||||
from control_logic import control_logic
|
||||
# Create the control logic module
|
||||
self.control_logic = self.mod_control_logic(num_rows=self.num_rows)
|
||||
self.add_mod(self.control_logic)
|
||||
|
||||
# Create the address and control flops (but not the clk)
|
||||
dff_size = self.addr_size
|
||||
from dff_array import dff_array
|
||||
self.addr_dff = dff_array(name="dff_array", rows=dff_size, columns=1)
|
||||
self.add_mod(self.addr_dff)
|
||||
|
||||
# Create the bank module (up to four are instantiated)
|
||||
from bank import bank
|
||||
self.bank = bank(word_size=self.word_size,
|
||||
num_words=self.num_words_per_bank,
|
||||
words_per_row=self.words_per_row,
|
||||
num_banks=self.num_banks,
|
||||
name="bank")
|
||||
self.add_mod(self.bank)
|
||||
|
||||
# Create bank decoder
|
||||
if(self.num_banks > 1):
|
||||
self.create_multi_bank_modules()
|
||||
|
||||
self.bank_count = 0
|
||||
|
||||
self.supply_rail_width = self.bank.supply_rail_width
|
||||
self.supply_rail_pitch = self.bank.supply_rail_pitch
|
||||
|
||||
|
||||
|
||||
def add_bank(self, bank_num, position, x_flip, y_flip):
|
||||
""" Place a bank at the given position with orientations """
|
||||
|
||||
# x_flip == 1 --> no flip in x_axis
|
||||
# x_flip == -1 --> flip in x_axis
|
||||
# y_flip == 1 --> no flip in y_axis
|
||||
# y_flip == -1 --> flip in y_axis
|
||||
|
||||
# x_flip and y_flip are used for position translation
|
||||
|
||||
if x_flip == -1 and y_flip == -1:
|
||||
bank_rotation = 180
|
||||
else:
|
||||
bank_rotation = 0
|
||||
|
||||
if x_flip == y_flip:
|
||||
bank_mirror = "R0"
|
||||
elif x_flip == -1:
|
||||
bank_mirror = "MX"
|
||||
elif y_flip == -1:
|
||||
bank_mirror = "MY"
|
||||
else:
|
||||
bank_mirror = "R0"
|
||||
|
||||
bank_inst=self.add_inst(name="bank{0}".format(bank_num),
|
||||
mod=self.bank,
|
||||
offset=position,
|
||||
mirror=bank_mirror,
|
||||
rotate=bank_rotation)
|
||||
|
||||
temp = []
|
||||
for i in range(self.word_size):
|
||||
temp.append("DOUT[{0}]".format(i))
|
||||
for i in range(self.word_size):
|
||||
temp.append("DIN[{0}]".format(i))
|
||||
for i in range(self.bank_addr_size):
|
||||
temp.append("A[{0}]".format(i))
|
||||
if(self.num_banks > 1):
|
||||
temp.append("bank_sel[{0}]".format(bank_num))
|
||||
temp.extend(["s_en", "w_en", "tri_en_bar", "tri_en",
|
||||
"clk_buf_bar","clk_buf" , "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
return bank_inst
|
||||
|
||||
|
||||
|
||||
def add_addr_dff(self, position):
|
||||
""" Add and place address and control flops """
|
||||
self.addr_dff_inst = self.add_inst(name="address",
|
||||
mod=self.addr_dff,
|
||||
offset=position)
|
||||
# inputs, outputs/output/bar
|
||||
inputs = []
|
||||
outputs = []
|
||||
for i in range(self.addr_size):
|
||||
inputs.append("ADDR[{}]".format(i))
|
||||
outputs.append("A[{}]".format(i))
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"])
|
||||
|
||||
def add_control_logic(self, position):
|
||||
""" Add and place control logic """
|
||||
inputs = []
|
||||
for i in self.control_logic_inputs:
|
||||
if i != "clk":
|
||||
inputs.append(i+"_s")
|
||||
else:
|
||||
inputs.append(i)
|
||||
|
||||
self.control_logic_inst=self.add_inst(name="control",
|
||||
mod=self.control_logic,
|
||||
offset=position)
|
||||
self.connect_inst(inputs + self.control_logic_outputs + ["vdd", "gnd"])
|
||||
def sp_write(self,name):
|
||||
self.s.sp_write(name)
|
||||
|
||||
def gds_write(self,name):
|
||||
self.s.gds_write(name)
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
""" This adds some points for easier debugging if LVS goes wrong.
|
||||
These should probably be turned off by default though, since extraction
|
||||
will show these as ports in the extracted netlist.
|
||||
"""
|
||||
if self.num_banks==1: return
|
||||
|
||||
for n in self.control_bus_names:
|
||||
self.add_label(text=n,
|
||||
layer="metal2",
|
||||
offset=self.vert_control_bus_positions[n])
|
||||
for n in self.bank_sel_bus_names:
|
||||
self.add_label(text=n,
|
||||
layer="metal2",
|
||||
offset=self.vert_control_bus_positions[n])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def connect_rail_from_left_m2m3(self, src_pin, dest_pin):
|
||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
||||
in_pos = src_pin.rc()
|
||||
out_pos = vector(dest_pin.cx(), in_pos.y)
|
||||
self.add_wire(("metal3","via2","metal2"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)])
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=src_pin.rc(),
|
||||
rotate=90)
|
||||
|
||||
def connect_rail_from_left_m2m1(self, src_pin, dest_pin):
|
||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
||||
in_pos = src_pin.rc()
|
||||
out_pos = vector(dest_pin.cx(), in_pos.y)
|
||||
self.add_wire(("metal2","via1","metal1"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)])
|
||||
def verilog_write(self,name):
|
||||
self.s.verilog_write(name)
|
||||
|
||||
|
||||
|
||||
def sp_write(self, sp_name):
|
||||
# Write the entire spice of the object to the file
|
||||
############################################################
|
||||
# Spice circuit
|
||||
############################################################
|
||||
sp = open(sp_name, 'w')
|
||||
|
||||
sp.write("**************************************************\n")
|
||||
sp.write("* OpenRAM generated memory.\n")
|
||||
sp.write("* Words: {}\n".format(self.num_words))
|
||||
sp.write("* Data bits: {}\n".format(self.word_size))
|
||||
sp.write("* Banks: {}\n".format(self.num_banks))
|
||||
sp.write("* Column mux: {}:1\n".format(self.words_per_row))
|
||||
sp.write("**************************************************\n")
|
||||
# This causes unit test mismatch
|
||||
# sp.write("* Created: {0}\n".format(datetime.datetime.now()))
|
||||
# sp.write("* User: {0}\n".format(getpass.getuser()))
|
||||
# sp.write(".global {0} {1}\n".format(spice["vdd_name"],
|
||||
# spice["gnd_name"]))
|
||||
usedMODS = list()
|
||||
self.sp_write_file(sp, usedMODS)
|
||||
del usedMODS
|
||||
sp.close()
|
||||
|
||||
def analytical_delay(self,slew,load):
|
||||
""" LH and HL are the same in analytical model. """
|
||||
return self.bank.analytical_delay(slew,load)
|
||||
|
||||
def save_output(self):
|
||||
def save(self):
|
||||
""" Save all the output files while reporting time to do it as well. """
|
||||
|
||||
# Save the spice file
|
||||
start_time = datetime.datetime.now()
|
||||
spname = OPTS.output_path + self.name + ".sp"
|
||||
spname = OPTS.output_path + self.s.name + ".sp"
|
||||
print("SP: Writing to {0}".format(spname))
|
||||
self.sp_write(spname)
|
||||
self.s.sp_write(spname)
|
||||
print_time("Spice writing", datetime.datetime.now(), start_time)
|
||||
|
||||
# Save the extracted spice file
|
||||
|
|
@ -480,12 +81,12 @@ class sram(sram_1bank,sram_2bank,sram_4bank):
|
|||
start_time = datetime.datetime.now()
|
||||
# Output the extracted design if requested
|
||||
sp_file = OPTS.output_path + "temp_pex.sp"
|
||||
verify.run_pex(self.name, gdsname, spname, output=sp_file)
|
||||
verify.run_pex(self.s.name, gdsname, spname, output=sp_file)
|
||||
print_time("Extraction", datetime.datetime.now(), start_time)
|
||||
else:
|
||||
# Use generated spice file for characterization
|
||||
sp_file = spname
|
||||
print(sys.path)
|
||||
|
||||
# Characterize the design
|
||||
start_time = datetime.datetime.now()
|
||||
from characterizer import lib
|
||||
|
|
@ -497,26 +98,26 @@ class sram(sram_1bank,sram_2bank,sram_4bank):
|
|||
print("Performing simulation-based characterization with {}".format(OPTS.spice_name))
|
||||
if OPTS.trim_netlist:
|
||||
print("Trimming netlist to speed up characterization.")
|
||||
lib(out_dir=OPTS.output_path, sram=self, sp_file=sp_file)
|
||||
lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file)
|
||||
print_time("Characterization", datetime.datetime.now(), start_time)
|
||||
|
||||
# Write the layout
|
||||
start_time = datetime.datetime.now()
|
||||
gdsname = OPTS.output_path + self.name + ".gds"
|
||||
gdsname = OPTS.output_path + self.s.name + ".gds"
|
||||
print("GDS: Writing to {0}".format(gdsname))
|
||||
self.gds_write(gdsname)
|
||||
self.s.gds_write(gdsname)
|
||||
print_time("GDS", datetime.datetime.now(), start_time)
|
||||
|
||||
# Create a LEF physical model
|
||||
start_time = datetime.datetime.now()
|
||||
lefname = OPTS.output_path + self.name + ".lef"
|
||||
lefname = OPTS.output_path + self.s.name + ".lef"
|
||||
print("LEF: Writing to {0}".format(lefname))
|
||||
self.lef_write(lefname)
|
||||
self.s.lef_write(lefname)
|
||||
print_time("LEF", datetime.datetime.now(), start_time)
|
||||
|
||||
# Write a verilog model
|
||||
start_time = datetime.datetime.now()
|
||||
vname = OPTS.output_path + self.name + ".v"
|
||||
vname = OPTS.output_path + self.s.name + ".v"
|
||||
print("Verilog: Writing to {0}".format(vname))
|
||||
self.verilog_write(vname)
|
||||
self.s.verilog_write(vname)
|
||||
print_time("Verilog", datetime.datetime.now(), start_time)
|
||||
|
|
|
|||
|
|
@ -7,18 +7,18 @@ import getpass
|
|||
from vector import vector
|
||||
from globals import OPTS, print_time
|
||||
|
||||
from design import design
|
||||
from sram_base import sram_base
|
||||
from bank import bank
|
||||
from dff_buf_array import dff_buf_array
|
||||
from dff_array import dff_array
|
||||
|
||||
|
||||
class sram_1bank(design):
|
||||
class sram_1bank(sram_base):
|
||||
"""
|
||||
Procedures specific to a two bank SRAM.
|
||||
Procedures specific to a one bank SRAM.
|
||||
"""
|
||||
def __init__(self, name):
|
||||
design.__init__(self, name)
|
||||
def __init__(self, word_size, num_words, name):
|
||||
sram_base.__init__(self, word_size, num_words, 1, name)
|
||||
|
||||
def add_modules(self):
|
||||
"""
|
||||
|
|
@ -29,37 +29,107 @@ class sram_1bank(design):
|
|||
# No orientation or offset
|
||||
self.bank_inst = self.add_bank(0, [0, 0], 1, 1)
|
||||
|
||||
# 3/5/18 MRG: Cannot reference positions inside submodules because boundaries
|
||||
# are not recomputed using instance placement. So, place the control logic such that it aligns
|
||||
# with the top of the SRAM.
|
||||
control_pos = vector(-self.control_logic.width - self.m3_pitch,
|
||||
3*self.supply_rail_width)
|
||||
self.bank.bank_center.y - self.control_logic.control_logic_center.y)
|
||||
self.add_control_logic(position=control_pos)
|
||||
|
||||
# Leave room for the control routes to the left of the flops
|
||||
addr_pos = vector(self.control_logic_inst.lx() + 4*self.m2_pitch,
|
||||
row_addr_pos = vector(self.control_logic_inst.rx() - self.row_addr_dff.width,
|
||||
control_pos.y + self.control_logic.height + self.m1_pitch)
|
||||
self.add_addr_dff(addr_pos)
|
||||
self.add_row_addr_dff(row_addr_pos)
|
||||
|
||||
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
||||
data_gap = -self.m2_pitch*(self.word_size+1)
|
||||
|
||||
# Add the column address below the bank under the control
|
||||
# Keep it aligned with the data flops
|
||||
if self.col_addr_dff:
|
||||
col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width,
|
||||
data_gap - self.col_addr_dff.height)
|
||||
self.add_col_addr_dff(col_addr_pos)
|
||||
|
||||
# Add the data flops below the bank
|
||||
# This relies on the center point of the bank:
|
||||
# decoder in upper left, bank in upper right, sensing in lower right.
|
||||
# These flops go below the sensing and leave a gap to channel route to the
|
||||
# sense amps.
|
||||
data_pos = vector(self.bank.bank_center.x,
|
||||
data_gap - self.data_dff.height)
|
||||
self.add_data_dff(data_pos)
|
||||
|
||||
# two supply rails are already included in the bank, so just 2 here.
|
||||
self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
|
||||
self.height = self.bank.height
|
||||
|
||||
def add_pins(self):
|
||||
def add_layout_pins(self):
|
||||
"""
|
||||
Add the top-level pins for a single bank SRAM with control.
|
||||
"""
|
||||
# Connect the control pins as inputs
|
||||
for n in self.control_logic_inputs + ["clk"]:
|
||||
self.copy_layout_pin(self.control_logic_inst, n)
|
||||
|
||||
for i in range(self.word_size):
|
||||
self.copy_layout_pin(self.bank_inst, "DOUT[{}]".format(i))
|
||||
dout_name = "dout[{}]".format(i)
|
||||
self.copy_layout_pin(self.bank_inst, dout_name, dout_name.upper())
|
||||
|
||||
# Lower address bits
|
||||
for i in range(self.col_addr_size):
|
||||
self.copy_layout_pin(self.col_addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i))
|
||||
# Upper address bits
|
||||
for i in range(self.row_addr_size):
|
||||
self.copy_layout_pin(self.row_addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i+self.col_addr_size))
|
||||
|
||||
for i in range(self.word_size):
|
||||
din_name = "din[{}]".format(i)
|
||||
self.copy_layout_pin(self.data_dff_inst, din_name, din_name.upper())
|
||||
|
||||
for i in range(self.addr_size):
|
||||
self.copy_layout_pin(self.addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i))
|
||||
|
||||
def route(self):
|
||||
""" Route a single bank SRAM """
|
||||
|
||||
# Route the outputs from the control logic module
|
||||
self.add_layout_pins()
|
||||
|
||||
self.route_vdd_gnd()
|
||||
|
||||
self.route_clk()
|
||||
|
||||
self.route_control_logic()
|
||||
|
||||
self.route_row_addr_dff()
|
||||
|
||||
if self.col_addr_dff:
|
||||
self.route_col_addr_dff()
|
||||
|
||||
self.route_data_dff()
|
||||
|
||||
def route_clk(self):
|
||||
""" Route the clock network """
|
||||
debug.warning("Clock is top-level must connect.")
|
||||
# For now, just have four clock pins for the address (x2), data, and control
|
||||
if self.col_addr_dff:
|
||||
self.copy_layout_pin(self.col_addr_dff_inst, "clk")
|
||||
self.copy_layout_pin(self.row_addr_dff_inst, "clk")
|
||||
self.copy_layout_pin(self.data_dff_inst, "clk")
|
||||
self.copy_layout_pin(self.control_logic_inst, "clk")
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||
|
||||
# These are the instances that every bank has
|
||||
top_instances = [self.bank_inst,
|
||||
self.row_addr_dff_inst,
|
||||
self.data_dff_inst,
|
||||
self.control_logic_inst]
|
||||
if self.col_addr_dff:
|
||||
top_instances.append(self.col_addr_dff_inst)
|
||||
|
||||
|
||||
for inst in top_instances:
|
||||
self.copy_layout_pin(inst, "vdd")
|
||||
self.copy_layout_pin(inst, "gnd")
|
||||
|
||||
def route_control_logic(self):
|
||||
""" Route the outputs from the control logic module """
|
||||
for n in self.control_logic_outputs:
|
||||
src_pin = self.control_logic_inst.get_pin(n)
|
||||
dest_pin = self.bank_inst.get_pin(n)
|
||||
|
|
@ -69,33 +139,64 @@ class sram_1bank(design):
|
|||
rotate=90)
|
||||
|
||||
|
||||
# Connect the output of the flops to the bank pins
|
||||
for i in range(self.addr_size):
|
||||
def route_row_addr_dff(self):
|
||||
""" Connect the output of the row flops to the bank pins """
|
||||
for i in range(self.row_addr_size):
|
||||
flop_name = "dout[{}]".format(i)
|
||||
bank_name = "A[{}]".format(i)
|
||||
flop_pin = self.addr_dff_inst.get_pin(flop_name)
|
||||
bank_name = "addr[{}]".format(i+self.col_addr_size)
|
||||
flop_pin = self.row_addr_dff_inst.get_pin(flop_name)
|
||||
bank_pin = self.bank_inst.get_pin(bank_name)
|
||||
flop_pos = flop_pin.center()
|
||||
bank_pos = vector(bank_pin.cx(),flop_pos.y)
|
||||
self.add_path("metal3",[flop_pos, bank_pos])
|
||||
bank_pos = bank_pin.center()
|
||||
mid_pos = vector(bank_pos.x,flop_pos.y)
|
||||
self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos])
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=flop_pos,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=bank_pos,
|
||||
rotate=90)
|
||||
|
||||
# Connect the control pins as inputs
|
||||
for n in self.control_logic_inputs + ["clk"]:
|
||||
self.copy_layout_pin(self.control_logic_inst, n)
|
||||
def route_col_addr_dff(self):
|
||||
""" Connect the output of the row flops to the bank pins """
|
||||
|
||||
# Connect the clock between the flops and control module
|
||||
flop_pin = self.addr_dff_inst.get_pin("clk")
|
||||
ctrl_pin = self.control_logic_inst.get_pin("clk_buf")
|
||||
flop_pos = flop_pin.uc()
|
||||
ctrl_pos = ctrl_pin.bc()
|
||||
mid_ypos = 0.5*(ctrl_pos.y+flop_pos.y)
|
||||
mid1_pos = vector(flop_pos.x, mid_ypos)
|
||||
mid2_pos = vector(ctrl_pos.x, mid_ypos)
|
||||
self.add_wire(("metal1","via1","metal2"),[flop_pin.uc(), mid1_pos, mid2_pos, ctrl_pin.bc()])
|
||||
bus_names = ["addr[{}]".format(x) for x in range(self.col_addr_size)]
|
||||
col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1",
|
||||
pitch=self.m1_pitch,
|
||||
offset=self.col_addr_dff_inst.ul() + vector(0, self.m1_pitch),
|
||||
names=bus_names,
|
||||
length=self.col_addr_dff_inst.width)
|
||||
|
||||
dff_names = ["dout[{}]".format(x) for x in range(self.col_addr_size)]
|
||||
data_dff_map = zip(dff_names, bus_names)
|
||||
self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_inst, col_addr_bus_offsets)
|
||||
|
||||
bank_names = ["addr[{}]".format(x) for x in range(self.col_addr_size)]
|
||||
data_bank_map = zip(bank_names, bus_names)
|
||||
self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets)
|
||||
|
||||
|
||||
def route_data_dff(self):
|
||||
""" Connect the output of the data flops to the write driver """
|
||||
# This is where the channel will start (y-dimension at least)
|
||||
offset = self.data_dff_inst.ul() + vector(0, self.m1_pitch)
|
||||
|
||||
dff_names = ["dout[{}]".format(x) for x in range(self.word_size)]
|
||||
bank_names = ["din[{}]".format(x) for x in range(self.word_size)]
|
||||
|
||||
route_map = list(zip(bank_names, dff_names))
|
||||
dff_pins = {key: self.data_dff_inst.get_pin(key) for key in dff_names }
|
||||
bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names }
|
||||
self.create_horizontal_channel_route(route_map, dff_pins, bank_pins, offset)
|
||||
|
||||
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
"""
|
||||
This adds some points for easier debugging if LVS goes wrong.
|
||||
These should probably be turned off by default though, since extraction
|
||||
will show these as ports in the extracted netlist.
|
||||
"""
|
||||
|
||||
for n in self.control_logic_outputs:
|
||||
pin = self.control_logic_inst.get_pin(n)
|
||||
self.add_label(text=n,
|
||||
layer=pin.layer,
|
||||
offset=pin.center())
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
import sys
|
||||
from tech import drc, spice
|
||||
import debug
|
||||
import design
|
||||
from math import log,sqrt,ceil
|
||||
import contact
|
||||
from bank import bank
|
||||
from dff_buf_array import dff_buf_array
|
||||
from dff_array import dff_array
|
||||
import datetime
|
||||
import getpass
|
||||
from vector import vector
|
||||
from globals import OPTS, print_time
|
||||
|
||||
class sram_2bank(design.design):
|
||||
from sram_base import sram_base
|
||||
from bank import bank
|
||||
from dff_buf_array import dff_buf_array
|
||||
from dff_array import dff_array
|
||||
|
||||
class sram_2bank(sram_base):
|
||||
"""
|
||||
Procedures specific to a two bank SRAM.
|
||||
"""
|
||||
def __init__(self, name):
|
||||
design.__init__(self, name)
|
||||
def __init__(self, word_size, num_words, name):
|
||||
sram_base.__init__(self, word_size, num_words, 2, name)
|
||||
|
||||
def compute_bank_offsets(self):
|
||||
""" Compute the overall offsets for a two bank SRAM """
|
||||
|
|
@ -214,3 +214,20 @@ class sram_2bank(design.design):
|
|||
|
||||
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
"""
|
||||
This adds some points for easier debugging if LVS goes wrong.
|
||||
These should probably be turned off by default though, since extraction
|
||||
will show these as ports in the extracted netlist.
|
||||
"""
|
||||
|
||||
if self.num_banks==1: return
|
||||
|
||||
for n in self.control_bus_names:
|
||||
self.add_label(text=n,
|
||||
layer="metal2",
|
||||
offset=self.vert_control_bus_positions[n])
|
||||
for n in self.bank_sel_bus_names:
|
||||
self.add_label(text=n,
|
||||
layer="metal2",
|
||||
offset=self.vert_control_bus_positions[n])
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
import sys
|
||||
from tech import drc, spice
|
||||
import debug
|
||||
import design
|
||||
from math import log,sqrt,ceil
|
||||
import contact
|
||||
from bank import bank
|
||||
from dff_buf_array import dff_buf_array
|
||||
from dff_array import dff_array
|
||||
import datetime
|
||||
import getpass
|
||||
from vector import vector
|
||||
from globals import OPTS, print_time
|
||||
|
||||
class sram_4bank(design.design):
|
||||
from sram_base import sram_base
|
||||
from bank import bank
|
||||
from dff_buf_array import dff_buf_array
|
||||
from dff_array import dff_array
|
||||
|
||||
class sram_4bank(sram_base):
|
||||
"""
|
||||
Procedures specific to a four bank SRAM.
|
||||
"""
|
||||
def __init__(self, name):
|
||||
design.__init__(self, name)
|
||||
|
||||
def __init__(self, word_size, num_words, name):
|
||||
sram_base.__init__(self, word_size, num_words, 4, name)
|
||||
|
||||
def compute_bank_offsets(self):
|
||||
""" Compute the overall offsets for a four bank SRAM """
|
||||
|
||||
|
|
@ -312,3 +312,20 @@ class sram_4bank(design.design):
|
|||
self.route_bank_supply_rails(left_banks=[0,2], bottom_banks=[2,3])
|
||||
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
"""
|
||||
This adds some points for easier debugging if LVS goes wrong.
|
||||
These should probably be turned off by default though, since extraction
|
||||
will show these as ports in the extracted netlist.
|
||||
"""
|
||||
|
||||
if self.num_banks==1: return
|
||||
|
||||
for n in self.control_bus_names:
|
||||
self.add_label(text=n,
|
||||
layer="metal2",
|
||||
offset=self.vert_control_bus_positions[n])
|
||||
for n in self.bank_sel_bus_names:
|
||||
self.add_label(text=n,
|
||||
layer="metal2",
|
||||
offset=self.vert_control_bus_positions[n])
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
@ -18,22 +18,22 @@ class multi_bank_test(openram_test):
|
|||
global verify
|
||||
import verify
|
||||
|
||||
import bank
|
||||
from bank import bank
|
||||
|
||||
debug.info(1, "No column mux")
|
||||
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=2, name="bank1_multi")
|
||||
a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=2, name="bank1_multi")
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Two way column mux")
|
||||
a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=2, name="bank2_multi")
|
||||
a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=2, name="bank2_multi")
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Four way column mux")
|
||||
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=2, name="bank3_multi")
|
||||
a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=2, name="bank3_multi")
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Eight way column mux")
|
||||
a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4_multi")
|
||||
a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4_multi")
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
|
|
|||
|
|
@ -18,23 +18,23 @@ class single_bank_test(openram_test):
|
|||
global verify
|
||||
import verify
|
||||
|
||||
import bank
|
||||
from bank import bank
|
||||
|
||||
debug.info(1, "No column mux")
|
||||
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_single")
|
||||
a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_single")
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Two way column mux")
|
||||
a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single")
|
||||
a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single")
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Four way column mux")
|
||||
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3_single")
|
||||
a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3_single")
|
||||
self.local_check(a)
|
||||
|
||||
# Eight way has a short circuit of one column mux select to gnd rail
|
||||
debug.info(1, "Eight way column mux")
|
||||
a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single")
|
||||
a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single")
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
|
|
|||
|
|
@ -18,23 +18,23 @@ class sram_1bank_test(openram_test):
|
|||
global verify
|
||||
import verify
|
||||
|
||||
import sram
|
||||
from sram import sram
|
||||
|
||||
debug.info(1, "Single bank, no column mux with control logic")
|
||||
a = sram.sram(word_size=4, num_words=16, num_banks=1, name="sram1")
|
||||
a = sram(word_size=4, num_words=16, num_banks=1, name="sram1")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Single bank two way column mux with control logic")
|
||||
a = sram.sram(word_size=4, num_words=32, num_banks=1, name="sram2")
|
||||
a = sram(word_size=4, num_words=32, num_banks=1, name="sram2")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Single bank, four way column mux with control logic")
|
||||
a = sram.sram(word_size=4, num_words=64, num_banks=1, name="sram3")
|
||||
a = sram(word_size=4, num_words=64, num_banks=1, name="sram3")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
# debug.info(1, "Single bank, eight way column mux with control logic")
|
||||
# a = sram.sram(word_size=2, num_words=128, num_banks=1, name="sram4")
|
||||
# self.local_check(a, final_verification=True)
|
||||
debug.info(1, "Single bank, eight way column mux with control logic")
|
||||
a = sram(word_size=2, num_words=128, num_banks=1, name="sram4")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -19,23 +19,23 @@ class sram_2bank_test(openram_test):
|
|||
global verify
|
||||
import verify
|
||||
|
||||
import sram
|
||||
from sram import sram
|
||||
|
||||
debug.info(1, "Two bank, no column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=32, num_banks=2, name="sram1")
|
||||
a = sram(word_size=16, num_words=32, num_banks=2, name="sram1")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Two bank two way column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=64, num_banks=2, name="sram2")
|
||||
a = sram(word_size=16, num_words=64, num_banks=2, name="sram2")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Two bank, four way column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=128, num_banks=2, name="sram3")
|
||||
a = sram(word_size=16, num_words=128, num_banks=2, name="sram3")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
# debug.info(1, "Two bank, eight way column mux with control logic")
|
||||
# a = sram.sram(word_size=2, num_words=256 num_banks=2, name="sram4")
|
||||
# self.local_check(a, final_verification=True)
|
||||
debug.info(1, "Two bank, eight way column mux with control logic")
|
||||
a = sram(word_size=2, num_words=256, num_banks=2, name="sram4")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -19,23 +19,23 @@ class sram_4bank_test(openram_test):
|
|||
global verify
|
||||
import verify
|
||||
|
||||
import sram
|
||||
from sram import sram
|
||||
|
||||
debug.info(1, "Four bank, no column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=64, num_banks=4, name="sram1")
|
||||
a = sram(word_size=16, num_words=64, num_banks=4, name="sram1")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Four bank two way column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=128, num_banks=4, name="sram2")
|
||||
a = sram(word_size=16, num_words=128, num_banks=4, name="sram2")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Four bank, four way column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=256, num_banks=4, name="sram3")
|
||||
a = sram(word_size=16, num_words=256, num_banks=4, name="sram3")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
# debug.info(1, "Four bank, eight way column mux with control logic")
|
||||
# a = sram.sram(word_size=2, num_words=256, num_banks=4, name="sram4")
|
||||
# self.local_check(a, final_verification=True)
|
||||
debug.info(1, "Four bank, eight way column mux with control logic")
|
||||
a = sram.sram(word_size=2, num_words=256, num_banks=4, name="sram4")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -39,16 +39,16 @@ class timing_sram_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
probe_address = "1" * s.addr_size
|
||||
probe_data = s.word_size - 1
|
||||
probe_address = "1" * s.s.addr_size
|
||||
probe_data = s.s.word_size - 1
|
||||
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
d = delay(s,tempspice,corner)
|
||||
d = delay(s.s, tempspice, corner)
|
||||
import tech
|
||||
loads = [tech.spice["msflop_in_cap"]*4]
|
||||
slews = [tech.spice["rise_time"]*2]
|
||||
data = d.analyze(probe_address, probe_data,slews,loads)
|
||||
data = d.analyze(probe_address, probe_data, slews, loads)
|
||||
#print data
|
||||
if OPTS.tech_name == "freepdk45":
|
||||
golden_data = {'leakage_power': 0.0006964536000000001,
|
||||
|
|
|
|||
|
|
@ -37,16 +37,16 @@ class timing_sram_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
probe_address = "1" * s.addr_size
|
||||
probe_data = s.word_size - 1
|
||||
probe_address = "1" * s.s.addr_size
|
||||
probe_data = s.s.word_size - 1
|
||||
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
d = delay(s,tempspice,corner)
|
||||
d = delay(s.s, tempspice, corner)
|
||||
import tech
|
||||
loads = [tech.spice["msflop_in_cap"]*4]
|
||||
slews = [tech.spice["rise_time"]*2]
|
||||
data = d.analyze(probe_address, probe_data,slews,loads)
|
||||
data = d.analyze(probe_address, probe_data, slews, loads)
|
||||
#print data
|
||||
if OPTS.tech_name == "freepdk45":
|
||||
golden_data = {'leakage_power': 0.0007348262,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class lib_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=True)
|
||||
lib(out_dir=OPTS.openram_temp, sram=s.s, sp_file=tempspice, use_model=True)
|
||||
|
||||
# get all of the .lib files generated
|
||||
files = os.listdir(OPTS.openram_temp)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class lib_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False)
|
||||
lib(out_dir=OPTS.openram_temp, sram=s.s, sp_file=tempspice, use_model=False)
|
||||
|
||||
# get all of the .lib files generated
|
||||
files = os.listdir(OPTS.openram_temp)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class lib_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False)
|
||||
lib(out_dir=OPTS.openram_temp, sram=s.s, sp_file=tempspice, use_model=False)
|
||||
|
||||
# get all of the .lib files generated
|
||||
files = os.listdir(OPTS.openram_temp)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import globals
|
|||
from globals import OPTS
|
||||
import debug
|
||||
|
||||
@unittest.skip("SKIPPING 24_lef_sram_test")
|
||||
class lef_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -17,7 +17,7 @@ class openram_test(unittest.TestCase):
|
|||
|
||||
result=verify.run_drc(w.name, tempgds)
|
||||
if result != 0:
|
||||
self.fail("DRC failed: {}".format(a.name))
|
||||
self.fail("DRC failed: {}".format(w.name))
|
||||
|
||||
self.cleanup()
|
||||
|
||||
|
|
@ -163,6 +163,11 @@ class openram_test(unittest.TestCase):
|
|||
|
||||
|
||||
def header(filename, technology):
|
||||
# Skip the header for gitlab regression
|
||||
import getpass
|
||||
if getpass.getuser() == "gitlab-runner":
|
||||
return
|
||||
|
||||
tst = "Running Test for:"
|
||||
print("\n")
|
||||
print(" ______________________________________________________________________________ ")
|
||||
|
|
|
|||
|
|
@ -173,8 +173,13 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
|
|||
'lvsMaskDBFile': OPTS.openram_temp + cell_name + ".maskdb",
|
||||
'cmnFDILayerMapFile': drc["layer_map"],
|
||||
'cmnFDIUseLayerMap': 1,
|
||||
'lvsRecognizeGates': 'NONE'
|
||||
#'cmnVConnectNamesState' : 'ALL', #connects all nets with the same name
|
||||
'cmnTranscriptFile': './lvs.log',
|
||||
'cmnTranscriptEchoToFile': 1,
|
||||
'lvsRecognizeGates': 'NONE',
|
||||
# FIXME: Remove when vdd/gnd connected
|
||||
'cmnVConnectNamesState' : 'ALL', #connects all nets with the same namee
|
||||
# FIXME: Remove when vdd/gnd connected
|
||||
'lvsAbortOnSupplyError' : 0
|
||||
}
|
||||
|
||||
# This should be removed for final verification
|
||||
|
|
@ -184,7 +189,6 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
|
|||
lvs_runset['cmnVConnectNames']='vdd gnd'
|
||||
|
||||
|
||||
|
||||
# write the runset file
|
||||
f = open(OPTS.openram_temp + "lvs_runset", "w")
|
||||
for k in sorted(iter(lvs_runset.keys())):
|
||||
|
|
@ -260,8 +264,19 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
|
|||
debug.error(e.strip("\n"))
|
||||
|
||||
out_errors = len(stdouterrors)
|
||||
|
||||
total_errors = summary_errors + out_errors + ext_errors
|
||||
|
||||
if total_errors > 0:
|
||||
debug.error("{0}\tSummary: {1}\tOutput: {2}\tExtraction: {3}".format(cell_name,
|
||||
summary_errors,
|
||||
out_errors,
|
||||
ext_errors))
|
||||
else:
|
||||
debug.info(1, "{0}\tSummary: {1}\tOutput: {2}\tExtraction: {3}".format(cell_name,
|
||||
summary_errors,
|
||||
out_errors,
|
||||
ext_errors))
|
||||
|
||||
return total_errors
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -272,7 +272,9 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
|
|||
# Just print out the whole file, it is short.
|
||||
for e in results:
|
||||
debug.info(1,e.strip("\n"))
|
||||
debug.error("LVS mismatch (results in {})".format(resultsfile))
|
||||
debug.error("{0}\tLVS mismatch (results in {1})".format(cell_name,resultsfile))
|
||||
else:
|
||||
debug.info(1, "{0}\tLVS matches".format(cell_name))
|
||||
|
||||
return total_errors
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue