mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' of https://github.com/VLSIDA/PrivateRAM into dev
This commit is contained in:
commit
ed54c7ab83
|
|
@ -46,7 +46,6 @@ class layout():
|
|||
except ImportError:
|
||||
self.pwr_grid_layer = "m3"
|
||||
|
||||
|
||||
############################################################
|
||||
# GDS layout
|
||||
############################################################
|
||||
|
|
@ -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))
|
||||
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]
|
||||
return None
|
||||
|
||||
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
|
||||
|
||||
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):
|
||||
"""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
|
||||
|
||||
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))
|
||||
|
||||
wire_path.wire_path(obj=self,
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,9 +89,6 @@ 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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#
|
||||
import debug
|
||||
|
||||
|
||||
class drc_lut():
|
||||
"""
|
||||
Implement a lookup table of rules.
|
||||
|
|
@ -32,7 +33,6 @@ 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.
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
|
||||
|
||||
class drc_value():
|
||||
"""
|
||||
A single DRC value.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -44,6 +42,7 @@ class control_logic(design.design):
|
|||
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
|
||||
|
|
@ -71,7 +70,6 @@ class control_logic(design.design):
|
|||
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")
|
||||
|
|
@ -101,7 +99,6 @@ 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)
|
||||
# plus data flops and control flops
|
||||
|
|
@ -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,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.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 ])
|
||||
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
|
||||
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)
|
||||
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_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))
|
||||
|
|
@ -266,24 +266,33 @@ 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))
|
||||
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
|
||||
|
|
@ -313,13 +322,17 @@ class control_logic(design.design):
|
|||
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.
|
||||
"""
|
||||
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 = []
|
||||
|
|
@ -366,7 +379,6 @@ 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
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
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
|
||||
|
|
@ -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",
|
||||
|
|
@ -488,11 +496,9 @@ class control_logic(design.design):
|
|||
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",
|
||||
|
|
@ -516,13 +522,12 @@ class control_logic(design.design):
|
|||
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)
|
||||
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())
|
||||
|
|
@ -557,15 +562,20 @@ class control_logic(design.design):
|
|||
|
||||
# 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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -656,7 +668,6 @@ 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):
|
||||
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)
|
||||
|
||||
|
||||
def route_sen(self):
|
||||
|
||||
if self.port_type=="rw":
|
||||
|
|
@ -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,7 +728,6 @@ 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):
|
||||
x_offset = self.control_x_offset
|
||||
|
||||
|
|
@ -784,7 +791,6 @@ class control_logic(design.design):
|
|||
|
||||
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. """
|
||||
|
||||
|
|
@ -795,8 +801,6 @@ class control_logic(design.design):
|
|||
start=out_pin.center(),
|
||||
end=right_pos)
|
||||
|
||||
|
||||
|
||||
def route_supply(self):
|
||||
""" 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, "vdd")
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
|
@ -852,17 +854,18 @@ 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)
|
||||
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
|
||||
|
||||
|
||||
def get_wordline_stage_efforts(self):
|
||||
"""Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts"""
|
||||
stage_effort_list = []
|
||||
|
|
@ -882,14 +885,18 @@ class control_logic(design.design):
|
|||
return stage_effort_list
|
||||
|
||||
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.
|
||||
"""
|
||||
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):
|
||||
|
|
@ -931,7 +938,7 @@ class control_logic(design.design):
|
|||
|
||||
stage_effort_list = []
|
||||
# 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
|
||||
last_stage_rise = False
|
||||
|
|
|
|||
|
|
@ -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,9 +107,19 @@ 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)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue