This commit is contained in:
jcirimel 2020-04-16 16:48:07 -07:00
commit ed54c7ab83
15 changed files with 399 additions and 280 deletions

View File

@ -46,7 +46,6 @@ class layout():
except ImportError:
self.pwr_grid_layer = "m3"
############################################################
# GDS layout
############################################################
@ -196,7 +195,7 @@ class layout():
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
debug.info(3, "adding instance {}".format(self.insts[-1]))
# This is commented out for runtime reasons
#debug.info(4, "instance list: " + ",".join(x.name for x in self.insts))
# debug.info(4, "instance list: " + ",".join(x.name for x in self.insts))
return self.insts[-1]
def get_inst(self, name):
@ -214,12 +213,14 @@ class layout():
width = drc["minwidth_{}".format(layer)]
if not height:
height = drc["minwidth_{}".format(layer)]
# negative layers indicate "unused" layers in a given technology
lpp = techlayer[layer]
if lpp[0] >= 0:
self.objs.append(geometry.rectangle(lpp, offset, width, height))
return self.objs[-1]
return None
if abs(offset[0]-5.16250)<0.01 and abs(offset[1]-8.70750)<0.01:
import pdb; pdb.set_trace()
self.objs.append(geometry.rectangle(lpp,
offset,
width,
height))
return self.objs[-1]
def add_rect_center(self, layer, offset, width=None, height=None):
"""
@ -230,16 +231,13 @@ class layout():
width = drc["minwidth_{}".format(layer)]
if not height:
height = drc["minwidth_{}".format(layer)]
# negative layers indicate "unused" layers in a given technology
lpp = techlayer[layer]
corrected_offset = offset - vector(0.5 * width, 0.5 * height)
if lpp[0] >= 0:
self.objs.append(geometry.rectangle(lpp,
corrected_offset,
width,
height))
return self.objs[-1]
return None
self.objs.append(geometry.rectangle(lpp,
corrected_offset,
width,
height))
return self.objs[-1]
def add_segment_center(self, layer, start, end):
"""
@ -252,15 +250,15 @@ class layout():
elif start.x != end.x:
offset = vector(0, 0.5 * minwidth_layer)
return self.add_rect(layer,
start-offset,
end.x-start.x,
start - offset,
end.x - start.x,
minwidth_layer)
else:
offset = vector(0.5 * minwidth_layer, 0)
return self.add_rect(layer,
start-offset,
start - offset,
minwidth_layer,
end.y-start.y)
end.y - start.y)
def get_pin(self, text):
"""
@ -268,14 +266,14 @@ class layout():
"""
try:
if len(self.pin_map[text]) > 1:
debug.error("Should use a pin iterator since more than one pin {}".format(text),-1)
debug.error("Should use a pin iterator since more than one pin {}".format(text), -1)
# If we have one pin, return it and not the list.
# Otherwise, should use get_pins()
any_pin = next(iter(self.pin_map[text]))
return any_pin
except Exception:
self.gds_write("missing_pin.gds")
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text,self.name),-1)
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text, self.name), -1)
def get_pins(self, text):
"""
@ -377,7 +375,7 @@ class layout():
height = drc["minwidth_{0}".format(layer)]
new_pin = pin_layout(text,
[offset, offset+vector(width, height)],
[offset, offset + vector(width, height)],
layer)
try:
@ -413,23 +411,18 @@ class layout():
def add_label(self, text, layer, offset=[0, 0], zoom=-1):
"""Adds a text label on the given layer,offset, and zoom level"""
# negative layers indicate "unused" layers in a given technology
debug.info(5, "add label " + str(text) + " " + layer + " " + str(offset))
lpp = techlayer[layer]
if lpp[0] >= 0:
self.objs.append(geometry.label(text, lpp, offset, zoom))
return self.objs[-1]
return None
self.objs.append(geometry.label(text, lpp, offset, zoom))
return self.objs[-1]
def add_path(self, layer, coordinates, width=None):
"""Connects a routing path on given layer,coordinates,width."""
debug.info(4, "add path " + str(layer) + " " + str(coordinates))
import wire_path
# NOTE: (UNTESTED) add_path(...) is currently not used
# negative layers indicate "unused" layers in a given technology
# lpp = techlayer[layer]
# if lpp[0] >= 0:
# self.objs.append(geometry.path(lpp, coordinates, width))
# self.objs.append(geometry.path(lpp, coordinates, width))
wire_path.wire_path(obj=self,
layer=layer,
@ -465,7 +458,7 @@ class layout():
from tech import preferred_directions
return preferred_directions[layer]
def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None):
def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None):
""" Add a three layer via structure. """
if not directions:
@ -487,7 +480,7 @@ class layout():
self.connect_inst([])
return inst
def add_via_center(self, layers, offset, directions=None, size=[1,1], implant_type=None, well_type=None):
def add_via_center(self, layers, offset, directions=None, size=[1, 1], implant_type=None, well_type=None):
"""
Add a three layer via structure by the center coordinate
accounting for mirroring and rotation.
@ -799,9 +792,7 @@ class layout():
self.add_rect(layer=layer,
offset=line_offset,
height=length)
# Make this the center of the rail
line_positions[names[i]] = line_offset + vector(half_minwidth,
0.5 * length)
line_positions[names[i]] = line_offset + vector(half_minwidth, 0)
else:
for i in range(len(names)):
line_offset = offset + vector(0,
@ -953,14 +944,14 @@ class layout():
min_y = min([pin.center().y for pin in pins])
# if we are less than a pitch, just create a non-preferred layer jog
if max_y-min_y <= pitch:
if max_y - min_y <= pitch:
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
# Add the vertical trunk on the horizontal layer!
self.add_path(self.horizontal_layer,
[vector(trunk_offset.x, min_y - half_layer_width),
vector(trunk_offset.x,max_y + half_layer_width)])
vector(trunk_offset.x, max_y + half_layer_width)])
# Route each pin to the trunk
for pin in pins:
@ -1000,7 +991,7 @@ class layout():
# Remove the pin from all conflicts
# FIXME: This is O(n^2), so maybe optimize it.
for other_pin,conflicts in g.items():
for other_pin, conflicts in g.items():
if pin in conflicts:
conflicts.remove(pin)
g[other_pin]=conflicts
@ -1151,8 +1142,8 @@ class layout():
else:
self.bounding_box = self.add_rect(layer=boundary_layer,
offset=ll,
height=ur.y-ll.y,
width=ur.x-ll.x)
height=ur.y - ll.y,
width=ur.x - ll.x)
def add_enclosure(self, insts, layer="nwell"):
""" Add a layer that surrounds the given instances. Useful
@ -1171,8 +1162,8 @@ class layout():
self.add_rect(layer=layer,
offset=vector(xmin, ymin),
width=xmax-xmin,
height=ymax-ymin)
width=xmax - xmin,
height=ymax - ymin)
def copy_power_pins(self, inst, name):
"""
@ -1192,7 +1183,7 @@ class layout():
else:
debug.warning("{0} pins of {1} should be on {2} or metal1 for "\
"supply router."
.format(name,inst.name,self.pwr_grid_layer))
.format(name, inst.name, self.pwr_grid_layer))
def add_power_pin(self, name, loc, size=[1, 1], vertical=False, start_layer="m1"):
"""
@ -1244,8 +1235,8 @@ class layout():
[ll, ur] = bbox
supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width
height = (ur.y-ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing
width = (ur.x-ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing
height = (ur.y - ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing
width = (ur.x - ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing
# LEFT vertical rails
offset = ll + vector(-2 * self.supply_rail_pitch,

View File

@ -6,10 +6,11 @@
# All rights reserved.
#
from tech import drc
import debug
import contact
from wire_path import wire_path
from sram_factory import factory
class wire(wire_path):
"""
Object metal wire; given the layer type
@ -36,6 +37,7 @@ class wire(wire_path):
# wires and wire_paths should not be offset to (0,0)
def setup_layers(self):
(horiz_layer, via_layer, vert_layer) = self.layer_stack
self.via_layer_name = via_layer
@ -47,8 +49,39 @@ class wire(wire_path):
via_connect = factory.create(module_type="contact",
layer_stack=self.layer_stack,
dimensions=(1, 1))
# This is used for short connections to avoid via-to-via spacing errors
self.vert_layer_contact_width = max(via_connect.second_layer_width,
via_connect.first_layer_width)
self.horiz_layer_contact_width = max(via_connect.second_layer_height,
via_connect.first_layer_height)
self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width,
drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height]
self.pitch = self.compute_pitch(self.layer_stack)
def compute_pitch(self, layer_stack):
"""
This is contact direction independent pitch,
i.e. we take the maximum contact dimension
"""
(layer1, via, layer2) = layer_stack
if layer1 == "poly" or layer1 == "active":
contact1 = getattr(contact, layer1 + "_contact")
else:
try:
contact1 = getattr(contact, layer1 + "_via")
except AttributeError:
contact1 = getattr(contact, layer2 + "_via")
max_contact = max(contact1.width, contact1.height)
layer1_space = drc("{0}_to_{0}".format(layer1))
layer2_space = drc("{0}_to_{0}".format(layer2))
pitch = max_contact + max(layer1_space, layer2_space)
return pitch
# create a 1x1 contact
def create_vias(self):
@ -56,12 +89,9 @@ class wire(wire_path):
self.c=factory.create(module_type="contact",
layer_stack=self.layer_stack,
dimensions=(1, 1))
c_width = self.c.width
c_height = self.c.height
from itertools import tee,islice
nwise = lambda g,n=2: zip(*(islice(g,i,None) for i,g in enumerate(tee(g,n))))
threewise=nwise(self.position_list,3)
from itertools import tee, islice
nwise = lambda g, n=2: zip(*(islice(g, i, None) for i, g in enumerate(tee(g, n))))
threewise = nwise(self.position_list, 3)
for (a, offset, c) in list(threewise):
# add a exceptions to prevent a via when we don't change directions
@ -72,7 +102,6 @@ class wire(wire_path):
self.obj.add_via_center(layers=self.layer_stack,
offset=offset)
def create_rectangles(self):
"""
Create the actual rectangles on the appropriate layers
@ -80,10 +109,18 @@ class wire(wire_path):
"""
pl = self.position_list # position list
for index in range(len(pl) - 1):
# Horizontal wire segment
if pl[index][0] != pl[index + 1][0]:
line_length = pl[index + 1][0] - pl[index][0]
# Make the wire wider to avoid via-to-via spacing problems
# But don't make it wider if it is shorter than one via
if abs(line_length) < self.pitch and abs(line_length) > self.horiz_layer_contact_width:
width = self.horiz_layer_contact_width
else:
width = self.horiz_layer_width
temp_offset = [pl[index][0],
pl[index][1] - 0.5*self.horiz_layer_width]
pl[index][1] - 0.5 * width]
# If we go in the negative direction, move the offset
if line_length < 0:
temp_offset = [temp_offset[0] + line_length,
temp_offset[1]]
@ -91,10 +128,17 @@ class wire(wire_path):
length=abs(line_length),
offset=temp_offset,
orientation="horizontal",
layer_width=self.horiz_layer_width)
layer_width=width)
# Vertical wire segment
elif pl[index][1] != pl[index + 1][1]:
line_length = pl[index + 1][1] - pl[index][1]
temp_offset = [pl[index][0] - 0.5 * self.vert_layer_width,
# Make the wire wider to avoid via-to-via spacing problems
# But don't make it wider if it is shorter than one via
if abs(line_length) < self.pitch and abs(line_length) > self.vert_layer_contact_width:
width = self.vert_layer_contact_width
else:
width = self.vert_layer_width
temp_offset = [pl[index][0] - 0.5 * width,
pl[index][1]]
if line_length < 0:
temp_offset = [temp_offset[0],
@ -103,11 +147,13 @@ class wire(wire_path):
length=abs(line_length),
offset=temp_offset,
orientation="vertical",
layer_width=self.vert_layer_width)
layer_width=width)
def assert_node(self, A, B):
""" Check if the node movements are not big enough for the
technology sizes."""
"""
Check if the node movements are not big enough for the
technology sizes.
"""
X_diff = abs(A[0] - B[0])
Y_diff = abs(A[1] - B[1])
[minX, minY] = self.node_to_node

