Merge branch 'dev' into discrete_models

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

View File

@ -46,7 +46,6 @@ class layout():
except ImportError: except ImportError:
self.pwr_grid_layer = "m3" self.pwr_grid_layer = "m3"
############################################################ ############################################################
# GDS layout # GDS layout
############################################################ ############################################################
@ -214,12 +213,14 @@ class layout():
width = drc["minwidth_{}".format(layer)] width = drc["minwidth_{}".format(layer)]
if not height: if not height:
height = drc["minwidth_{}".format(layer)] height = drc["minwidth_{}".format(layer)]
# negative layers indicate "unused" layers in a given technology
lpp = techlayer[layer] lpp = techlayer[layer]
if lpp[0] >= 0: if abs(offset[0]-5.16250)<0.01 and abs(offset[1]-8.70750)<0.01:
self.objs.append(geometry.rectangle(lpp, offset, width, height)) import pdb; pdb.set_trace()
self.objs.append(geometry.rectangle(lpp,
offset,
width,
height))
return self.objs[-1] return self.objs[-1]
return None
def add_rect_center(self, layer, offset, width=None, height=None): def add_rect_center(self, layer, offset, width=None, height=None):
""" """
@ -230,16 +231,13 @@ class layout():
width = drc["minwidth_{}".format(layer)] width = drc["minwidth_{}".format(layer)]
if not height: if not height:
height = drc["minwidth_{}".format(layer)] height = drc["minwidth_{}".format(layer)]
# negative layers indicate "unused" layers in a given technology
lpp = techlayer[layer] lpp = techlayer[layer]
corrected_offset = offset - vector(0.5 * width, 0.5 * height) corrected_offset = offset - vector(0.5 * width, 0.5 * height)
if lpp[0] >= 0:
self.objs.append(geometry.rectangle(lpp, self.objs.append(geometry.rectangle(lpp,
corrected_offset, corrected_offset,
width, width,
height)) height))
return self.objs[-1] return self.objs[-1]
return None
def add_segment_center(self, layer, start, end): def add_segment_center(self, layer, start, end):
""" """
@ -413,22 +411,17 @@ class layout():
def add_label(self, text, layer, offset=[0, 0], zoom=-1): def add_label(self, text, layer, offset=[0, 0], zoom=-1):
"""Adds a text label on the given layer,offset, and zoom level""" """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)) debug.info(5, "add label " + str(text) + " " + layer + " " + str(offset))
lpp = techlayer[layer] lpp = techlayer[layer]
if lpp[0] >= 0:
self.objs.append(geometry.label(text, lpp, offset, zoom)) self.objs.append(geometry.label(text, lpp, offset, zoom))
return self.objs[-1] return self.objs[-1]
return None
def add_path(self, layer, coordinates, width=None): def add_path(self, layer, coordinates, width=None):
"""Connects a routing path on given layer,coordinates,width.""" """Connects a routing path on given layer,coordinates,width."""
debug.info(4, "add path " + str(layer) + " " + str(coordinates)) debug.info(4, "add path " + str(layer) + " " + str(coordinates))
import wire_path import wire_path
# NOTE: (UNTESTED) add_path(...) is currently not used # NOTE: (UNTESTED) add_path(...) is currently not used
# negative layers indicate "unused" layers in a given technology
# lpp = techlayer[layer] # 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, wire_path.wire_path(obj=self,
@ -799,9 +792,7 @@ class layout():
self.add_rect(layer=layer, self.add_rect(layer=layer,
offset=line_offset, offset=line_offset,
height=length) height=length)
# Make this the center of the rail line_positions[names[i]] = line_offset + vector(half_minwidth, 0)
line_positions[names[i]] = line_offset + vector(half_minwidth,
0.5 * length)
else: else:
for i in range(len(names)): for i in range(len(names)):
line_offset = offset + vector(0, line_offset = offset + vector(0,

View File

@ -6,10 +6,11 @@
# All rights reserved. # All rights reserved.
# #
from tech import drc from tech import drc
import debug import contact
from wire_path import wire_path from wire_path import wire_path
from sram_factory import factory from sram_factory import factory
class wire(wire_path): class wire(wire_path):
""" """
Object metal wire; given the layer type 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) # wires and wire_paths should not be offset to (0,0)
def setup_layers(self): def setup_layers(self):
(horiz_layer, via_layer, vert_layer) = self.layer_stack (horiz_layer, via_layer, vert_layer) = self.layer_stack
self.via_layer_name = via_layer self.via_layer_name = via_layer
@ -47,8 +49,39 @@ class wire(wire_path):
via_connect = factory.create(module_type="contact", via_connect = factory.create(module_type="contact",
layer_stack=self.layer_stack, layer_stack=self.layer_stack,
dimensions=(1, 1)) 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, self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width,
drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height] 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 # create a 1x1 contact
def create_vias(self): def create_vias(self):
@ -56,9 +89,6 @@ class wire(wire_path):
self.c=factory.create(module_type="contact", self.c=factory.create(module_type="contact",
layer_stack=self.layer_stack, layer_stack=self.layer_stack,
dimensions=(1, 1)) dimensions=(1, 1))
c_width = self.c.width
c_height = self.c.height
from itertools import tee, islice from itertools import tee, islice
nwise = lambda g, n=2: zip(*(islice(g, i, None) for i, g in enumerate(tee(g, n)))) nwise = lambda g, n=2: zip(*(islice(g, i, None) for i, g in enumerate(tee(g, n))))
threewise = nwise(self.position_list, 3) threewise = nwise(self.position_list, 3)
@ -72,7 +102,6 @@ class wire(wire_path):
self.obj.add_via_center(layers=self.layer_stack, self.obj.add_via_center(layers=self.layer_stack,
offset=offset) offset=offset)
def create_rectangles(self): def create_rectangles(self):
""" """
Create the actual rectangles on the appropriate layers Create the actual rectangles on the appropriate layers
@ -80,10 +109,18 @@ class wire(wire_path):
""" """
pl = self.position_list # position list pl = self.position_list # position list
for index in range(len(pl) - 1): for index in range(len(pl) - 1):
# Horizontal wire segment
if pl[index][0] != pl[index + 1][0]: if pl[index][0] != pl[index + 1][0]:
line_length = pl[index + 1][0] - pl[index][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], 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: if line_length < 0:
temp_offset = [temp_offset[0] + line_length, temp_offset = [temp_offset[0] + line_length,
temp_offset[1]] temp_offset[1]]
@ -91,10 +128,17 @@ class wire(wire_path):
length=abs(line_length), length=abs(line_length),
offset=temp_offset, offset=temp_offset,
orientation="horizontal", orientation="horizontal",
layer_width=self.horiz_layer_width) layer_width=width)
# Vertical wire segment
elif pl[index][1] != pl[index + 1][1]: elif pl[index][1] != pl[index + 1][1]:
line_length = pl[index + 1][1] - pl[index][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]] pl[index][1]]
if line_length < 0: if line_length < 0:
temp_offset = [temp_offset[0], temp_offset = [temp_offset[0],
@ -103,11 +147,13 @@ class wire(wire_path):
length=abs(line_length), length=abs(line_length),
offset=temp_offset, offset=temp_offset,
orientation="vertical", orientation="vertical",
layer_width=self.vert_layer_width) layer_width=width)
def assert_node(self, A, B): 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]) X_diff = abs(A[0] - B[0])
Y_diff = abs(A[1] - B[1]) Y_diff = abs(A[1] - B[1])
[minX, minY] = self.node_to_node [minX, minY] = self.node_to_node

View File

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

View File

@ -7,6 +7,7 @@
# #
import debug import debug
class drc_lut(): class drc_lut():
""" """
Implement a lookup table of rules. Implement a lookup table of rules.
@ -32,7 +33,6 @@ class drc_lut():
if self.match(key, table_key): if self.match(key, table_key):
return self.table[table_key] return self.table[table_key]
def match(self, key1, key2): def match(self, key1, key2):
""" """
Determine if key1>=key2 for all tuple pairs. Determine if key1>=key2 for all tuple pairs.

View File

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

View File

@ -5,18 +5,16 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
from math import log
import design import design
from tech import drc, parameter
from tech import cell_properties as props from tech import cell_properties as props
import debug import debug
import contact
from sram_factory import factory from sram_factory import factory
import math import math
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
import logical_effort import logical_effort
class control_logic(design.design): class control_logic(design.design):
""" """
Dynamically generated Control logic for the total SRAM circuit. Dynamically generated Control logic for the total SRAM circuit.
@ -44,6 +42,7 @@ class control_logic(design.design):
self.inv_parasitic_delay = logical_effort.logical_effort.pinv self.inv_parasitic_delay = logical_effort.logical_effort.pinv
# Determines how much larger the sen delay should be. Accounts for possible error in model. # 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_timing_tolerance = 1
self.wl_stage_efforts = None self.wl_stage_efforts = None
self.sen_stage_efforts = None self.sen_stage_efforts = None
@ -71,7 +70,6 @@ class control_logic(design.design):
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
""" Add the pins to the control logic module. """ """ 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.input_list + ["clk"] + self.rbl_list, "INPUT")
@ -101,7 +99,6 @@ class control_logic(design.design):
height=dff_height) height=dff_height)
self.add_mod(self.rbl_driver) self.add_mod(self.rbl_driver)
# clk_buf drives a flop for every address # 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 # plus data flops and control flops
@ -151,7 +148,6 @@ class control_logic(design.design):
height=dff_height) height=dff_height)
self.add_mod(self.p_en_bar_driver) self.add_mod(self.p_en_bar_driver)
self.nand2 = factory.create(module_type="pnand2", self.nand2 = factory.create(module_type="pnand2",
height=dff_height) height=dff_height)
self.add_mod(self.nand2) self.add_mod(self.nand2)
@ -195,7 +191,8 @@ class control_logic(design.design):
# self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing # self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing
# self.delay_chain_resized = True # 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", 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) self.add_mod(self.delay_chain)
@ -251,13 +248,16 @@ class control_logic(design.design):
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)) 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 # 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 # 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") 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)) delay_per_stage = delay_fanout + 1 + self.inv_parasitic_delay
if delay_stages%2 == 1: #force an even number of stages. delay_stages = ceil(required_delay / delay_per_stage)
# force an even number of stages.
if delay_stages % 2 == 1:
delay_stages += 1 delay_stages += 1
# Fanout can be varied as well but is a little more complicated but potentially optimal. # 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)) debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay))
@ -266,24 +266,33 @@ class control_logic(design.design):
def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout): 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""" """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)) debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
fanout_rise = fanout_fall = 2 # This can be anything >=2 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 # 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 # 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_fall = self.wl_delay_fall * self.wl_timing_tolerance - \
required_delay_rise = self.wl_delay_rise*self.wl_timing_tolerance - (self.sen_delay_rise-previous_delay_chain_delay/2) (self.sen_delay_fall - previous_delay_chain_delay / 2)
debug.info(2,"Required delays from chain: fall={}, rise={}".format(required_delay_fall,required_delay_rise)) 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. # If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
WARNING_FANOUT_DIFF = 5 WARNING_FANOUT_DIFF = 5
stages_close = False stages_close = False
# The stages need to be equal (or at least a even number of stages with matching rise/fall delays) # The stages need to be equal (or at least a even number of stages with matching rise/fall delays)
while True: while True:
stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,fanout_fall) stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,
stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,fanout_rise) fanout_fall)
debug.info(1,"Fall stages={}, rise stages={}".format(stages_fall,stages_rise)) 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: if abs(stages_fall - stages_rise) == 1 and not stages_close:
stages_close = True stages_close = True
safe_fanout_rise = fanout_rise safe_fanout_rise = fanout_rise
@ -313,13 +322,17 @@ class control_logic(design.design):
def calculate_stages_with_fixed_fanout(self, required_delay, fanout): def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
from math import ceil 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 # 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 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 return delay_stages
def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall): 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. """
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. Assumes the first stage is falling.
""" """
stage_list = [] stage_list = []
@ -366,7 +379,6 @@ class control_logic(design.design):
self.supply_list = ["vdd", "gnd"] self.supply_list = ["vdd", "gnd"]
def route_rails(self): def route_rails(self):
""" Add the input signal inverted tracks """ """ Add the input signal inverted tracks """
height = self.control_logic_center.y - self.m2_pitch height = self.control_logic_center.y - self.m2_pitch
@ -374,7 +386,6 @@ class control_logic(design.design):
self.rail_offsets = self.create_vertical_bus("m2", self.m2_pitch, offset, self.internal_bus_list, height) self.rail_offsets = self.create_vertical_bus("m2", self.m2_pitch, offset, self.internal_bus_list, height)
def create_instances(self): def create_instances(self):
""" Create all the instances """ """ Create all the instances """
self.create_dffs() self.create_dffs()
@ -390,8 +401,6 @@ class control_logic(design.design):
self.create_delay() self.create_delay()
self.create_pen_row() self.create_pen_row()
def place_instances(self): def place_instances(self):
""" Place all the instances """ """ Place all the instances """
# Keep track of all right-most instances to determine row boundary # Keep track of all right-most instances to determine row boundary
@ -459,7 +468,6 @@ class control_logic(design.design):
self.route_gated_clk_buf() self.route_gated_clk_buf()
self.route_supply() self.route_supply()
def create_delay(self): def create_delay(self):
""" Create the replica bitline """ """ Create the replica bitline """
self.delay_inst=self.add_inst(name="delay_chain", self.delay_inst=self.add_inst(name="delay_chain",
@ -488,11 +496,9 @@ class control_logic(design.design):
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=in_pos) offset=in_pos)
# Input from RBL goes to the delay line for futher delay # Input from RBL goes to the delay line for futher delay
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl") self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
def create_clk_buf_row(self): def create_clk_buf_row(self):
""" Create the multistage and gated clock buffer """ """ Create the multistage and gated clock buffer """
self.clk_buf_inst = self.add_inst(name="clkbuf", self.clk_buf_inst = self.add_inst(name="clkbuf",
@ -516,13 +522,12 @@ class control_logic(design.design):
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=clk_pos) offset=clk_pos)
# Connect this at the bottom of the buffer # Connect this at the bottom of the buffer
out_pos = self.clk_buf_inst.get_pin("Z").center() 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) mid2 = vector(self.rail_offsets["clk_buf"].x, mid1.y)
bus_pos = self.rail_offsets["clk_buf"] 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 # The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=self.clk_buf_inst.get_pin("Z").center()) offset=self.clk_buf_inst.get_pin("Z").center())
@ -557,15 +562,20 @@ class control_logic(design.design):
# This is the second gate over, so it needs to be on M3 # This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["A"], ["cs"]) 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 # The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_bar_inst.get_pin("A").center()) offset=self.gated_clk_bar_inst.get_pin("A").center())
# This is the second gate over, so it needs to be on M3 # This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["Z"], ["gated_clk_bar"]) 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 # The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_bar_inst.get_pin("Z").center()) offset=self.gated_clk_bar_inst.get_pin("Z").center())
@ -586,9 +596,11 @@ class control_logic(design.design):
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"]) clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets) self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets)
clkbuf_map = zip(["Z"], ["gated_clk_buf"]) 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 # The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_buf_inst.get_pin("Z").center()) offset=self.gated_clk_buf_inst.get_pin("Z").center())
@ -656,7 +668,6 @@ class control_logic(design.design):
# hence we use rbl_bl_delay as well. # hence we use rbl_bl_delay as well.
self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"]) 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.control_x_offset
@ -664,7 +675,6 @@ class control_logic(design.design):
self.row_end_inst.append(self.s_en_gate_inst) self.row_end_inst.append(self.s_en_gate_inst)
def route_sen(self): def route_sen(self):
if self.port_type=="rw": if self.port_type=="rw":
@ -700,11 +710,9 @@ class control_logic(design.design):
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center()) offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center())
rbl_map = zip(["A"], ["rbl_bl_delay"]) rbl_map = zip(["A"], ["rbl_bl_delay"])
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets) self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets)
def create_wen_row(self): def create_wen_row(self):
# input: we (or cs) output: w_en # input: we (or cs) output: w_en
@ -720,7 +728,6 @@ class control_logic(design.design):
# Only drive the writes in the second half of the clock cycle during a write operation. # 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"]) 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.control_x_offset
@ -784,7 +791,6 @@ class control_logic(design.design):
return (y_off, mirror) return (y_off, mirror)
def connect_output(self, inst, pin_name, out_name): def connect_output(self, inst, pin_name, out_name):
""" Create an output pin on the right side from the pin of a given instance. """ """ Create an output pin on the right side from the pin of a given instance. """
@ -795,8 +801,6 @@ class control_logic(design.design):
start=out_pin.center(), start=out_pin.center(),
end=right_pos) end=right_pos)
def route_supply(self): def route_supply(self):
""" Add vdd and gnd to the instance cells """ """ Add vdd and gnd to the instance cells """
@ -824,8 +828,6 @@ class control_logic(design.design):
self.copy_layout_pin(self.ctrl_dff_inst, "gnd") self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
self.copy_layout_pin(self.ctrl_dff_inst, "vdd") self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
def add_lvs_correspondence_points(self): def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong. """ This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction These should probably be turned off by default though, since extraction
@ -852,17 +854,18 @@ class control_logic(design.design):
height=pin.height(), height=pin.height(),
width=pin.width()) width=pin.width())
def get_delays_to_wl(self): def get_delays_to_wl(self):
"""Get the delay (in delay units) of the clk to a wordline in the bitcell array""" """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.") 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() 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 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)) 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 return clk_to_wl_rise, clk_to_wl_fall
def get_wordline_stage_efforts(self): def get_wordline_stage_efforts(self):
"""Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts""" """Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts"""
stage_effort_list = [] stage_effort_list = []
@ -882,14 +885,18 @@ class control_logic(design.design):
return stage_effort_list return stage_effort_list
def get_delays_to_sen(self): def get_delays_to_sen(self):
"""Get the delay (in delay units) of the clk to a sense amp enable. """
Get the delay (in delay units) of the clk to a sense amp enable.
This does not incorporate the delay of the replica bitline. 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.") 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() 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) 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 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 return clk_to_sen_rise, clk_to_sen_fall
def get_sa_enable_stage_efforts(self): def get_sa_enable_stage_efforts(self):
@ -931,7 +938,7 @@ class control_logic(design.design):
stage_effort_list = [] stage_effort_list = []
# Calculate the load on clk_buf_bar # Calculate the load on clk_buf_bar
ext_clk_buf_cout = self.sram.get_clk_bar_cin() # 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 last_stage_rise = False

View File

@ -7,13 +7,13 @@
# #
import debug import debug
import design import design
from tech import drc,parameter from tech import parameter
from tech import cell_properties as props from tech import cell_properties as props
from math import log
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
class dff_buf(design.design): class dff_buf(design.design):
""" """
This is a simple buffered DFF. The output is buffered This is a simple buffered DFF. The output is buffered
@ -107,9 +107,19 @@ class dff_buf(design.design):
self.dff_inst.place(vector(0,0)) self.dff_inst.place(vector(0,0))
# Add INV1 to the right # Add INV1 to the right
well_spacing = max(self.nwell_space, well_spacing = 0
self.pwell_space, try:
self.pwell_to_nwell) 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)) self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0))
# Add INV2 to the right # Add INV2 to the right

View File

@ -33,7 +33,6 @@ class hierarchical_decoder(design.design):
except AttributeError: except AttributeError:
self.cell_multiple = 1 self.cell_multiple = 1
# For debugging # For debugging
# self.cell_multiple = 2
self.cell_height = self.cell_multiple * b.height self.cell_height = self.cell_multiple * b.height
self.num_outputs = num_outputs 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)): for i in range(len(self.local_insts)):
inst = self.local_insts[i] inst = self.local_insts[i]
gnd_pin = inst.get_pin("gnd")
self.add_power_pin(name="gnd", self.add_power_pin(name="gnd",
loc=inst.get_pin("gnd").center(), loc=gnd_pin.center(),
start_layer="m2", start_layer=gnd_pin.layer,
vertical=True) vertical=True)
vdd_pin = inst.get_pin("vdd")
self.add_power_pin(name="vdd", self.add_power_pin(name="vdd",
loc=inst.get_pin("vdd").center(), loc=vdd_pin.center(),
start_layer="m2", start_layer=vdd_pin.layer,
vertical=True) vertical=True)
bl_pin = inst.get_pin(inst.mod.get_bl_names()) 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, self.add_power_pin(name=n,
loc=pin.center(), loc=pin.center(),
vertical=True, vertical=True,
start_layer="m2") start_layer=pin.layer)
if self.write_size: if self.write_size:
for bit in range(self.num_wmasks): for bit in range(self.num_wmasks):
inst = self.driver_insts[bit * self.write_size] 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") a = factory.create(module_type="hierarchical_predecode2x4")
self.local_check(a) 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() globals.end_openram()
# run the test from the command line # 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") a = factory.create(module_type="hierarchical_predecode3x8")
self.local_check(a) 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() globals.end_openram()
# run the test from the command line # run the test from the command line