Merge branch 'dev' into bisr

This commit is contained in:
Aditi Sinha 2020-05-02 07:48:35 +00:00
commit 2498ff07ea
96 changed files with 3358 additions and 1983 deletions

2
.gitignore vendored
View File

@ -8,3 +8,5 @@
*.toc
*.synctex.gz
**/model_data
outputs
technology/freepdk45/ncsu_basekit

View File

@ -74,7 +74,7 @@ class design(hierarchy_design):
return pitch
def setup_drc_constants(self):
"""
"""
These are some DRC constants used in many places
in the compiler.
"""

15
compiler/base/errors.py Normal file
View File

@ -0,0 +1,15 @@
class drc_error(Exception):
"""Exception raised for DRC errors.
Attributes:
expression -- input expression in which the error occurred
message -- explanation of the error
"""
# def __init__(self, expression, message):
# self.expression = expression
# self.message = message
def __init__(self, message):
self.message = message

View File

@ -296,11 +296,11 @@ class path(geometry):
def __str__(self):
""" override print function output """
return "path: layer=" + self.layerNumber + " w=" + self.width
return "path: layer=" + self.layerNumber + " purpose=" + str(self.layerPurpose) + " w=" + self.width
def __repr__(self):
""" override print function output """
return "( path: layer=" + self.layerNumber + " w=" + self.width + " coords=" + str(self.coordinates) + " )"
return "( path: layer=" + self.layerNumber + " purpose=" + str(self.layerPurpose) + " w=" + self.width + " coords=" + str(self.coordinates) + " )"
class label(geometry):
@ -340,11 +340,11 @@ class label(geometry):
def __str__(self):
""" override print function output """
return "label: " + self.text + " layer=" + str(self.layerNumber)
return "label: " + self.text + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose)
def __repr__(self):
""" override print function output """
return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " )"
return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose) + " )"
class rectangle(geometry):
@ -391,4 +391,4 @@ class rectangle(geometry):
def __repr__(self):
""" override print function output """
return "( rect: @" + str(self.offset) + " WxH=" + str(self.width) + "x" + str(self.height) + " layer=" + str(self.layerNumber) + " )"
return "( rect: @" + str(self.offset) + " WxH=" + str(self.width) + "x" + str(self.height) + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose) + " )"

View File

@ -7,15 +7,11 @@
#
import hierarchy_layout
import hierarchy_spice
import globals
import verify
import debug
import os
from globals import OPTS
import graph_util
total_drc_errors = 0
total_lvs_errors = 0
class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
"""
@ -28,120 +24,157 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds"
self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp"
# If we have a separate lvs directory, then all the lvs files
# should be in there (all or nothing!)
lvs_dir = OPTS.openram_tech + "lvs_lib/"
if os.path.exists(lvs_dir):
self.lvs_file = lvs_dir + name + ".sp"
else:
self.lvs_file = self.sp_file
self.name = name
hierarchy_spice.spice.__init__(self, name)
hierarchy_layout.layout.__init__(self, name)
self.init_graph_params()
def get_layout_pins(self,inst):
def get_layout_pins(self, inst):
""" Return a map of pin locations of the instance offset """
# find the instance
for i in self.insts:
if i.name == inst.name:
break
else:
debug.error("Couldn't find instance {0}".format(inst_name),-1)
debug.error("Couldn't find instance {0}".format(inst_name), -1)
inst_map = inst.mod.pin_map
return inst_map
def DRC_LVS(self, final_verification=False, top_level=False):
def DRC_LVS(self, final_verification=False, force_check=False):
"""Checks both DRC and LVS for a module"""
# Final verification option does not allow nets to be connected by label.
# No layout to check
if OPTS.netlist_only:
return ("skipped", "skipped")
# Unit tests will check themselves.
if OPTS.is_unit_test:
return
if not OPTS.check_lvsdrc:
return
if not force_check and OPTS.is_unit_test:
return ("skipped", "skipped")
if not force_check and not OPTS.check_lvsdrc:
return ("skipped", "skipped")
# Do not run if disabled in options.
if (OPTS.inline_lvsdrc or top_level):
if (OPTS.inline_lvsdrc or force_check or final_verification):
global total_drc_errors
global total_lvs_errors
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
self.sp_write(tempspice)
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
self.lvs_write(tempspice)
self.gds_write(tempgds)
# Final verification option does not allow nets to be connected by label.
num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification)
num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification)
num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors))
debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors))
total_drc_errors += num_drc_errors
total_lvs_errors += num_lvs_errors
# force_check is used to determine decoder height and other things, so we shouldn't fail
# if that flag is set
if OPTS.inline_lvsdrc and not force_check:
debug.check(num_drc_errors == 0,
"DRC failed for {0} with {1} error(s)".format(self.name,
num_drc_errors))
debug.check(num_lvs_errors == 0,
"LVS failed for {0} with {1} errors(s)".format(self.name,
num_lvs_errors))
os.remove(tempspice)
os.remove(tempgds)
return (num_drc_errors, num_lvs_errors)
else:
return ("skipped", "skipped")
def DRC(self, final_verification=False):
"""Checks DRC for a module"""
# Unit tests will check themselves.
# Do not run if disabled in options.
# No layout to check
if OPTS.netlist_only:
return "skipped"
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
global total_drc_errors
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
self.gds_write(tempgds)
num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification)
total_drc_errors += num_errors
debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error))
num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification)
debug.check(num_errors == 0,
"DRC failed for {0} with {1} error(s)".format(self.name,
num_errors))
os.remove(tempgds)
return num_errors
else:
return "skipped"
def LVS(self, final_verification=False):
"""Checks LVS for a module"""
# Unit tests will check themselves.
# Do not run if disabled in options.
# No layout to check
if OPTS.netlist_only:
return "skipped"
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
global total_lvs_errors
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
self.sp_write(tempspice)
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
self.lvs_write(tempspice)
self.gds_write(tempgds)
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
total_lvs_errors += num_errors
debug.check(num_errors == 0,"LVS failed for {0} with {1} error(s)".format(self.name,num_errors))
debug.check(num_errors == 0,
"LVS failed for {0} with {1} error(s)".format(self.name,
num_errors))
os.remove(tempspice)
os.remove(tempgds)
os.remove(tempgds)
return num_errors
else:
return "skipped"
def init_graph_params(self):
"""Initializes parameters relevant to the graph creation"""
#Only initializes a set for checking instances which should not be added
# Only initializes a set for checking instances which should not be added
self.graph_inst_exclude = set()
def build_graph(self, graph, inst_name, port_nets):
def build_graph(self, graph, inst_name, port_nets):
"""Recursively create graph from instances in module."""
#Translate port names to external nets
# Translate port names to external nets
if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,self.pins),1)
port_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
self.pins),
1)
port_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
debug.info(3, "Instance name={}".format(inst_name))
for subinst, conns in zip(self.insts, self.conns):
if subinst in self.graph_inst_exclude:
continue
subinst_name = inst_name+'.X'+subinst.name
subinst_name = inst_name + '.X' + subinst.name
subinst_ports = self.translate_nets(conns, port_dict, inst_name)
subinst.mod.build_graph(graph, subinst_name, subinst_ports)
def build_names(self, name_dict, inst_name, port_nets):
"""Collects all the nets and the parent inst of that net."""
#Translate port names to external nets
# Translate port names to external nets
if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,self.pins),1)
port_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
self.pins),
1)
port_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
debug.info(3, "Instance name={}".format(inst_name))
for subinst, conns in zip(self.insts, self.conns):
subinst_name = inst_name+'.X'+subinst.name
subinst_name = inst_name + '.X' + subinst.name
subinst_ports = self.translate_nets(conns, port_dict, inst_name)
for si_port, conn in zip(subinst_ports, conns):
#Only add for first occurrence
# Only add for first occurrence
if si_port.lower() not in name_dict:
mod_info = {'mod':self, 'int_net':conn}
mod_info = {'mod': self, 'int_net': conn}
name_dict[si_port.lower()] = mod_info
subinst.mod.build_names(name_dict, subinst_name, subinst_ports)
subinst.mod.build_names(name_dict, subinst_name, subinst_ports)
def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None):
"""Given a list of nets, will compare the internal alias of a mod to determine
@ -161,17 +194,17 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
int_mod = self.name_dict[net]['mod']
if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set):
aliases.append(net)
return aliases
return aliases
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):
"""Checks if the alias_net in input mod is the same as the input net for this mod (self)."""
if self in exclusion_set:
return False
#Check ports of this mod
# Check ports of this mod
for pin in self.pins:
if self.is_net_alias_name_check(known_net, pin, net_alias, mod):
return True
#Check connections of all other subinsts
# Check connections of all other subinsts
mod_set = set()
for subinst, inst_conns in zip(self.insts, self.conns):
for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins):
@ -181,7 +214,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if subinst.mod.is_net_alias(mod_pin, net_alias, mod, exclusion_set):
return True
mod_set.add(subinst.mod)
return False
return False
def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod):
"""Utility function for checking single net alias."""
@ -190,8 +223,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
parent_net.lower() == alias_net.lower()
def get_mod_net(self, parent_net, child_inst, child_conns):
"""Given an instance and net, returns the internal net in the mod
corresponding to input net."""
"""
Given an instance and net, returns the internal net in the mod
corresponding to input net.
"""
for conn, pin in zip(child_conns, child_inst.mod.pins):
if parent_net.lower() == conn.lower():
return pin
@ -205,27 +240,27 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
converted_conns.append(port_dict[conn])
else:
converted_conns.append("{}.{}".format(inst_name, conn))
return converted_conns
return converted_conns
def add_graph_edges(self, graph, port_nets):
"""For every input, adds an edge to every output.
Only intended to be used for gates and other simple modules."""
#The final pin names will depend on the spice hierarchy, so
#they are passed as an input.
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
# The final pin names will depend on the spice hierarchy, so
# they are passed as an input.
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
input_pins = self.get_inputs()
output_pins = self.get_outputs()
inout_pins = self.get_inouts()
for inp in input_pins+inout_pins:
for out in output_pins+inout_pins:
if inp != out: #do not add self loops
graph.add_edge(pin_dict[inp], pin_dict[out], self)
for inp in input_pins + inout_pins:
for out in output_pins + inout_pins:
if inp != out: # do not add self loops
graph.add_edge(pin_dict[inp], pin_dict[out], self)
def __str__(self):
""" override print function output """
pins = ",".join(self.pins)
insts = [" {}".format(x) for x in self.insts]
objs = [" {}".format(x) for x in self.objs]
objs = [" {}".format(x) for x in self.objs]
s = "********** design {0} **********".format(self.name)
s += "\n pins ({0})={1}\n".format(len(self.pins), pins)
s += "\n objs ({0})=\n{1}\n".format(len(self.objs), "\n".join(objs))
@ -236,8 +271,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
""" override print function output """
text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n"
for i in self.objs:
text+=str(i)+",\n"
text+=str(i) + ",\n"
for i in self.insts:
text+=str(i)+",\n"
text+=str(i) + ",\n"
return text

View File

@ -9,6 +9,7 @@ import collections
import geometry
import gdsMill
import debug
from math import sqrt
from tech import drc, GDS
from tech import layer as techlayer
from tech import layer_stacks
@ -16,6 +17,7 @@ import os
from globals import OPTS
from vector import vector
from pin_layout import pin_layout
from utils import round_to_grid
class layout():
@ -45,7 +47,6 @@ class layout():
except ImportError:
self.pwr_grid_layer = "m3"
############################################################
# GDS layout
############################################################
@ -195,7 +196,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):
@ -213,12 +214,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):
"""
@ -229,16 +232,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):
"""
@ -251,15 +251,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):
"""
@ -267,14 +267,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):
"""
@ -326,7 +326,7 @@ class layout():
file_name = "non_rectilinear.gds"
self.gds_write(file_name)
debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1)
minwidth_layer = drc["minwidth_{}".format(layer)]
# one of these will be zero
@ -376,7 +376,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:
@ -412,23 +412,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,
@ -464,7 +459,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:
@ -486,7 +481,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.
@ -798,9 +793,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,
@ -952,14 +945,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:
@ -982,6 +975,7 @@ class layout():
def create_channel_route(self, netlist,
offset,
layer_stack,
layer_dirs=None,
vertical=False):
"""
The net list is a list of the nets. Each net is a list of pins
@ -999,7 +993,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
@ -1020,25 +1014,38 @@ class layout():
def vcg_pin_overlap(pin1, pin2, vertical, pitch):
""" Check for vertical or horizontal overlap of the two pins """
# FIXME: If the pins are not in a row, this may break.
# However, a top pin shouldn't overlap another top pin,
# for example, so the
# extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x)<pitch
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
# Pin 1 must be in the "LEFT" set
y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y-pin2.center().y)<pitch
y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch
overlaps = (not vertical and x_overlap) or (vertical and y_overlap)
return overlaps
if self.get_preferred_direction(layer_stack[0]) == "V":
self.vertical_layer = layer_stack[0]
self.horizontal_layer = layer_stack[2]
if not layer_dirs:
# Use the preferred layer directions
if self.get_preferred_direction(layer_stack[0]) == "V":
self.vertical_layer = layer_stack[0]
self.horizontal_layer = layer_stack[2]
else:
self.vertical_layer = layer_stack[2]
self.horizontal_layer = layer_stack[0]
else:
self.vertical_layer = layer_stack[2]
self.horizontal_layer = layer_stack[0]
# Use the layer directions specified to the router rather than
# the preferred directions
debug.check(layer_dirs[0] != layer_dirs[1], "Must have unique layer directions.")
if layer_dirs[0] == "V":
self.vertical_layer = layer_stack[0]
self.horizontal_layer = layer_stack[2]
else:
self.horizontal_layer = layer_stack[0]
self.vertical_layer = layer_stack[2]
layer_stuff = self.get_layer_pitch(self.vertical_layer)
(self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff
@ -1121,17 +1128,17 @@ class layout():
self.horizontal_pitch)
offset += vector(0, self.horizontal_pitch)
def create_vertical_channel_route(self, netlist, offset, layer_stack):
def create_vertical_channel_route(self, netlist, offset, layer_stack, layer_dirs=None):
"""
Wrapper to create a vertical channel route
"""
self.create_channel_route(netlist, offset, layer_stack, vertical=True)
self.create_channel_route(netlist, offset, layer_stack, layer_dirs, vertical=True)
def create_horizontal_channel_route(self, netlist, offset, layer_stack):
def create_horizontal_channel_route(self, netlist, offset, layer_stack, layer_dirs=None):
"""
Wrapper to create a horizontal channel route
"""
self.create_channel_route(netlist, offset, layer_stack, vertical=False)
self.create_channel_route(netlist, offset, layer_stack, layer_dirs, vertical=False)
def add_boundary(self, ll=vector(0, 0), ur=None):
""" Add boundary for debugging dimensions """
@ -1150,8 +1157,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
@ -1170,8 +1177,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):
"""
@ -1191,13 +1198,11 @@ 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"):
"""
Add a single power pin from the lowest power_grid layer down to M1 at
Add a single power pin from the lowest power_grid layer down to M1 (or li) at
the given center location. The starting layer is specified to determine
which vias are needed.
"""
@ -1211,23 +1216,28 @@ class layout():
else:
direction = None
via = self.add_via_stack_center(from_layer=start_layer,
to_layer=self.pwr_grid_layer,
size=size,
offset=loc,
direction=direction)
if start_layer == self.pwr_grid_layer:
self.add_layout_pin_rect_center(text=name,
layer=self.pwr_grid_layer,
offset=loc)
else:
# Hack for min area
if OPTS.tech_name == "s8":
width = round_to_grid(sqrt(drc["minarea_m3"]))
height = round_to_grid(drc["minarea_m3"]/width)
else:
width = via.width
height = via.height
self.add_layout_pin_rect_center(text=name,
layer=self.pwr_grid_layer,
offset=loc,
width=via.width,
height=via.height)
width=width,
height=height)
def add_power_ring(self, bbox):
"""
@ -1241,8 +1251,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

@ -15,6 +15,7 @@ from wire_spice_model import *
from power_data import *
import logical_effort
class spice():
"""
This provides a set of useful generic types for hierarchy
@ -30,19 +31,19 @@ class spice():
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
# Holds subckts/mods for this module
self.mods = []
self.mods = []
# Holds the pins for this module
self.pins = []
# The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND
# for each instance, this is the set of nets/nodes that map to the pins for this instance
self.pin_type = {}
self.pin_type = {}
# THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the
# Spice format)
self.conns = []
# Keep track of any comments to add the the spice
try:
self.commments
except:
except AttributeError:
self.comments = []
self.sp_read()
@ -56,7 +57,7 @@ class spice():
try:
self.commments
except:
except AttributeError:
self.comments = []
self.comments.append(comment)
@ -65,7 +66,9 @@ class spice():
""" Adds a pin to the pins list. Default type is INOUT signal. """
self.pins.append(name)
self.pin_type[name]=pin_type
debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(name,pin_type))
debug.check(pin_type in self.valid_signal_types,
"Invalid signaltype for {0}: {1}".format(name,
pin_type))
def add_pin_list(self, pin_list, pin_type="INOUT"):
""" Adds a pin_list to the pins list """
@ -73,36 +76,43 @@ class spice():
# or a list that is the same length as the pin list.
if type(pin_type)==str:
for pin in pin_list:
debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(pin,pin_type))
self.add_pin(pin,pin_type)
debug.check(pin_type in self.valid_signal_types,
"Invalid signaltype for {0}: {1}".format(pin,
pin_type))
self.add_pin(pin, pin_type)
elif len(pin_type)==len(pin_list):
for (pin,ptype) in zip(pin_list, pin_type):
debug.check(ptype in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(pin,ptype))
self.add_pin(pin,ptype)
for (pin, ptype) in zip(pin_list, pin_type):
debug.check(ptype in self.valid_signal_types,
"Invalid signaltype for {0}: {1}".format(pin,
ptype))
self.add_pin(pin, ptype)
else:
debug.error("Mismatch in type and pin list lengths.", -1)
def add_pin_types(self, type_list):
"""Add pin types for all the cell's pins.
Typically, should only be used for handmade cells."""
#This only works if self.pins == bitcell.pin_names
"""
Add pin types for all the cell's pins.
Typically, should only be used for handmade cells.
"""
# This only works if self.pins == bitcell.pin_names
if self.pin_names != self.pins:
debug.error("{} spice subcircuit port names do not match pin_names\
\n SPICE names={}\
\n Module names={}\
".format(self.name, self.pin_names, self.pins),1)
self.pin_type = {pin:type for pin,type in zip(self.pin_names, type_list)}
".format(self.name, self.pin_names, self.pins), 1)
self.pin_type = {pin: type for pin, type in zip(self.pin_names, type_list)}
def get_pin_type(self, name):
""" Returns the type of the signal pin. """
pin_type = self.pin_type[name]
debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(name,pin_type))
debug.check(pin_type in self.valid_signal_types,
"Invalid signaltype for {0}: {1}".format(name, pin_type))
return pin_type
def get_pin_dir(self, name):
""" Returns the direction of the pin. (Supply/ground are INOUT). """
if self.pin_type[name] in ["POWER","GROUND"]:
if self.pin_type[name] in ["POWER", "GROUND"]:
return "INOUT"
else:
return self.pin_type[name]
@ -125,11 +135,10 @@ class spice():
output_list.append(pin)
return output_list
def copy_pins(self, other_module, suffix=""):
""" This will copy all of the pins from the other module and add an optional suffix."""
for pin in other_module.pins:
self.add_pin(pin+suffix, other_module.get_pin_type(pin))
self.add_pin(pin + suffix, other_module.get_pin_type(pin))
def get_inouts(self):
""" These use pin types to determine pin lists. These
@ -144,7 +153,6 @@ class spice():
"""Adds a subckt/submodule to the subckt hierarchy"""
self.mods.append(mod)
def connect_inst(self, args, check=True):
"""Connects the pins of the last instance added
It is preferred to use the function with the check to find if
@ -169,21 +177,23 @@ class spice():
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
len(self.insts),
len(self.conns)))
debug.error("Instances: \n"+str(insts_string))
debug.error("Instances: \n" + str(insts_string))
debug.error("-----")
debug.error("Connections: \n"+str(conns_string),1)
debug.error("Connections: \n" + str(conns_string), 1)
def get_conns(self, inst):
"""Returns the connections of a given instance."""
for i in range(len(self.insts)):
if inst is self.insts[i]:
return self.conns[i]
#If not found, returns None
# If not found, returns None
return None
def sp_read(self):
"""Reads the sp file (and parse the pins) from the library
Otherwise, initialize it to null for dynamic generation"""
"""
Reads the sp file (and parse the pins) from the library
Otherwise, initialize it to null for dynamic generation
"""
if self.sp_file and os.path.isfile(self.sp_file):
debug.info(3, "opening {0}".format(self.sp_file))
f = open(self.sp_file)
@ -200,15 +210,34 @@ class spice():
else:
self.spice = []
# We don't define self.lvs and will use self.spice if dynamically created
# or they are the same file
if self.lvs_file!=self.sp_file and os.path.isfile(self.lvs_file):
debug.info(3, "opening {0}".format(self.lvs_file))
f = open(self.lvs_file)
self.lvs = f.readlines()
for i in range(len(self.lvs)):
self.lvs[i] = self.lvs[i].rstrip(" \n")
f.close()
# pins and subckt should be the same
# find the correct subckt line in the file
subckt = re.compile("^.subckt {}".format(self.name), re.IGNORECASE)
subckt_line = list(filter(subckt.search, self.lvs))[0]
# parses line into ports and remove subckt
lvs_pins = subckt_line.split(" ")[2:]
debug.check(lvs_pins == self.pins, "LVS and spice file pin mismatch.")
def check_net_in_spice(self, net_name):
"""Checks if a net name exists in the current. Intended to be check nets in hand-made cells."""
#Remove spaces and lower case then add spaces. Nets are separated by spaces.
net_formatted = ' '+net_name.lstrip().rstrip().lower()+' '
# Remove spaces and lower case then add spaces.
# Nets are separated by spaces.
net_formatted = ' ' + net_name.lstrip().rstrip().lower() + ' '
for line in self.spice:
#Lowercase the line and remove any part of the line that is a comment.
# Lowercase the line and remove any part of the line that is a comment.
line = line.lower().split('*')[0]
#Skip .subckt or .ENDS lines
# Skip .subckt or .ENDS lines
if line.find('.') == 0:
continue
if net_formatted in line:
@ -220,7 +249,7 @@ class spice():
nets_match = True
for net in nets:
nets_match = nets_match and self.check_net_in_spice(net)
return nets_match
return nets_match
def contains(self, mod, modlist):
for x in modlist:
@ -228,54 +257,56 @@ class spice():
return True
return False
def sp_write_file(self, sp, usedMODS):
""" Recursive spice subcircuit write;
Writes the spice subcircuit from the library or the dynamically generated one"""
def sp_write_file(self, sp, usedMODS, lvs_netlist=False):
"""
Recursive spice subcircuit write;
Writes the spice subcircuit from the library or the dynamically generated one
"""
if not self.spice:
# recursively write the modules
for i in self.mods:
if self.contains(i, usedMODS):
continue
usedMODS.append(i)
i.sp_write_file(sp, usedMODS)
i.sp_write_file(sp, usedMODS, lvs_netlist)
if len(self.insts) == 0:
return
if self.pins == []:
return
# write out the first spice line (the subcircuit)
sp.write("\n.SUBCKT {0} {1}\n".format(self.name,
" ".join(self.pins)))
for pin in self.pins:
sp.write("* {1:6}: {0} \n".format(pin,self.pin_type[pin]))
sp.write("* {1:6}: {0} \n".format(pin, self.pin_type[pin]))
for line in self.comments:
sp.write("* {}\n".format(line))
# every instance must have a set of connections, even if it is empty.
if len(self.insts)!=len(self.conns):
if len(self.insts) != len(self.conns):
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
len(self.insts),
len(self.conns)))
debug.error("Instances: \n"+str(self.insts))
debug.error("Instances: \n" + str(self.insts))
debug.error("-----")
debug.error("Connections: \n"+str(self.conns),1)
debug.error("Connections: \n" + str(self.conns), 1)
for i in range(len(self.insts)):
# we don't need to output connections of empty instances.
# these are wires and paths
if self.conns[i] == []:
continue
if hasattr(self.insts[i].mod,"spice_device"):
if lvs_netlist and hasattr(self.insts[i].mod, "lvs_device"):
sp.write(self.insts[i].mod.lvs_device.format(self.insts[i].name,
" ".join(self.conns[i])))
sp.write("\n")
elif hasattr(self.insts[i].mod, "spice_device"):
sp.write(self.insts[i].mod.spice_device.format(self.insts[i].name,
" ".join(self.conns[i])))
sp.write("\n")
else:
sp.write("X{0} {1} {2}\n".format(self.insts[i].name,
" ".join(self.conns[i]),
@ -286,9 +317,12 @@ class spice():
else:
# write the subcircuit itself
# Including the file path makes the unit test fail for other users.
#if os.path.isfile(self.sp_file):
# if os.path.isfile(self.sp_file):
# sp.write("\n* {0}\n".format(self.sp_file))
sp.write("\n".join(self.spice))
if lvs_netlist and hasattr(self, "lvs"):
sp.write("\n".join(self.lvs))
else:
sp.write("\n".join(self.spice))
sp.write("\n")
@ -302,10 +336,21 @@ class spice():
del usedMODS
spfile.close()
def lvs_write(self, spname):
"""Writes the lvs to files"""
debug.info(3, "Writing to {0}".format(spname))
spfile = open(spname, 'w')
spfile.write("*FIRST LINE IS A COMMENT\n")
usedMODS = list()
self.sp_write_file(spfile, usedMODS, True)
del usedMODS
spfile.close()
def analytical_delay(self, corner, slew, load=0.0):
"""Inform users undefined delay module while building new modules"""
# FIXME: Slew is not used in the model right now. Can be added heuristically as linear factor
# FIXME: Slew is not used in the model right now.
# Can be added heuristically as linear factor
relative_cap = logical_effort.convert_farad_to_relative_c(load)
stage_effort = self.get_stage_effort(relative_cap)
@ -316,7 +361,7 @@ class spice():
abs_delay = stage_effort.get_absolute_delay()
corner_delay = self.apply_corners_analytically(abs_delay, corner)
SLEW_APPROXIMATION = 0.1
corner_slew = SLEW_APPROXIMATION*corner_delay
corner_slew = SLEW_APPROXIMATION * corner_delay
return delay_data(corner_delay, corner_slew)
def get_stage_effort(self, cout, inp_is_rise=True):
@ -326,7 +371,7 @@ class spice():
debug.warning("Class {0} name {1}"
.format(self.__class__.__name__,
self.name))
return None
return None
def get_cin(self):
"""Returns input load in Femto-Farads. All values generated using
@ -342,35 +387,35 @@ class spice():
debug.warning("Design Class {0} input capacitance function needs to be defined"
.format(self.__class__.__name__))
debug.warning("Class {0} name {1}"
.format(self.__class__.__name__,
self.name))
return 0
.format(self.__class__.__name__,
self.name))
return 0
def cal_delay_with_rc(self, corner, r, c ,slew, swing = 0.5):
"""
Calculate the delay of a mosfet by
def cal_delay_with_rc(self, corner, r, c, slew, swing=0.5):
"""
Calculate the delay of a mosfet by
modeling it as a resistance driving a capacitance
"""
swing_factor = abs(math.log(1-swing)) # time constant based on swing
delay = swing_factor * r * c #c is in ff and delay is in fs
swing_factor = abs(math.log(1 - swing)) # time constant based on swing
delay = swing_factor * r * c # c is in ff and delay is in fs
delay = self.apply_corners_analytically(delay, corner)
delay = delay * 0.001 #make the unit to ps
delay = delay * 0.001 # make the unit to ps
# Output slew should be linear to input slew which is described
# Output slew should be linear to input slew which is described
# as 0.005* slew.
# The slew will be also influenced by the delay.
# If no input slew(or too small to make impact)
# The mimum slew should be the time to charge RC.
# If no input slew(or too small to make impact)
# The mimum slew should be the time to charge RC.
# Delay * 2 is from 0 to 100% swing. 0.6*2*delay is from 20%-80%.
slew = delay * 0.6 * 2 + 0.005 * slew
return delay_data(delay = delay, slew = slew)
return delay_data(delay=delay, slew=slew)
def apply_corners_analytically(self, delay, corner):
"""Multiply delay by corner factors"""
proc,vdd,temp = corner
#FIXME: type of delay is needed to know which process to use.
proc_mult = max(self.get_process_delay_factor(proc))
proc, vdd, temp = corner
# FIXME: type of delay is needed to know which process to use.
proc_mult = max(self.get_process_delay_factor(proc))
volt_mult = self.get_voltage_delay_factor(vdd)
temp_mult = self.get_temp_delay_factor(temp)
return delay * proc_mult * volt_mult * temp_mult
@ -385,48 +430,51 @@ class spice():
elif mos_proc == 'F':
proc_factors.append(0.9)
elif mos_proc == 'S':
proc_factors.append(1.1)
proc_factors.append(1.1)
return proc_factors
def get_voltage_delay_factor(self, voltage):
"""Returns delay increase due to voltage.
Implemented as linear factor based off nominal voltage.
"""
return tech.spice["nom_supply_voltage"]/voltage
return tech.spice["nom_supply_voltage"] / voltage
def get_temp_delay_factor(self, temp):
"""Returns delay increase due to temperature (in C).
Determines effect on threshold voltage and then linear factor is estimated.
"""
#Some portions of equation condensed (phi_t = k*T/q for T in Kelvin) in mV
#(k/q)/100 = .008625, The division 100 simplifies the conversion from C to K and mV to V
thermal_voltage_nom = 0.008625*tech.spice["nom_temperature"]
thermal_voltage = 0.008625*temp
vthresh = (tech.spice["nom_threshold"]+2*(thermal_voltage-thermal_voltage_nom))
#Calculate effect on Vdd-Vth. The current vdd is not used here. A separate vdd factor is calculated.
return (tech.spice["nom_supply_voltage"] - tech.spice["nom_threshold"])/(tech.spice["nom_supply_voltage"]-vthresh)
# Some portions of equation condensed (phi_t = k*T/q for T in Kelvin) in mV
# (k/q)/100 = .008625, The division 100 simplifies the conversion from C to K and mV to V
thermal_voltage_nom = 0.008625 * tech.spice["nom_temperature"]
thermal_voltage = 0.008625 * temp
vthresh = (tech.spice["nom_threshold"] + 2 * (thermal_voltage - thermal_voltage_nom))
# Calculate effect on Vdd-Vth.
# The current vdd is not used here.
# A separate vdd factor is calculated.
return (tech.spice["nom_supply_voltage"] - tech.spice["nom_threshold"]) / (tech.spice["nom_supply_voltage"] - vthresh)
def return_delay(self, delay, slew):
return delay_data(delay, slew)
def generate_rc_net(self,lump_num, wire_length, wire_width):
def generate_rc_net(self, lump_num, wire_length, wire_width):
return wire_spice_model(lump_num, wire_length, wire_width)
def calc_dynamic_power(self, corner, c, freq, swing=1.0):
"""
"""
Calculate dynamic power using effective capacitance, frequency, and corner (PVT)
"""
proc,vdd,temp = corner
net_vswing = vdd*swing
power_dyn = c*vdd*net_vswing*freq
proc, vdd, temp = corner
net_vswing = vdd * swing
power_dyn = c * vdd * net_vswing * freq
#Apply process and temperature factors. Roughly, process and Vdd affect the delay which affects the power.
#No other estimations are currently used. Increased delay->slower freq.->less power
proc_div = max(self.get_process_delay_factor(proc))
# A pply process and temperature factors.
# Roughly, process and Vdd affect the delay which affects the power.
# No other estimations are currently used. Increased delay->slower freq.->less power
proc_div = max(self.get_process_delay_factor(proc))
temp_div = self.get_temp_delay_factor(temp)
power_dyn = power_dyn/(proc_div*temp_div)
power_dyn = power_dyn / (proc_div * temp_div)
return power_dyn
return power_dyn
def return_power(self, dynamic=0.0, leakage=0.0):
return power_data(dynamic, leakage)