View File

@ -9,6 +9,7 @@ import debug
from drc_value import *
from drc_lut import *
class design_rules(dict):
"""
This is a class that implements the design rules structures.

View File

@ -7,6 +7,7 @@
#
import debug
class drc_lut():
"""
Implement a lookup table of rules.
@ -32,15 +33,14 @@ class drc_lut():
if self.match(key, table_key):
return self.table[table_key]
def match(self, key1, key2):
"""
Determine if key1>=key2 for all tuple pairs.
(i.e. return false if key1<key2 for any pair.)
"""
# If any one pair is less than, return False
debug.check(len(key1)==len(key2),"Comparing invalid key lengths.")
for k1,k2 in zip(key1,key2):
debug.check(len(key1) == len(key2), "Comparing invalid key lengths.")
for k1, k2 in zip(key1, key2):
if k1 < k2:
return False
return True

View File

@ -6,6 +6,7 @@
# All rights reserved.
#
class drc_value():
"""
A single DRC value.

View File

@ -5,18 +5,16 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from math import log
import design
from tech import drc, parameter
from tech import cell_properties as props
import debug
import contact
from sram_factory import factory
import math
from vector import vector
from globals import OPTS
import logical_effort
class control_logic(design.design):
"""
Dynamically generated Control logic for the total SRAM circuit.
@ -37,13 +35,14 @@ class control_logic(design.design):
self.word_size = word_size
self.port_type = port_type
self.num_cols = word_size*words_per_row
self.num_words = num_rows*words_per_row
self.num_cols = word_size * words_per_row
self.num_words = num_rows * words_per_row
self.enable_delay_chain_resizing = False
self.inv_parasitic_delay = logical_effort.logical_effort.pinv
# Determines how much larger the sen delay should be. Accounts for possible error in model.
# FIXME: This should be made a parameter
self.wl_timing_tolerance = 1
self.wl_stage_efforts = None
self.sen_stage_efforts = None
@ -67,17 +66,16 @@ class control_logic(design.design):
""" Create layout and route between modules """
self.place_instances()
self.route_all()
#self.add_lvs_correspondence_points()
# self.add_lvs_correspondence_points()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
""" Add the pins to the control logic module. """
self.add_pin_list(self.input_list + ["clk"] + self.rbl_list, "INPUT")
self.add_pin_list(self.output_list,"OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
self.add_pin_list(self.output_list, "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
""" Add all the required modules """
@ -101,14 +99,13 @@ class control_logic(design.design):
height=dff_height)
self.add_mod(self.rbl_driver)
# clk_buf drives a flop for every address
addr_flops = math.log(self.num_words,2) + math.log(self.words_per_row,2)
addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2)
# plus data flops and control flops
num_flops = addr_flops + self.word_size + self.num_control_signals
# each flop internally has a FO 5 approximately
# plus about 5 fanouts for the control logic
clock_fanout = 5*num_flops + 5
clock_fanout = 5 * num_flops + 5
self.clk_buf_driver = factory.create(module_type="pdriver",
fanout=clock_fanout,
height=dff_height)
@ -117,7 +114,7 @@ class control_logic(design.design):
# We will use the maximum since this same value is used to size the wl_en
# and the p_en_bar drivers
max_fanout = max(self.num_rows,self.num_cols)
max_fanout = max(self.num_rows, self.num_cols)
# wl_en drives every row in the bank
self.wl_en_driver = factory.create(module_type="pdriver",
@ -127,7 +124,7 @@ class control_logic(design.design):
# w_en drives every write driver
self.wen_and = factory.create(module_type="pand3",
size=self.word_size+8,
size=self.word_size + 8,
height=dff_height)
self.add_mod(self.wen_and)
@ -151,7 +148,6 @@ class control_logic(design.design):
height=dff_height)
self.add_mod(self.p_en_bar_driver)
self.nand2 = factory.create(module_type="pnand2",
height=dff_height)
self.add_mod(self.nand2)
@ -195,9 +191,10 @@ class control_logic(design.design):
# self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing
# self.delay_chain_resized = True
debug.check(OPTS.delay_chain_stages%2, "Must use odd number of delay chain stages for inverting delay chain.")
debug.check(OPTS.delay_chain_stages % 2,
"Must use odd number of delay chain stages for inverting delay chain.")
self.delay_chain=factory.create(module_type="delay_chain",
fanout_list = OPTS.delay_chain_stages*[OPTS.delay_chain_fanout_per_stage])
fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ])
self.add_mod(self.delay_chain)
def get_heuristic_delay_chain_size(self):
@ -219,17 +216,17 @@ class control_logic(design.design):
def set_sen_wl_delays(self):
"""Set delays for wordline and sense amp enable"""
self.wl_delay_rise,self.wl_delay_fall = self.get_delays_to_wl()
self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen()
self.wl_delay = self.wl_delay_rise+self.wl_delay_fall
self.sen_delay = self.sen_delay_rise+self.sen_delay_fall
self.wl_delay_rise, self.wl_delay_fall = self.get_delays_to_wl()
self.sen_delay_rise, self.sen_delay_fall = self.get_delays_to_sen()
self.wl_delay = self.wl_delay_rise + self.wl_delay_fall
self.sen_delay = self.sen_delay_rise + self.sen_delay_fall
def does_sen_rise_fall_timing_match(self):
"""Compare the relative rise/fall delays of the sense amp enable and wordline"""
self.set_sen_wl_delays()
# This is not necessarily more reliable than total delay in some cases.
if (self.wl_delay_rise*self.wl_timing_tolerance >= self.sen_delay_rise or
self.wl_delay_fall*self.wl_timing_tolerance >= self.sen_delay_fall):
if (self.wl_delay_rise * self.wl_timing_tolerance >= self.sen_delay_rise or
self.wl_delay_fall * self.wl_timing_tolerance >= self.sen_delay_fall):
return False
else:
return True
@ -240,7 +237,7 @@ class control_logic(design.design):
# The sen delay must always be bigger than than the wl
# delay. This decides how much larger the sen delay must be
# before a re-size is warranted.
if self.wl_delay*self.wl_timing_tolerance >= self.sen_delay:
if self.wl_delay * self.wl_timing_tolerance >= self.sen_delay:
return False
else:
return True
@ -248,17 +245,20 @@ class control_logic(design.design):
def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
from math import ceil
previous_delay_chain_delay = (previous_fanout+1+self.inv_parasitic_delay)*previous_stages
previous_delay_chain_delay = (previous_fanout + 1 + self.inv_parasitic_delay) * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
delay_fanout = 3 # This can be anything >=2
# This can be anything >=2
delay_fanout = 3
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay = self.wl_delay*self.wl_timing_tolerance - (self.sen_delay-previous_delay_chain_delay)
required_delay = self.wl_delay * self.wl_timing_tolerance - (self.sen_delay - previous_delay_chain_delay)
debug.check(required_delay > 0, "Cannot size delay chain to have negative delay")
delay_stages = ceil(required_delay/(delay_fanout+1+self.inv_parasitic_delay))
if delay_stages%2 == 1: #force an even number of stages.
delay_stages+=1
delay_per_stage = delay_fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
# force an even number of stages.
if delay_stages % 2 == 1:
delay_stages += 1
# Fanout can be varied as well but is a little more complicated but potentially optimal.
debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay))
return (delay_stages, delay_fanout)
@ -266,32 +266,41 @@ class control_logic(design.design):
def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
previous_delay_chain_delay = (previous_fanout+1+self.inv_parasitic_delay)*previous_stages
previous_delay_per_stage = previous_fanout + 1 + self.inv_parasitic_delay
previous_delay_chain_delay = previous_delay_per_stage * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
fanout_rise = fanout_fall = 2 # This can be anything >=2
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay_fall = self.wl_delay_fall*self.wl_timing_tolerance - (self.sen_delay_fall-previous_delay_chain_delay/2)
required_delay_rise = self.wl_delay_rise*self.wl_timing_tolerance - (self.sen_delay_rise-previous_delay_chain_delay/2)
debug.info(2,"Required delays from chain: fall={}, rise={}".format(required_delay_fall,required_delay_rise))
required_delay_fall = self.wl_delay_fall * self.wl_timing_tolerance - \
(self.sen_delay_fall - previous_delay_chain_delay / 2)
required_delay_rise = self.wl_delay_rise * self.wl_timing_tolerance - \
(self.sen_delay_rise - previous_delay_chain_delay / 2)
debug.info(2,
"Required delays from chain: fall={}, rise={}".format(required_delay_fall,
required_delay_rise))
# If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
WARNING_FANOUT_DIFF = 5
stages_close = False
# The stages need to be equal (or at least a even number of stages with matching rise/fall delays)
while True:
stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,fanout_fall)
stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,fanout_rise)
debug.info(1,"Fall stages={}, rise stages={}".format(stages_fall,stages_rise))
if abs(stages_fall-stages_rise) == 1 and not stages_close:
stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,
fanout_fall)
stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,
fanout_rise)
debug.info(1,
"Fall stages={}, rise stages={}".format(stages_fall,
stages_rise))
if abs(stages_fall - stages_rise) == 1 and not stages_close:
stages_close = True
safe_fanout_rise = fanout_rise
safe_fanout_fall = fanout_fall
if stages_fall == stages_rise:
break
elif abs(stages_fall-stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall-fanout_rise):
elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise):
debug.info(1, "Delay chain fanouts between stages are large. Making chain size larger for safety.")
fanout_rise = safe_fanout_rise
fanout_fall = safe_fanout_fall
@ -303,28 +312,32 @@ class control_logic(design.design):
else:
fanout_rise+=1
total_stages = max(stages_fall,stages_rise)*2
total_stages = max(stages_fall, stages_rise) * 2
debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall))
# Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage.
stage_list = [fanout_fall if i%2==0 else fanout_rise for i in range(total_stages)]
stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)]
return stage_list
def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
from math import ceil
# Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay
if required_delay <= 3+self.inv_parasitic_delay: #3 is the minimum delay per stage (with pinv=0).
# 3 is the minimum delay per stage (with pinv=0).
if required_delay <= 3 + self.inv_parasitic_delay:
return 1
delay_stages = ceil(required_delay/(fanout+1+self.inv_parasitic_delay))
delay_per_stage = fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
return delay_stages
def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall):
"""Produces a list of fanouts which determine the size of the delay chain. List length is the number of stages.
Assumes the first stage is falling.
"""
Produces a list of fanouts which determine the size of the delay chain.
List length is the number of stages.
Assumes the first stage is falling.
"""
stage_list = []
for i in range(total_stages):
if i%2 == 0:
if i % 2 == 0:
stage_list.append()
def setup_signal_busses(self):
@ -351,7 +364,7 @@ class control_logic(design.design):
else:
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
# leave space for the bus plus one extra space
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
self.internal_bus_width = (len(self.internal_bus_list) + 1) * self.m2_pitch
# Outputs to the bank
if self.port_type == "rw":
@ -366,15 +379,13 @@ class control_logic(design.design):
self.supply_list = ["vdd", "gnd"]
def route_rails(self):
""" Add the input signal inverted tracks """
height = self.control_logic_center.y - self.m2_pitch
offset = vector(self.ctrl_dff_array.width,0)
offset = vector(self.ctrl_dff_array.width, 0)
self.rail_offsets = self.create_vertical_bus("m2", self.m2_pitch, offset, self.internal_bus_list, height)
def create_instances(self):
""" Create all the instances """
self.create_dffs()
@ -390,8 +401,6 @@ class control_logic(design.design):
self.create_delay()
self.create_pen_row()
def place_instances(self):
""" Place all the instances """
# Keep track of all right-most instances to determine row boundary
@ -435,11 +444,11 @@ class control_logic(design.design):
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y)
# Extra pitch on top and right
self.height = height + 2*self.m1_pitch
self.height = height + 2 * self.m1_pitch
# Max of modules or logic rows
self.width = max([inst.rx() for inst in self.row_end_inst])
if (self.port_type == "rw") or (self.port_type == "r"):
self.width = max(self.delay_inst.rx() , self.width)
self.width = max(self.delay_inst.rx(), self.width)
self.width += self.m2_pitch
def route_all(self):
@ -459,7 +468,6 @@ class control_logic(design.design):
self.route_gated_clk_buf()
self.route_supply()
def create_delay(self):
""" Create the replica bitline """
self.delay_inst=self.add_inst(name="delay_chain",
@ -467,9 +475,9 @@ class control_logic(design.design):
# rbl_bl_delay is asserted (1) when the bitline has been discharged
self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"])
def place_delay(self,row):
def place_delay(self, row):
""" Place the replica bitline """
y_off = row * self.and2.height + 2*self.m1_pitch
y_off = row * self.and2.height + 2 * self.m1_pitch
# Add the RBL above the rows
# Add to the right of the control rows and routing channel
@ -482,24 +490,22 @@ class control_logic(design.design):
# Connect to the rail level with the vdd rail
# Use pen since it is in every type of control logic
vdd_ypos = self.p_en_bar_nand_inst.get_pin("vdd").by()
in_pos = vector(self.rail_offsets["rbl_bl_delay"].x,vdd_ypos)
mid1 = vector(out_pos.x,in_pos.y)
self.add_wire(self.m1_stack,[out_pos, mid1, in_pos])
in_pos = vector(self.rail_offsets["rbl_bl_delay"].x, vdd_ypos)
mid1 = vector(out_pos.x, in_pos.y)
self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
self.add_via_center(layers=self.m1_stack,
offset=in_pos)
# Input from RBL goes to the delay line for futher delay
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
def create_clk_buf_row(self):
""" Create the multistage and gated clock buffer """
self.clk_buf_inst = self.add_inst(name="clkbuf",
mod=self.clk_buf_driver)
self.connect_inst(["clk","clk_buf","vdd","gnd"])
self.connect_inst(["clk", "clk_buf", "vdd", "gnd"])
def place_clk_buf_row(self,row):
def place_clk_buf_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_buf_inst, x_offset, row)
@ -512,17 +518,16 @@ class control_logic(design.design):
self.add_layout_pin_segment_center(text="clk",
layer="m2",
start=clk_pos,
end=clk_pos.scale(1,0))
end=clk_pos.scale(1, 0))
self.add_via_center(layers=self.m1_stack,
offset=clk_pos)
# Connect this at the bottom of the buffer
out_pos = self.clk_buf_inst.get_pin("Z").center()
mid1 = vector(out_pos.x,2*self.m2_pitch)
mid1 = vector(out_pos.x, 2 * self.m2_pitch)
mid2 = vector(self.rail_offsets["clk_buf"].x, mid1.y)
bus_pos = self.rail_offsets["clk_buf"]
self.add_wire(("m3","via2","m2"),[out_pos, mid1, mid2, bus_pos])
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
# The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack,
offset=self.clk_buf_inst.get_pin("Z").center())
@ -532,13 +537,13 @@ class control_logic(design.design):
def create_gated_clk_bar_row(self):
self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
mod=self.inv)
self.connect_inst(["clk_buf","clk_bar","vdd","gnd"])
self.connect_inst(["clk_buf", "clk_bar", "vdd", "gnd"])
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
mod=self.and2)
self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"])
self.connect_inst(["cs", "clk_bar", "gated_clk_bar", "vdd", "gnd"])
def place_gated_clk_bar_row(self,row):
def place_gated_clk_bar_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_bar_inst, x_offset, row)
@ -552,20 +557,25 @@ class control_logic(design.design):
out_pos = self.clk_bar_inst.get_pin("Z").center()
in_pos = self.gated_clk_bar_inst.get_pin("B").center()
mid1 = vector(in_pos.x,out_pos.y)
self.add_path("m1",[out_pos, mid1, in_pos])
mid1 = vector(in_pos.x, out_pos.y)
self.add_path("m1", [out_pos, mid1, in_pos])
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["A"], ["cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("m3", "via2", "m2"))
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_bar_inst,
self.rail_offsets,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_bar_inst.get_pin("A").center())
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["Z"], ["gated_clk_bar"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("m3", "via2", "m2"))
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_bar_inst,
self.rail_offsets,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_bar_inst.get_pin("Z").center())
@ -573,9 +583,9 @@ class control_logic(design.design):
def create_gated_clk_buf_row(self):
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf",
mod=self.and2)
self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"])
self.connect_inst(["clk_buf", "cs", "gated_clk_buf", "vdd", "gnd"])
def place_gated_clk_buf_row(self,row):
def place_gated_clk_buf_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row)
@ -586,9 +596,11 @@ class control_logic(design.design):
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets)
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("m3", "via2", "m2"))
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_buf_inst,
self.rail_offsets,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_buf_inst.get_pin("Z").center())
@ -623,7 +635,7 @@ class control_logic(design.design):
mod=self.p_en_bar_driver)
self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"])
def place_pen_row(self,row):
def place_pen_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.p_en_bar_nand_inst, x_offset, row)
@ -637,8 +649,8 @@ class control_logic(design.design):
out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc()
in_pos = self.p_en_bar_driver_inst.get_pin("A").lc()
mid1 = vector(out_pos.x,in_pos.y)
self.add_wire(self.m1_stack,[out_pos, mid1,in_pos])
mid1 = vector(out_pos.x, in_pos.y)
self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
@ -656,15 +668,13 @@ class control_logic(design.design):
# hence we use rbl_bl_delay as well.
self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"])
def place_sen_row(self,row):
def place_sen_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.s_en_gate_inst, x_offset, row)
self.row_end_inst.append(self.s_en_gate_inst)
def route_sen(self):
if self.port_type=="rw":
@ -683,7 +693,7 @@ class control_logic(design.design):
mod=self.inv)
self.connect_inst(["rbl_bl_delay", "rbl_bl_delay_bar", "vdd", "gnd"])
def place_rbl_delay_row(self,row):
def place_rbl_delay_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row)
@ -700,11 +710,9 @@ class control_logic(design.design):
self.add_via_center(layers=self.m1_stack,
offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center())
rbl_map = zip(["A"], ["rbl_bl_delay"])
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets)
def create_wen_row(self):
# input: we (or cs) output: w_en
@ -720,8 +728,7 @@ class control_logic(design.design):
# Only drive the writes in the second half of the clock cycle during a write operation.
self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"])
def place_wen_row(self,row):
def place_wen_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.w_en_gate_inst, x_offset, row)
@ -750,7 +757,7 @@ class control_logic(design.design):
self.connect_inst(inst_pins)
def place_dffs(self):
self.ctrl_dff_inst.place(vector(0,0))
self.ctrl_dff_inst.place(vector(0, 0))
def route_dffs(self):
if self.port_type == "rw":
@ -763,9 +770,9 @@ class control_logic(design.design):
# 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,2*self.m2_pitch)
mid_pos = in_pos + vector(0, 2 * self.m2_pitch)
rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y)
self.add_wire(self.m1_stack,[in_pos, mid_pos, rail_pos])
self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
@ -773,30 +780,27 @@ class control_logic(design.design):
if (self.port_type == "rw"):
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
def get_offset(self,row):
def get_offset(self, row):
""" Compute the y-offset and mirroring """
y_off = row*self.and2.height
y_off = row * self.and2.height
if row % 2:
y_off += self.and2.height
mirror="MX"
else:
mirror="R0"
return (y_off,mirror)
return (y_off, mirror)
def connect_output(self, inst, pin_name, out_name):
""" Create an output pin on the right side from the pin of a given instance. """
out_pin = inst.get_pin(pin_name)
right_pos=out_pin.center() + vector(self.width-out_pin.cx(),0)
right_pos = out_pin.center() + vector(self.width - out_pin.cx(), 0)
self.add_layout_pin_segment_center(text=out_name,
layer="m1",
start=out_pin.center(),
end=right_pos)
def route_supply(self):
""" Add vdd and gnd to the instance cells """
@ -818,13 +822,11 @@ class control_logic(design.design):
self.add_power_pin("gnd", pin_loc)
self.add_path("m1", [row_loc, pin_loc])
self.copy_layout_pin(self.delay_inst,"gnd")
self.copy_layout_pin(self.delay_inst,"vdd")
self.copy_layout_pin(self.ctrl_dff_inst,"gnd")
self.copy_layout_pin(self.ctrl_dff_inst,"vdd")
self.copy_layout_pin(self.delay_inst, "gnd")
self.copy_layout_pin(self.delay_inst, "vdd")
self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
@ -852,65 +854,70 @@ class control_logic(design.design):
height=pin.height(),
width=pin.width())
def get_delays_to_wl(self):
"""Get the delay (in delay units) of the clk to a wordline in the bitcell array"""
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
self.wl_stage_efforts = self.get_wordline_stage_efforts()
clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts)
clk_to_wl_rise, clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts)
total_delay = clk_to_wl_rise + clk_to_wl_fall
debug.info(1, "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise, clk_to_wl_fall,total_delay))
return clk_to_wl_rise,clk_to_wl_fall
debug.info(1,
"Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise,
clk_to_wl_fall,
total_delay))
return clk_to_wl_rise, clk_to_wl_fall
def get_wordline_stage_efforts(self):
"""Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts"""
stage_effort_list = []
#Initial direction of gated_clk_bar signal for this path
# Initial direction of gated_clk_bar signal for this path
is_clk_bar_rise = True
#Calculate the load on wl_en within the module and add it to external load
# Calculate the load on wl_en within the module and add it to external load
external_cout = self.sram.get_wl_en_cin()
#First stage is the clock buffer
# First stage is the clock buffer
stage_effort_list += self.clk_buf_driver.get_stage_efforts(external_cout, is_clk_bar_rise)
last_stage_is_rise = stage_effort_list[-1].is_rise
#Then ask the sram for the other path delays (from the bank)
# Then ask the sram for the other path delays (from the bank)
stage_effort_list += self.sram.get_wordline_stage_efforts(last_stage_is_rise)
return stage_effort_list
def get_delays_to_sen(self):
"""Get the delay (in delay units) of the clk to a sense amp enable.
This does not incorporate the delay of the replica bitline.
"""
Get the delay (in delay units) of the clk to a sense amp enable.
This does not incorporate the delay of the replica bitline.
"""
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
self.sen_stage_efforts = self.get_sa_enable_stage_efforts()
clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts)
total_delay = clk_to_sen_rise + clk_to_sen_fall
debug.info(1, "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise, clk_to_sen_fall,total_delay))
debug.info(1,
"Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise,
clk_to_sen_fall,
total_delay))
return clk_to_sen_rise, clk_to_sen_fall
def get_sa_enable_stage_efforts(self):
"""Follows the gated_clk_bar signal to the sense amp enable signal adding each stages stage effort to a list"""
stage_effort_list = []
#Initial direction of clock signal for this path
# Initial direction of clock signal for this path
last_stage_rise = True
#First stage, gated_clk_bar -(and2)-> rbl_in. Only for RW ports.
# First stage, gated_clk_bar -(and2)-> rbl_in. Only for RW ports.
if self.port_type == "rw":
stage1_cout = self.replica_bitline.get_en_cin()
stage_effort_list += self.and2.get_stage_efforts(stage1_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise
#Replica bitline stage, rbl_in -(rbl)-> pre_s_en
# Replica bitline stage, rbl_in -(rbl)-> pre_s_en
stage2_cout = self.sen_and2.get_cin()
stage_effort_list += self.replica_bitline.determine_sen_stage_efforts(stage2_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise
#buffer stage, pre_s_en -(buffer)-> s_en
# buffer stage, pre_s_en -(buffer)-> s_en
stage3_cout = self.sram.get_sen_cin()
stage_effort_list += self.s_en_driver.get_stage_efforts(stage3_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise
@ -918,7 +925,7 @@ class control_logic(design.design):
return stage_effort_list
def get_wl_sen_delays(self):
"""Gets a list of the stages and delays in order of their path."""
""" Gets a list of the stages and delays in order of their path. """
if self.sen_stage_efforts == None or self.wl_stage_efforts == None:
debug.error("Model delays not calculated for SRAM.", 1)
@ -927,32 +934,32 @@ class control_logic(design.design):
return wl_delays, sen_delays
def analytical_delay(self, corner, slew, load):
"""Gets the analytical delay from clk input to wl_en output"""
""" Gets the analytical delay from clk input to wl_en output """
stage_effort_list = []
#Calculate the load on clk_buf_bar
ext_clk_buf_cout = self.sram.get_clk_bar_cin()
# Calculate the load on clk_buf_bar
# ext_clk_buf_cout = self.sram.get_clk_bar_cin()
#Operations logic starts on negative edge
# Operations logic starts on negative edge
last_stage_rise = False
#First stage(s), clk -(pdriver)-> clk_buf.
#clk_buf_cout = self.replica_bitline.get_en_cin()
# First stage(s), clk -(pdriver)-> clk_buf.
# clk_buf_cout = self.replica_bitline.get_en_cin()
clk_buf_cout = 0
stage_effort_list += self.clk_buf_driver.get_stage_efforts(clk_buf_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise
#Second stage, clk_buf -(inv)-> clk_bar
# Second stage, clk_buf -(inv)-> clk_bar
clk_bar_cout = self.and2.get_cin()
stage_effort_list += self.and2.get_stage_efforts(clk_bar_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise
#Third stage clk_bar -(and)-> gated_clk_bar
# Third stage clk_bar -(and)-> gated_clk_bar
gated_clk_bar_cin = self.get_gated_clk_bar_cin()
stage_effort_list.append(self.inv.get_stage_effort(gated_clk_bar_cin, last_stage_rise))
last_stage_rise = stage_effort_list[-1].is_rise
#Stages from gated_clk_bar -------> wordline
# Stages from gated_clk_bar -------> wordline
stage_effort_list += self.get_wordline_stage_efforts()
return stage_effort_list
@ -962,10 +969,10 @@ class control_logic(design.design):
Includes all the DFFs and some logic.
"""
#Control logic internal load
# Control logic internal load
int_clk_buf_cap = self.inv.get_cin() + self.ctrl_dff_array.get_clk_cin() + self.and2.get_cin()
#Control logic external load (in the other parts of the SRAM)
# Control logic external load (in the other parts of the SRAM)
ext_clk_buf_cap = self.sram.get_clk_bar_cin()
return int_clk_buf_cap + ext_clk_buf_cap
@ -976,7 +983,7 @@ class control_logic(design.design):
total_cin = 0
total_cin += self.wl_en_driver.get_cin()
if self.port_type == 'rw':
total_cin +=self.and2.get_cin()
total_cin += self.and2.get_cin()
return total_cin
def graph_exclude_dffs(self):
@ -989,7 +996,7 @@ class control_logic(design.design):
def place_util(self, inst, x_offset, row):
""" Utility to place a row and compute the next offset """
(y_offset,mirror)=self.get_offset(row)
(y_offset, mirror) = self.get_offset(row)
offset = vector(x_offset, y_offset)
inst.place(offset, mirror)
return x_offset+inst.width
return x_offset + inst.width

View File

@ -7,13 +7,13 @@
#
import debug
import design
from tech import drc,parameter
from tech import parameter
from tech import cell_properties as props
from math import log
from vector import vector
from globals import OPTS
from sram_factory import factory
class dff_buf(design.design):
"""
This is a simple buffered DFF. The output is buffered
@ -107,13 +107,23 @@ class dff_buf(design.design):
self.dff_inst.place(vector(0,0))
# Add INV1 to the right
well_spacing = max(self.nwell_space,
self.pwell_space,
self.pwell_to_nwell)
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active,0))
well_spacing = 0
try:
well_spacing = max(well_spacing, self.nwell_space)
except AttributeError:
pass
try:
well_spacing = max(well_spacing, self.pwell_space)
except AttributeError:
pass
try:
well_spacing = max(well_spacing, self.pwell_to_nwell)
except AttributeError:
pass
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0))
# Add INV2 to the right
self.inv2_inst.place(vector(self.inv1_inst.rx(),0))
self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
def route_wires(self):
# Route dff q to inv1 a

View File

@ -33,7 +33,6 @@ class hierarchical_decoder(design.design):
except AttributeError:
self.cell_multiple = 1
# For debugging
# self.cell_multiple = 2
self.cell_height = self.cell_multiple * b.height
self.num_outputs = num_outputs

View File

@ -126,14 +126,16 @@ class sense_amp_array(design.design):
for i in range(len(self.local_insts)):
inst = self.local_insts[i]
gnd_pin = inst.get_pin("gnd")
self.add_power_pin(name="gnd",
loc=inst.get_pin("gnd").center(),
start_layer="m2",
loc=gnd_pin.center(),
start_layer=gnd_pin.layer,
vertical=True)
vdd_pin = inst.get_pin("vdd")
self.add_power_pin(name="vdd",
loc=inst.get_pin("vdd").center(),
start_layer="m2",
loc=vdd_pin.center(),
start_layer=vdd_pin.layer,
vertical=True)
bl_pin = inst.get_pin(inst.mod.get_bl_names())

View File

@ -166,7 +166,7 @@ class write_driver_array(design.design):
self.add_power_pin(name=n,
loc=pin.center(),
vertical=True,
start_layer="m2")
start_layer=pin.layer)
if self.write_size:
for bit in range(self.num_wmasks):
inst = self.driver_insts[bit * self.write_size]

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys,os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class hierarchical_predecode2x4_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# checking hierarchical precode 2x4 for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)")
a = factory.create(module_type="hierarchical_predecode2x4")
self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -26,16 +26,6 @@ class hierarchical_predecode2x4_test(openram_test):
a = factory.create(module_type="hierarchical_predecode2x4")
self.local_check(a)
# checking hierarchical precode 2x4 for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)")
a = factory.create(module_type="hierarchical_predecode2x4")
self.local_check(a)
globals.end_openram()
# run the test from the command line

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys,os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class hierarchical_predecode3x8_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# checking hierarchical precode 3x8 for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)")
a = factory.create(module_type="hierarchical_predecode3x8")
self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -26,16 +26,6 @@ class hierarchical_predecode3x8_test(openram_test):
a = factory.create(module_type="hierarchical_predecode3x8")
self.local_check(a)
# checking hierarchical precode 3x8 for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)")
a = factory.create(module_type="hierarchical_predecode3x8")
self.local_check(a)
globals.end_openram()
# run the test from the command line