View File

@ -6,18 +6,19 @@
# 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
Add a wire of minimium metal width between a set of points.
Add a wire of minimium metal width between a set of points.
The points should be rectilinear to control the bend points. If
not, it will always go down first.
The points are the center of the wire.
The layer stack is the vertical, contact/via, and horizontal layers, respectively.
The layer stack is the vertical, contact/via, and horizontal layers, respectively.
"""
def __init__(self, obj, layer_stack, position_list):
self.obj = obj
@ -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,21 +49,49 @@ 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):
""" Add a via and corner square at every corner of the 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,18 +102,25 @@ 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
using the position list of the corners.
using the position list of the corners.
"""
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

@ -7,7 +7,7 @@
#
import contact
import debug
from tech import drc, parameter
from tech import drc, parameter, layer
from vector import vector
from ptx import ptx
from globals import OPTS
@ -975,42 +975,44 @@ class pbitcell(bitcell_base.bitcell_base):
"""
Connects wells between ptx modules and places well contacts
"""
# extend pwell to encompass entire nmos region of the cell up to the
# height of the tallest nmos transistor
max_nmos_well_height = max(self.inverter_nmos.well_height,
self.readwrite_nmos.well_height,
self.write_nmos.well_height,
self.read_nmos.well_height)
well_height = max_nmos_well_height + self.port_ypos \
- self.nwell_enclose_active - self.gnd_position.y
# FIXME fudge factor xpos
well_width = self.width + 2*self.nwell_enclose_active
offset = vector(self.leftmost_xpos - self.nwell_enclose_active, self.botmost_ypos)
self.add_rect(layer="pwell",
offset=offset,
width=well_width,
height=well_height)
if "pwell" in layer:
# extend pwell to encompass entire nmos region of the cell up to the
# height of the tallest nmos transistor
max_nmos_well_height = max(self.inverter_nmos.well_height,
self.readwrite_nmos.well_height,
self.write_nmos.well_height,
self.read_nmos.well_height)
well_height = max_nmos_well_height + self.port_ypos \
- self.nwell_enclose_active - self.gnd_position.y
# FIXME fudge factor xpos
well_width = self.width + 2*self.nwell_enclose_active
offset = vector(self.leftmost_xpos - self.nwell_enclose_active, self.botmost_ypos)
self.add_rect(layer="pwell",
offset=offset,
width=well_width,
height=well_height)
# extend nwell to encompass inverter_pmos
# calculate offset of the left pmos well
inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
- self.nwell_enclose_active
inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \
+ self.inverter_gap - self.nwell_enclose_active
if "nwell" in layer:
inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
- self.nwell_enclose_active
inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \
+ self.inverter_gap - self.nwell_enclose_active
# calculate width of the two combined nwells
# calculate height to encompass nimplant connected to vdd
well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
+ 2 * self.nwell_enclose_active
well_height = self.vdd_position.y - inverter_well_ypos \
+ self.nwell_enclose_active + drc["minwidth_tx"]
# calculate width of the two combined nwells
# calculate height to encompass nimplant connected to vdd
well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
+ 2 * self.nwell_enclose_active
well_height = self.vdd_position.y - inverter_well_ypos \
+ self.nwell_enclose_active + drc["minwidth_tx"]
# FIXME fudge factor xpos
offset = [inverter_well_xpos + 2*self.nwell_enclose_active, inverter_well_ypos]
self.add_rect(layer="nwell",
offset=offset,
width=well_width,
height=well_height)
# FIXME fudge factor xpos
offset = [inverter_well_xpos + 2*self.nwell_enclose_active, inverter_well_ypos]
self.add_rect(layer="nwell",
offset=offset,
width=well_width,
height=well_height)
# add well contacts
# connect pimplants to gnd

View File

@ -59,7 +59,8 @@ class delay(simulation):
""" Create measurement names. The names themselves currently define the type of measurement """
self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"]
self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"]
self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power",
"disabled_read0_power", "disabled_read1_power", "disabled_write0_power", "disabled_write1_power"]
# self.voltage_when_names = ["volt_bl", "volt_br"]
# self.bitline_delay_names = ["delay_bl", "delay_br"]
@ -108,6 +109,11 @@ class delay(simulation):
self.read_lib_meas.append(power_measure("read0_power", "FALL", measure_scale=1e3))
self.read_lib_meas[-1].meta_str = sram_op.READ_ZERO
self.read_lib_meas.append(power_measure("disabled_read1_power", "RISE", measure_scale=1e3))
self.read_lib_meas[-1].meta_str = "disabled_read1"
self.read_lib_meas.append(power_measure("disabled_read0_power", "FALL", measure_scale=1e3))
self.read_lib_meas[-1].meta_str = "disabled_read0"
# This will later add a half-period to the spice time delay. Only for reading 0.
for obj in self.read_lib_meas:
if obj.meta_str is sram_op.READ_ZERO:
@ -156,6 +162,11 @@ class delay(simulation):
self.write_lib_meas.append(power_measure("write0_power", "FALL", measure_scale=1e3))
self.write_lib_meas[-1].meta_str = sram_op.WRITE_ZERO
self.write_lib_meas.append(power_measure("disabled_write1_power", "RISE", measure_scale=1e3))
self.write_lib_meas[-1].meta_str = "disabled_write1"
self.write_lib_meas.append(power_measure("disabled_write0_power", "FALL", measure_scale=1e3))
self.write_lib_meas[-1].meta_str = "disabled_write0"
write_measures = []
write_measures.append(self.write_lib_meas)
write_measures.append(self.create_write_bit_measures())
@ -665,7 +676,7 @@ class delay(simulation):
if not success:
feasible_period = 2 * feasible_period
continue
# Positions of measurements currently hardcoded. First 2 are delays, next 2 are slews
feasible_delays = [results[port][mname] for mname in self.delay_meas_names if "delay" in mname]
feasible_slews = [results[port][mname] for mname in self.delay_meas_names if "slew" in mname]
@ -1199,6 +1210,9 @@ class delay(simulation):
write_port)
self.measure_cycles[write_port][sram_op.WRITE_ZERO] = len(self.cycle_times)-1
self.add_noop_clock_one_port(write_port)
self.measure_cycles[write_port]["disabled_write0"] = len(self.cycle_times)-1
# This also ensures we will have a H->L transition on the next read
self.add_read("R data 1 address {} to set dout caps".format(inverse_address),
inverse_address,
@ -1209,6 +1223,10 @@ class delay(simulation):
read_port)
self.measure_cycles[read_port][sram_op.READ_ZERO] = len(self.cycle_times)-1
self.add_noop_clock_one_port(read_port)
self.measure_cycles[read_port]["disabled_read0"] = len(self.cycle_times) - 1
self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)")
self.add_write("W data 1 address {} to write value".format(self.probe_address),
@ -1218,12 +1236,19 @@ class delay(simulation):
write_port)
self.measure_cycles[write_port][sram_op.WRITE_ONE] = len(self.cycle_times)-1
self.add_noop_clock_one_port(write_port)
self.measure_cycles[write_port]["disabled_write1"] = len(self.cycle_times)-1
self.add_write("W data 0 address {} to clear din caps".format(inverse_address),
inverse_address,
data_zeros,
wmask_ones,
write_port)
self.add_noop_clock_one_port(read_port)
self.measure_cycles[read_port]["disabled_read1"] = len(self.cycle_times) - 1
# This also ensures we will have a L->H transition on the next read
self.add_read("R data 0 address {} to clear dout caps".format(inverse_address),
inverse_address,

View File

@ -45,7 +45,7 @@ class lib:
""" Determine the load/slews if they aren't specified in the config file. """
# These are the parameters to determine the table sizes
#self.load_scales = np.array([0.1, 0.25, 0.5, 1, 2, 4, 8])
self.load_scales = np.array([0.25, 1, 8])
self.load_scales = np.array([0.25, 1, 4])
#self.load_scales = np.array([0.25, 1])
self.load = tech.spice["dff_in_cap"]
self.loads = self.load_scales*self.load
@ -181,17 +181,20 @@ class lib:
self.lib.write(" dont_touch : true;\n")
self.lib.write(" area : {};\n\n".format(self.sram.width * self.sram.height))
#Build string of all control signals.
self.write_pg_pin()
#Build string of all control signals.
control_str = 'csb0' #assume at least 1 port
for i in range(1, self.total_port_num):
control_str += ' & csb{0}'.format(i)
# Leakage is included in dynamic when macro is enabled
self.lib.write(" leakage_power () {\n")
self.lib.write(" when : \"{0}\";\n".format(control_str))
# 'when' condition unnecessary when cs pin does not turn power to devices
# self.lib.write(" when : \"{0}\";\n".format(control_str))
self.lib.write(" value : {};\n".format(self.char_sram_results["leakage_power"]))
self.lib.write(" }\n")
self.lib.write(" cell_leakage_power : {};\n".format(0))
self.lib.write(" cell_leakage_power : {};\n".format(self.char_sram_results["leakage_power"]))
def write_units(self):
@ -240,6 +243,9 @@ class lib:
self.lib.write(" default_max_fanout : 4.0 ;\n")
self.lib.write(" default_connection_class : universal ;\n\n")
self.lib.write(" voltage_map ( VDD, {} );\n".format(tech.spice["nom_supply_voltage"]))
self.lib.write(" voltage_map ( GND, 0 );\n\n")
def create_list(self,values):
""" Helper function to create quoted, line wrapped list """
list_values = ", ".join(str(v) for v in values)
@ -516,42 +522,69 @@ class lib:
if port in self.write_ports:
if port in self.read_ports:
web_name = " & !web{0}".format(port)
avg_write_power = np.mean(self.char_port_results[port]["write1_power"] + self.char_port_results[port]["write0_power"])
write1_power = np.mean(self.char_port_results[port]["write1_power"])
write0_power = np.mean(self.char_port_results[port]["write0_power"])
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"!csb{0} & clk{0}{1}\"; \n".format(port, web_name))
self.lib.write(" when : \"!csb{0}{1}\"; \n".format(port, web_name))
self.lib.write(" rise_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(avg_write_power/2.0))
self.lib.write(" values(\"{0:.6e}\");\n".format(write1_power))
self.lib.write(" }\n")
self.lib.write(" fall_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(avg_write_power/2.0))
self.lib.write(" values(\"{0:.6e}\");\n".format(write0_power))
self.lib.write(" }\n")
self.lib.write(" }\n")
# Disabled power.
disabled_write1_power = np.mean(self.char_port_results[port]["disabled_write1_power"])
disabled_write0_power = np.mean(self.char_port_results[port]["disabled_write0_power"])
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"csb{0}{1}\"; \n".format(port, web_name))
self.lib.write(" rise_power(scalar){\n")
self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_write1_power))
self.lib.write(" }\n")
self.lib.write(" fall_power(scalar){\n")
self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_write0_power))
self.lib.write(" }\n")
self.lib.write(" }\n")
if port in self.read_ports:
if port in self.write_ports:
web_name = " & web{0}".format(port)
avg_read_power = np.mean(self.char_port_results[port]["read1_power"] + self.char_port_results[port]["read0_power"])
read1_power = np.mean(self.char_port_results[port]["read1_power"])
read0_power = np.mean(self.char_port_results[port]["read0_power"])
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"!csb{0} & !clk{0}{1}\"; \n".format(port, web_name))
self.lib.write(" when : \"!csb{0}{1}\"; \n".format(port, web_name))
self.lib.write(" rise_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(avg_read_power/2.0))
self.lib.write(" values(\"{0:.6e}\");\n".format(read1_power))
self.lib.write(" }\n")
self.lib.write(" fall_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(avg_read_power/2.0))
self.lib.write(" values(\"{0:.6e}\");\n".format(read0_power))
self.lib.write(" }\n")
self.lib.write(" }\n")
# Have 0 internal power when disabled, this will be represented as leakage power.
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"csb{0}\"; \n".format(port))
self.lib.write(" rise_power(scalar){\n")
self.lib.write(" values(\"0\");\n")
self.lib.write(" }\n")
self.lib.write(" fall_power(scalar){\n")
self.lib.write(" values(\"0\");\n")
self.lib.write(" }\n")
self.lib.write(" }\n")
# Disabled power.
disabled_read1_power = np.mean(self.char_port_results[port]["disabled_read1_power"])
disabled_read0_power = np.mean(self.char_port_results[port]["disabled_read0_power"])
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"csb{0}{1}\"; \n".format(port, web_name))
self.lib.write(" rise_power(scalar){\n")
self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read1_power))
self.lib.write(" }\n")
self.lib.write(" fall_power(scalar){\n")
self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read0_power))
self.lib.write(" }\n")
self.lib.write(" }\n")
def write_pg_pin(self):
self.lib.write(" pg_pin(vdd) {\n")
self.lib.write(" voltage_name : VDD;\n")
self.lib.write(" pg_type : primary_power;\n")
self.lib.write(" }\n\n")
self.lib.write(" pg_pin(gnd) {\n")
self.lib.write(" voltage_name : GND;\n")
self.lib.write(" pg_type : primary_ground;\n")
self.lib.write(" }\n\n")
def compute_delay(self):
"""Compute SRAM delays for current corner"""
self.d = delay(self.sram, self.sp_file, self.corner)
@ -624,17 +657,12 @@ class lib:
))
# information of checks
from hierarchy_design import total_drc_errors
from hierarchy_design import total_lvs_errors
DRC = 'skipped'
LVS = 'skipped'
if OPTS.check_lvsdrc:
DRC = str(total_drc_errors)
LVS = str(total_lvs_errors)
datasheet.write("{0},{1},".format(DRC, LVS))
(drc_errors, lvs_errors) = self.sram.DRC_LVS(final_verification=True)
datasheet.write("{0},{1},".format(drc_errors, lvs_errors))
# write area
datasheet.write(str(self.sram.width * self.sram.height)+',')
datasheet.write(str(self.sram.width * self.sram.height) + ',')
# write timing information for all ports
for port in self.all_ports:
#din timings

View File

@ -284,6 +284,23 @@ class simulation():
except:
self.add_wmask("0"*self.num_wmasks, port)
def add_noop_clock_one_port(self, port):
""" Add the control values for a noop to a single port. Increments the period. """
debug.info(2, 'Clock only on port {}'.format(port))
self.fn_cycle_comments.append('Clock only on port {}'.format(port))
self.append_cycle_comment(port, 'Clock only on port {}'.format(port))
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.add_noop_one_port(port)
#Add noops to all other ports.
for unselected_port in self.all_ports:
if unselected_port != port:
self.add_noop_one_port(unselected_port)
def append_cycle_comment(self, port, comment):
"""Add comment to list to be printed in stimulus file"""
#Clean up time before appending. Make spacing dynamic as well.

View File

@ -34,6 +34,10 @@ class stimuli():
self.sf = stim_file
(self.process, self.voltage, self.temperature) = corner
try:
self.device_libraries = tech.spice["fet_libraries"][self.process]
except:
debug.info(2, "Not using spice library")
self.device_models = tech.spice["fet_models"][self.process]
self.sram_name = "Xsram"
@ -247,8 +251,17 @@ class stimuli():
def write_include(self, circuit):
"""Writes include statements, inputs are lists of model files"""
includes = self.device_models + [circuit]
self.sf.write("* {} process corner\n".format(self.process))
if OPTS.tech_name == "s8":
libraries = self.device_libraries
for item in list(libraries):
if os.path.isfile(item[0]):
self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1]))
else:
debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0]))
for item in list(includes):
if os.path.isfile(item):
self.sf.write(".include \"{0}\"\n".format(item))

View File

@ -9,9 +9,10 @@ import debug
from drc_value import *
from drc_lut import *
class design_rules(dict):
"""
This is a class that implements the design rules structures.
"""
This is a class that implements the design rules structures.
"""
def __init__(self, name):
self.tech_name = name

View File

@ -7,9 +7,10 @@
#
import debug
class drc_lut():
"""
Implement a lookup table of rules.
"""
Implement a lookup table of rules.
Each element is a tuple with the last value being the rule.
It searches through backwards until all of the key values are
met and returns the rule value.
@ -31,7 +32,6 @@ class drc_lut():
for table_key in sorted(self.table.keys(), reverse=True):
if self.match(key, table_key):
return self.table[table_key]
def match(self, key1, key2):
"""
@ -39,8 +39,8 @@ class drc_lut():
(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,8 +6,9 @@
# All rights reserved.
#
class drc_value():
"""
"""
A single DRC value.
"""
def __init__(self, value):

View File

@ -19,7 +19,7 @@ import re
import copy
import importlib
VERSION = "1.1.4"
VERSION = "1.1.5"
NAME = "OpenRAM v{}".format(VERSION)
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
@ -589,4 +589,4 @@ def report_status():
if OPTS.trim_netlist:
debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).")
if OPTS.nominal_corner_only:
debug.print_raw("Only characterizing nominal corner.")
debug.print_raw("Only characterizing nominal corner.")

View File

@ -45,11 +45,9 @@ class bank_select(design.design):
self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width
self.width = max([x.rx() for x in self.inv_inst])
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
# Number of control lines in the bus
@ -65,19 +63,18 @@ class bank_select(design.design):
if (self.port == "rw") or (self.port == "r"):
self.input_control_signals.append("s_en")
# These will be outputs of the gaters if this is multibank
self.control_signals = ["gated_"+str for str in self.input_control_signals]
self.control_signals = ["gated_" + str for str in self.input_control_signals]
self.add_pin_list(self.input_control_signals, "INPUT")
self.add_pin("bank_sel")
self.add_pin_list(self.control_signals, "OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
""" Create modules for later instantiation """
self.bitcell = factory.create(module_type="bitcell")
height = self.bitcell.height + drc("poly_to_active")
self.dff = factory.create(module_type="dff")
height = self.dff.height + drc("poly_to_active")
# 1x Inverter
self.inv_sel = factory.create(module_type="pinv", height=height)
@ -98,17 +95,15 @@ class bank_select(design.design):
def calculate_module_offsets(self):
self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_bank_sel_inv = 0
self.xoffset_nand = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_nor = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_bank_sel_inv = 0
self.xoffset_inputs = 0
self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height
def create_instances(self):
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
mod=self.inv_sel)
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
@ -125,36 +120,36 @@ class bank_select(design.design):
# (writes occur on clk low)
if input_name in ("clk_buf"):
self.logic_inst.append(self.add_inst(name=name_nor,
mod=self.nor2))
self.logic_inst.append(self.add_inst(name=name_nor,
mod=self.nor2))
self.connect_inst([input_name,
"bank_sel_bar",
gated_name+"_temp_bar",
gated_name + "_temp_bar",
"vdd",
"gnd"])
# They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv,
self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x_nor))
self.connect_inst([gated_name+"_temp_bar",
self.connect_inst([gated_name + "_temp_bar",
gated_name,
"vdd",
"gnd"])
# the rest are AND (nand2+inv) gates
else:
self.logic_inst.append(self.add_inst(name=name_nand,
self.logic_inst.append(self.add_inst(name=name_nand,
mod=self.nand2))
self.connect_inst([input_name,
"bank_sel",
gated_name+"_temp_bar",
gated_name + "_temp_bar",
"vdd",
"gnd"])
# They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv,
self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x))
self.connect_inst([gated_name+"_temp_bar",
self.connect_inst([gated_name + "_temp_bar",
gated_name,
"vdd",
"gnd"])
@ -177,9 +172,9 @@ class bank_select(design.design):
if i == 0:
y_offset = 0
else:
y_offset = self.inv4x_nor.height + self.inv4x.height * (i-1)
y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1)
if i%2:
if i % 2:
y_offset += self.inv4x.height
mirror = "MX"
else:
@ -200,7 +195,6 @@ class bank_select(design.design):
# They all get inverters on the output
inv_inst.place(offset=[logic_inst.rx(), y_offset],
mirror=mirror)
def route_instances(self):
@ -222,57 +216,56 @@ class bank_select(design.design):
end=bank_sel_pin_end)
self.add_via_center(layers=self.m2_stack,
offset=bank_sel_pin_end,
directions=("H","H"))
directions=("H", "H"))
# bank_sel_bar is vertical wire
bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z")
xoffset_bank_sel_bar = bank_sel_bar_pin.rx()
self.add_label_pin(text="bank_sel_bar",
layer="m2",
offset=vector(xoffset_bank_sel_bar, 0),
layer="m2",
offset=vector(xoffset_bank_sel_bar, 0),
height=self.inv4x.height)
self.add_via_center(layers=self.m1_stack,
offset=bank_sel_bar_pin.rc())
for i in range(self.num_control_lines):
logic_inst = self.logic_inst[i]
inv_inst = self.inv_inst[i]
input_name = self.input_control_signals[i]
gated_name = self.control_signals[i]
gated_name = self.control_signals[i]
if input_name in ("clk_buf"):
xoffset_bank_signal = xoffset_bank_sel_bar
else:
xoffset_bank_signal = xoffset_bank_sel
# Connect the logic output to inverter input
pre = logic_inst.get_pin("Z").lc()
out_position = logic_inst.get_pin("Z").rc() + vector(0.5*self.m1_width,0)
in_position = inv_inst.get_pin("A").lc() + vector(0.5*self.m1_width,0)
post = inv_inst.get_pin("A").rc()
self.add_path("m1", [pre, out_position, in_position, post])
out_pin = logic_inst.get_pin("Z")
out_pos = out_pin.rc()
in_pin = inv_inst.get_pin("A")
in_pos = in_pin.lc()
mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
# Connect the logic B input to bank_sel/bank_sel_bar
logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1_via.height,0)
# Connect the logic B input to bank_sel / bank_sel_bar
logic_pos = logic_inst.get_pin("B").lc() - vector(0.5 * contact.m1_via.height, 0)
input_pos = vector(xoffset_bank_signal, logic_pos.y)
self.add_path("m2",[logic_pos, input_pos])
self.add_path("m2", [logic_pos, input_pos])
self.add_via_center(layers=self.m1_stack,
offset=logic_pos,
directions=("H","H"))
directions=("H", "H"))
# Connect the logic A input to the input pin
logic_pos = logic_inst.get_pin("A").lc()
input_pos = vector(0,logic_pos.y)
input_pos = vector(0, logic_pos.y)
self.add_via_center(layers=self.m1_stack,
offset=logic_pos,
directions=("H","H"))
directions=("H", "H"))
self.add_via_center(layers=self.m2_stack,
offset=logic_pos,
directions=("H","H"))
directions=("H", "H"))
self.add_layout_pin_segment_center(text=input_name,
layer="m3",
start=input_pos,
@ -286,7 +279,6 @@ class bank_select(design.design):
width=inv_inst.rx() - out_pin.lx(),
height=out_pin.height())
# Find the x offsets for where the vias/pins should be placed
a_xoffset = self.logic_inst[0].lx()
b_xoffset = self.inv_inst[0].lx()
@ -294,7 +286,7 @@ class bank_select(design.design):
# Route both supplies
for n in ["vdd", "gnd"]:
supply_pin = self.inv_inst[num].get_pin(n)
supply_offset = supply_pin.ll().scale(0,1)
supply_offset = supply_pin.ll().scale(0, 1)
self.add_rect(layer="m1",
offset=supply_offset,
width=self.width)
@ -304,10 +296,10 @@ class bank_select(design.design):
pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=self.m1_stack,
offset=pin_pos,
directions=("H","H"))
directions=("H", "H"))
self.add_via_center(layers=self.m2_stack,
offset=pin_pos,
directions=("H","H"))
directions=("H", "H"))
self.add_layout_pin_rect_center(text=n,
layer="m3",
offset=pin_pos)

View File

@ -28,7 +28,6 @@ class bitcell_array(bitcell_base_array):
# the replica bitcell in the control logic
# self.offset_all_coordinates()
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
@ -56,22 +55,21 @@ class bitcell_array(bitcell_base_array):
for col in range(self.column_size):
for row in range(self.row_size):
name = "bit_r{0}_c{1}".format(row, col)
self.cell_inst[row,col]=self.add_inst(name=name,
mod=self.cell)
self.cell_inst[row, col]=self.add_inst(name=name,
mod=self.cell)
self.connect_inst(self.get_bitcell_pins(col, row))
def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW."""
from tech import drc, parameter
# Dynamic Power from Bitline
bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap()
cell_load = 2 * bl_wire.return_input_cap()
bl_swing = OPTS.rbl_delay_percentage
freq = spice["default_event_frequency"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
# Calculate the bitcell power which currently only includes leakage
# Calculate the bitcell power which currently only includes leakage
cell_power = self.cell.analytical_power(corner, load)
# Leakage power grows with entire array and bitlines.
@ -85,7 +83,7 @@ class bitcell_array(bitcell_base_array):
else:
width = self.width
wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1"))
wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
return wl_wire
def gen_bl_wire(self):
@ -94,26 +92,26 @@ class bitcell_array(bitcell_base_array):
else:
height = self.height
bl_pos = 0
bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_m1"))
bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1"))
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
return bl_wire
def get_wordline_cin(self):
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
#A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
# A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
bitcell_wl_cin = self.cell.get_wl_cin()
total_cin = bitcell_wl_cin * self.column_size
return total_cin
def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target"""
#Function is not robust with column mux configurations
# Function is not robust with column mux configurations
for row in range(self.row_size):
for col in range(self.column_size):
if row == targ_row and col == targ_col:
continue
self.graph_inst_exclude.add(self.cell_inst[row,col])
self.graph_inst_exclude.add(self.cell_inst[row, col])
def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell."""
return inst_name+'.x'+self.cell_inst[row,col].name, self.cell_inst[row,col]
"""Gets the spice name of the target bitcell."""
return inst_name + '.x' + self.cell_inst[row, col].name, self.cell_inst[row, col]

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.
@ -29,7 +27,7 @@ class control_logic(design.design):
debug.info(1, "Creating {}".format(name))
self.add_comment("num_rows: {0}".format(num_rows))
self.add_comment("words_per_row: {0}".format(words_per_row))
self.add_comment("word_size {0}".format(word_size))
self.add_comment("word_size {0}".format(word_size))
self.sram=sram
self.num_rows = num_rows
@ -37,14 +35,15 @@ 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.
self.wl_timing_tolerance = 1
# 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 """
@ -92,7 +90,7 @@ class control_logic(design.design):
self.add_mod(self.ctrl_dff_array)
self.and2 = factory.create(module_type="pand2",
size=4,
size=12,
height=dff_height)
self.add_mod(self.and2)
@ -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)
# 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
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)
@ -137,7 +134,7 @@ class control_logic(design.design):
height=dff_height)
self.add_mod(self.sen_and3)
# used to generate inverted signals with low fanout
# used to generate inverted signals with low fanout
self.inv = factory.create(module_type="pinv",
size=1,
height=dff_height)
@ -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)
@ -179,14 +175,14 @@ class control_logic(design.design):
# delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic,
# bitcell_loads=bitcell_loads)
# #Resize if necessary, condition depends on resizing method
# if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match():
# if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match():
# #This resizes to match fall and rise delays, can make the delay chain weird sizes.
# stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic)
# self.replica_bitline = factory.create(module_type="replica_bitline",
# delay_fanout_list=stage_list,
# bitcell_loads=bitcell_loads)
# #This resizes based on total delay.
# #This resizes based on total delay.
# # delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic)
# # self.replica_bitline = factory.create(module_type="replica_bitline",
# # delay_fanout_list=[delay_fanout]*delay_stages,
@ -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,91 +237,107 @@ 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
return True
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)
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:
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
break
# There should also be a condition to make sure the fanout does not get too large.
# There should also be a condition to make sure the fanout does not get too large.
# Otherwise, increase the fanout of delay with the most stages, calculate new stages
elif stages_fall>stages_rise:
fanout_fall+=1
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()
@ -388,9 +399,7 @@ class control_logic(design.design):
if (self.port_type == "rw") or (self.port_type == "r"):
self.create_sen_row()
self.create_delay()
self.create_pen_row()
self.create_pen_row()
def place_instances(self):
""" Place all the instances """
@ -406,13 +415,13 @@ class control_logic(design.design):
row = 0
# Add the logic on the right of the bus
self.place_clk_buf_row(row)
self.place_clk_buf_row(row)
row += 1
self.place_gated_clk_bar_row(row)
self.place_gated_clk_bar_row(row)
row += 1
self.place_gated_clk_buf_row(row)
self.place_gated_clk_buf_row(row)
row += 1
self.place_wlen_row(row)
self.place_wlen_row(row)
row += 1
if (self.port_type == "rw") or (self.port_type == "w"):
self.place_wen_row(row)
@ -421,10 +430,10 @@ class control_logic(design.design):
row += 1
self.place_pen_row(row)
row += 1
if (self.port_type == "rw") or (self.port_type == "w"):
if (self.port_type == "rw") or (self.port_type == "w"):
self.place_rbl_delay_row(row)
row += 1
if (self.port_type == "rw") or (self.port_type == "r"):
if (self.port_type == "rw") or (self.port_type == "r"):
self.place_sen_row(row)
row += 1
self.place_delay(row)
@ -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,40 +537,45 @@ 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)
x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row)
x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row)
self.row_end_inst.append(self.gated_clk_bar_inst)
def route_gated_clk_bar(self):
clkbuf_map = zip(["A"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
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)
@ -584,11 +594,13 @@ class control_logic(design.design):
def route_gated_clk_buf(self):
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"])
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())
@ -602,7 +614,7 @@ class control_logic(design.design):
def place_wlen_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.wl_en_inst, x_offset, row)
x_offset = self.place_util(self.wl_en_inst, x_offset, row)
self.row_end_inst.append(self.wl_en_inst)
@ -623,11 +635,11 @@ 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)
x_offset = self.place_util(self.p_en_bar_driver_inst, x_offset, row)
x_offset = self.place_util(self.p_en_bar_nand_inst, x_offset, row)
x_offset = self.place_util(self.p_en_bar_driver_inst, x_offset, row)
self.row_end_inst.append(self.p_en_bar_driver_inst)
@ -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,14 +668,12 @@ 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):
@ -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,22 +757,22 @@ 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":
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
elif self.port_type == "r":
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
else:
dff_out_map = zip(["dout_bar_0"], ["cs"])
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("m3", "via2", "m2"))
# 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,34 +780,31 @@ 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 """
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
for inst in self.row_end_inst:
pins = inst.get_pins("vdd")
for pin in pins:
@ -818,16 +822,14 @@ 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.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.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.
""" This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
@ -851,74 +853,79 @@ class control_logic(design.design):
offset=pin.ll(),
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))
return clk_to_wl_rise,clk_to_wl_fall
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
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))
return 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))
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
return stage_effort_list
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,45 +934,45 @@ 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
last_stage_rise = False
# 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
def get_clk_buf_cin(self):
"""
Get the loads that are connected to the buffered clock.
Get the loads that are connected to the buffered clock.
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,12 +7,11 @@
#
import debug
import design
from tech import drc
from contact import contact
from vector import vector
from globals import OPTS
from sram_factory import factory
class delay_chain(design.design):
"""
Generate a delay chain with the given number of stages and fanout.
@ -28,7 +27,7 @@ class delay_chain(design.design):
# Two fanouts are needed so that we can route the vdd/gnd connections
for f in fanout_list:
debug.check(f>=2,"Must have >=2 fanouts for each stage.")
debug.check(f>=2, "Must have >=2 fanouts for each stage.")
# number of inverters including any fanout loads.
self.fanout_list = fanout_list
@ -36,7 +35,6 @@ class delay_chain(design.design):
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_modules()
@ -45,12 +43,13 @@ class delay_chain(design.design):
def create_layout(self):
# Each stage is a a row
self.height = len(self.fanout_list)*self.inv.height
self.height = len(self.fanout_list) * self.inv.height
# The width is determined by the largest fanout plus the driver
self.width = (max(self.fanout_list)+1) * self.inv.width
self.width = (max(self.fanout_list) + 1) * self.inv.width
self.place_inverters()
self.route_inverters()
self.route_supplies()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
@ -69,9 +68,8 @@ class delay_chain(design.design):
def create_inverters(self):
""" Create the inverters and connect them based on the stage list """
self.driver_inst_list = []
self.rightest_load_inst = {}
self.load_inst_map = {}
for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
for stage_num, fanout_size in zip(range(len(self.fanout_list)), self.fanout_list):
# Add the inverter
cur_driver=self.add_inst(name="dinv{}".format(stage_num),
mod=self.inv)
@ -79,40 +77,37 @@ class delay_chain(design.design):
self.driver_inst_list.append(cur_driver)
# Hook up the driver
if stage_num+1==len(self.fanout_list):
if stage_num + 1 == len(self.fanout_list):
stageout_name = "out"
else:
stageout_name = "dout_{}".format(stage_num+1)
stageout_name = "dout_{}".format(stage_num + 1)
if stage_num == 0:
stagein_name = "in"
else:
stagein_name = "dout_{}".format(stage_num)
stagein_name = "dout_{}".format(stage_num)
self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"])
# Now add the dummy loads to the right
self.load_inst_map[cur_driver]=[]
for i in range(fanout_size):
cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num,i),
cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num, i),
mod=self.inv)
# Fanout stage is always driven by driver and output is disconnected
disconnect_name = "n_{0}_{1}".format(stage_num,i)
disconnect_name = "n_{0}_{1}".format(stage_num, i)
self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"])
# Keep track of all the loads to connect their inputs as a load
self.load_inst_map[cur_driver].append(cur_load)
else:
# Keep track of the last one so we can add the the wire later
self.rightest_load_inst[cur_driver]=cur_load
def place_inverters(self):
""" Place the inverters and connect them based on the stage list """
for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
for stage_num, fanout_size in zip(range(len(self.fanout_list)), self.fanout_list):
if stage_num % 2:
inv_mirror = "MX"
inv_offset = vector(0, (stage_num+1)* self.inv.height)
inv_offset = vector(0, (stage_num + 1) * self.inv.height)
else:
inv_mirror = "R0"
inv_offset = vector(0, stage_num * self.inv.height)
inv_offset = vector(0, stage_num * self.inv.height)
# Add the inverter
cur_driver=self.driver_inst_list[stage_num]
@ -122,10 +117,9 @@ class delay_chain(design.design):
# Now add the dummy loads to the right
load_list = self.load_inst_map[cur_driver]
for i in range(fanout_size):
inv_offset += vector(self.inv.width,0)
inv_offset += vector(self.inv.width, 0)
load_list[i].place(offset=inv_offset,
mirror=inv_mirror)
def add_route(self, pin1, pin2):
""" This guarantees that we route from the top to bottom row correctly. """
@ -134,9 +128,9 @@ class delay_chain(design.design):
if pin1_pos.y == pin2_pos.y:
self.add_path("m2", [pin1_pos, pin2_pos])
else:
mid_point = vector(pin2_pos.x, 0.5*(pin1_pos.y+pin2_pos.y))
mid_point = vector(pin2_pos.x, 0.5 * (pin1_pos.y + pin2_pos.y))
# Written this way to guarantee it goes right first if we are switching rows
self.add_path("m2", [pin1_pos, vector(pin1_pos.x,mid_point.y), mid_point, vector(mid_point.x,pin2_pos.y), pin2_pos])
self.add_path("m2", [pin1_pos, vector(pin1_pos.x, mid_point.y), mid_point, vector(mid_point.x, pin2_pos.y), pin2_pos])
def route_inverters(self):
""" Add metal routing for each of the fanout stages """
@ -145,7 +139,7 @@ class delay_chain(design.design):
inv = self.driver_inst_list[i]
for load in self.load_inst_map[inv]:
# Drop a via on each A pin
a_pin = load.get_pin("A")
a_pin = load.get_pin("A")
self.add_via_center(layers=self.m1_stack,
offset=a_pin.center())
self.add_via_center(layers=self.m2_stack,
@ -154,54 +148,42 @@ class delay_chain(design.design):
# Route an M3 horizontal wire to the furthest
z_pin = inv.get_pin("Z")
a_pin = inv.get_pin("A")
a_max = self.rightest_load_inst[inv].get_pin("A")
a_max = self.load_inst_map[inv][-1].get_pin("A")
self.add_via_center(layers=self.m1_stack,
offset=a_pin.center())
self.add_via_center(layers=self.m1_stack,
offset=z_pin.center())
self.add_via_center(layers=self.m2_stack,
offset=z_pin.center())
self.add_path("m3",[z_pin.center(), a_max.center()])
self.add_path("m3", [z_pin.center(), a_max.center()])
# Route Z to the A of the next stage
if i+1 < len(self.driver_inst_list):
if i + 1 < len(self.driver_inst_list):
z_pin = inv.get_pin("Z")
next_inv = self.driver_inst_list[i+1]
next_inv = self.driver_inst_list[i + 1]
next_a_pin = next_inv.get_pin("A")
y_mid = (z_pin.cy() + next_a_pin.cy())/2
y_mid = (z_pin.cy() + next_a_pin.cy()) / 2
mid1_point = vector(z_pin.cx(), y_mid)
mid2_point = vector(next_a_pin.cx(), y_mid)
self.add_path("m2",[z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
def add_layout_pins(self):
""" Add vdd and gnd rails and the input/output. Connect the gnd rails internally on
the top end with no input/output to obstruct. """
self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
def route_supplies(self):
# Add power and ground to all the cells except:
# the fanout driver, the right-most load
# The routing to connect the loads is over the first and last cells
# We have an even number of drivers and must only do every other
# supply rail
for i in range(0,len(self.driver_inst_list),2):
inv = self.driver_inst_list[i]
for load in self.load_inst_map[inv]:
if load==self.rightest_load_inst[inv]:
continue
for pin_name in ["vdd", "gnd"]:
pin = load.get_pin(pin_name)
self.add_power_pin(pin_name, pin.rc())
else:
# We have an even number of rows, so need to get the last gnd rail
inv = self.driver_inst_list[-1]
for load in self.load_inst_map[inv]:
if load==self.rightest_load_inst[inv]:
continue
pin_name = "gnd"
pin = load.get_pin(pin_name)
self.add_power_pin(pin_name, pin.rc())
for inst in self.driver_inst_list:
load_list = self.load_inst_map[inst]
for pin_name in ["vdd", "gnd"]:
pin = load_list[0].get_pin(pin_name)
self.add_power_pin(pin_name, pin.rc() - vector(self.m1_pitch, 0))
pin = load_list[-1].get_pin(pin_name)
self.add_power_pin(pin_name, pin.rc() - vector(0.5 * self.m1_pitch, 0))
def add_layout_pins(self):
# input is A pin of first inverter
a_pin = self.driver_inst_list[0].get_pin("A")
@ -209,36 +191,36 @@ class delay_chain(design.design):
offset=a_pin.center())
self.add_layout_pin(text="in",
layer="m2",
offset=a_pin.ll().scale(1,0),
offset=a_pin.ll().scale(1, 0),
height=a_pin.cy())
# output is A pin of last load inverter
last_driver_inst = self.driver_inst_list[-1]
a_pin = self.rightest_load_inst[last_driver_inst].get_pin("A")
a_pin = self.load_inst_map[last_driver_inst][-1].get_pin("A")
self.add_via_center(layers=self.m1_stack,
offset=a_pin.center())
mid_point = vector(a_pin.cx()+3*self.m2_width,a_pin.cy())
self.add_path("m2",[a_pin.center(), mid_point, mid_point.scale(1,0)])
mid_point = vector(a_pin.cx() + 3 * self.m2_width, a_pin.cy())
self.add_path("m2", [a_pin.center(), mid_point, mid_point.scale(1, 0)])
self.add_layout_pin_segment_center(text="out",
layer="m2",
start=mid_point,
end=mid_point.scale(1,0))
end=mid_point.scale(1, 0))
def get_cin(self):
"""Get the enable input ralative capacitance"""
#Only 1 input to the delay chain which is connected to an inverter.
# Only 1 input to the delay chain which is connected to an inverter.
dc_cin = self.inv.get_cin()
return dc_cin
return dc_cin
def determine_delayed_en_stage_efforts(self, ext_delayed_en_cout, inp_is_rise=True):
"""Get the stage efforts from the en to s_en. Does not compute the delay for the bitline load."""
stage_effort_list = []
#Add a stage to the list for every stage in delay chain. Stages only differ in fanout except the last which has an external cout.
# Add a stage to the list for every stage in delay chain.
# Stages only differ in fanout except the last which has an external cout.
last_stage_is_rise = inp_is_rise
for stage_fanout in self.fanout_list:
stage_cout = self.inv.get_cin()*(stage_fanout+1)
if len(stage_effort_list) == len(self.fanout_list)-1: #last stage
stage_cout = self.inv.get_cin() * (stage_fanout + 1)
if len(stage_effort_list) == len(self.fanout_list) - 1:
stage_cout+=ext_delayed_en_cout
stage = self.inv.get_stage_effort(stage_cout, last_stage_is_rise)
stage_effort_list.append(stage)

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
@ -35,7 +35,7 @@ class dff_buf(design.design):
# This causes a DRC in the pinv which assumes min width rails. This ensures the output
# contact does not violate spacing to the rail in the NMOS.
debug.check(inv1_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
debug.check(inv2_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
debug.check(inv2_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
self.inv1_size=inv1_size
self.inv2_size=inv2_size
@ -52,14 +52,13 @@ class dff_buf(design.design):
def create_layout(self):
self.place_instances()
self.width = self.inv2_inst.rx()
self.height = self.dff.height
self.route_wires()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
self.dff = factory.create(module_type="dff")
self.add_mod(self.dff)
@ -73,8 +72,6 @@ class dff_buf(design.design):
size=self.inv2_size,
height=self.dff.height)
self.add_mod(self.inv2)
def add_pins(self):
self.add_pin("D", "INPUT")
@ -91,35 +88,47 @@ class dff_buf(design.design):
def create_instances(self):
self.dff_inst=self.add_inst(name="dff_buf_dff",
mod=self.dff)
self.connect_inst(props.dff_buff.buf_ports)
#self.connect_inst(["D", "qint", "clk", "vdd", "gnd"])
self.connect_inst(["D", "qint", "clk", "vdd", "gnd"])
self.inv1_inst=self.add_inst(name="dff_buf_inv1",
mod=self.inv1)
self.connect_inst(["qint", "Qb", "vdd", "gnd"])
self.connect_inst(["qint", "Qb", "vdd", "gnd"])
self.inv2_inst=self.add_inst(name="dff_buf_inv2",
mod=self.inv2)
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
def place_instances(self):
# Add the DFF
self.dff_inst.place(vector(0,0))
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))
# The INV needs well spacing because the DFF is likely from a library
# with different well construction rules
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
q_pin = self.dff_inst.get_pin("Q")
a1_pin = self.inv1_inst.get_pin("A")
mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx())
mid_x_offset = 0.5 * (a1_pin.cx() + q_pin.cx())
mid1 = vector(mid_x_offset, q_pin.cy())
mid2 = vector(mid_x_offset, a1_pin.cy())
self.add_path("m3", [q_pin.center(), mid1, mid2, a1_pin.center()])
@ -133,7 +142,7 @@ class dff_buf(design.design):
# Route inv1 z to inv2 a
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_x_offset = 0.5*(z1_pin.cx() + a2_pin.cx())
mid_x_offset = 0.5 * (z1_pin.cx() + a2_pin.cx())
self.mid_qb_pos = vector(mid_x_offset, z1_pin.cy())
mid2 = vector(mid_x_offset, a2_pin.cy())
self.add_path("m1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()])
@ -171,8 +180,8 @@ class dff_buf(design.design):
height=din_pin.height())
dout_pin = self.inv2_inst.get_pin("Z")
mid_pos = dout_pin.center() + vector(self.m1_pitch,0)
q_pos = mid_pos - vector(0,self.m2_pitch)
mid_pos = dout_pin.center() + vector(self.m1_pitch, 0)
q_pos = mid_pos - vector(0, self.m2_pitch)
self.add_layout_pin_rect_center(text="Q",
layer="m2",
offset=q_pos)
@ -180,7 +189,7 @@ class dff_buf(design.design):
self.add_via_center(layers=self.m1_stack,
offset=q_pos)
qb_pos = self.mid_qb_pos + vector(0,self.m2_pitch)
qb_pos = self.mid_qb_pos + vector(0, self.m2_pitch)
self.add_layout_pin_rect_center(text="Qb",
layer="m2",
offset=qb_pos)
@ -190,7 +199,7 @@ class dff_buf(design.design):
def get_clk_cin(self):
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width.
#FIXME: Dff changed in a past commit. The parameter need to be updated.
# This is a handmade cell so the value must be entered in the tech.py file or estimated.
# Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width.
# FIXME: Dff changed in a past commit. The parameter need to be updated.
return parameter["dff_clk_cin"]

View File

@ -7,13 +7,12 @@
#
import debug
import design
from tech import drc
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_array(design.design):
"""
This is a simple row (or multiple rows) of flops.
@ -49,18 +48,19 @@ class dff_buf_array(design.design):
self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height
self.place_dff_array()
self.route_supplies()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
for row in range(self.rows):
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_din_name(row,col), "INPUT")
for row in range(self.rows):
self.add_pin(self.get_din_name(row, col), "INPUT")
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_dout_name(row,col), "OUTPUT")
self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT")
self.add_pin(self.get_dout_name(row, col), "OUTPUT")
self.add_pin(self.get_dout_bar_name(row, col), "OUTPUT")
self.add_pin("clk", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
@ -75,17 +75,16 @@ class dff_buf_array(design.design):
inv2_size=self.inv2_size)
self.add_mod(self.dff)
def create_dff_array(self):
self.dff_insts={}
for row in range(self.rows):
for row in range(self.rows):
for col in range(self.columns):
name = "dff_r{0}_c{1}".format(row,col)
self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff)
inst_ports = [self.get_din_name(row,col),
self.get_dout_name(row,col),
self.get_dout_bar_name(row,col),
name = "dff_r{0}_c{1}".format(row, col)
self.dff_insts[row, col]=self.add_inst(name=name,
mod=self.dff)
inst_ports = [self.get_din_name(row, col),
self.get_dout_name(row, col),
self.get_dout_bar_name(row, col),
"clk",
"vdd",
"gnd"]
@ -96,23 +95,33 @@ class dff_buf_array(design.design):
def place_dff_array(self):
well_spacing = max(self.nwell_space,
self.pwell_space,
self.pwell_to_nwell)
well_spacing = 0
try:
well_spacing = max(self.nwell_space, well_spacing)
except AttributeError:
pass
try:
well_spacing = max(self.pwell_space, well_spacing)
except AttributeError:
pass
try:
well_spacing = max(self.pwell_to_nwell, well_spacing)
except AttributeError:
pass
dff_pitch = self.dff.width + well_spacing + self.well_extend_active
for row in range(self.rows):
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
# name = "Xdff_r{0}_c{1}".format(row, col)
if (row % 2 == 0):
base = vector(col*dff_pitch,row*self.dff.height)
base = vector(col * dff_pitch, row * self.dff.height)
mirror = "R0"
else:
base = vector(col*dff_pitch,(row+1)*self.dff.height)
base = vector(col * dff_pitch, (row + 1) * self.dff.height)
mirror = "MX"
self.dff_insts[row,col].place(offset=base,
mirror=mirror)
self.dff_insts[row, col].place(offset=base,
mirror=mirror)
def get_din_name(self, row, col):
if self.columns == 1:
@ -120,7 +129,7 @@ class dff_buf_array(design.design):
elif self.rows == 1:
din_name = "din_{0}".format(col)
else:
din_name = "din_{0}_{1}".format(row,col)
din_name = "din_{0}_{1}".format(row, col)
return din_name
@ -130,7 +139,7 @@ class dff_buf_array(design.design):
elif self.rows == 1:
dout_name = "dout_{0}".format(col)
else:
dout_name = "dout_{0}_{1}".format(row,col)
dout_name = "dout_{0}_{1}".format(row, col)
return dout_name
@ -140,75 +149,84 @@ class dff_buf_array(design.design):
elif self.rows == 1:
dout_bar_name = "dout_bar_{0}".format(col)
else:
dout_bar_name = "dout_bar_{0}_{1}".format(row,col)
dout_bar_name = "dout_bar_{0}_{1}".format(row, col)
return dout_bar_name
def add_layout_pins(self):
def route_supplies(self):
for row in range(self.rows):
for col in range(self.columns):
vdd0_pin=self.dff_insts[row, 0].get_pin("vdd")
vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd")
self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height())
gnd0_pin=self.dff_insts[row, 0].get_pin("gnd")
gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd")
self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height())
for row in range(self.rows):
for col in range(self.columns):
# Continous vdd rail along with label.
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
vdd_pin=self.dff_insts[row, col].get_pin("vdd")
self.add_power_pin("vdd", vdd_pin.lc())
# Continous gnd rail along with label.
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
gnd_pin=self.dff_insts[row, col].get_pin("gnd")
self.add_power_pin("gnd", gnd_pin.lc())
def add_layout_pins(self):
for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row,col].get_pin("D")
debug.check(din_pin.layer=="m2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(row,col),
for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row, col].get_pin("D")
debug.check(din_pin.layer=="m2", "DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(row, col),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.dff_insts[row,col].get_pin("Q")
debug.check(dout_pin.layer=="m2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(row,col),
dout_pin = self.dff_insts[row, col].get_pin("Q")
debug.check(dout_pin.layer=="m2", "DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(row, col),
layer=dout_pin.layer,
offset=dout_pin.ll(),
width=dout_pin.width(),
height=dout_pin.height())
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
debug.check(dout_bar_pin.layer=="m2","DFF Qb pin not on metal2")
self.add_layout_pin(text=self.get_dout_bar_name(row,col),
dout_bar_pin = self.dff_insts[row, col].get_pin("Qb")
debug.check(dout_bar_pin.layer=="m2", "DFF Qb pin not on metal2")
self.add_layout_pin(text=self.get_dout_bar_name(row, col),
layer=dout_bar_pin.layer,
offset=dout_bar_pin.ll(),
width=dout_bar_pin.width(),
height=dout_bar_pin.height())
# Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0,0].get_pin("clk")
clk_ypos = 2*self.m3_pitch+self.m3_width
debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2")
clk_pin = self.dff_insts[0, 0].get_pin("clk")
clk_ypos = 2 * self.m3_pitch + self.m3_width
debug.check(clk_pin.layer=="m2", "DFF clk pin not on metal2")
if self.columns==1:
self.add_layout_pin(text="clk",
layer="m2",
offset=clk_pin.ll().scale(1,0),
offset=clk_pin.ll().scale(1, 0),
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin_segment_center(text="clk",
layer="m3",
start=vector(0,clk_ypos),
end=vector(self.width,clk_ypos))
layer="m3",
start=vector(0, clk_ypos),
end=vector(self.width, clk_ypos))
for col in range(self.columns):
clk_pin = self.dff_insts[0,col].get_pin("clk")
clk_pin = self.dff_insts[0, col].get_pin("clk")
# Make a vertical strip for each column
self.add_rect(layer="m2",
offset=clk_pin.ll().scale(1,0),
offset=clk_pin.ll().scale(1, 0),
width=self.m2_width,
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=self.m2_stack,
offset=vector(clk_pin.cx(),clk_ypos))
offset=vector(clk_pin.cx(), clk_ypos))
def get_clk_cin(self):
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""

View File

@ -11,13 +11,15 @@ import math
from sram_factory import factory
from vector import vector
from globals import OPTS
from errors import drc_error
from tech import cell_properties
class hierarchical_decoder(design.design):
"""
Dynamically generated hierarchical decoder.
"""
def __init__(self, name, rows):
def __init__(self, name, num_outputs):
design.design.__init__(self, name)
self.AND_FORMAT = "DEC_AND_{0}"
@ -26,15 +28,55 @@ class hierarchical_decoder(design.design):
self.pre3x8_inst = []
b = factory.create(module_type="bitcell")
self.cell_height = b.height
self.rows = rows
self.num_inputs = math.ceil(math.log(self.rows, 2))
try:
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
except AttributeError:
self.cell_multiple = 1
self.cell_height = self.cell_multiple * b.height
self.num_outputs = num_outputs
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def find_decoder_height(self):
"""
Dead code. This would dynamically determine the bitcell multiple,
but I just decided to hard code it in the tech file if it is not 1
because a DRC tool would be required even to run in front-end mode.
"""
b = factory.create(module_type="bitcell")
# Old behavior
if OPTS.netlist_only:
return (b.height, 1)
# Search for the smallest multiple that works
cell_multiple = 1
while cell_multiple < 5:
cell_height = cell_multiple * b.height
# debug.info(2,"Trying mult = {0} height={1}".format(cell_multiple, cell_height))
try:
and3 = factory.create(module_type="pand3",
height=cell_height)
except drc_error:
# debug.info(1, "Incrementing decoder height by 1 bitcell height {}".format(b.height))
pass
else:
(drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True)
total_errors = drc_errors + lvs_errors
if total_errors == 0:
debug.info(1, "Decoder height is multiple of {} bitcells.".format(cell_multiple))
return (cell_height, cell_multiple)
cell_multiple += 1
else:
debug.error("Couldn't find a valid decoder height multiple.", -1)
def create_netlist(self):
self.add_modules()
self.setup_netlist_constants()
@ -46,8 +88,8 @@ class hierarchical_decoder(design.design):
self.setup_layout_constants()
self.place_pre_decoder()
self.place_row_decoder()
self.route_input_rails()
self.route_predecode_rails()
self.route_inputs()
self.route_decoder_bus()
self.route_vdd_gnd()
self.offset_all_coordinates()
self.add_boundary()
@ -101,7 +143,7 @@ class hierarchical_decoder(design.design):
def setup_netlist_constants(self):
self.predec_groups = [] # This array is a 2D array.
# Distributing vertical rails to different groups. One group belongs to one pre-decoder.
# Distributing vertical bus to different groups. One group belongs to one pre-decoder.
# For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will
# have total 16 output lines out of these 3 pre-decoders and they will
# be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ]
@ -140,39 +182,46 @@ class hierarchical_decoder(design.design):
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8
# We may have more than one bitcell per decoder row
self.num_rows = math.ceil(self.num_outputs / self.cell_multiple)
# We will place this many final decoders per row
self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows)
# Calculates height and width of row-decoder
if (self.num_inputs == 4 or self.num_inputs == 5):
nand_width = self.and2.width
else:
nand_width = self.and3.width
self.internal_routing_width = self.m2_pitch * self.total_number_of_predecoder_outputs
self.row_decoder_height = self.inv.height * self.rows
self.internal_routing_width = self.m2_pitch * (self.total_number_of_predecoder_outputs + 1)
self.row_decoder_height = self.inv.height * self.num_rows
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
# Calculates height and width of hierarchical decoder
self.height = self.row_decoder_height
# Add extra pitch for good measure
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m3_pitch
self.width = self.input_routing_width + self.predecoder_width \
+ self.internal_routing_width + nand_width + self.inv.width
+ self.internal_routing_width \
+ self.decoders_per_row * nand_width + self.inv.width
def route_input_rails(self):
""" Create input rails for the predecoders """
def route_inputs(self):
""" Create input bus for the predecoders """
# inputs should be as high as the decoders
input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height
# Find the left-most predecoder
min_x = 0
if self.no_of_pre2x4 > 0:
min_x = min(min_x, -self.pre2_4.width)
min_x = min(min_x, self.pre2x4_inst[0].lx())
if self.no_of_pre3x8 > 0:
min_x = min(min_x, -self.pre3_8.width)
min_x = min(min_x, self.pre3x8_inst[0].lx())
input_offset=vector(min_x - self.input_routing_width, 0)
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
self.input_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
length=input_height)
self.input_bus = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
length=input_height)
self.route_input_to_predecodes()
@ -182,7 +231,7 @@ class hierarchical_decoder(design.design):
for i in range(2):
index = pre_num * 2 + i
input_pos = self.input_rails["addr_{}".format(index)]
input_pos = self.input_bus["addr_{}".format(index)]
in_name = "in_{}".format(i)
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
@ -192,13 +241,13 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_rail(decoder_offset, input_offset)
self.route_input_bus(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre3x8):
for i in range(3):
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
input_pos = self.input_rails["addr_{}".format(index)]
input_pos = self.input_bus["addr_{}".format(index)]
in_name = "in_{}".format(i)
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
@ -208,10 +257,13 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_rail(decoder_offset, input_offset)
self.route_input_bus(decoder_offset, input_offset)
def route_input_rail(self, input_offset, output_offset):
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
def route_input_bus(self, input_offset, output_offset):
"""
Route a vertical M2 coordinate to another
vertical M2 coordinate to the predecode inputs
"""
self.add_via_center(layers=self.m2_stack,
offset=input_offset)
@ -225,7 +277,7 @@ class hierarchical_decoder(design.design):
for i in range(self.num_inputs):
self.add_pin("addr_{0}".format(i), "INPUT")
for j in range(self.rows):
for j in range(self.num_outputs):
self.add_pin("decode_{0}".format(j), "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
@ -294,18 +346,17 @@ class hierarchical_decoder(design.design):
else:
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
self.pre2x4_inst[num].place(base)
self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0))
def place_pre3x8(self, num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
if (self.num_inputs == 3):
offset = vector(-self.pre_3_8.width, 0)
mirror = "R0"
else:
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
offset = vector(-self.pre3_8.width, height)
self.pre3x8_inst[num].place(offset)
self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0))
def create_row_decoder(self):
""" Create the row-decoder by placing AND2/AND3 and Inverters
@ -322,14 +373,14 @@ class hierarchical_decoder(design.design):
if (self.num_inputs == 4 or self.num_inputs == 5):
for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])):
row = len(self.predec_groups[0]) * j + i
if (row < self.rows):
name = self.AND_FORMAT.format(row)
output = len(self.predec_groups[0]) * j + i
if (output < self.num_outputs):
name = self.AND_FORMAT.format(output)
self.and_inst.append(self.add_inst(name=name,
mod=self.and2))
pins =["out_{0}".format(i),
"out_{0}".format(j + len(self.predec_groups[0])),
"decode_{0}".format(row),
"decode_{0}".format(output),
"vdd", "gnd"]
self.connect_inst(pins)
@ -338,18 +389,18 @@ class hierarchical_decoder(design.design):
for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])):
for k in range(len(self.predec_groups[2])):
row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \
+ len(self.predec_groups[0]) * j + i
output = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \
+ len(self.predec_groups[0]) * j + i
if (row < self.rows):
name = self.AND_FORMAT.format(row)
if (output < self.num_outputs):
name = self.AND_FORMAT.format(output)
self.and_inst.append(self.add_inst(name=name,
mod=self.and3))
pins = ["out_{0}".format(i),
"out_{0}".format(j + len(self.predec_groups[0])),
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
"decode_{0}".format(row),
"decode_{0}".format(output),
"vdd", "gnd"]
self.connect_inst(pins)
@ -363,7 +414,10 @@ class hierarchical_decoder(design.design):
self.route_decoder()
def place_decoder_and_array(self):
""" Add a column of AND gates for final decode """
"""
Add a column of AND gates for final decode.
This may have more than one decoder per row to match the bitcell height.
"""
# Row Decoder AND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_inputs == 5):
@ -375,9 +429,13 @@ class hierarchical_decoder(design.design):
self.place_and_array(and_mod=self.and3)
def place_and_array(self, and_mod):
""" Add a column of AND gates for the decoder above the predecoders."""
for row in range(self.rows):
"""
Add a column of AND gates for the decoder above the predecoders.
"""
for inst_index in range(self.num_outputs):
row = math.floor(inst_index / self.decoders_per_row)
dec = inst_index % self.decoders_per_row
if ((row % 2) == 0):
y_off = and_mod.height * row
mirror = "R0"
@ -385,46 +443,52 @@ class hierarchical_decoder(design.design):
y_off = and_mod.height * (row + 1)
mirror = "MX"
self.and_inst[row].place(offset=[self.internal_routing_width, y_off],
mirror=mirror)
x_off = self.internal_routing_width + dec * and_mod.width
self.and_inst[inst_index].place(offset=vector(x_off, y_off),
mirror=mirror)
def route_decoder(self):
""" Add the pins. """
for row in range(self.rows):
z_pin = self.and_inst[row].get_pin("Z")
self.add_layout_pin(text="decode_{0}".format(row),
for output in range(self.num_outputs):
z_pin = self.and_inst[output].get_pin("Z")
self.add_layout_pin(text="decode_{0}".format(output),
layer="m1",
offset=z_pin.ll(),
width=z_pin.width(),
height=z_pin.height())
def route_predecode_rails(self):
""" Creates vertical metal 2 rails to connect predecoder and decoder stages."""
def route_decoder_bus(self):
"""
Creates vertical metal 2 bus to connect predecoder and decoder stages.
"""
# This is not needed for inputs <4 since they have no pre/decode stages.
if (self.num_inputs >= 4):
input_offset = vector(0.5 * self.m2_width, 0)
# This leaves an offset for the predecoder output jogs
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
self.predecode_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
length=self.height)
self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=vector(0, 0),
names=input_bus_names,
length=self.height)
self.route_rails_to_predecodes()
self.route_rails_to_decoder()
def route_rails_to_predecodes(self):
""" Iterates through all of the predecodes and connects to the rails including the offsets """
self.route_predecodes_to_bus()
self.route_bus_to_decoder()
def route_predecodes_to_bus(self):
"""
Iterates through all of the predecodes
and connects to the rails including the offsets
"""
# FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre2x4):
for i in range(4):
predecode_name = "predecode_{}".format(pre_num * 4 + i)
out_name = "out_{}".format(i)
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
self.route_predecode_rail_m3(predecode_name, pin)
x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
# FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre3x8):
@ -432,52 +496,82 @@ class hierarchical_decoder(design.design):
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
out_name = "out_{}".format(i)
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
self.route_predecode_rail_m3(predecode_name, pin)
x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
def route_rails_to_decoder(self):
""" Use the self.predec_groups to determine the connections to the decoder AND gates.
Inputs of AND2/AND3 gates come from different groups.
For example for these groups [ [0,1,2,3] ,[4,5,6,7],
[8,9,10,11,12,13,14,15] ] the first AND3 inputs are connected to
[0,4,8] and second AND3 is connected to [0,4,9] ........... and the
128th AND3 is connected to [3,7,15]
def route_bus_to_decoder(self):
"""
row_index = 0
Use the self.predec_groups to determine the connections to the decoder AND gates.
Inputs of AND2/AND3 gates come from different groups.
For example for these groups
[ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ]
the first AND3 inputs are connected to [0,4,8],
second AND3 is connected to [0,4,9],
...
and the 128th AND3 is connected to [3,7,15]
"""
output_index = 0
if (self.num_inputs == 4 or self.num_inputs == 5):
for index_B in self.predec_groups[1]:
for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus?
if (row_index < self.rows):
if (output_index < self.num_outputs):
row_index = math.floor(output_index / self.decoders_per_row)
row_remainder = (output_index % self.decoders_per_row)
row_offset = row_index * self.and_inst[0].height + (2 * row_remainder + 1) * self.m3_pitch
predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("A"),
row_offset)
predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B"))
row_index = row_index + 1
self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("B"),
row_offset + self.m3_pitch)
output_index = output_index + 1
elif (self.num_inputs > 5):
for index_C in self.predec_groups[2]:
for index_B in self.predec_groups[1]:
for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus?
if (row_index < self.rows):
if (output_index < self.num_outputs):
row_index = math.floor(output_index / self.decoders_per_row)
row_remainder = (output_index % self.decoders_per_row)
row_offset = row_index * self.and_inst[0].height + (3 * row_remainder + 1) * self.m3_pitch
predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("A"),
row_offset)
predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B"))
self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("B"),
row_offset + self.m3_pitch)
predecode_name = "predecode_{}".format(index_C)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("C"))
row_index = row_index + 1
self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("C"),
row_offset + 2 * self.m3_pitch)
output_index = output_index + 1
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
"""
Add a pin for each row of vdd/gnd which are
must-connects next level up.
"""
# The vias will be placed in the center and right of the cells, respectively.
xoffset = self.and_inst[0].rx()
for num in range(0, self.rows):
# The vias will be placed at the right of the cells.
xoffset = max(x.rx() for x in self.and_inst)
for num in range(0, self.num_outputs):
# Only add the power pin for the 1st in each row
if num % self.decoders_per_row:
continue
for pin_name in ["vdd", "gnd"]:
# The nand and inv are the same height rows...
supply_pin = self.and_inst[num].get_pin(pin_name)
pin_pos = vector(xoffset, supply_pin.cy())
self.add_path("m1",
[supply_pin.lc(), vector(xoffset, supply_pin.cy())])
self.add_power_pin(name=pin_name,
loc=pin_pos)
@ -486,23 +580,42 @@ class hierarchical_decoder(design.design):
self.copy_layout_pin(pre, "vdd")
self.copy_layout_pin(pre, "gnd")
def route_predecode_rail(self, rail_name, pin):
""" Connect the routing rail to the given metal1 pin """
rail_pos = vector(self.predecode_rails[rail_name].x, pin.lc().y)
self.add_path("m1", [rail_pos, pin.lc()])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
def route_predecode_bus_outputs(self, rail_name, pin, y_offset):
"""
Connect the routing rail to the given metal1 pin
using a routing track at the given y_offset
"""
pin_pos = pin.center()
# If we have a single decoder per row, we can route on M1
if self.decoders_per_row == 1:
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
self.add_path("m1", [rail_pos, pin_pos])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
# If not, we must route over the decoder cells on M3
else:
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
mid_pos = vector(pin_pos.x, rail_pos.y)
self.add_wire(self.m2_stack[::-1], [rail_pos, mid_pos, pin_pos])
self.add_via_center(layers=self.m2_stack,
offset=rail_pos)
self.add_via_center(layers=self.m1_stack,
offset=pin_pos)
def route_predecode_rail_m3(self, rail_name, pin):
""" Connect the routing rail to the given metal1 pin """
def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
"""
Connect the routing rail to the given metal1 pin using a jog
to the right of the cell at the given x_offset.
"""
# This routes the pin up to the rail, basically, to avoid conflicts.
# It would be fixed with a channel router.
mid_point = vector(pin.cx(), pin.cy() + self.inv.height / 2)
rail_pos = vector(self.predecode_rails[rail_name].x, mid_point.y)
pin_pos = pin.center()
mid_point1 = vector(x_offset, pin_pos.y)
mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2)
rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
self.add_wire(self.m1_stack, [pin_pos, mid_point1, mid_point2, rail_pos])
self.add_via_center(layers=self.m1_stack,
offset=pin.center())
self.add_wire(("m3", "via2", "m2"), [rail_pos, mid_point, pin.uc()])
self.add_via_center(layers=self.m2_stack,
offset=rail_pos)
def input_load(self):

View File

@ -90,17 +90,14 @@ class port_address(design.design):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc()
driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc()
mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0)
mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1)
mid1 = decoder_out_pos.scale(0.5, 1) + driver_in_pos.scale(0.5, 0)
mid2 = decoder_out_pos.scale(0.5, 0) + driver_in_pos.scale(0.5, 1)
self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos])
def add_modules(self):
self.row_decoder = factory.create(module_type="decoder",
rows=self.num_rows)
num_outputs=self.num_rows)
self.add_mod(self.row_decoder)
self.wordline_driver = factory.create(module_type="wordline_driver",
@ -108,11 +105,10 @@ class port_address(design.design):
cols=self.num_cols)
self.add_mod(self.wordline_driver)
def create_row_decoder(self):
""" Create the hierarchical row decoder """
self.row_decoder_inst = self.add_inst(name="row_decoder",
self.row_decoder_inst = self.add_inst(name="row_decoder",
mod=self.row_decoder)
temp = []

View File

@ -783,8 +783,13 @@ class port_data(design.design):
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
top_names = self._get_bitline_pins(top_inst_group, bit)
if bottom_names[0].layer == "m2":
bitline_dirs = ("H", "V")
elif bottom_names[0].layer == "m1":
bitline_dirs = ("V", "H")
route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
self.create_horizontal_channel_route(route_map, offset, self.m1_stack, bitline_dirs)
def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bls_template="{inst}_{bit}",

View File

@ -73,11 +73,12 @@ class precharge_array(design.design):
def add_layout_pins(self):
en_bar_pin = self.pc_cell.get_pin("en_bar")
self.add_layout_pin(text="en_bar",
layer="m1",
offset=self.pc_cell.get_pin("en_bar").ll(),
layer=en_bar_pin.layer,
offset=en_bar_pin.ll(),
width=self.width,
height=drc("minwidth_m1"))
height=en_bar_pin.height())
for inst in self.local_insts:
self.copy_layout_pin(inst, "vdd")

View File

@ -13,6 +13,7 @@ import debug
from globals import OPTS
import logical_effort
class sense_amp_array(design.design):
"""
Array of sense amplifiers to read the bitlines through the column mux.
@ -22,7 +23,7 @@ class sense_amp_array(design.design):
def __init__(self, name, word_size, words_per_row, num_spare_cols=None):
design.design.__init__(self, name)
debug.info(1, "Creating {0}".format(self.name))
self.add_comment("word_size {0}".format(word_size))
self.add_comment("word_size {0}".format(word_size))
self.add_comment("words_per_row: {0}".format(words_per_row))
self.word_size = word_size
@ -60,7 +61,7 @@ class sense_amp_array(design.design):
def create_layout(self):
self.height = self.amp.height
if self.bitcell.width > self.amp.width:
self.width = self.bitcell.width * (self.word_size * self.words_per_row + self.num_spare_cols)
else:
@ -80,16 +81,16 @@ class sense_amp_array(design.design):
self.add_pin(self.en_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.amp = factory.create(module_type="sense_amp")
self.add_mod(self.amp)
# This is just used for measurements,
# so don't add the module
self.bitcell = factory.create(module_type="bitcell")
def create_sense_amp_array(self):
self.local_insts = []
for i in range(0,self.word_size + self.num_spare_cols):
@ -110,7 +111,7 @@ class sense_amp_array(design.design):
amp_spacing = self.amp.width * self.words_per_row
spare_cols_spacing = self.amp.width
for i in range(0,self.word_size):
for i in range(0, self.word_size):
xoffset = amp_spacing * i
# align the xoffset to the grid of bitcells. This way we
# know when to do the mirroring.
@ -142,19 +143,20 @@ class sense_amp_array(design.design):
amp_position = vector(xoffset, 0)
self.local_insts[index].place(offset=amp_position,mirror=mirror)
def add_layout_pins(self):
for i in range(len(self.local_insts)):
inst = self.local_insts[i]
self.add_power_pin(name = "gnd",
loc = inst.get_pin("gnd").center(),
start_layer="m2",
vertical=True)
self.add_power_pin(name = "vdd",
loc = inst.get_pin("vdd").center(),
start_layer="m2",
gnd_pin = inst.get_pin("gnd")
self.add_power_pin(name="gnd",
loc=gnd_pin.center(),
start_layer=gnd_pin.layer,
vertical=True)
vdd_pin = inst.get_pin("vdd")
self.add_power_pin(name="vdd",
loc=vdd_pin.center(),
start_layer=vdd_pin.layer,
vertical=True)
bl_pin = inst.get_pin(inst.mod.get_bl_names())
@ -162,43 +164,43 @@ class sense_amp_array(design.design):
dout_pin = inst.get_pin(inst.mod.dout_name)
self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i),
layer="m2",
layer=bl_pin.layer,
offset=bl_pin.ll(),
width=bl_pin.width(),
height=bl_pin.height())
self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i),
layer="m2",
layer=br_pin.layer,
offset=br_pin.ll(),
width=br_pin.width(),
height=br_pin.height())
self.add_layout_pin(text=self.data_name + "_{0}".format(i),
layer="m2",
layer=dout_pin.layer,
offset=dout_pin.ll(),
width=dout_pin.width(),
height=dout_pin.height())
def route_rails(self):
# add sclk rail across entire array
sclk_offset = self.amp.get_pin(self.amp.en_name).ll().scale(0,1)
sclk = self.amp.get_pin(self.amp.en_name)
sclk_offset = self.amp.get_pin(self.amp.en_name).ll().scale(0, 1)
self.add_layout_pin(text=self.en_name,
layer="m1",
offset=sclk_offset,
width=self.width,
height=drc("minwidth_m1"))
layer=sclk.layer,
offset=sclk_offset,
width=self.width,
height=drc("minwidth_" + sclk.layer))
def input_load(self):
return self.amp.input_load()
def get_en_cin(self):
"""Get the relative capacitance of all the sense amp enable connections in the array"""
sense_amp_en_cin = self.amp.get_en_cin()
return sense_amp_en_cin * (self.word_size + self.num_spare_cols)
return sense_amp_en_cin * self.word_size
def get_drain_cin(self):
"""Get the relative capacitance of the drain of the PMOS isolation TX"""
from tech import parameter
#Bitcell drain load being used to estimate PMOS drain load
# Bitcell drain load being used to estimate PMOS drain load
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
return drain_load

View File

@ -5,17 +5,15 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from math import log
import design
import contact
from tech import drc
import debug
import math
from tech import layer
from vector import vector
from sram_factory import factory
from globals import OPTS
import logical_effort
class single_level_column_mux_array(design.design):
"""
Dynamically generated column mux array.
@ -26,13 +24,20 @@ class single_level_column_mux_array(design.design):
design.design.__init__(self, name)
debug.info(1, "Creating {0}".format(self.name))
self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br))
self.columns = columns
self.word_size = word_size
self.words_per_row = int(self.columns / self.word_size)
self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br
if "li" in layer:
self.col_mux_stack = self.li_stack
self.col_mux_stack_pitch = self.li_pitch
else:
self.col_mux_stack = self.m1_stack
self.col_mux_stack_pitch = self.m1_pitch
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -49,20 +54,20 @@ class single_level_column_mux_array(design.design):
self.add_modules()
self.add_pins()
self.create_array()
def create_layout(self):
self.setup_layout_constants()
self.place_array()
self.add_routing()
# Find the highest shapes to determine height before adding well
highest = self.find_highest_coords()
self.height = highest.y
self.height = highest.y
self.add_layout_pins()
self.add_enclosure(self.mux_inst, "pwell")
if "pwell" in layer:
self.add_enclosure(self.mux_inst, "pwell")
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
for i in range(self.columns):
self.add_pin("bl_{}".format(i))
@ -74,23 +79,19 @@ class single_level_column_mux_array(design.design):
self.add_pin("br_out_{}".format(i))
self.add_pin("gnd")
def add_modules(self):
self.mux = factory.create(module_type="single_level_column_mux",
bitcell_bl=self.bitcell_bl,
bitcell_br=self.bitcell_br)
self.add_mod(self.mux)
def setup_layout_constants(self):
self.column_addr_size = num_of_inputs = int(self.words_per_row / 2)
self.column_addr_size = int(self.words_per_row / 2)
self.width = self.columns * self.mux.width
# one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br
# one extra route pitch is to space from the sense amp
self.route_height = (self.words_per_row + 3)*self.m1_pitch
self.route_height = (self.words_per_row + 3) * self.col_mux_stack_pitch
def create_array(self):
self.mux_inst = []
# For every column, add a pass gate
@ -98,11 +99,11 @@ class single_level_column_mux_array(design.design):
name = "XMUX{0}".format(col_num)
self.mux_inst.append(self.add_inst(name=name,
mod=self.mux))
self.connect_inst(["bl_{}".format(col_num),
"br_{}".format(col_num),
"bl_out_{}".format(int(col_num/self.words_per_row)),
"br_out_{}".format(int(col_num/self.words_per_row)),
"bl_out_{}".format(int(col_num / self.words_per_row)),
"br_out_{}".format(int(col_num / self.words_per_row)),
"sel_{}".format(col_num % self.words_per_row),
"gnd"])
@ -117,32 +118,31 @@ class single_level_column_mux_array(design.design):
else:
mirror = ""
name = "XMUX{0}".format(col_num)
offset = vector(xoffset, self.route_height)
self.mux_inst[col_num].place(offset=offset, mirror=mirror)
def add_layout_pins(self):
""" Add the pins after we determine the height. """
# For every column, add a pass gate
for col_num in range(self.columns):
mux_inst = self.mux_inst[col_num]
offset = mux_inst.get_pin("bl").ll()
bl_pin = mux_inst.get_pin("bl")
offset = bl_pin.ll()
self.add_layout_pin(text="bl_{}".format(col_num),
layer="m2",
layer=bl_pin.layer,
offset=offset,
height=self.height-offset.y)
height=self.height - offset.y)
offset = mux_inst.get_pin("br").ll()
br_pin = mux_inst.get_pin("br")
offset = br_pin.ll()
self.add_layout_pin(text="br_{}".format(col_num),
layer="m2",
layer=br_pin.layer,
offset=offset,
height=self.height-offset.y)
height=self.height - offset.y)
for inst in self.mux_inst:
self.copy_layout_pin(inst, "gnd")
def add_routing(self):
self.add_horizontal_input_rail()
self.add_vertical_poly_rail()
@ -151,15 +151,15 @@ class single_level_column_mux_array(design.design):
def add_horizontal_input_rail(self):
""" Create address input rails on M1 below the mux transistors """
for j in range(self.words_per_row):
offset = vector(0, self.route_height + (j-self.words_per_row)*self.m1_pitch)
offset = vector(0, self.route_height + (j - self.words_per_row) * self.col_mux_stack_pitch)
self.add_layout_pin(text="sel_{}".format(j),
layer="m1",
layer=self.col_mux_stack[0],
offset=offset,
width=self.mux.width * self.columns)
def add_vertical_poly_rail(self):
""" Connect the poly to the address rails """
# Offset to the first transistor gate in the pass gate
for col in range(self.columns):
# which select bit should this column connect to depends on the position in the word
@ -167,11 +167,12 @@ class single_level_column_mux_array(design.design):
# Add the column x offset to find the right select bit
gate_offset = self.mux_inst[col].get_pin("sel").bc()
# height to connect the gate to the correct horizontal row
sel_height = self.get_pin("sel_{}".format(sel_index)).by()
# sel_height = self.get_pin("sel_{}".format(sel_index)).by()
# use the y offset from the sel pin and the x offset from the gate
offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy())
offset = vector(gate_offset.x,
self.get_pin("sel_{}".format(sel_index)).cy())
# Add the poly contact with a shift to account for the rotation
self.add_via_center(layers=("m1", "contact", "poly"),
self.add_via_center(layers=self.poly_stack,
offset=offset)
self.add_path("poly", [offset, gate_offset])
@ -182,11 +183,11 @@ class single_level_column_mux_array(design.design):
bl_offset = self.mux_inst[j].get_pin("bl_out").bc()
br_offset = self.mux_inst[j].get_pin("br_out").bc()
bl_out_offset = bl_offset - vector(0,(self.words_per_row+1)*self.m1_pitch)
br_out_offset = br_offset - vector(0,(self.words_per_row+2)*self.m1_pitch)
bl_out_offset = bl_offset - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch)
br_out_offset = br_offset - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch)
bl_out_offset_end = bl_out_offset + vector(0,self.route_height)
br_out_offset_end = br_out_offset + vector(0,self.route_height)
bl_out_offset_end = bl_out_offset + vector(0, self.route_height)
br_out_offset_end = br_out_offset + vector(0, self.route_height)
if cell_properties.bitcell.mirror.y and j % 2:
tmp_bl_out_end = br_out_offset_end
@ -208,43 +209,41 @@ class single_level_column_mux_array(design.design):
else:
dist = 0
self.add_path("m1", [bl_out_offset, bl_out_offset+vector(width+dist,0)])
self.add_path("m1", [br_out_offset, br_out_offset+vector(width-dist,0)])
self.add_path(self.col_mux_stack[0], [bl_out_offset, bl_out_offset + vector(width + dist, 0)])
self.add_path(self.col_mux_stack[0], [br_out_offset, br_out_offset + vector(width - dist, 0)])
# Extend the bitline output rails and gnd downward on the first bit of each n-way mux
self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j/self.words_per_row)),
layer="m2",
self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)),
layer=self.col_mux_stack[2],
start=bl_out_offset,
end=tmp_bl_out_end)
self.add_layout_pin_segment_center(text="br_out_{}".format(int(j/self.words_per_row)),
layer="m2",
self.add_layout_pin_segment_center(text="br_out_{}".format(int(j / self.words_per_row)),
layer=self.col_mux_stack[2],
start=br_out_offset,
end=tmp_br_out_end)
# This via is on the right of the wire
self.add_via_center(layers=self.m1_stack,
# This via is on the right of the wire
self.add_via_center(layers=self.col_mux_stack,
offset=bl_out_offset)
# This via is on the left of the wire
self.add_via_center(layers=self.m1_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=br_out_offset)
else:
self.add_path("m2", [ bl_out_offset, tmp_bl_out_end])
self.add_path("m2", [ br_out_offset, tmp_br_out_end])
self.add_path(self.col_mux_stack[2], [bl_out_offset, bl_offset])
self.add_path(self.col_mux_stack[2], [br_out_offset, br_offset])
# This via is on the right of the wire
self.add_via_center(layers=self.m1_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=bl_out_offset)
# This via is on the left of the wire
self.add_via_center(layers=self.m1_stack,
# This via is on the left of the wire
self.add_via_center(layers=self.col_mux_stack,
offset=br_out_offset)
def get_drain_cin(self):
"""Get the relative capacitance of the drain of the NMOS pass TX"""
from tech import parameter
#Bitcell drain load being used to estimate mux NMOS drain load
# Bitcell drain load being used to estimate mux NMOS drain load
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
return drain_load
return drain_load

View File

@ -7,10 +7,12 @@
#
import debug
import design
import math
import contact
from vector import vector
from sram_factory import factory
from globals import OPTS
from tech import cell_properties
class wordline_driver(design.design):
@ -26,6 +28,13 @@ class wordline_driver(design.design):
self.rows = rows
self.cols = cols
b = factory.create(module_type="bitcell")
try:
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
except AttributeError:
self.cell_multiple = 1
self.cell_height = self.cell_multiple * b.height
self.create_netlist()
if not OPTS.netlist_only:
@ -37,6 +46,7 @@ class wordline_driver(design.design):
self.create_drivers()
def create_layout(self):
self.setup_layout_constants()
self.place_drivers()
self.route_layout()
self.route_vdd_gnd()
@ -56,17 +66,10 @@ class wordline_driver(design.design):
self.add_pin("gnd", "GROUND")
def add_modules(self):
b = factory.create(module_type="bitcell")
self.inv = factory.create(module_type="pdriver",
fanout=self.cols,
neg_polarity=True,
height=b.height)
self.add_mod(self.inv)
self.nand2 = factory.create(module_type="pnand2",
height=b.height)
self.add_mod(self.nand2)
self.and2 = factory.create(module_type="pand2",
height=self.cell_height,
size=self.cols)
self.add_mod(self.and2)
def route_vdd_gnd(self):
"""
@ -75,68 +78,64 @@ class wordline_driver(design.design):
"""
# Find the x offsets for where the vias/pins should be placed
a_xoffset = self.nand_inst[0].lx()
xoffset_list = [self.and_inst[0].lx()]
for num in range(self.rows):
# this will result in duplicate polygons for rails, but who cares
# use the inverter offset even though it will be the nand's too
# use the inverter offset even though it will be the and's too
(gate_offset, y_dir) = self.get_gate_offset(0,
self.inv.height,
self.and2.height,
num)
# Route both supplies
for name in ["vdd", "gnd"]:
supply_pin = self.inv2_inst[num].get_pin(name)
supply_pin = self.and_inst[num].get_pin(name)
# Add pins in two locations
for xoffset in [a_xoffset]:
for xoffset in xoffset_list:
pin_pos = vector(xoffset, supply_pin.cy())
self.add_power_pin(name, pin_pos)
def create_drivers(self):
self.nand_inst = []
self.inv2_inst = []
self.and_inst = []
for row in range(self.rows):
name_nand = "wl_driver_nand{}".format(row)
name_inv2 = "wl_driver_inv{}".format(row)
name_and = "wl_driver_and{}".format(row)
# add nand 2
self.nand_inst.append(self.add_inst(name=name_nand,
mod=self.nand2))
# add and2
self.and_inst.append(self.add_inst(name=name_and,
mod=self.and2))
self.connect_inst(["en",
"in_{0}".format(row),
"wl_bar_{0}".format(row),
"vdd", "gnd"])
# add inv2
self.inv2_inst.append(self.add_inst(name=name_inv2,
mod=self.inv))
self.connect_inst(["wl_bar_{0}".format(row),
"wl_{0}".format(row),
"vdd", "gnd"])
def setup_layout_constants(self):
# We may have more than one bitcell per decoder row
self.num_rows = math.ceil(self.rows / self.cell_multiple)
# We will place this many final decoders per row
self.decoders_per_row = math.ceil(self.rows / self.num_rows)
def place_drivers(self):
nand2_xoffset = 2 * self.m1_width + 5 * self.m1_space
inv2_xoffset = nand2_xoffset + self.nand2.width
and2_xoffset = 2 * self.m1_width + 5 * self.m1_space
self.width = inv2_xoffset + self.inv.width
self.height = self.inv.height * self.rows
self.width = and2_xoffset + self.and2.width
self.height = self.and2.height * self.num_rows
for row in range(self.rows):
#row = math.floor(inst_index / self.decoders_per_row)
#dec = inst_index % self.decoders_per_row
if (row % 2):
y_offset = self.inv.height * (row + 1)
y_offset = self.and2.height * (row + 1)
inst_mirror = "MX"
else:
y_offset = self.inv.height * row
y_offset = self.and2.height * row
inst_mirror = "R0"
nand2_offset = [nand2_xoffset, y_offset]
inv2_offset = [inv2_xoffset, y_offset]
# x_off = self.internal_routing_width + dec * and_mod.width
and2_offset = [and2_xoffset, y_offset]
# add nand 2
self.nand_inst[row].place(offset=nand2_offset,
mirror=inst_mirror)
# add inv2
self.inv2_inst[row].place(offset=inv2_offset,
# add and2
self.and_inst[row].place(offset=and2_offset,
mirror=inst_mirror)
def route_layout(self):
@ -151,11 +150,10 @@ class wordline_driver(design.design):
height=self.height)
for row in range(self.rows):
nand_inst = self.nand_inst[row]
inv2_inst = self.inv2_inst[row]
and_inst = self.and_inst[row]
# en connection
a_pin = nand_inst.get_pin("A")
a_pin = and_inst.get_pin("A")
a_pos = a_pin.lc()
clk_offset = vector(en_pin.bc().x, a_pos.y)
self.add_segment_center(layer="m1",
@ -164,18 +162,10 @@ class wordline_driver(design.design):
self.add_via_center(layers=self.m1_stack,
offset=clk_offset)
# Nand2 out to 2nd inv
zr_pos = nand_inst.get_pin("Z").rc()
al_pos = inv2_inst.get_pin("A").lc()
# ensure the bend is in the middle
mid1_pos = vector(0.5 * (zr_pos.x + al_pos.x), zr_pos.y)
mid2_pos = vector(0.5 * (zr_pos.x + al_pos.x), al_pos.y)
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
# connect the decoder input pin to nand2 B
b_pin = nand_inst.get_pin("B")
# connect the decoder input pin to and2 B
b_pin = and_inst.get_pin("B")
b_pos = b_pin.lc()
# needs to move down since B nand input is
# needs to move down since B and input is
# nearly aligned with A inv input
up_or_down = self.m2_space if row % 2 else -self.m2_space
input_offset = vector(0, b_pos.y + up_or_down)
@ -192,7 +182,7 @@ class wordline_driver(design.design):
offset=mid_via_offset,
directions=("V", "V"))
# now connect to the nand2 B
# now connect to the and2 B
self.add_path("m2", [mid_via_offset, b_pos])
contact_offset = b_pos - vector(0.5 * contact.m1_via.height, 0)
self.add_via_center(layers=self.m1_stack,
@ -200,7 +190,7 @@ class wordline_driver(design.design):
directions=("H", "H"))
# output each WL on the right
wl_offset = inv2_inst.get_pin("Z").rc()
wl_offset = and_inst.get_pin("Z").rc()
self.add_layout_pin_segment_center(text="wl_{0}".format(row),
layer="m1",
start=wl_offset,
@ -213,13 +203,8 @@ class wordline_driver(design.design):
"""
stage_effort_list = []
stage1_cout = self.inv.get_cin()
stage1 = self.nand2.get_stage_effort(stage1_cout, inp_is_rise)
stage1 = self.and2.get_stage_effort(external_cout, inp_is_rise)
stage_effort_list.append(stage1)
last_stage_is_rise = stage1.is_rise
stage2 = self.inv.get_stage_efforts(external_cout, last_stage_is_rise)
stage_effort_list.extend(stage2)
return stage_effort_list
@ -228,6 +213,6 @@ class wordline_driver(design.design):
Get the relative capacitance of all
the enable connections in the bank
"""
# The enable is connected to a nand2 for every row.
total_cin = self.nand2.get_cin() * self.rows
# The enable is connected to a and2 for every row.
total_cin = self.and2.get_cin() * self.rows
return total_cin

View File

@ -5,14 +5,13 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from math import log
import design
from tech import drc
import debug
from sram_factory import factory
from vector import vector
from globals import OPTS
class write_driver_array(design.design):
"""
Array of tristate drivers to write to the bitlines through the column mux.
@ -23,7 +22,7 @@ class write_driver_array(design.design):
design.design.__init__(self, name)
debug.info(1, "Creating {0}".format(self.name))
self.add_comment("columns: {0}".format(columns))
self.add_comment("word_size {0}".format(word_size))
self.add_comment("word_size {0}".format(word_size))
self.columns = columns
self.word_size = word_size
@ -35,7 +34,7 @@ class write_driver_array(design.design):
self.num_spare_cols = num_spare_cols
if self.write_size:
self.num_wmasks = int(self.word_size/self.write_size)
self.num_wmasks = int(self.word_size / self.write_size)
self.create_netlist()
if not OPTS.netlist_only:
@ -61,9 +60,9 @@ class write_driver_array(design.design):
self.add_modules()
self.add_pins()
self.create_write_array()
def create_layout(self):
if self.bitcell.width > self.driver.width:
self.width = (self.columns + self.num_spare_cols) * self.bitcell.width
self.width_regular_cols = self.columns * self.bitcell.width
@ -73,7 +72,7 @@ class write_driver_array(design.design):
self.width_regular_cols = self.columns * self.driver.width
self.single_col_width = self.driver.width
self.height = self.driver.height
self.place_write_array()
self.add_layout_pins()
self.add_boundary()
@ -108,9 +107,9 @@ class write_driver_array(design.design):
self.driver_insts = {}
w = 0
windex=0
for i in range(0,self.columns,self.words_per_row):
for i in range(0, self.columns, self.words_per_row):
name = "write_driver{}".format(i)
index = int(i/self.words_per_row)
index = int(i / self.words_per_row)
self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver)
@ -155,8 +154,8 @@ class write_driver_array(design.design):
self.driver_spacing = self.bitcell.width
else:
self.driver_spacing = self.driver.width
for i in range(0,self.columns,self.words_per_row):
index = int(i/self.words_per_row)
for i in range(0, self.columns, self.words_per_row):
index = int(i / self.words_per_row)
xoffset = i * self.driver_spacing
if cell_properties.bitcell.mirror.y and i % 2:
@ -182,26 +181,25 @@ class write_driver_array(design.design):
base = vector(xoffset, 0)
self.driver_insts[index].place(offset=base, mirror=mirror)
def add_layout_pins(self):
for i in range(self.word_size + self.num_spare_cols):
inst = self.driver_insts[i]
din_pin = inst.get_pin(inst.mod.din_name)
self.add_layout_pin(text=self.data_name + "_{0}".format(i),
layer="m2",
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
bl_pin = inst.get_pin(inst.mod.get_bl_names())
self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i),
layer="m2",
layer=bl_pin.layer,
offset=bl_pin.ll(),
width=bl_pin.width(),
height=bl_pin.height())
br_pin = inst.get_pin(inst.mod.get_br_names())
self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i),
layer="m2",
layer=br_pin.layer,
offset=br_pin.ll(),
width=br_pin.width(),
height=br_pin.height())
@ -209,16 +207,16 @@ class write_driver_array(design.design):
for n in ["vdd", "gnd"]:
pin_list = self.driver_insts[i].get_pins(n)
for pin in pin_list:
self.add_power_pin(name = n,
loc = pin.center(),
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]
inst = self.driver_insts[bit * self.write_size]
en_pin = inst.get_pin(inst.mod.en_name)
# Determine width of wmask modified en_pin with/without col mux
wmask_en_len = self.words_per_row*(self.write_size * self.driver_spacing)
wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing)
if (self.words_per_row == 1):
en_gap = self.driver_spacing - en_pin.width()
else:
@ -227,7 +225,7 @@ class write_driver_array(design.design):
self.add_layout_pin(text=self.en_name + "_{0}".format(bit),
layer=en_pin.layer,
offset=en_pin.ll(),
width=wmask_en_len-en_gap,
width=wmask_en_len - en_gap,
height=en_pin.height())
elif self.num_spare_cols:
@ -250,11 +248,10 @@ class write_driver_array(design.design):
inst = self.driver_insts[0]
self.add_layout_pin(text=self.en_name,
layer="m1",
offset=inst.get_pin(inst.mod.en_name).ll().scale(0,1),
offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1),
width=self.width)
def get_w_en_cin(self):
"""Get the relative capacitance of all the enable connections in the bank"""
#The enable is connected to a nand2 for every row.
# The enable is connected to a nand2 for every row.
return self.driver.get_w_en_cin() * len(self.driver_insts)

View File

@ -30,13 +30,12 @@ class pand2(pgate.pgate):
self.create_insts()
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
self.nand = factory.create(module_type="pnand2", height=self.height)
self.add_mod(self.nand)
self.inv = factory.create(module_type="pdriver",
neg_polarity=True,
fanout=3*self.size,
fanout=self.size,
height=self.height)
self.add_mod(self.inv)
@ -45,6 +44,7 @@ class pand2(pgate.pgate):
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):

View File

@ -44,6 +44,7 @@ class pand3(pgate.pgate):
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):

View File

@ -37,6 +37,7 @@ class pbuf(pgate.pgate):
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.add_boundary()
def add_pins(self):
self.add_pin("A", "INPUT")

View File

@ -76,6 +76,7 @@ class pdriver(pgate.pgate):
self.width = self.inv_inst_list[-1].rx()
self.height = self.inv_inst_list[0].height
self.add_boundary()
def add_pins(self):
self.add_pin("A", "INPUT")

View File

@ -8,10 +8,14 @@
import contact
import design
import debug
from tech import layer
import math
from bisect import bisect_left
from tech import layer, drc
from vector import vector
from globals import OPTS
if(OPTS.tech_name == "s8"):
from tech import nmos_bins, pmos_bins, accuracy_requirement
class pgate(design.design):
"""
@ -64,7 +68,7 @@ class pgate(design.design):
width=source_pin.width())
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left"):
"""
"""
Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or
right of gate.
@ -107,8 +111,12 @@ class pgate(design.design):
else:
debug.error("Invalid contact placement option.", -1)
v=self.add_via_center(layers=self.poly_stack,
offset=contact_offset)
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=contact_offset)
self.add_via_center(layers=self.poly_stack,
offset=contact_offset)
self.add_layout_pin_rect_center(text=name,
layer="m1",
@ -182,13 +190,17 @@ class pgate(design.design):
contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact in x and y
contact_offset += vector(0.5 * pmos.active_contact.first_layer_width,
0.5 * pmos.active_contact.first_layer_height)
0.5 * pmos.active_contact.first_layer_height)
self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=contact_offset,
implant_type="n",
well_type="n")
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=contact_offset)
self.add_rect_center(layer="m1",
offset=contact_offset + vector(0, 0.5 * (self.height-contact_offset.y)),
offset=contact_offset + vector(0, 0.5 * (self.height - contact_offset.y)),
width=self.nwell_contact.mod.second_layer_width,
height=self.height - contact_offset.y)
@ -221,8 +233,6 @@ class pgate(design.design):
layer_stack = self.active_stack
pwell_position = vector(0, -0.5 * self.m1_width)
# To the right a spacing away from the nmos right active edge
contact_xoffset = nmos_pos.x + nmos.active_width \
+ self.active_space
@ -240,8 +250,13 @@ class pgate(design.design):
offset=contact_offset,
implant_type="p",
well_type="p")
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=contact_offset)
self.add_rect_center(layer="m1",
offset=contact_offset.scale(1,0.5),
offset=contact_offset.scale(1, 0.5),
width=self.pwell_contact.mod.second_layer_width,
height=contact_offset.y)
@ -271,5 +286,61 @@ class pgate(design.design):
self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width
self.well_width = self.width + 2 * self.nwell_enclose_active
# Height is an input parameter, so it is not recomputed.
def bin_width(self, tx_type, target_width):
if tx_type == "nmos":
bins = nmos_bins[drc("minwidth_poly")]
elif tx_type == "pmos":
bins = pmos_bins[drc("minwidth_poly")]
else:
debug.error("invalid tx type")
bins = bins[0:bisect_left(bins, target_width) + 1]
if len(bins) == 1:
selected_bin = bins[0]
scaling_factor = math.ceil(target_width / selected_bin)
scaled_bin = bins[0] * scaling_factor
else:
scaled_bins = []
scaling_factors = []
scaled_bins.append(bins[-1])
scaling_factors.append(1)
for width in bins[0:-1]:
m = math.ceil(target_width / width)
scaling_factors.append(m)
scaled_bins.append(m * width)
select = bisect_left(scaled_bins, target_width)
scaling_factor = scaling_factors[select]
scaled_bin = scaled_bins[select]
select = (select + 1) % len(scaled_bins)
selected_bin = bins[select]
debug.info(2, "binning {0} tx, target: {4}, found {1} x {2} = {3}".format(tx_type, selected_bin, scaling_factor, scaled_bin, target_width))
return(selected_bin, scaling_factor)
def permute_widths(self, tx_type, target_width):
if tx_type == "nmos":
bins = nmos_bins[drc("minwidth_poly")]
elif tx_type == "pmos":
bins = pmos_bins[drc("minwidth_poly")]
else:
debug.error("invalid tx type")
bins = bins[0:bisect_left(bins, target_width) + 1]
if len(bins) == 1:
scaled_bins = [(bins[0], math.ceil(target_width / bins[0]))]
else:
scaled_bins = []
scaled_bins.append((bins[-1], 1))
for width in bins[:-1]:
m = math.ceil(target_width / width)
scaled_bins.append((m * width, m))
return(scaled_bins)
def bin_accuracy(self, ideal_width, width):
return abs(1-(ideal_width - width)/ideal_width)

View File

@ -8,14 +8,19 @@
import contact
import pgate
import debug
import operator
from tech import drc, parameter, spice
from vector import vector
from math import ceil
from globals import OPTS
from utils import round_to_grid
from bisect import bisect_left
import logical_effort
from sram_factory import factory
from errors import drc_error
if(OPTS.tech_name == "s8"):
from tech import nmos_bins, pmos_bins, accuracy_requirement
class pinv(pgate.pgate):
"""
@ -63,6 +68,7 @@ class pinv(pgate.pgate):
"A",
position="farleft")
self.route_outputs()
self.add_boundary()
def add_pins(self):
""" Adds pins for spice netlist """
@ -81,6 +87,9 @@ class pinv(pgate.pgate):
self.tx_mults = 1
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
if OPTS.tech_name == "s8":
(self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width)
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
return
# Do a quick sanity check and bail if unlikely feasible height
@ -105,11 +114,14 @@ class pinv(pgate.pgate):
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active + self.poly_space)
total_height = tx_height + min_channel + 2 * self.top_bottom_space
debug.check(self.height > total_height,
"Cell height {0} too small for simple min height {1}.".format(self.height,
total_height))
total_height = tx_height + min_channel + 2 * self.top_bottom_space
# debug.check(self.height > total_height,
# "Cell height {0} too small for simple min height {1}.".format(self.height,
# total_height))
if total_height > self.height:
msg = "Cell height {0} too small for simple min height {1}.".format(self.height, total_height)
raise drc_error(msg)
# Determine the height left to the transistors to determine
# the number of fingers
@ -127,26 +139,62 @@ class pinv(pgate.pgate):
# Determine the number of mults for each to fit width
# into available space
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
nmos_required_mults = max(int(ceil(self.nmos_width / nmos_height_available)), 1)
pmos_required_mults = max(int(ceil(self.pmos_width / pmos_height_available)), 1)
# The mults must be the same for easy connection of poly
self.tx_mults = max(nmos_required_mults, pmos_required_mults)
if OPTS.tech_name != "s8":
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
nmos_required_mults = max(int(ceil(self.nmos_width / nmos_height_available)), 1)
pmos_required_mults = max(int(ceil(self.pmos_width / pmos_height_available)), 1)
# The mults must be the same for easy connection of poly
self.tx_mults = max(nmos_required_mults, pmos_required_mults)
# Recompute each mult width and check it isn't too small
# This could happen if the height is narrow and the size is small
# User should pick a bigger size to fix it...
# We also need to round the width to the grid or we will end up
# with LVS property mismatch errors when fingers are not a grid
# length and get rounded in the offset geometry.
self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults)
debug.check(self.nmos_width >= drc("minwidth_tx"),
"Cannot finger NMOS transistors to fit cell height.")
self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults)
debug.check(self.pmos_width >= drc("minwidth_tx"),
"Cannot finger PMOS transistors to fit cell height.")
# Recompute each mult width and check it isn't too small
# This could happen if the height is narrow and the size is small
# User should pick a bigger size to fix it...
# We also need to round the width to the grid or we will end up
# with LVS property mismatch errors when fingers are not a grid
# length and get rounded in the offset geometry.
self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults)
# debug.check(self.nmos_width >= drc("minwidth_tx"),
# "Cannot finger NMOS transistors to fit cell height.")
if self.nmos_width < drc("minwidth_tx"):
raise drc_error("Cannot finger NMOS transistors to fit cell height.")
self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults)
#debug.check(self.pmos_width >= drc("minwidth_tx"),
# "Cannot finger PMOS transistors to fit cell height.")
if self.pmos_width < drc("minwidth_tx"):
raise drc_error("Cannot finger NMOS transistors to fit cell height.")
else:
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
nmos_bins = self.permute_widths("nmos", self.nmos_width)
pmos_bins = self.permute_widths("pmos", self.pmos_width)
valid_pmos = []
for bin in pmos_bins:
if self.bin_accuracy(self.pmos_width, bin[0]) > accuracy_requirement:
valid_pmos.append(bin)
valid_pmos.sort(key = operator.itemgetter(1))
valid_nmos = []
for bin in nmos_bins:
if self.bin_accuracy(self.nmos_width, bin[0]) > accuracy_requirement:
valid_nmos.append(bin)
valid_nmos.sort(key = operator.itemgetter(1))
for bin in valid_pmos:
if bin[0]/bin[1] < pmos_height_available:
self.pmos_width = bin[0]/bin[1]
pmos_mults = valid_pmos[0][1]
break
for bin in valid_nmos:
if bin[0]/bin[1] < nmos_height_available:
self.nmos_width = bin[0]/bin[1]
nmos_mults = valid_pmos[0][1]
break
self.tx_mults = max(pmos_mults, nmos_mults)
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """

View File

@ -47,6 +47,7 @@ class pinvbuf(pgate.pgate):
self.place_modules()
self.route_wires()
self.add_layout_pins()
self.add_boundary()
self.offset_all_coordinates()

View File

@ -9,6 +9,7 @@ import contact
import pgate
import debug
from tech import drc, parameter, spice
from globals import OPTS
from vector import vector
import logical_effort
from sram_factory import factory
@ -37,6 +38,10 @@ class pnand2(pgate.pgate):
debug.check(size == 1, "Size 1 pnand2 is only supported now.")
self.tx_mults = 1
if OPTS.tech_name == "s8":
(self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width)
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
@ -57,7 +62,8 @@ class pnand2(pgate.pgate):
self.extend_wells()
self.route_inputs()
self.route_output()
self.add_boundary()
def add_pins(self):
""" Adds pins for spice netlist """
pin_list = ["A", "B", "Z", "vdd", "gnd"]
@ -66,13 +72,23 @@ class pnand2(pgate.pgate):
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos)
self.nmos_nd = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_drain_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos_nd)
self.nmos_ns = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos_ns)
self.pmos = factory.create(module_type="ptx",
width=self.pmos_width,
@ -88,7 +104,7 @@ class pnand2(pgate.pgate):
# metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
self.m1_space + contact.m1_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width,
self.m3_space + contact.m2_via.second_layer_width)
@ -99,9 +115,9 @@ class pnand2(pgate.pgate):
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
extra_contact_space = max(-self.nmos_nd.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active + self.poly_space)
def route_supply_rails(self):
@ -130,11 +146,11 @@ class pnand2(pgate.pgate):
self.connect_inst(["Z", "B", "vdd", "vdd"])
self.nmos1_inst = self.add_inst(name="pnand2_nmos1",
mod=self.nmos)
mod=self.nmos_nd)
self.connect_inst(["Z", "B", "net1", "gnd"])
self.nmos2_inst = self.add_inst(name="pnand2_nmos2",
mod=self.nmos)
mod=self.nmos_ns)
self.connect_inst(["net1", "A", "gnd", "gnd"])
def place_ptx(self):
@ -160,16 +176,18 @@ class pnand2(pgate.pgate):
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos_nd.active_height))
def add_well_contacts(self):
"""
"""
Add n/p well taps to the layout and connect to supplies
AFTER the wells are created
"""
self.add_nwell_contact(self.pmos, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos)
self.add_nwell_contact(self.pmos,
self.pmos2_pos + vector(self.m1_pitch, 0))
self.add_pwell_contact(self.nmos_nd,
self.nmos2_pos + vector(self.m1_pitch, 0))
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
@ -187,7 +205,7 @@ class pnand2(pgate.pgate):
self.nmos2_inst,
inputB_yoffset,
"B",
position="right")
position="center")
# This will help with the wells and the input/output placement
self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \
@ -199,6 +217,7 @@ class pnand2(pgate.pgate):
def route_output(self):
""" Route the Z output """
# PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center()
@ -207,29 +226,46 @@ class pnand2(pgate.pgate):
bottom_pin_offset = nmos_pin.center()
# Output pin
out_offset = vector(nmos_pin.center().x + self.m1_pitch,
c_pin = self.get_pin("B")
out_offset = vector(c_pin.cx() + self.m1_pitch,
self.inputA_yoffset)
# Midpoints of the L routes go horizontal first then vertical
mid1_offset = vector(out_offset.x, top_pin_offset.y)
# This routes on M2
# # Midpoints of the L routes go horizontal first then vertical
# mid1_offset = vector(out_offset.x, top_pin_offset.y)
# mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
# # Non-preferred active contacts
# self.add_via_center(layers=self.m1_stack,
# directions=("V", "H"),
# offset=pmos_pin.center())
# # Non-preferred active contacts
# self.add_via_center(layers=self.m1_stack,
# directions=("V", "H"),
# offset=nmos_pin.center())
# self.add_via_center(layers=self.m1_stack,
# offset=out_offset)
# # PMOS1 to mid-drain to NMOS2 drain
# self.add_path("m2",
# [top_pin_offset, mid1_offset, out_offset,
# mid2_offset, bottom_pin_offset])
# This routes on M1
# Midpoints of the L routes goes vertical first then horizontal
mid1_offset = vector(top_pin_offset.x, out_offset.y)
# Midpoints of the L routes goes horizontal first then vertical
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
# Non-preferred active contacts
self.add_via_center(layers=self.m1_stack,
directions=("V", "H"),
offset=pmos_pin.center())
# Non-preferred active contacts
self.add_via_center(layers=self.m1_stack,
directions=("V", "H"),
offset=nmos_pin.center())
self.add_via_center(layers=self.m1_stack,
offset=out_offset)
self.add_path("m1",
[top_pin_offset, mid1_offset, out_offset])
# Route in two segments to have the width rule
self.add_path("m1",
[bottom_pin_offset, mid2_offset + vector(0.5 * self.m1_width, 0)],
width=nmos_pin.height())
self.add_path("m1",
[mid2_offset, out_offset])
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("m2",
[top_pin_offset, mid1_offset, out_offset,
mid2_offset, bottom_pin_offset])
# This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z",
layer="m1",

View File

@ -12,7 +12,7 @@ from tech import drc, parameter, spice
from vector import vector
import logical_effort
from sram_factory import factory
from globals import OPTS
class pnand3(pgate.pgate):
"""
@ -40,6 +40,10 @@ class pnand3(pgate.pgate):
"Size 1 pnand3 is only supported now.")
self.tx_mults = 1
if OPTS.tech_name == "s8":
(self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width)
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
@ -66,17 +70,38 @@ class pnand3(pgate.pgate):
self.extend_wells()
self.route_inputs()
self.route_output()
self.add_boundary()
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos)
self.nmos_nsnd = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=False,
add_drain_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos_nsnd)
self.nmos_ns = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos_ns)
self.nmos_nd = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_drain_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos_nd)
self.pmos = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
@ -130,15 +155,15 @@ class pnand3(pgate.pgate):
self.connect_inst(["Z", "C", "vdd", "vdd"])
self.nmos1_inst = self.add_inst(name="pnand3_nmos1",
mod=self.nmos)
mod=self.nmos_nd)
self.connect_inst(["Z", "C", "net1", "gnd"])
self.nmos2_inst = self.add_inst(name="pnand3_nmos2",
mod=self.nmos)
mod=self.nmos_nsnd)
self.connect_inst(["net1", "B", "net2", "gnd"])
self.nmos3_inst = self.add_inst(name="pnand3_nmos3",
mod=self.nmos)
mod=self.nmos_ns)
self.connect_inst(["net2", "A", "gnd", "gnd"])
def place_ptx(self):
@ -170,8 +195,10 @@ class pnand3(pgate.pgate):
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.pmos, self.pmos3_pos)
self.add_pwell_contact(self.nmos, self.nmos3_pos)
self.add_nwell_contact(self.pmos,
self.pmos3_pos + vector(self.m1_pitch, 0))
self.add_pwell_contact(self.nmos_ns,
self.nmos3_pos + vector(self.m1_pitch, 0))
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
@ -194,22 +221,28 @@ class pnand3(pgate.pgate):
"B",
position="center")
self.inputC_yoffset = self.inputB_yoffset - m1_pitch
# FIXME: constant hack
self.inputC_yoffset = self.inputB_yoffset - 1.15 * m1_pitch
self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
self.inputC_yoffset,
"C",
position="center")
position="right")
self.inputA_yoffset = self.inputB_yoffset + m1_pitch
# FIXME: constant hack
if OPTS.tech_name == "s8":
self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch
else:
self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A",
position="center")
position="left")
def route_output(self):
""" Route the Z output """
# PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain
@ -217,29 +250,56 @@ class pnand3(pgate.pgate):
# NMOS3 drain
nmos3_pin = self.nmos3_inst.get_pin("D")
# Go up to metal2 for ease on all output pins
self.add_via_center(layers=self.m1_stack,
offset=pmos1_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
offset=pmos3_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
offset=nmos3_pin.center(),
directions=("V", "V"))
# midpoint for routing
mid_offset = vector(nmos3_pin.cx() + self.m1_pitch,
self.inputA_yoffset)
# Aligned with the well taps
out_offset = vector(self.nwell_contact.cx(),
self.inputA_yoffset)
# PMOS3 and NMOS3 are drain aligned
self.add_path("m2", [pmos3_pin.center(), nmos3_pin.center()])
# Route in the A input track (top track)
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
# Go up to metal2 for ease on all output pins
# self.add_via_center(layers=self.m1_stack,
# offset=pmos1_pin.center(),
# directions=("V", "V"))
# self.add_via_center(layers=self.m1_stack,
# offset=pmos3_pin.center(),
# directions=("V", "V"))
# self.add_via_center(layers=self.m1_stack,
# offset=nmos3_pin.center(),
# directions=("V", "V"))
# # Route in the A input track (top track)
# mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
# self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
# This extends the output to the edge of the cell
self.add_via_center(layers=self.m1_stack,
offset=mid_offset)
# self.add_via_center(layers=self.m1_stack,
# offset=mid_offset)
top_left_pin_offset = pmos1_pin.center()
top_right_pin_offset = pmos3_pin.center()
bottom_pin_offset = nmos3_pin.center()
# PMOS1 to output
self.add_path("m1", [top_left_pin_offset,
vector(top_left_pin_offset.x, out_offset.y),
out_offset])
# PMOS3 to output
self.add_path("m1", [top_right_pin_offset,
vector(top_right_pin_offset.x, mid_offset.y),
mid_offset])
# NMOS3 to output
mid2_offset = vector(mid_offset.x, bottom_pin_offset.y)
self.add_path("m1",
[bottom_pin_offset, mid2_offset],
width=nmos3_pin.height())
mid3_offset = vector(mid_offset.x, nmos3_pin.by())
self.add_path("m1", [mid3_offset, mid_offset])
self.add_layout_pin_rect_center(text="Z",
layer="m1",
offset=mid_offset,
offset=out_offset,
width=contact.m1_via.first_layer_width,
height=contact.m1_via.first_layer_height)
@ -259,7 +319,7 @@ class pnand3(pgate.pgate):
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1094
return transition_prob * (c_load + c_para)
return transition_prob * (c_load + c_para)
def input_load(self):
"""Return the relative input capacitance of a single input"""

View File

@ -8,6 +8,7 @@
import contact
import pgate
import debug
from globals import OPTS
from tech import drc, parameter, spice
from vector import vector
from sram_factory import factory
@ -36,6 +37,10 @@ class pnor2(pgate.pgate):
debug.check(size==1, "Size 1 pnor2 is only supported now.")
self.tx_mults = 1
if OPTS.tech_name == "s8":
(self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width)
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
@ -56,6 +61,7 @@ class pnor2(pgate.pgate):
self.extend_wells()
self.route_inputs()
self.route_output()
self.add_boundary()
def add_pins(self):
""" Adds pins for spice netlist """
@ -73,33 +79,43 @@ class pnor2(pgate.pgate):
connect_active=True)
self.add_mod(self.nmos)
self.pmos = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.pmos)
self.pmos_nd = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_drain_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.pmos_nd)
self.pmos_ns = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.pmos_ns)
def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """
# metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
self.m1_space + contact.m1_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width,
self.m3_space + contact.m2_via.second_layer_width)
# Compute the other pmos2 location, but determining
# offset to overlap the source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
self.overlap_offset = self.pmos_ns.get_pin("D").ll() - self.pmos_nd.get_pin("S").ll()
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.width = 2 * self.pmos.active_width \
+ self.pmos.active_contact.width \
+ 2 * self.active_space \
+ 0.5 * self.nwell_enclose_active
self.width = 2 * self.pmos_ns.active_width \
+ self.pmos_ns.active_contact.width \
+ 2 * self.active_space \
+ 0.5 * self.nwell_enclose_active
self.well_width = self.width + 2 * self.nwell_enclose_active
# Height is an input parameter, so it is not recomputed.
@ -107,7 +123,7 @@ class pnor2(pgate.pgate):
# to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active,
self.poly_space)
@ -130,11 +146,11 @@ class pnor2(pgate.pgate):
"""
self.pmos1_inst = self.add_inst(name="pnor2_pmos1",
mod=self.pmos)
mod=self.pmos_nd)
self.connect_inst(["vdd", "A", "net1", "vdd"])
self.pmos2_inst = self.add_inst(name="pnor2_pmos2",
mod=self.pmos)
mod=self.pmos_ns)
self.connect_inst(["net1", "B", "Z", "vdd"])
self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
@ -151,15 +167,15 @@ class pnor2(pgate.pgate):
to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height \
pmos1_pos = vector(self.pmos_ns.active_offset.x,
self.height - self.pmos_ns.active_height \
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset
self.pmos2_inst.place(self.pmos2_pos)
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
nmos1_pos = vector(self.pmos_ns.active_offset.x, self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos)
self.nmos2_pos = nmos1_pos + self.overlap_offset
@ -172,7 +188,7 @@ class pnor2(pgate.pgate):
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.pmos, self.pmos2_pos)
self.add_nwell_contact(self.pmos_ns, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos)
def connect_rails(self):
@ -187,8 +203,7 @@ class pnor2(pgate.pgate):
def route_inputs(self):
""" Route the A and B inputs """
# Use M2 spaces so we can drop vias on the pins later!
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
+ self.m2_space + self.m2_width
inputB_yoffset = self.nmos2_inst.uy() + contact.poly_contact.height
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,

View File

@ -8,6 +8,7 @@
import contact
import design
import debug
from pgate import pgate
from tech import parameter
from vector import vector
from globals import OPTS
@ -28,6 +29,7 @@ class precharge(design.design):
self.bitcell = factory.create(module_type="bitcell")
self.beta = parameter["beta"]
self.ptx_width = self.beta * parameter["min_tx_size"]
self.ptx_mults = 1
self.width = self.bitcell.width
self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br
@ -68,7 +70,8 @@ class precharge(design.design):
self.route_vdd_rail()
self.route_bitlines()
self.connect_to_bitlines()
self.add_boundary()
def add_pins(self):
self.add_pin_list(["bl", "br", "en_bar", "vdd"],
["OUTPUT", "OUTPUT", "INPUT", "POWER"])
@ -77,8 +80,11 @@ class precharge(design.design):
"""
Initializes the upper and lower pmos
"""
if(OPTS.tech_name == "s8"):
(self.ptx_width, self.ptx_mults) = pgate.bin_width(self, "pmos", self.ptx_width)
self.pmos = factory.create(module_type="ptx",
width=self.ptx_width,
mults=self.ptx_mults,
tx_type="pmos")
self.add_mod(self.pmos)
@ -96,17 +102,24 @@ class precharge(design.design):
height=layer_width)
pmos_pin = self.upper_pmos2_inst.get_pin("S")
# center of vdd rail
pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos])
# if enable is not on M1, the supply can be
if self.en_layer != "m1":
self.add_via_center(layers=self.m1_stack,
offset=pmos_vdd_pos)
self.add_power_pin("vdd",
self.well_contact_pos,
vertical=True)
# Add vdd pin above the transistor
self.add_power_pin("vdd", self.well_contact_pos, vertical=True)
# Hack for li layers
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=self.well_contact_pos)
def create_ptx(self):
"""
@ -159,7 +172,7 @@ class precharge(design.design):
Connects the upper and lower pmos together
"""
offset = self.lower_pmos_inst.get_pin("G").ll()
offset = self.lower_pmos_inst.get_pin("G").ul()
# connects the top and bottom pmos' gates together
ylength = self.upper_pmos1_inst.get_pin("G").ll().y - offset.y
self.add_rect(layer="poly",
@ -191,7 +204,9 @@ class precharge(design.design):
if self.en_layer == "m2":
self.add_via_center(layers=self.m1_stack,
offset=offset)
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=offset)
# adds the en rail on metal1
self.add_layout_pin_segment_center(text="en_bar",
@ -205,13 +220,18 @@ class precharge(design.design):
"""
# adds the contact from active to metal1
self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \
+ vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \
+ self.nwell_extend_active)
offset_height = self.upper_pmos1_inst.uy() + \
0.5 * contact.active_contact.height + \
self.nwell_extend_active
self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \
vector(0, offset_height)
self.add_via_center(layers=self.active_stack,
offset=self.well_contact_pos,
implant_type="n",
well_type="n")
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=self.well_contact_pos)
self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space

View File

@ -56,6 +56,7 @@ class ptristate_inv(pgate.pgate):
self.connect_rails()
self.route_inputs()
self.route_outputs()
self.add_boundary()
def add_pins(self):
""" Adds pins for spice netlist """

View File

@ -11,6 +11,9 @@ from tech import layer, drc, spice
from vector import vector
from sram_factory import factory
import contact
import logical_effort
import os
from globals import OPTS
class ptx(design.design):
@ -27,15 +30,23 @@ class ptx(design.design):
width=drc("minwidth_tx"),
mults=1,
tx_type="nmos",
add_source_contact=True,
add_drain_contact=True,
series_devices=False,
connect_active=False,
connect_poly=False,
series_devices=False,
num_contacts=None):
# We need to keep unique names because outputting to GDSII
# will use the last record with a given name. I.e., you will
# over-write a design in GDS if one has and the other doesn't
# have poly connected, for example.
name = "{0}_m{1}_w{2:.3f}".format(tx_type, mults, width)
if not add_source_contact:
name += "_ns"
if not add_drain_contact:
name += "_nd"
if series_devices:
name += "_sd"
if connect_active:
name += "_a"
if connect_poly:
@ -52,6 +63,8 @@ class ptx(design.design):
self.tx_width = width
self.connect_active = connect_active
self.connect_poly = connect_poly
self.add_source_contact = add_source_contact
self.add_drain_contact = add_drain_contact
self.series_devices = series_devices
self.num_contacts = num_contacts
@ -89,22 +102,38 @@ class ptx(design.design):
body_dir = 'POWER'
dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir]
self.add_pin_list(pin_list, dir_list)
# self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
# " ".join(self.pins)))
# Just make a guess since these will actually
# be decided in the layout later.
area_sd = 2.5 * self.poly_width * self.tx_width
perimeter_sd = 2 * self.poly_width + 2 * self.tx_width
main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"))
area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd,
area_sd)
if OPTS.tech_name == "s8":
# s8 technology is in microns
main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"))
# Perimeters are in microns
# Area is in u since it is microns square
area_str = "pd={0:.2f} ps={0:.2f} as={1:.2f}u ad={1:.2f}u".format(perimeter_sd,
area_sd)
else:
main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"))
area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd,
area_sd)
self.spice_device = main_str + area_str
self.spice.append("\n* ptx " + self.spice_device)
# self.spice.append(".ENDS {0}".format(self.name))
# LVS lib is always in SI units
if os.path.exists(OPTS.openram_tech + "lvs_lib"):
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"))
def setup_layout_constants(self):
"""
@ -186,6 +215,8 @@ class ptx(design.design):
# The well is not included in the height and width
self.height = self.poly_height
self.width = self.active_width
self.well_height = self.height
self.well_width = self.width
# This is the center of the first active contact offset (centered vertically)
self.contact_offset = self.active_offset + vector(0.5 * self.active_contact.width,
@ -343,10 +374,10 @@ class ptx(design.design):
if not (well_name in layer or "vtg" in layer):
return
center_pos = self.active_offset + vector(0.5*self.active_width,
0.5*self.active_height)
well_ll = center_pos - vector(0.5*self.well_width,
0.5*self.well_height)
center_pos = self.active_offset + vector(0.5 * self.active_width,
0.5 * self.active_height)
well_ll = center_pos - vector(0.5 * self.well_width,
0.5 * self.well_height)
if well_name in layer:
self.add_rect(layer=well_name,
offset=well_ll,
@ -379,18 +410,13 @@ class ptx(design.design):
# First one is always a SOURCE
label = "S"
pos = self.contact_offset
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text=label,
layer="m1",
offset=pos,
width=contact.mod.second_layer_width,
height=contact.mod.second_layer_height)
self.source_contacts.append(contact)
if self.add_source_contact:
contact = self.add_diff_contact(label, pos)
self.source_contacts.append(contact)
else:
self.add_layout_pin_rect_center(text=label,
layer="active",
offset=pos)
source_positions.append(pos)
# Skip these if they are going to be in series
@ -406,22 +432,16 @@ class ptx(design.design):
label = "S"
source_positions.append(pos)
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text=label,
layer="m1",
offset=pos,
width=contact.mod.second_layer_width,
height=contact.mod.second_layer_height)
if label == "S":
self.source_contacts.append(contact)
if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact):
contact = self.add_diff_contact(label, pos)
if label == "S":
self.source_contacts.append(contact)
else:
self.drain_contacts.append(contact)
else:
self.drain_contacts.append(contact)
self.add_layout_pin_rect_center(text=label,
layer="active",
offset=pos)
pos = vector(self.active_offset.x + self.active_width - 0.5 * self.active_contact.width,
self.contact_offset.y)
@ -432,25 +452,71 @@ class ptx(design.design):
else:
label = "S"
source_positions.append(pos)
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text=label,
layer="m1",
offset=pos,
width=contact.mod.second_layer_width,
height=contact.mod.second_layer_height)
if label == "S":
self.source_contacts.append(contact)
if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact):
contact = self.add_diff_contact(label, pos)
if label == "S":
self.source_contacts.append(contact)
else:
self.drain_contacts.append(contact)
else:
self.drain_contacts.append(contact)
self.add_layout_pin_rect_center(text=label,
layer="active",
offset=pos)
if self.connect_active:
self.connect_fingered_active(drain_positions, source_positions)
def get_stage_effort(self, cout):
"""Returns an object representing the parameters for delay in tau units."""
# FIXME: Using the same definition as the pinv.py.
parasitic_delay = 1
size = self.mults * self.tx_width / drc("minwidth_tx")
return logical_effort.logical_effort(self.name,
size,
self.input_load(),
cout,
parasitic_delay)
def input_load(self):
"""
Returns the relative gate cin of the tx
"""
# FIXME: this will be applied for the loads of the drain/source
return self.mults * self.tx_width / drc("minwidth_tx")
def add_diff_contact(self, label, pos):
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("V", "V"),
implant_type=self.implant_type,
well_type=self.well_type)
if hasattr(self, "li_stack"):
contact=self.add_via_center(layers=self.li_stack,
offset=pos,
directions=("V", "V"))
# contact_area = contact.mod.second_layer_width * contact.mod.second_layer_height
# min_area = drc("minarea_m1")
# width = contact.mod.second_layer_width
# if contact_area < min_area:
# height = min_area / width
# else:
# height = contact.mod.second_layer_height
width = contact.mod.second_layer_width
height = contact.mod.second_layer_height
self.add_layout_pin_rect_center(text=label,
layer="m1",
offset=pos,
width=width,
height=height)
return(contact)
def get_cin(self):
"""Returns the relative gate cin of the tx"""
return self.tx_width / drc("minwidth_tx")

View File

@ -52,6 +52,7 @@ class pwrite_driver(design.design):
self.place_modules()
self.route_wires()
self.route_supplies()
self.add_boundary()
def add_pins(self):
self.add_pin("din", "INPUT")

View File

@ -7,7 +7,7 @@
#
import pgate
import debug
from tech import drc
from tech import drc, layer
from vector import vector
from sram_factory import factory
import logical_effort
@ -22,13 +22,13 @@ class single_level_column_mux(pgate.pgate):
for optimal speed
"""
def __init__(self, name, tx_size=8, bitcell_bl="bl", bitcell_br="br"):
debug.info(2, "creating single column mux cell: {0}".format(name))
self.tx_size = int(tx_size)
self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br
pgate.pgate.__init__(self, name)
def get_bl_names(self):
@ -41,9 +41,9 @@ class single_level_column_mux(pgate.pgate):
self.add_modules()
self.add_pins()
self.add_ptx()
def create_layout(self):
self.pin_height = 2 * self.m2_width
self.width = self.bitcell.width
self.height = self.nmos_upper.uy() + self.pin_height
@ -57,41 +57,47 @@ class single_level_column_mux(pgate.pgate):
# Adds nmos_lower,nmos_upper to the module
self.ptx_width = self.tx_size * drc("minwidth_tx")
self.nmos = factory.create(module_type="ptx", width=self.ptx_width)
self.nmos = factory.create(module_type="ptx",
width=self.ptx_width,
add_source_contact=False,
add_drain_contact=False)
self.add_mod(self.nmos)
def add_pins(self):
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
def add_bitline_pins(self):
""" Add the top and bottom pins to this cell """
bl_pos = vector(self.bitcell.get_pin(self.bitcell_bl).lx(), 0)
br_pos = vector(self.bitcell.get_pin(self.bitcell_br).lx(), 0)
bl_pin=self.bitcell.get_pin(self.bitcell_bl)
br_pin=self.bitcell.get_pin(self.bitcell_br)
bl_pos = vector(bl_pin.lx(), 0)
br_pos = vector(br_pin.lx(), 0)
# bl and br
self.add_layout_pin(text="bl",
layer="m2",
layer=bl_pin.layer,
offset=bl_pos + vector(0, self.height - self.pin_height),
height=self.pin_height)
self.add_layout_pin(text="br",
layer="m2",
layer=br_pin.layer,
offset=br_pos + vector(0, self.height - self.pin_height),
height=self.pin_height)
# bl_out and br_out
self.add_layout_pin(text="bl_out",
layer="m2",
layer=bl_pin.layer,
offset=bl_pos,
height=self.pin_height)
self.add_layout_pin(text="br_out",
layer="m2",
layer=br_pin.layer,
offset=br_pos,
height=self.pin_height)
def add_ptx(self):
""" Create the two pass gate NMOS transistors to switch the bitlines"""
# Space it in the center
nmos_lower_position = self.nmos.active_offset.scale(0,1) \
+ vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0)
@ -102,7 +108,7 @@ class single_level_column_mux(pgate.pgate):
# This aligns it directly above the other tx with gates abutting
nmos_upper_position = nmos_lower_position \
+ vector(0, self.nmos.active_height + self.poly_space)
+ vector(0, self.nmos.active_height + max(self.active_space,self.poly_space))
self.nmos_upper = self.add_inst(name="mux_tx2",
mod=self.nmos,
offset=nmos_upper_position)
@ -110,15 +116,30 @@ class single_level_column_mux(pgate.pgate):
def connect_poly(self):
""" Connect the poly gate of the two pass transistors """
height = self.nmos_upper.get_pin("G").uy() - self.nmos_lower.get_pin("G").by()
# offset is the top of the lower nmos' diffusion
# height is the distance between the nmos' diffusions, which depends on max(self.active_space,self.poly_space)
offset = self.nmos_lower.get_pin("G").ul() - vector(0,self.poly_extend_active)
height = self.nmos_upper.get_pin("G").by() + self.poly_extend_active - offset.y
self.add_rect(layer="poly",
offset=offset,
height=height)
# Add the sel pin to the bottom of the mux
self.add_layout_pin(text="sel",
layer="poly",
offset=self.nmos_lower.get_pin("G").ll(),
height=height)
height=self.poly_extend_active)
def connect_bitlines(self):
""" Connect the bitlines to the mux transistors """
# If li exists, use li and m1 for the mux, otherwise use m1 and m2
if "li" in layer:
self.col_mux_stack = self.li_stack
else:
self.col_mux_stack = self.m1_stack
# These are on metal2
bl_pin = self.get_pin("bl")
br_pin = self.get_pin("br")
@ -132,35 +153,60 @@ class single_level_column_mux(pgate.pgate):
nmos_upper_d_pin = self.nmos_upper.get_pin("D")
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
self.add_via_center(layers=self.m1_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=bl_pin.bc(),
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=br_out_pin.uc(),
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=nmos_upper_s_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=nmos_lower_d_pin.center(),
directions=("V", "V"))
# Add diffusion contacts
# These were previously omitted with the options: add_source_contact=False, add_drain_contact=False
# They are added now and not previously so that they do not include m1 (which is usually included by default)
# This is only a concern when the local interconnect (li) layer is being used
self.add_via_center(layers=self.active_stack,
offset=nmos_upper_d_pin.center(),
directions=("V", "V"),
implant_type="n",
well_type="nwell")
self.add_via_center(layers=self.active_stack,
offset=nmos_lower_s_pin.center(),
directions=("V", "V"),
implant_type="n",
well_type="nwell")
self.add_via_center(layers=self.active_stack,
offset=nmos_upper_s_pin.center(),
directions=("V", "V"),
implant_type="n",
well_type="nwell")
self.add_via_center(layers=self.active_stack,
offset=nmos_lower_d_pin.center(),
directions=("V", "V"),
implant_type="n",
well_type="nwell")
# bl -> nmos_upper/D on metal1
# bl_out -> nmos_upper/S on metal2
self.add_path("m1",
self.add_path(self.col_mux_stack[0],
[bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()),
nmos_upper_d_pin.center()])
# halfway up, move over
mid1 = bl_out_pin.uc().scale(1, 0.4) \
+ nmos_upper_s_pin.bc().scale(0, 0.4)
mid2 = bl_out_pin.uc().scale(0, 0.4) \
+ nmos_upper_s_pin.bc().scale(1, 0.4)
self.add_path("m2",
[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()])
+ nmos_upper_s_pin.bc().scale(1, 0.4)
self.add_path(self.col_mux_stack[2],
[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.center()])
# br -> nmos_lower/D on metal2
# br_out -> nmos_lower/S on metal1
self.add_path("m1",
self.add_path(self.col_mux_stack[0],
[br_out_pin.uc(),
vector(nmos_lower_s_pin.cx(), br_out_pin.uy()),
nmos_lower_s_pin.center()])
@ -169,9 +215,9 @@ class single_level_column_mux(pgate.pgate):
+ nmos_lower_d_pin.uc().scale(0,0.5)
mid2 = br_pin.bc().scale(0,0.5) \
+ nmos_lower_d_pin.uc().scale(1,0.5)
self.add_path("m2",
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()])
self.add_path(self.col_mux_stack[2],
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()])
def add_wells(self):
"""
Add a well and implant over the whole cell. Also, add the
@ -186,17 +232,22 @@ class single_level_column_mux(pgate.pgate):
implant_type="p",
well_type="p")
# If there is a li layer, include it in the power stack
self.add_via_center(layers=self.col_mux_stack,
offset=active_pos)
# Add the M1->..->power_grid_layer stack
self.add_power_pin(name = "gnd",
loc = active_pos,
start_layer="m1")
# Add well enclosure over all the tx and contact
self.add_rect(layer="pwell",
offset=vector(0, 0),
width=self.bitcell.width,
height=self.height)
if "pwell" in layer:
self.add_rect(layer="pwell",
offset=vector(0, 0),
width=self.bitcell.width,
height=self.height)
def get_stage_effort(self, corner, slew, load):
"""
Returns relative delay that the column mux.
@ -211,4 +262,3 @@ class single_level_column_mux(pgate.pgate):
load,
parasitic_delay,
False)

View File

@ -51,6 +51,9 @@ class sram():
def sp_write(self, name):
self.s.sp_write(name)
def lvs_write(self, name):
self.s.lvs_write(name)
def lef_write(self, name):
self.s.lef_write(name)

View File

@ -121,7 +121,7 @@ class sram_base(design, verilog, lef):
start_time = datetime.datetime.now()
# We only enable final verification if we have routed the design
self.DRC_LVS(final_verification=OPTS.route_supplies, top_level=True)
self.DRC_LVS(final_verification=OPTS.route_supplies)
if not OPTS.is_unit_test:
print_time("Verification", datetime.datetime.now(), start_time)
@ -561,7 +561,7 @@ class sram_base(design, verilog, lef):
self.add_via_center(layers=self.m2_stack,
offset=out_pos)
def sp_write(self, sp_name):
def sp_write(self, sp_name, lvs_netlist=False):
# Write the entire spice of the object to the file
############################################################
# Spice circuit
@ -574,17 +574,20 @@ class sram_base(design, verilog, lef):
sp.write("* Data bits: {}\n".format(self.word_size))
sp.write("* Banks: {}\n".format(self.num_banks))
sp.write("* Column mux: {}:1\n".format(self.words_per_row))
sp.write("**************************************************\n")
sp.write("**************************************************\n")
# This causes unit test mismatch
# sp.write("* Created: {0}\n".format(datetime.datetime.now()))
# sp.write("* User: {0}\n".format(getpass.getuser()))
# sp.write(".global {0} {1}\n".format(spice["vdd_name"],
# sp.write(".global {0} {1}\n".format(spice["vdd_name"],
# spice["gnd_name"]))
usedMODS = list()
self.sp_write_file(sp, usedMODS)
self.sp_write_file(sp, usedMODS, lvs_netlist=lvs_netlist)
del usedMODS
sp.close()
def lvs_write(self, sp_name):
self.sp_write(sp_name, lvs_netlist=True)
def get_wordline_stage_efforts(self, inp_is_rise=True):
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""
stage_effort_list = []

View File

@ -45,7 +45,9 @@ class library_lvs_test(openram_test):
def setup_files():
gds_dir = OPTS.openram_tech + "/gds_lib"
sp_dir = OPTS.openram_tech + "/sp_lib"
sp_dir = OPTS.openram_tech + "/lvs_lib"
if not os.path.exists(sp_dir):
sp_dir = OPTS.openram_tech + "/sp_lib"
files = os.listdir(gds_dir)
nametest = re.compile("\.gds$", re.IGNORECASE)
gds_files = list(filter(nametest.search, files))

View File

@ -15,6 +15,7 @@ from globals import OPTS
from sram_factory import factory
import debug
class ptx_1finger_nmos_test(openram_test):
def runTest(self):

View File

@ -0,0 +1,52 @@
#!/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 precharge_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# check precharge in multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(2, "Checking precharge for pbitcell (innermost connections)")
tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(tx)
factory.reset()
debug.info(2, "Checking precharge for pbitcell (innermost connections)")
tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1")
self.local_check(tx)
factory.reset()
debug.info(2, "Checking precharge for pbitcell (outermost connections)")
tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl2", bitcell_br="br2")
self.local_check(tx)
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

@ -15,6 +15,7 @@ from globals import OPTS
from sram_factory import factory
import debug
class precharge_test(openram_test):
def runTest(self):
@ -26,27 +27,6 @@ class precharge_test(openram_test):
tx = factory.create(module_type="precharge", size=1)
self.local_check(tx)
# check precharge in multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(2, "Checking precharge for pbitcell (innermost connections)")
tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(tx)
factory.reset()
debug.info(2, "Checking precharge for pbitcell (innermost connections)")
tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1")
self.local_check(tx)
factory.reset()
debug.info(2, "Checking precharge for pbitcell (outermost connections)")
tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl2", bitcell_br="br2")
self.local_check(tx)
globals.end_openram()
# run the test from the command line

View File

@ -0,0 +1,49 @@
#!/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
#@unittest.skip("SKIPPING 04_driver_test")
class single_level_column_mux_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# check single level column mux in multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(2, "Checking column mux for pbitcell (innermost connections)")
tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(tx)
factory.reset()
debug.info(2, "Checking column mux for pbitcell (outermost connections)")
tx = factory.create(module_type="single_level_column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2")
self.local_check(tx)
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

@ -22,30 +22,14 @@ class single_level_column_mux_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# check single level column mux in single port
debug.info(2, "Checking column mux")
tx = factory.create(module_type="single_level_column_mux", tx_size=8)
self.local_check(tx)
# check single level column mux in multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(2, "Checking column mux for pbitcell (innermost connections)")
tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(tx)
factory.reset()
debug.info(2, "Checking column mux for pbitcell (outermost connections)")
tx = factory.create(module_type="single_level_column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2")
self.local_check(tx)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California
# Copyright (c) 2016-2019 Regents of the University of California
# All rights reserved.
#
import unittest
@ -13,7 +13,7 @@ from globals import OPTS
from sram_factory import factory
import debug
class replica_bitcell_array_test(openram_test):
class replica_pbitcell_array_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
@ -41,7 +41,7 @@ class replica_bitcell_array_test(openram_test):
debug.info(2, "Testing 4x4 array for pbitcell")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0])
self.local_check(a)
globals.end_openram()
# run the test from the command line

View File

@ -0,0 +1,73 @@
#!/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_decoder_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# check hierarchical decoder for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=23)
self.local_check(a)
debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=65)
self.local_check(a)
debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=128)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=341)
self.local_check(a)
debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
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

@ -20,92 +20,40 @@ class hierarchical_decoder_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# Doesn't require hierarchical decoder
# debug.info(1, "Testing 4 row sample for hierarchical_decoder")
# a = hierarchical_decoder.hierarchical_decoder(name="hd1, rows=4)
# self.local_check(a)
# Doesn't require hierarchical decoder
# debug.info(1, "Testing 8 row sample for hierarchical_decoder")
# a = hierarchical_decoder.hierarchical_decoder(name="hd2", rows=8)
# self.local_check(a)
# check hierarchical decoder for single port
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=16)
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
self.local_check(a)
debug.info(1, "Testing 17 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=17)
a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
self.local_check(a)
debug.info(1, "Testing 23 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=23)
a = factory.create(module_type="hierarchical_decoder", num_outputs=23)
self.local_check(a)
debug.info(1, "Testing 32 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=32)
a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
self.local_check(a)
debug.info(1, "Testing 65 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=65)
a = factory.create(module_type="hierarchical_decoder", num_outputs=65)
self.local_check(a)
debug.info(1, "Testing 128 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=128)
a = factory.create(module_type="hierarchical_decoder", num_outputs=128)
self.local_check(a)
debug.info(1, "Testing 341 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=341)
a = factory.create(module_type="hierarchical_decoder", num_outputs=341)
self.local_check(a)
debug.info(1, "Testing 512 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=512)
a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
self.local_check(a)
# check hierarchical decoder for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=16)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=17)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=23)
self.local_check(a)
debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=32)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=65)
self.local_check(a)
debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=128)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=341)
self.local_check(a)
debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=512)
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_predecode2x4_pbitcell_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,18 +26,8 @@ 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
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()

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_pbitcell_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

@ -25,16 +25,6 @@ class hierarchical_predecode3x8_test(openram_test):
debug.info(1, "Testing sample for hierarchy_predecode3x8")
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()

View File

@ -0,0 +1,55 @@
#!/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.
#
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 single_level_column_mux_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
import single_level_column_mux_array
# check single level column mux array in multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(1, "Testing sample for 2-way column_mux_array in multi-port")
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 4-way column_mux_array in multi-port")
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)")
a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)")
a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2")
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

@ -20,7 +20,7 @@ class single_level_column_mux_test(openram_test):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
import single_level_column_mux_array
# check single level column mux array in single port
debug.info(1, "Testing sample for 2-way column_mux_array")
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8)
@ -33,32 +33,9 @@ class single_level_column_mux_test(openram_test):
debug.info(1, "Testing sample for 8-way column_mux_array")
a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4)
self.local_check(a)
# check single level column mux array in multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(1, "Testing sample for 2-way column_mux_array in multi-port")
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 4-way column_mux_array in multi-port")
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)")
a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)")
a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2")
self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":

View File

@ -0,0 +1,50 @@
#!/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 precharge_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# check precharge array in multi-port
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
factory.reset()
debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell")
pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(pc)
# debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)")
# pc = precharge_array.precharge_array(name="pre3", columns=3, bitcell_bl="bl0", bitcell_br="br0")
# self.local_check(pc)
# debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)")
# pc = precharge_array.precharge_array(name="pre4", columns=3, bitcell_bl="bl2", bitcell_br="br2")
# self.local_check(pc)
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

@ -25,25 +25,6 @@ class precharge_test(openram_test):
debug.info(2, "Checking 3 column precharge")
pc = factory.create(module_type="precharge_array", columns=3)
self.local_check(pc)
# check precharge array in multi-port
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
factory.reset()
debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell")
pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(pc)
# debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)")
# pc = precharge_array.precharge_array(name="pre3", columns=3, bitcell_bl="bl0", bitcell_br="br0")
# self.local_check(pc)
# debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)")
# pc = precharge_array.precharge_array(name="pre4", columns=3, bitcell_bl="bl2", bitcell_br="br2")
# self.local_check(pc)
globals.end_openram()

View File

@ -0,0 +1,44 @@
#!/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
#@unittest.skip("SKIPPING 04_driver_test")
class wordline_driver_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# check wordline driver for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Checking driver (multi-port case)")
tx = factory.create(module_type="wordline_driver", rows=8, cols=64)
self.local_check(tx)
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

@ -28,19 +28,8 @@ class wordline_driver_test(openram_test):
tx = factory.create(module_type="wordline_driver", rows=8, cols=32)
self.local_check(tx)
# check wordline driver for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Checking driver (multi-port case)")
tx = factory.create(module_type="wordline_driver", rows=8, cols=64)
self.local_check(tx)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()

View File

@ -22,6 +22,10 @@ class sense_amp_test(openram_test):
globals.init_openram(config_file)
# check sense amp array for single port
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=1")
a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=1)
self.local_check(a)
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2")
a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2)
self.local_check(a)
@ -29,24 +33,9 @@ class sense_amp_test(openram_test):
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4")
a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4)
self.local_check(a)
# check sense amp array for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)")
a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2)
self.local_check(a)
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 (multi-port case)")
a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4)
self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()

View File

@ -0,0 +1,46 @@
#!/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 sense_amp_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
#check sense amp array for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)")
a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2)
self.local_check(a)
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 (multi-port case)")
a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4)
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

@ -0,0 +1,46 @@
#!/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 write_driver_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# check write driver array for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)")
a = factory.create(module_type="write_driver_array", columns=8, word_size=8)
self.local_check(a)
debug.info(2, "Testing write_driver_array for columns=16, word_size=8 (multi-port case)")
a = factory.create(module_type="write_driver_array", columns=16, word_size=8)
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

@ -29,22 +29,7 @@ class write_driver_test(openram_test):
debug.info(2, "Testing write_driver_array for columns=16, word_size=8")
a = factory.create(module_type="write_driver_array", columns=16, word_size=8)
self.local_check(a)
# check write driver array for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)")
a = factory.create(module_type="write_driver_array", columns=8, word_size=8)
self.local_check(a)
debug.info(2, "Testing write_driver_array for columns=16, word_size=8 (multi-port case)")
a = factory.create(module_type="write_driver_array", columns=16, word_size=8)
self.local_check(a)
globals.end_openram()
# run the test from the command line

View File

@ -0,0 +1,49 @@
#!/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 write_driver_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# check write driver array for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4 (multi-port case)")
a = factory.create(module_type="write_driver_array", columns=8, word_size=8, write_size=4)
self.local_check(a)
debug.info(2, "Testing write_driver_array for columns=16, word_size=8, write_size=4 (multi-port case)")
a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4)
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

@ -36,21 +36,6 @@ class write_driver_test(openram_test):
a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4)
self.local_check(a)
# check write driver array for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4 (multi-port case)")
a = factory.create(module_type="write_driver_array", columns=8, word_size=8, write_size=4)
self.local_check(a)
debug.info(2, "Testing write_driver_array for columns=16, word_size=8, write_size=4 (multi-port case)")
a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4)
self.local_check(a)
globals.end_openram()

View File

@ -0,0 +1,49 @@
#!/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 write_mask_and_array_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# check write driver array for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4 (multi-port case)")
a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4)
self.local_check(a)
debug.info(2, "Testing write_mask_and_array for columns=16, word_size=8, write_size=2 (multi-port case)")
a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2)
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

@ -36,21 +36,6 @@ class write_mask_and_array_test(openram_test):
a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2)
self.local_check(a)
# check write driver array for multi-port
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4 (multi-port case)")
a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4)
self.local_check(a)
debug.info(2, "Testing write_mask_and_array for columns=16, word_size=8, write_size=2 (multi-port case)")
a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2)
self.local_check(a)
globals.end_openram()

View File

@ -0,0 +1,78 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California
# 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 port_data_1rw_1r_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
from sram_config import sram_config
OPTS.bitcell = "bitcell_1w_1r"
OPTS.num_rw_ports = 0
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
c = sram_config(word_size=4,
num_words=16)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("port_data", sram_config=c, port=0)
self.local_check(a)
a = factory.create("port_data", sram_config=c, port=1)
self.local_check(a)
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create("port_data", sram_config=c, port=0)
self.local_check(a)
a = factory.create("port_data", sram_config=c, port=1)
self.local_check(a)
c.num_words=64
c.words_per_row=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create("port_data", sram_config=c, port=0)
self.local_check(a)
a = factory.create("port_data", sram_config=c, port=1)
self.local_check(a)
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("port_data", sram_config=c, port=0)
self.local_check(a)
a = factory.create("port_data", sram_config=c, port=1)
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

@ -55,52 +55,6 @@ class port_data_test(openram_test):
a = factory.create("port_data", sram_config=c, port=0)
self.local_check(a)
OPTS.bitcell = "bitcell_1w_1r"
OPTS.num_rw_ports = 0
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
c.num_words=16
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("port_data", sram_config=c, port=0)
self.local_check(a)
a = factory.create("port_data", sram_config=c, port=1)
self.local_check(a)
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create("port_data", sram_config=c, port=0)
self.local_check(a)
a = factory.create("port_data", sram_config=c, port=1)
self.local_check(a)
c.num_words=64
c.words_per_row=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create("port_data", sram_config=c, port=0)
self.local_check(a)
a = factory.create("port_data", sram_config=c, port=1)
self.local_check(a)
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("port_data", sram_config=c, port=0)
self.local_check(a)
a = factory.create("port_data", sram_config=c, port=1)
self.local_check(a)
globals.end_openram()
# run the test from the command line

View File

@ -0,0 +1,48 @@
#!/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 bank_select_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
OPTS.bitcell = "pbitcell"
debug.info(1, "No column mux, rw control logic")
a = factory.create(module_type="bank_select", port="rw")
self.local_check(a)
OPTS.num_rw_ports = 0
OPTS.num_w_ports = 1
OPTS.num_r_ports = 1
debug.info(1, "No column mux, w control logic")
a = factory.create(module_type="bank_select", port="w")
self.local_check(a)
debug.info(1, "No column mux, r control logic")
a = factory.create(module_type="bank_select", port="r")
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

@ -24,26 +24,9 @@ class bank_select_test(openram_test):
debug.info(1, "No column mux, rw control logic")
a = factory.create(module_type="bank_select", port="rw")
self.local_check(a)
OPTS.bitcell = "pbitcell"
debug.info(1, "No column mux, rw control logic")
a = factory.create(module_type="bank_select", port="rw")
self.local_check(a)
OPTS.num_rw_ports = 0
OPTS.num_w_ports = 1
OPTS.num_r_ports = 1
debug.info(1, "No column mux, w control logic")
a = factory.create(module_type="bank_select", port="w")
self.local_check(a)
debug.info(1, "No column mux, r control logic")
a = factory.create(module_type="bank_select", port="r")
self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()

View File

@ -60,29 +60,36 @@ class timing_sram_test(openram_test):
data, port_data = d.analyze(probe_address, probe_data, slews, loads)
#Combine info about port into all data
data.update(port_data[0])
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [0.2383338],
'delay_lh': [0.2383338],
'leakage_power': 0.0014532999999999998,
'min_period': 0.898,
'read0_power': [0.30059800000000003],
'read1_power': [0.30061810000000005],
'slew_hl': [0.25358420000000004],
'slew_lh': [0.25358420000000004],
'write0_power': [0.34616749999999996],
'write1_power': [0.2792924]}
golden_data = {'min_period': 0.898,
'write1_power': [0.2659137999999999],
'disabled_write0_power': [0.1782495],
'disabled_read0_power': [0.14490679999999997],
'write0_power': [0.3330119],
'disabled_write1_power': [0.1865223],
'leakage_power': 0.0014532,
'disabled_read1_power': [0.1627516],
'slew_lh': [0.25367799999999996],
'slew_hl': [0.25367799999999996],
'delay_lh': [0.23820930000000004],
'delay_hl': [0.23820930000000004],
'read1_power': [0.3005756],
'read0_power': [0.3005888]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [1.7448],
'delay_lh': [1.7448],
'leakage_power': 0.0006356744000000001,
golden_data = {'leakage_power': 0.0006356576000000001,
'write1_power': [11.292700000000002],
'read0_power': [12.98],
'disabled_write1_power': [8.3707],
'write0_power': [14.4447], 'delay_hl': [1.7445000000000002],
'disabled_read0_power': [6.4325],
'slew_hl': [1.7437],
'disabled_write0_power': [8.1307],
'slew_lh': [1.7437],
'read1_power': [12.9869],
'disabled_read1_power': [7.706],
'min_period': 6.25,
'read0_power': [12.9846],
'read1_power': [12.9722],
'slew_hl': [1.7433],
'slew_lh': [1.7433],
'write0_power': [14.8772],
'write1_power': [11.7217]}
'delay_lh': [1.7445000000000002]}
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results

View File

@ -55,27 +55,35 @@ class timing_sram_test(openram_test):
data.update(port_data[0])
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [0.2264205],
'delay_lh': [0.2264205],
'leakage_power': 0.0021017429999999997,
'min_period': 0.859,
'read0_power': [0.3339161],
'read1_power': [0.31329440000000003],
'slew_hl': [0.2590786],
'slew_lh': [0.2590786],
'write0_power': [0.36360849999999995],
'write1_power': [0.3486931]}
golden_data = {'slew_lh': [0.2592187],
'slew_hl': [0.2592187],
'delay_lh': [0.2465583],
'disabled_write0_power': [0.1924678],
'disabled_read0_power': [0.152483],
'write0_power': [0.3409064],
'disabled_read1_power': [0.1737818],
'read0_power': [0.3096708],
'read1_power': [0.3107916],
'delay_hl': [0.2465583],
'write1_power': [0.26915849999999997],
'leakage_power': 0.002044307,
'min_period': 0.898,
'disabled_write1_power': [0.201411]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [1.85985],
'delay_lh': [1.85985],
golden_data = {'read1_power': [12.11658],
'write1_power': [10.52653],
'read0_power': [11.956710000000001],
'disabled_write0_power': [7.673665],
'disabled_write1_power': [7.981922000000001],
'slew_lh': [1.868836],
'slew_hl': [1.868836],
'delay_hl': [1.8598510000000001],
'delay_lh': [1.8598510000000001],
'leakage_power': 0.008613619,
'disabled_read0_power': [5.904712],
'min_period': 6.875,
'read0_power': [12.656310000000001],
'read1_power': [12.11682],
'slew_hl': [1.868942],
'slew_lh': [1.868942],
'write0_power': [13.978110000000001],
'write1_power': [11.437930000000001]}
'disabled_read1_power': [7.132159],
'write0_power': [13.406400000000001]}
else:
self.assertTrue(False) # other techs fail

View File

@ -19,7 +19,9 @@ class verilog_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
OPTS.route_supplies=False
OPTS.check_lvsdrc=False
OPTS.netlist_only=True
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=2,

View File

@ -51,7 +51,7 @@ class openram_back_end_test(openram_test):
debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage")
exe_name = "{0}/openram.py ".format(OPENRAM_HOME)
else:
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
config_name = "{0}/tests/configs/config_back_end.py".format(OPENRAM_HOME)
cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
out_file,
@ -64,33 +64,32 @@ class openram_back_end_test(openram_test):
# assert an error until we actually check a resul
for extension in ["gds", "v", "lef", "sp"]:
filename = "{0}/{1}.{2}".format(out_path,out_file,extension)
debug.info(1,"Checking for file: " + filename)
self.assertEqual(os.path.exists(filename),True)
filename = "{0}/{1}.{2}".format(out_path, out_file, extension)
debug.info(1, "Checking for file: " + filename)
self.assertEqual(os.path.exists(filename), True)
# Make sure there is any .lib file
import glob
files = glob.glob('{0}/*.lib'.format(out_path))
self.assertTrue(len(files)>0)
# Make sure there is any .html file
# Make sure there is any .html file
if os.path.exists(out_path):
datasheets = glob.glob('{0}/*html'.format(out_path))
self.assertTrue(len(datasheets)>0)
# grep any errors from the output
output_log = open("{0}/output.log".format(out_path),"r")
output_log = open("{0}/output.log".format(out_path), "r")
output = output_log.read()
output_log.close()
self.assertEqual(len(re.findall('ERROR',output)),0)
self.assertEqual(len(re.findall('WARNING',output)),0)
self.assertEqual(len(re.findall('ERROR', output)), 0)
self.assertEqual(len(re.findall('WARNING', output)), 0)
# now clean up the directory
if OPTS.purge_temp:
if os.path.exists(out_path):
shutil.rmtree(out_path, ignore_errors=True)
self.assertEqual(os.path.exists(out_path),False)
self.assertEqual(os.path.exists(out_path), False)
globals.end_openram()

View File

@ -51,7 +51,7 @@ class openram_front_end_test(openram_test):
debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage")
exe_name = "{0}/openram.py ".format(OPENRAM_HOME)
else:
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
config_name = "{0}/tests/configs/config_front_end.py".format(OPENRAM_HOME)
cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
out_file,
@ -64,33 +64,32 @@ class openram_front_end_test(openram_test):
# assert an error until we actually check a result
for extension in ["v", "lef", "sp", "gds"]:
filename = "{0}/{1}.{2}".format(out_path,out_file,extension)
debug.info(1,"Checking for file: " + filename)
self.assertEqual(os.path.exists(filename),True)
filename = "{0}/{1}.{2}".format(out_path, out_file, extension)
debug.info(1, "Checking for file: " + filename)
self.assertEqual(os.path.exists(filename), True)
# Make sure there is any .lib file
import glob
files = glob.glob('{0}/*.lib'.format(out_path))
self.assertTrue(len(files)>0)
# Make sure there is any .html file
# Make sure there is any .html file
if os.path.exists(out_path):
datasheets = glob.glob('{0}/*html'.format(out_path))
self.assertTrue(len(datasheets)>0)
# grep any errors from the output
output_log = open("{0}/output.log".format(out_path),"r")
output_log = open("{0}/output.log".format(out_path), "r")
output = output_log.read()
output_log.close()
self.assertEqual(len(re.findall('ERROR',output)),0)
self.assertEqual(len(re.findall('WARNING',output)),0)
self.assertEqual(len(re.findall('ERROR', output)), 0)
self.assertEqual(len(re.findall('WARNING', output)), 0)
# now clean up the directory
# now clean up the directory
if OPTS.purge_temp:
if os.path.exists(out_path):
shutil.rmtree(out_path, ignore_errors=True)
self.assertEqual(os.path.exists(out_path),False)
self.assertEqual(os.path.exists(out_path), False)
globals.end_openram()

View File

@ -35,11 +35,14 @@ library (sram_2_16_1_freepdk45_FF_1p0V_25C_lib){
default_max_fanout : 4.0 ;
default_connection_class : universal ;
voltage_map ( VDD, 1.0 );
voltage_map ( GND, 0 );
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.00125, 0.005, 0.04");
index_2("0.052275, 0.2091, 1.6728");
index_2("5.2275e-05, 0.0002091, 0.0008364");
}
lu_table_template(CONSTRAINT_TABLE){
@ -78,17 +81,26 @@ cell (sram_2_16_1_freepdk45){
dont_use : true;
map_only : true;
dont_touch : true;
area : 1124.88;
area : 0;
pg_pin(vdd) {
voltage_name : VDD;
pg_type : primary_power;
}
pg_pin(gnd) {
voltage_name : GND;
pg_type : primary_ground;
}
leakage_power () {
when : "csb0";
value : 0.000167;
value : 0.000198;
}
cell_leakage_power : 0;
cell_leakage_power : 0.000198;
bus(din0){
bus_type : data;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
memory_write(){
address : addr0;
clocked_on : clk0;
@ -127,8 +139,8 @@ cell (sram_2_16_1_freepdk45){
bus(dout0){
bus_type : data;
direction : output;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
max_capacitance : 0.0008364000000000001;
min_capacitance : 5.2275000000000003e-05;
memory_read(){
address : addr0;
}
@ -138,14 +150,14 @@ cell (sram_2_16_1_freepdk45){
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.088, 0.088, 0.088",\
"0.088, 0.088, 0.088",\
"0.088, 0.088, 0.088");
values("0.193, 0.193, 0.194",\
"0.193, 0.193, 0.194",\
"0.193, 0.193, 0.194");
}
cell_fall(CELL_TABLE) {
values("0.088, 0.088, 0.088",\
"0.088, 0.088, 0.088",\
"0.088, 0.088, 0.088");
values("0.193, 0.193, 0.194",\
"0.193, 0.193, 0.194",\
"0.193, 0.193, 0.194");
}
rise_transition(CELL_TABLE) {
values("0.001, 0.001, 0.001",\
@ -164,7 +176,7 @@ cell (sram_2_16_1_freepdk45){
bus(addr0){
bus_type : addr;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
max_transition : 0.04;
pin(addr0[3:0]){
timing(){
@ -200,7 +212,7 @@ cell (sram_2_16_1_freepdk45){
pin(csb0){
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -233,7 +245,7 @@ cell (sram_2_16_1_freepdk45){
pin(web0){
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -267,52 +279,61 @@ cell (sram_2_16_1_freepdk45){
pin(clk0){
clock : true;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
internal_power(){
when : "!csb0 & clk0 & !web0";
when : "!csb0 & !web0";
rise_power(scalar){
values("0.033101244168888884");
values("9.240667e-02");
}
fall_power(scalar){
values("0.033101244168888884");
values("9.240667e-02");
}
}
internal_power(){
when : "!csb0 & !clk0 & web0";
when : "csb0 & !web0";
rise_power(scalar){
values("0.033101244168888884");
values("9.240667e-02");
}
fall_power(scalar){
values("0.033101244168888884");
values("9.240667e-02");
}
}
internal_power(){
when : "csb0";
when : "!csb0 & web0";
rise_power(scalar){
values("0");
values("9.240667e-02");
}
fall_power(scalar){
values("0");
values("9.240667e-02");
}
}
internal_power(){
when : "csb0 & web0";
rise_power(scalar){
values("9.240667e-02");
}
fall_power(scalar){
values("9.240667e-02");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("0.009");
values("0.0195");
}
fall_constraint(scalar) {
values("0.009");
values("0.0195");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("0.018");
values("0.039");
}
fall_constraint(scalar) {
values("0.018");
values("0.039");
}
}
}

View File

@ -35,11 +35,14 @@ library (sram_2_16_1_freepdk45_SS_1p0V_25C_lib){
default_max_fanout : 4.0 ;
default_connection_class : universal ;
voltage_map ( VDD, 1.0 );
voltage_map ( GND, 0 );
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.00125, 0.005, 0.04");
index_2("0.052275, 0.2091, 1.6728");
index_2("5.2275e-05, 0.0002091, 0.0008364");
}
lu_table_template(CONSTRAINT_TABLE){
@ -78,17 +81,26 @@ cell (sram_2_16_1_freepdk45){
dont_use : true;
map_only : true;
dont_touch : true;
area : 1124.88;
area : 0;
pg_pin(vdd) {
voltage_name : VDD;
pg_type : primary_power;
}
pg_pin(gnd) {
voltage_name : GND;
pg_type : primary_ground;
}
leakage_power () {
when : "csb0";
value : 0.000167;
value : 0.000198;
}
cell_leakage_power : 0;
cell_leakage_power : 0.000198;
bus(din0){
bus_type : data;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
memory_write(){
address : addr0;
clocked_on : clk0;
@ -127,8 +139,8 @@ cell (sram_2_16_1_freepdk45){
bus(dout0){
bus_type : data;
direction : output;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
max_capacitance : 0.0008364000000000001;
min_capacitance : 5.2275000000000003e-05;
memory_read(){
address : addr0;
}
@ -138,14 +150,14 @@ cell (sram_2_16_1_freepdk45){
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.107, 0.107, 0.107",\
"0.107, 0.107, 0.107",\
"0.107, 0.107, 0.107");
values("0.236, 0.236, 0.237",\
"0.236, 0.236, 0.237",\
"0.236, 0.236, 0.237");
}
cell_fall(CELL_TABLE) {
values("0.107, 0.107, 0.107",\
"0.107, 0.107, 0.107",\
"0.107, 0.107, 0.107");
values("0.236, 0.236, 0.237",\
"0.236, 0.236, 0.237",\
"0.236, 0.236, 0.237");
}
rise_transition(CELL_TABLE) {
values("0.001, 0.001, 0.001",\
@ -164,7 +176,7 @@ cell (sram_2_16_1_freepdk45){
bus(addr0){
bus_type : addr;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
max_transition : 0.04;
pin(addr0[3:0]){
timing(){
@ -200,7 +212,7 @@ cell (sram_2_16_1_freepdk45){
pin(csb0){
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -233,7 +245,7 @@ cell (sram_2_16_1_freepdk45){
pin(web0){
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -267,52 +279,61 @@ cell (sram_2_16_1_freepdk45){
pin(clk0){
clock : true;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
internal_power(){
when : "!csb0 & clk0 & !web0";
when : "!csb0 & !web0";
rise_power(scalar){
values("0.033101244168888884");
values("7.560546e-02");
}
fall_power(scalar){
values("0.033101244168888884");
values("7.560546e-02");
}
}
internal_power(){
when : "!csb0 & !clk0 & web0";
when : "csb0 & !web0";
rise_power(scalar){
values("0.033101244168888884");
values("7.560546e-02");
}
fall_power(scalar){
values("0.033101244168888884");
values("7.560546e-02");
}
}
internal_power(){
when : "csb0";
when : "!csb0 & web0";
rise_power(scalar){
values("0");
values("7.560546e-02");
}
fall_power(scalar){
values("0");
values("7.560546e-02");
}
}
internal_power(){
when : "csb0 & web0";
rise_power(scalar){
values("7.560546e-02");
}
fall_power(scalar){
values("7.560546e-02");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("0.0105");
values("0.0235");
}
fall_constraint(scalar) {
values("0.0105");
values("0.0235");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("0.021");
values("0.047");
}
fall_constraint(scalar) {
values("0.021");
values("0.047");
}
}
}

View File

@ -35,11 +35,14 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){
default_max_fanout : 4.0 ;
default_connection_class : universal ;
voltage_map ( VDD, 1.0 );
voltage_map ( GND, 0 );
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.00125, 0.005, 0.04");
index_2("0.052275, 0.2091, 1.6728");
index_2("5.2275e-05, 0.0002091, 0.0008364");
}
lu_table_template(CONSTRAINT_TABLE){
@ -78,17 +81,26 @@ cell (sram_2_16_1_freepdk45){
dont_use : true;
map_only : true;
dont_touch : true;
area : 977.4951374999999;
area : 0;
pg_pin(vdd) {
voltage_name : VDD;
pg_type : primary_power;
}
pg_pin(gnd) {
voltage_name : GND;
pg_type : primary_ground;
}
leakage_power () {
when : "csb0";
value : 0.0011164579999999999;
value : 0.00163;
}
cell_leakage_power : 0;
cell_leakage_power : 0.00163;
bus(din0){
bus_type : data;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
memory_write(){
address : addr0;
clocked_on : clk0;
@ -98,9 +110,9 @@ cell (sram_2_16_1_freepdk45){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.033, 0.033, 0.039",\
"0.033, 0.033, 0.039",\
"0.033, 0.033, 0.039");
values("0.033, 0.033, 0.033",\
"0.033, 0.033, 0.033",\
"0.033, 0.033, 0.033");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.027, 0.027, 0.033",\
@ -112,14 +124,14 @@ cell (sram_2_16_1_freepdk45){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("-0.01, -0.016, -0.022",\
"-0.01, -0.016, -0.022",\
"-0.01, -0.016, -0.022");
values("-0.01, -0.01, 0.021",\
"-0.01, -0.01, 0.021",\
"-0.01, -0.01, 0.021");
}
fall_constraint(CONSTRAINT_TABLE) {
values("-0.016, -0.016, -0.016",\
"-0.016, -0.016, -0.016",\
"-0.016, -0.016, -0.016");
values("-0.016, -0.01, -0.016",\
"-0.016, -0.01, -0.016",\
"-0.016, -0.01, -0.016");
}
}
}
@ -127,8 +139,8 @@ cell (sram_2_16_1_freepdk45){
bus(dout0){
bus_type : data;
direction : output;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
max_capacitance : 0.0008364000000000001;
min_capacitance : 5.2275000000000003e-05;
memory_read(){
address : addr0;
}
@ -138,24 +150,24 @@ cell (sram_2_16_1_freepdk45){
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.235, 0.235, 0.239",\
"0.235, 0.236, 0.24",\
"0.241, 0.242, 0.246");
values("0.226, 0.227, 0.232",\
"0.227, 0.228, 0.233",\
"0.232, 0.234, 0.238");
}
cell_fall(CELL_TABLE) {
values("2.583, 2.585, 2.612",\
"2.584, 2.585, 2.613",\
"2.59, 2.592, 2.62");
values("0.226, 0.227, 0.232",\
"0.227, 0.228, 0.233",\
"0.232, 0.234, 0.238");
}
rise_transition(CELL_TABLE) {
values("0.022, 0.022, 0.03",\
"0.022, 0.023, 0.03",\
"0.022, 0.022, 0.03");
values("0.256, 0.256, 0.257",\
"0.256, 0.256, 0.257",\
"0.256, 0.256, 0.257");
}
fall_transition(CELL_TABLE) {
values("0.078, 0.079, 0.083",\
"0.078, 0.079, 0.083",\
"0.079, 0.079, 0.083");
values("0.256, 0.256, 0.257",\
"0.256, 0.256, 0.257",\
"0.256, 0.256, 0.257");
}
}
}
@ -164,16 +176,16 @@ cell (sram_2_16_1_freepdk45){
bus(addr0){
bus_type : addr;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
max_transition : 0.04;
pin(addr0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.033, 0.033, 0.039",\
"0.033, 0.033, 0.039",\
"0.033, 0.033, 0.039");
values("0.033, 0.033, 0.033",\
"0.033, 0.033, 0.033",\
"0.033, 0.033, 0.033");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.027, 0.027, 0.033",\
@ -185,14 +197,14 @@ cell (sram_2_16_1_freepdk45){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("-0.01, -0.016, -0.022",\
"-0.01, -0.016, -0.022",\
"-0.01, -0.016, -0.022");
values("-0.01, -0.01, 0.021",\
"-0.01, -0.01, 0.021",\
"-0.01, -0.01, 0.021");
}
fall_constraint(CONSTRAINT_TABLE) {
values("-0.016, -0.016, -0.016",\
"-0.016, -0.016, -0.016",\
"-0.016, -0.016, -0.016");
values("-0.016, -0.01, -0.016",\
"-0.016, -0.01, -0.016",\
"-0.016, -0.01, -0.016");
}
}
}
@ -200,14 +212,14 @@ cell (sram_2_16_1_freepdk45){
pin(csb0){
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.033, 0.033, 0.039",\
"0.033, 0.033, 0.039",\
"0.033, 0.033, 0.039");
values("0.033, 0.033, 0.033",\
"0.033, 0.033, 0.033",\
"0.033, 0.033, 0.033");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.027, 0.027, 0.033",\
@ -219,28 +231,28 @@ cell (sram_2_16_1_freepdk45){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("-0.01, -0.016, -0.022",\
"-0.01, -0.016, -0.022",\
"-0.01, -0.016, -0.022");
values("-0.01, -0.01, 0.021",\
"-0.01, -0.01, 0.021",\
"-0.01, -0.01, 0.021");
}
fall_constraint(CONSTRAINT_TABLE) {
values("-0.016, -0.016, -0.016",\
"-0.016, -0.016, -0.016",\
"-0.016, -0.016, -0.016");
values("-0.016, -0.01, -0.016",\
"-0.016, -0.01, -0.016",\
"-0.016, -0.01, -0.016");
}
}
}
pin(web0){
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.033, 0.033, 0.039",\
"0.033, 0.033, 0.039",\
"0.033, 0.033, 0.039");
values("0.033, 0.033, 0.033",\
"0.033, 0.033, 0.033",\
"0.033, 0.033, 0.033");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.027, 0.027, 0.033",\
@ -252,14 +264,14 @@ cell (sram_2_16_1_freepdk45){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("-0.01, -0.016, -0.022",\
"-0.01, -0.016, -0.022",\
"-0.01, -0.016, -0.022");
values("-0.01, -0.01, 0.021",\
"-0.01, -0.01, 0.021",\
"-0.01, -0.01, 0.021");
}
fall_constraint(CONSTRAINT_TABLE) {
values("-0.016, -0.016, -0.016",\
"-0.016, -0.016, -0.016",\
"-0.016, -0.016, -0.016");
values("-0.016, -0.01, -0.016",\
"-0.016, -0.01, -0.016",\
"-0.016, -0.01, -0.016");
}
}
}
@ -267,52 +279,61 @@ cell (sram_2_16_1_freepdk45){
pin(clk0){
clock : true;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
internal_power(){
when : "!csb0 & clk0 & !web0";
when : "!csb0 & !web0";
rise_power(scalar){
values("0.03599689694444445");
values("3.069977e-01");
}
fall_power(scalar){
values("0.03599689694444445");
values("3.686680e-01");
}
}
internal_power(){
when : "!csb0 & !clk0 & web0";
when : "csb0 & !web0";
rise_power(scalar){
values("0.029906643888888886");
values("2.055845e-01");
}
fall_power(scalar){
values("0.029906643888888886");
values("1.933561e-01");
}
}
internal_power(){
when : "csb0";
when : "!csb0 & web0";
rise_power(scalar){
values("0");
values("3.315565e-01");
}
fall_power(scalar){
values("0");
values("3.314553e-01");
}
}
internal_power(){
when : "csb0 & web0";
rise_power(scalar){
values("1.777355e-01");
}
fall_power(scalar){
values("1.615044e-01");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("2.422");
values("0.449");
}
fall_constraint(scalar) {
values("2.422");
values("0.449");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("4.844");
values("0.898");
}
fall_constraint(scalar) {
values("4.844");
values("0.898");
}
}
}

View File

@ -35,11 +35,14 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){
default_max_fanout : 4.0 ;
default_connection_class : universal ;
voltage_map ( VDD, 1.0 );
voltage_map ( GND, 0 );
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.00125, 0.005, 0.04");
index_2("0.052275, 0.2091, 1.6728");
index_2("5.2275e-05, 0.0002091, 0.0008364");
}
lu_table_template(CONSTRAINT_TABLE){
@ -78,17 +81,26 @@ cell (sram_2_16_1_freepdk45){
dont_use : true;
map_only : true;
dont_touch : true;
area : 977.4951374999999;
area : 0;
pg_pin(vdd) {
voltage_name : VDD;
pg_type : primary_power;
}
pg_pin(gnd) {
voltage_name : GND;
pg_type : primary_ground;
}
leakage_power () {
when : "csb0";
value : 0.000179;
value : 0.000198;
}
cell_leakage_power : 0;
cell_leakage_power : 0.000198;
bus(din0){
bus_type : data;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
memory_write(){
address : addr0;
clocked_on : clk0;
@ -127,8 +139,8 @@ cell (sram_2_16_1_freepdk45){
bus(dout0){
bus_type : data;
direction : output;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
max_capacitance : 0.0008364000000000001;
min_capacitance : 5.2275000000000003e-05;
memory_read(){
address : addr0;
}
@ -138,14 +150,14 @@ cell (sram_2_16_1_freepdk45){
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.098, 0.098, 0.098",\
"0.098, 0.098, 0.098",\
"0.098, 0.098, 0.098");
values("0.215, 0.215, 0.216",\
"0.215, 0.215, 0.216",\
"0.215, 0.215, 0.216");
}
cell_fall(CELL_TABLE) {
values("0.098, 0.098, 0.098",\
"0.098, 0.098, 0.098",\
"0.098, 0.098, 0.098");
values("0.215, 0.215, 0.216",\
"0.215, 0.215, 0.216",\
"0.215, 0.215, 0.216");
}
rise_transition(CELL_TABLE) {
values("0.001, 0.001, 0.001",\
@ -164,7 +176,7 @@ cell (sram_2_16_1_freepdk45){
bus(addr0){
bus_type : addr;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
max_transition : 0.04;
pin(addr0[3:0]){
timing(){
@ -200,7 +212,7 @@ cell (sram_2_16_1_freepdk45){
pin(csb0){
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -233,7 +245,7 @@ cell (sram_2_16_1_freepdk45){
pin(web0){
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -267,52 +279,61 @@ cell (sram_2_16_1_freepdk45){
pin(clk0){
clock : true;
direction : input;
capacitance : 0.2091;
capacitance : 0.00020910000000000001;
internal_power(){
when : "!csb0 & clk0 & !web0";
when : "!csb0 & !web0";
rise_power(scalar){
values("0.0747594982142222");
values("8.316600e-02");
}
fall_power(scalar){
values("0.0747594982142222");
values("8.316600e-02");
}
}
internal_power(){
when : "!csb0 & !clk0 & web0";
when : "csb0 & !web0";
rise_power(scalar){
values("0.0747594982142222");
values("8.316600e-02");
}
fall_power(scalar){
values("0.0747594982142222");
values("8.316600e-02");
}
}
internal_power(){
when : "csb0";
when : "!csb0 & web0";
rise_power(scalar){
values("0");
values("8.316600e-02");
}
fall_power(scalar){
values("0");
values("8.316600e-02");
}
}
internal_power(){
when : "csb0 & web0";
rise_power(scalar){
values("8.316600e-02");
}
fall_power(scalar){
values("8.316600e-02");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("0.0");
values("0.0215");
}
fall_constraint(scalar) {
values("0.0");
values("0.0215");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("0");
values("0.043");
}
fall_constraint(scalar) {
values("0");
values("0.043");
}
}
}

View File

@ -35,11 +35,14 @@ library (sram_2_16_1_scn4m_subm_FF_5p0V_25C_lib){
default_max_fanout : 4.0 ;
default_connection_class : universal ;
voltage_map ( VDD, 5.0 );
voltage_map ( GND, 0 );
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.0125, 0.05, 0.4");
index_2("2.45605, 9.8242, 78.5936");
index_2("0.00245605, 0.0098242, 0.0392968");
}
lu_table_template(CONSTRAINT_TABLE){
@ -78,17 +81,26 @@ cell (sram_2_16_1_scn4m_subm){
dont_use : true;
map_only : true;
dont_touch : true;
area : 73068.14000000001;
area : 0;
pg_pin(vdd) {
voltage_name : VDD;
pg_type : primary_power;
}
pg_pin(gnd) {
voltage_name : GND;
pg_type : primary_ground;
}
leakage_power () {
when : "csb0";
value : 0.000167;
value : 0.000198;
}
cell_leakage_power : 0;
cell_leakage_power : 0.000198;
bus(din0){
bus_type : data;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
memory_write(){
address : addr0;
clocked_on : clk0;
@ -127,8 +139,8 @@ cell (sram_2_16_1_scn4m_subm){
bus(dout0){
bus_type : data;
direction : output;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
max_capacitance : 0.0392968;
min_capacitance : 0.00245605;
memory_read(){
address : addr0;
}
@ -138,24 +150,24 @@ cell (sram_2_16_1_scn4m_subm){
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.241, 0.241, 0.241",\
"0.241, 0.241, 0.241",\
"0.241, 0.241, 0.241");
values("1.183, 1.199, 1.264",\
"1.183, 1.199, 1.264",\
"1.183, 1.199, 1.264");
}
cell_fall(CELL_TABLE) {
values("0.241, 0.241, 0.241",\
"0.241, 0.241, 0.241",\
"0.241, 0.241, 0.241");
values("1.183, 1.199, 1.264",\
"1.183, 1.199, 1.264",\
"1.183, 1.199, 1.264");
}
rise_transition(CELL_TABLE) {
values("0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004");
values("0.006, 0.007, 0.014",\
"0.006, 0.007, 0.014",\
"0.006, 0.007, 0.014");
}
fall_transition(CELL_TABLE) {
values("0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004");
values("0.006, 0.007, 0.014",\
"0.006, 0.007, 0.014",\
"0.006, 0.007, 0.014");
}
}
}
@ -164,7 +176,7 @@ cell (sram_2_16_1_scn4m_subm){
bus(addr0){
bus_type : addr;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
max_transition : 0.4;
pin(addr0[3:0]){
timing(){
@ -200,7 +212,7 @@ cell (sram_2_16_1_scn4m_subm){
pin(csb0){
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -233,7 +245,7 @@ cell (sram_2_16_1_scn4m_subm){
pin(web0){
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -267,52 +279,61 @@ cell (sram_2_16_1_scn4m_subm){
pin(clk0){
clock : true;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
internal_power(){
when : "!csb0 & clk0 & !web0";
when : "!csb0 & !web0";
rise_power(scalar){
values("4.99880645");
values("7.797263e+00");
}
fall_power(scalar){
values("4.99880645");
values("7.797263e+00");
}
}
internal_power(){
when : "!csb0 & !clk0 & web0";
when : "csb0 & !web0";
rise_power(scalar){
values("4.99880645");
values("7.797263e+00");
}
fall_power(scalar){
values("4.99880645");
values("7.797263e+00");
}
}
internal_power(){
when : "csb0";
when : "!csb0 & web0";
rise_power(scalar){
values("0");
values("7.797263e+00");
}
fall_power(scalar){
values("0");
values("7.797263e+00");
}
}
internal_power(){
when : "csb0 & web0";
rise_power(scalar){
values("7.797263e+00");
}
fall_power(scalar){
values("7.797263e+00");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("0.024");
values("0.1265");
}
fall_constraint(scalar) {
values("0.024");
values("0.1265");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("0.048");
values("0.253");
}
fall_constraint(scalar) {
values("0.048");
values("0.253");
}
}
}

View File

@ -35,11 +35,14 @@ library (sram_2_16_1_scn4m_subm_SS_5p0V_25C_lib){
default_max_fanout : 4.0 ;
default_connection_class : universal ;
voltage_map ( VDD, 5.0 );
voltage_map ( GND, 0 );
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.0125, 0.05, 0.4");
index_2("2.45605, 9.8242, 78.5936");
index_2("0.00245605, 0.0098242, 0.0392968");
}
lu_table_template(CONSTRAINT_TABLE){
@ -78,17 +81,26 @@ cell (sram_2_16_1_scn4m_subm){
dont_use : true;
map_only : true;
dont_touch : true;
area : 73068.14000000001;
area : 0;
pg_pin(vdd) {
voltage_name : VDD;
pg_type : primary_power;
}
pg_pin(gnd) {
voltage_name : GND;
pg_type : primary_ground;
}
leakage_power () {
when : "csb0";
value : 0.000167;
value : 0.000198;
}
cell_leakage_power : 0;
cell_leakage_power : 0.000198;
bus(din0){
bus_type : data;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
memory_write(){
address : addr0;
clocked_on : clk0;
@ -127,8 +139,8 @@ cell (sram_2_16_1_scn4m_subm){
bus(dout0){
bus_type : data;
direction : output;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
max_capacitance : 0.0392968;
min_capacitance : 0.00245605;
memory_read(){
address : addr0;
}
@ -138,24 +150,24 @@ cell (sram_2_16_1_scn4m_subm){
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.294, 0.294, 0.294",\
"0.294, 0.294, 0.294",\
"0.294, 0.294, 0.294");
values("1.446, 1.466, 1.545",\
"1.446, 1.466, 1.545",\
"1.446, 1.466, 1.545");
}
cell_fall(CELL_TABLE) {
values("0.294, 0.294, 0.294",\
"0.294, 0.294, 0.294",\
"0.294, 0.294, 0.294");
values("1.446, 1.466, 1.545",\
"1.446, 1.466, 1.545",\
"1.446, 1.466, 1.545");
}
rise_transition(CELL_TABLE) {
values("0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004");
values("0.007, 0.009, 0.017",\
"0.007, 0.009, 0.017",\
"0.007, 0.009, 0.017");
}
fall_transition(CELL_TABLE) {
values("0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004");
values("0.007, 0.009, 0.017",\
"0.007, 0.009, 0.017",\
"0.007, 0.009, 0.017");
}
}
}
@ -164,7 +176,7 @@ cell (sram_2_16_1_scn4m_subm){
bus(addr0){
bus_type : addr;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
max_transition : 0.4;
pin(addr0[3:0]){
timing(){
@ -200,7 +212,7 @@ cell (sram_2_16_1_scn4m_subm){
pin(csb0){
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -233,7 +245,7 @@ cell (sram_2_16_1_scn4m_subm){
pin(web0){
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -267,52 +279,61 @@ cell (sram_2_16_1_scn4m_subm){
pin(clk0){
clock : true;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
internal_power(){
when : "!csb0 & clk0 & !web0";
when : "!csb0 & !web0";
rise_power(scalar){
values("4.99880645");
values("6.379579e+00");
}
fall_power(scalar){
values("4.99880645");
values("6.379579e+00");
}
}
internal_power(){
when : "!csb0 & !clk0 & web0";
when : "csb0 & !web0";
rise_power(scalar){
values("4.99880645");
values("6.379579e+00");
}
fall_power(scalar){
values("4.99880645");
values("6.379579e+00");
}
}
internal_power(){
when : "csb0";
when : "!csb0 & web0";
rise_power(scalar){
values("0");
values("6.379579e+00");
}
fall_power(scalar){
values("0");
values("6.379579e+00");
}
}
internal_power(){
when : "csb0 & web0";
rise_power(scalar){
values("6.379579e+00");
}
fall_power(scalar){
values("6.379579e+00");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("0.0295");
values("0.1545");
}
fall_constraint(scalar) {
values("0.0295");
values("0.1545");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("0.059");
values("0.309");
}
fall_constraint(scalar) {
values("0.059");
values("0.309");
}
}
}

View File

@ -35,11 +35,14 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){
default_max_fanout : 4.0 ;
default_connection_class : universal ;
voltage_map ( VDD, 5.0 );
voltage_map ( GND, 0 );
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.0125, 0.05, 0.4");
index_2("2.45605, 9.8242, 78.5936");
index_2("0.00245605, 0.0098242, 0.0392968");
}
lu_table_template(CONSTRAINT_TABLE){
@ -78,17 +81,26 @@ cell (sram_2_16_1_scn4m_subm){
dont_use : true;
map_only : true;
dont_touch : true;
area : 60774.3;
area : 0;
pg_pin(vdd) {
voltage_name : VDD;
pg_type : primary_power;
}
pg_pin(gnd) {
voltage_name : GND;
pg_type : primary_ground;
}
leakage_power () {
when : "csb0";
value : 0.0009813788999999999;
value : 0.000198;
}
cell_leakage_power : 0;
cell_leakage_power : 0.000198;
bus(din0){
bus_type : data;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
memory_write(){
address : addr0;
clocked_on : clk0;
@ -98,28 +110,28 @@ cell (sram_2_16_1_scn4m_subm){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.167, 0.167, 0.228",\
"0.167, 0.167, 0.228",\
"0.167, 0.167, 0.228");
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.131, 0.125, 0.137",\
"0.131, 0.125, 0.137",\
"0.131, 0.125, 0.137");
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("-0.065, -0.071, -0.114",\
"-0.065, -0.071, -0.114",\
"-0.065, -0.071, -0.114");
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("-0.089, -0.089, -0.089",\
"-0.089, -0.089, -0.089",\
"-0.089, -0.089, -0.089");
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
@ -127,8 +139,8 @@ cell (sram_2_16_1_scn4m_subm){
bus(dout0){
bus_type : data;
direction : output;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
max_capacitance : 0.0392968;
min_capacitance : 0.00245605;
memory_read(){
address : addr0;
}
@ -138,24 +150,24 @@ cell (sram_2_16_1_scn4m_subm){
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("1.556, 1.576, 1.751",\
"1.559, 1.579, 1.754",\
"1.624, 1.643, 1.819");
values("1.314, 1.332, 1.404",\
"1.314, 1.332, 1.404",\
"1.314, 1.332, 1.404");
}
cell_fall(CELL_TABLE) {
values("3.445, 3.504, 3.926",\
"3.448, 3.507, 3.93",\
"3.49, 3.549, 3.972");
values("1.314, 1.332, 1.404",\
"1.314, 1.332, 1.404",\
"1.314, 1.332, 1.404");
}
rise_transition(CELL_TABLE) {
values("0.13, 0.169, 0.574",\
"0.13, 0.169, 0.574",\
"0.13, 0.169, 0.574");
values("0.006, 0.008, 0.015",\
"0.006, 0.008, 0.015",\
"0.006, 0.008, 0.015");
}
fall_transition(CELL_TABLE) {
values("0.467, 0.49, 0.959",\
"0.467, 0.49, 0.959",\
"0.47, 0.493, 0.96");
values("0.006, 0.008, 0.015",\
"0.006, 0.008, 0.015",\
"0.006, 0.008, 0.015");
}
}
}
@ -164,35 +176,35 @@ cell (sram_2_16_1_scn4m_subm){
bus(addr0){
bus_type : addr;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
max_transition : 0.4;
pin(addr0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.167, 0.167, 0.228",\
"0.167, 0.167, 0.228",\
"0.167, 0.167, 0.228");
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.131, 0.125, 0.137",\
"0.131, 0.125, 0.137",\
"0.131, 0.125, 0.137");
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("-0.065, -0.071, -0.114",\
"-0.065, -0.071, -0.114",\
"-0.065, -0.071, -0.114");
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("-0.089, -0.089, -0.089",\
"-0.089, -0.089, -0.089",\
"-0.089, -0.089, -0.089");
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
@ -200,66 +212,66 @@ cell (sram_2_16_1_scn4m_subm){
pin(csb0){
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.167, 0.167, 0.228",\
"0.167, 0.167, 0.228",\
"0.167, 0.167, 0.228");
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.131, 0.125, 0.137",\
"0.131, 0.125, 0.137",\
"0.131, 0.125, 0.137");
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("-0.065, -0.071, -0.114",\
"-0.065, -0.071, -0.114",\
"-0.065, -0.071, -0.114");
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("-0.089, -0.089, -0.089",\
"-0.089, -0.089, -0.089",\
"-0.089, -0.089, -0.089");
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
pin(web0){
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.167, 0.167, 0.228",\
"0.167, 0.167, 0.228",\
"0.167, 0.167, 0.228");
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.131, 0.125, 0.137",\
"0.131, 0.125, 0.137",\
"0.131, 0.125, 0.137");
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("-0.065, -0.071, -0.114",\
"-0.065, -0.071, -0.114",\
"-0.065, -0.071, -0.114");
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("-0.089, -0.089, -0.089",\
"-0.089, -0.089, -0.089",\
"-0.089, -0.089, -0.089");
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
@ -267,52 +279,61 @@ cell (sram_2_16_1_scn4m_subm){
pin(clk0){
clock : true;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
internal_power(){
when : "!csb0 & clk0 & !web0";
when : "!csb0 & !web0";
rise_power(scalar){
values("9.972790277777777");
values("7.017537e+00");
}
fall_power(scalar){
values("9.972790277777777");
values("7.017537e+00");
}
}
internal_power(){
when : "!csb0 & !clk0 & web0";
when : "csb0 & !web0";
rise_power(scalar){
values("8.899322499999998");
values("7.017537e+00");
}
fall_power(scalar){
values("8.899322499999998");
values("7.017537e+00");
}
}
internal_power(){
when : "csb0";
when : "!csb0 & web0";
rise_power(scalar){
values("0");
values("7.017537e+00");
}
fall_power(scalar){
values("0");
values("7.017537e+00");
}
}
internal_power(){
when : "csb0 & web0";
rise_power(scalar){
values("7.017537e+00");
}
fall_power(scalar){
values("7.017537e+00");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("2.344");
values("0.1405");
}
fall_constraint(scalar) {
values("2.344");
values("0.1405");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("4.688");
values("0.281");
}
fall_constraint(scalar) {
values("4.688");
values("0.281");
}
}
}

View File

@ -35,11 +35,14 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){
default_max_fanout : 4.0 ;
default_connection_class : universal ;
voltage_map ( VDD, 5.0 );
voltage_map ( GND, 0 );
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.0125, 0.05, 0.4");
index_2("2.45605, 9.8242, 78.5936");
index_2("0.00245605, 0.0098242, 0.0392968");
}
lu_table_template(CONSTRAINT_TABLE){
@ -78,17 +81,26 @@ cell (sram_2_16_1_scn4m_subm){
dont_use : true;
map_only : true;
dont_touch : true;
area : 60774.3;
area : 0;
pg_pin(vdd) {
voltage_name : VDD;
pg_type : primary_power;
}
pg_pin(gnd) {
voltage_name : GND;
pg_type : primary_ground;
}
leakage_power () {
when : "csb0";
value : 0.000179;
value : 0.000198;
}
cell_leakage_power : 0;
cell_leakage_power : 0.000198;
bus(din0){
bus_type : data;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
memory_write(){
address : addr0;
clocked_on : clk0;
@ -127,8 +139,8 @@ cell (sram_2_16_1_scn4m_subm){
bus(dout0){
bus_type : data;
direction : output;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
max_capacitance : 0.0392968;
min_capacitance : 0.00245605;
memory_read(){
address : addr0;
}
@ -138,24 +150,24 @@ cell (sram_2_16_1_scn4m_subm){
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.268, 0.268, 0.268",\
"0.268, 0.268, 0.268",\
"0.268, 0.268, 0.268");
values("1.314, 1.332, 1.404",\
"1.314, 1.332, 1.404",\
"1.314, 1.332, 1.404");
}
cell_fall(CELL_TABLE) {
values("0.268, 0.268, 0.268",\
"0.268, 0.268, 0.268",\
"0.268, 0.268, 0.268");
values("1.314, 1.332, 1.404",\
"1.314, 1.332, 1.404",\
"1.314, 1.332, 1.404");
}
rise_transition(CELL_TABLE) {
values("0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004");
values("0.006, 0.008, 0.015",\
"0.006, 0.008, 0.015",\
"0.006, 0.008, 0.015");
}
fall_transition(CELL_TABLE) {
values("0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004");
values("0.006, 0.008, 0.015",\
"0.006, 0.008, 0.015",\
"0.006, 0.008, 0.015");
}
}
}
@ -164,7 +176,7 @@ cell (sram_2_16_1_scn4m_subm){
bus(addr0){
bus_type : addr;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
max_transition : 0.4;
pin(addr0[3:0]){
timing(){
@ -200,7 +212,7 @@ cell (sram_2_16_1_scn4m_subm){
pin(csb0){
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -233,7 +245,7 @@ cell (sram_2_16_1_scn4m_subm){
pin(web0){
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -267,52 +279,61 @@ cell (sram_2_16_1_scn4m_subm){
pin(clk0){
clock : true;
direction : input;
capacitance : 9.8242;
capacitance : 0.0098242;
internal_power(){
when : "!csb0 & clk0 & !web0";
when : "!csb0 & !web0";
rise_power(scalar){
values("11.3049604371");
values("7.017537e+00");
}
fall_power(scalar){
values("11.3049604371");
values("7.017537e+00");
}
}
internal_power(){
when : "!csb0 & !clk0 & web0";
when : "csb0 & !web0";
rise_power(scalar){
values("11.3049604371");
values("7.017537e+00");
}
fall_power(scalar){
values("11.3049604371");
values("7.017537e+00");
}
}
internal_power(){
when : "csb0";
when : "!csb0 & web0";
rise_power(scalar){
values("0");
values("7.017537e+00");
}
fall_power(scalar){
values("0");
values("7.017537e+00");
}
}
internal_power(){
when : "csb0 & web0";
rise_power(scalar){
values("7.017537e+00");
}
fall_power(scalar){
values("7.017537e+00");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("0.0");
values("0.1405");
}
fall_constraint(scalar) {
values("0.0");
values("0.1405");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("0");
values("0.281");
}
fall_constraint(scalar) {
values("0");
values("0.281");
}
}
}

View File

@ -39,27 +39,40 @@ class openram_test(unittest.TestCase):
tempspice = "{0}{1}.sp".format(OPTS.openram_temp,a.name)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp,a.name)
a.sp_write(tempspice)
a.lvs_write(tempspice)
# cannot write gds in netlist_only mode
if not OPTS.netlist_only:
a.gds_write(tempgds)
import verify
result=verify.run_drc(a.name, tempgds, extract=True, final_verification=final_verification)
if result != 0:
# Run both DRC and LVS even if DRC might fail
# Magic can still extract despite DRC failing, so it might be ok in some techs
# if we ignore things like minimum metal area of pins
drc_result=verify.run_drc(a.name, tempgds, extract=True, final_verification=final_verification)
# Always run LVS if we are using magic
if "magic" in OPTS.drc_exe or drc_result == 0:
lvs_result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification)
# Only allow DRC to fail and LVS to pass if we are using magic
if "magic" in OPTS.drc_exe and lvs_result == 0 and drc_result != 0:
#zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid())
#debug.info(0,"Archiving failed files to {}.zip".format(zip_file))
#shutil.make_archive(zip_file, 'zip', OPTS.openram_temp)
debug.warning("DRC failed but LVS passed: {}".format(a.name))
elif drc_result != 0:
#zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid())
#debug.info(0,"Archiving failed files to {}.zip".format(zip_file))
#shutil.make_archive(zip_file, 'zip', OPTS.openram_temp)
self.fail("DRC failed: {}".format(a.name))
result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification)
if result != 0:
if lvs_result != 0:
#zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid())
#debug.info(0,"Archiving failed files to {}.zip".format(zip_file))
#shutil.make_archive(zip_file, 'zip', OPTS.openram_temp)
self.fail("LVS mismatch: {}".format(a.name))
# For debug...
#import pdb; pdb.set_trace()
if OPTS.purge_temp:

View File

@ -22,7 +22,6 @@ and include its appropriate license.
import os
import re
import time
import shutil
import debug
from globals import OPTS
@ -67,26 +66,29 @@ def write_magic_script(cell_name, extract=False, final_verification=False):
else:
pre = ""
if final_verification:
f.write(pre+"extract unique all\n".format(cell_name))
f.write(pre+"extract\n".format(cell_name))
#f.write(pre+"ext2spice hierarchy on\n")
#f.write(pre+"ext2spice scale off\n")
f.write(pre + "extract unique all\n".format(cell_name))
# Hack to work around unit scales in SkyWater
if OPTS.tech_name=="s8":
f.write(pre + "extract style ngspice(si)\n")
f.write(pre + "extract\n".format(cell_name))
# f.write(pre + "ext2spice hierarchy on\n")
# f.write(pre + "ext2spice scale off\n")
# lvs exists in 8.2.79, but be backword compatible for now
#f.write(pre+"ext2spice lvs\n")
f.write(pre+"ext2spice hierarchy on\n")
f.write(pre+"ext2spice format ngspice\n")
f.write(pre+"ext2spice cthresh infinite\n")
f.write(pre+"ext2spice rthresh infinite\n")
f.write(pre+"ext2spice renumber off\n")
f.write(pre+"ext2spice scale off\n")
f.write(pre+"ext2spice blackbox on\n")
f.write(pre+"ext2spice subcircuit top auto\n")
f.write(pre+"ext2spice global off\n")
# f.write(pre + "ext2spice lvs\n")
f.write(pre + "ext2spice hierarchy on\n")
f.write(pre + "ext2spice format ngspice\n")
f.write(pre + "ext2spice cthresh infinite\n")
f.write(pre + "ext2spice rthresh infinite\n")
f.write(pre + "ext2spice renumber off\n")
f.write(pre + "ext2spice scale off\n")
f.write(pre + "ext2spice blackbox on\n")
f.write(pre + "ext2spice subcircuit top auto\n")
f.write(pre + "ext2spice global off\n")
# Can choose hspice, ngspice, or spice3,
# but they all seem compatible enough.
#f.write(pre+"ext2spice format ngspice\n")
f.write(pre+"ext2spice {}\n".format(cell_name))
#f.write(pre + "ext2spice format ngspice\n")
f.write(pre + "ext2spice {}\n".format(cell_name))
f.write("quit -noprompt\n")
f.write("EOF\n")

View File

@ -16,14 +16,16 @@ drc_warned = False
lvs_warned = False
pex_warned = False
def run_drc(cell_name, gds_name, extract=False, final_verification=False):
global drc_warned
if not drc_warned:
debug.warning("DRC unable to run.")
drc_warned=True
# Since we warned, return a failing test.
# Since we warned, return a failing test.
return 1
def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
global lvs_warned
if not lvs_warned:
@ -32,17 +34,23 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
# Since we warned, return a failing test.
return 1
def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
global pex_warned
global pex_warned
if not pex_warned:
debug.warning("PEX unable to run.")
pex_warned=True
# Since we warned, return a failing test.
# Since we warned, return a failing test.
return 1
def print_drc_stats():
pass
def print_lvs_stats():
pass
def print_pex_stats():
pass

View File

@ -89,7 +89,7 @@ power_grid = m3_stack
###################################################
# create the GDS layer map
layer={}
layer={}
layer["pwell"] = (41, 0)
layer["nwell"] = (42, 0)
layer["active"] = (43, 0)
@ -98,9 +98,9 @@ layer["nimplant"] = (45, 0)
layer["poly"] = (46, 0)
layer["poly_contact"] = (47, 0)
layer["active_contact"] = (48, 0)
layer["m1"] = (49, 0)
layer["m1"] = (49, 0)
layer["via1"] = (50, 0)
layer["m2"] = (51, 0)
layer["m2"] = (51, 0)
layer["via2"] = (61, 0)
layer["m3"] = (62, 0)
layer["via3"] = (30, 0)