Merge branch 'dev' into s8_single_port

This commit is contained in:
jcirimel 2020-11-03 15:21:03 -08:00
commit e1d7d9dff7
310 changed files with 3204 additions and 3466 deletions

View File

@ -17,7 +17,7 @@ class channel_net():
self.name = net_name
self.pins = pins
self.vertical = vertical
# Keep track of the internval
if vertical:
self.min_value = min(i.by() for i in pins)
@ -25,34 +25,34 @@ class channel_net():
else:
self.min_value = min(i.lx() for i in pins)
self.max_value = max(i.rx() for i in pins)
# Keep track of the conflicts
self.conflicts = []
def __str__(self):
return self.name
def __repr__(self):
return self.name
def __lt__(self, other):
return self.min_value < other.min_value
def pin_overlap(self, pin1, pin2, 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
# Pin 1 must be in the "LEFT" set
y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch
overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap)
return overlaps
def pins_overlap(self, other, pitch):
"""
Check all the pin pairs on two nets and return a pin
@ -73,8 +73,8 @@ class channel_net():
min_overlap = self.min_value >= other.min_value and self.min_value <= other.max_value
max_overlap = self.max_value >= other.min_value and self.max_value <= other.max_value
return min_overlap or max_overlap
class channel_route(design.design):
unique_id = 0
@ -98,7 +98,7 @@ class channel_route(design.design):
name = "cr_{0}".format(channel_route.unique_id)
channel_route.unique_id += 1
super().__init__(name)
self.netlist = netlist
self.offset = offset
self.layer_stack = layer_stack
@ -106,7 +106,7 @@ class channel_route(design.design):
self.vertical = vertical
# For debugging...
self.parent = parent
if not directions or directions == "pref":
# Use the preferred layer directions
if self.get_preferred_direction(layer_stack[0]) == "V":
@ -154,7 +154,7 @@ class channel_route(design.design):
if pin in conflicts:
g[other_pin].remove(pin)
return g
def route(self):
# Create names for the nets for the graphs
nets = []
@ -180,7 +180,7 @@ class channel_route(design.design):
except KeyError:
hcg[net2.name] = set([net1.name])
# Initialize the vertical conflict graph (vcg)
# and make a list of all pins
vcg = collections.OrderedDict()
@ -204,12 +204,12 @@ class channel_route(design.design):
# Skip yourself
if net1.name == net2.name:
continue
if net1.pins_overlap(net2, pitch):
vcg[net2.name].add(net1.name)
# Check if there are any cycles net1 <---> net2 in the VCG
# Some of the pins may be to the left/below the channel offset,
# so adjust if this is the case
@ -226,7 +226,7 @@ class channel_route(design.design):
while len(nets) > 0:
current_offset_value = current_offset.y if self.vertical else current_offset.x
# from pprint import pformat
# print("VCG:\n", pformat(vcg))
# for name,net in vcg.items():
@ -253,7 +253,7 @@ class channel_route(design.design):
# Remove the net from other constriants in the VCG
vcg = self.remove_net_from_graph(net.name, vcg)
nets.remove(net)
break
else:
# If we made a full pass and the offset didn't change...
@ -276,7 +276,7 @@ class channel_route(design.design):
current_offset = vector(current_offset.x + self.horizontal_nonpref_pitch, real_channel_offset.y)
else:
current_offset = vector(real_channel_offset.x, current_offset.y + self.vertical_nonpref_pitch)
# Return the size of the channel
if self.vertical:
self.width = current_offset.x + self.horizontal_nonpref_pitch - self.offset.x
@ -284,7 +284,7 @@ class channel_route(design.design):
else:
self.width = self.max_value + self.horizontal_nonpref_pitch - self.offset.x
self.height = current_offset.y + self.vertical_nonpref_pitch - self.offset.y
def get_layer_pitch(self, layer):
""" Return the track pitch on a given layer """
try:
@ -307,10 +307,10 @@ class channel_route(design.design):
"""
max_x = max([pin.center().x for pin in pins])
min_x = min([pin.center().x for pin in pins])
# if we are less than a pitch, just create a non-preferred layer jog
non_preferred_route = max_x - min_x <= pitch
if non_preferred_route:
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
# Add the horizontal trunk on the vertical layer!
@ -324,7 +324,7 @@ class channel_route(design.design):
pin_pos = pin.uc()
else:
pin_pos = pin.bc()
# No bend needed here
mid = vector(pin_pos.x, trunk_offset.y)
self.add_path(self.vertical_layer, [pin_pos, mid])
@ -361,10 +361,10 @@ class channel_route(design.design):
"""
max_y = max([pin.center().y for pin in pins])
min_y = min([pin.center().y for pin in pins])
# if we are less than a pitch, just create a non-preferred layer jog
non_preferred_route = max_y - min_y <= pitch
if non_preferred_route:
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
# Add the vertical trunk on the horizontal layer!

View File

@ -33,8 +33,8 @@ class contact(hierarchy_design.hierarchy_design):
implant_type=None, well_type=None, name=""):
# This will ignore the name parameter since
# we can guarantee a unique name here
super().__init__(name)
super().__init__(name, name)
debug.info(4, "create contact object {0}".format(name))
self.add_comment("layers: {0}".format(layer_stack))
@ -80,7 +80,7 @@ class contact(hierarchy_design.hierarchy_design):
self.create_first_layer_enclosure()
self.create_second_layer_enclosure()
self.create_nitride_cut_enclosure()
self.height = max(self.first_layer_position.y + self.first_layer_height,
self.second_layer_position.y + self.second_layer_height)
self.width = max(self.first_layer_position.x + self.first_layer_width,
@ -99,7 +99,7 @@ class contact(hierarchy_design.hierarchy_design):
(first_layer, via_layer, second_layer) = self.layer_stack
self.first_layer_name = first_layer
self.second_layer_name = second_layer
# Contacts will have unique per first layer
if via_layer in tech.layer:
self.via_layer_name = via_layer
@ -115,7 +115,7 @@ class contact(hierarchy_design.hierarchy_design):
def setup_layout_constants(self):
""" Determine the design rules for the enclosure layers """
self.contact_width = drc("minwidth_{0}". format(self.via_layer_name))
contact_to_contact = drc("{0}_to_{0}".format(self.via_layer_name))
self.contact_pitch = self.contact_width + contact_to_contact
@ -126,7 +126,7 @@ class contact(hierarchy_design.hierarchy_design):
# DRC rules
# The extend rule applies to asymmetric enclosures in one direction.
# The enclosure rule applies to symmetric enclosure component.
self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name))
# If there's a different rule for active
@ -171,7 +171,7 @@ class contact(hierarchy_design.hierarchy_design):
(self.second_layer_minwidth - self.contact_array_width) / 2)
else:
debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1)
def create_contact_array(self):
""" Create the contact array at the origin"""
# offset for the via array
@ -210,7 +210,7 @@ class contact(hierarchy_design.hierarchy_design):
offset=self.second_layer_position - npc_enclose_offset,
width=self.second_layer_width + 2 * npc_enclose_poly,
height=self.second_layer_height + 2 * npc_enclose_poly)
def create_first_layer_enclosure(self):
# this is if the first and second layers are different
self.first_layer_position = vector(
@ -269,12 +269,12 @@ class contact(hierarchy_design.hierarchy_design):
offset=well_position,
width=self.well_width,
height=self.well_height)
def analytical_power(self, corner, load):
""" Get total power of a module """
return self.return_power()
# Set up a static for each layer to be used for measurements
for layer_stack in tech.layer_stacks:
(layer1, via, layer2) = layer_stack
@ -295,7 +295,7 @@ if "nwell" in tech.layer:
well_type="n")
module = sys.modules[__name__]
setattr(module, "nwell_contact", cont)
if "pwell" in tech.layer:
cont = factory.create(module_type="contact",
layer_stack=tech.active_stack,

View File

@ -15,7 +15,7 @@ class _pins:
for k, v in pin_dict.items():
self.__dict__[k] = v
class _cell:
def __init__(self, pin_dict):
pin_dict.update(self._default_power_pins())
@ -29,24 +29,24 @@ class _cell:
return {'vdd': 'vdd',
'gnd': 'gnd'}
class _mirror_axis:
def __init__(self, x, y):
self.x = x
self.y = y
class _ptx:
def __init__(self, model_is_subckt, bin_spice_models):
self.model_is_subckt = model_is_subckt
self.bin_spice_models = bin_spice_models
class _pgate:
def __init__(self, add_implants):
self.add_implants = add_implants
class _bitcell:
def __init__(self, mirror, cell_s8_6t, cell_6t, cell_1rw1r, cell_1w1r):
self.mirror = mirror
@ -110,25 +110,25 @@ class _dff:
self.custom_type_list = custom_type_list
self.clk_pin = clk_pin
class _dff_buff:
def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts):
self.use_custom_ports = use_custom_ports
self.buf_ports = custom_buff_ports
self.add_body_contacts = add_body_contacts
class _dff_buff_array:
def __init__(self, use_custom_ports, add_body_contacts):
self.use_custom_ports = use_custom_ports
self.add_body_contacts = add_body_contacts
class _bitcell_array:
def __init__(self, use_custom_cell_arrangement):
self.use_custom_cell_arrangement = use_custom_cell_arrangement
class cell_properties():
"""
This contains meta information about the custom designed cells. For
@ -142,14 +142,14 @@ class cell_properties():
self._ptx = _ptx(model_is_subckt=False,
bin_spice_models=False)
self._pgate = _pgate(add_implants=False)
self._dff = _dff(use_custom_ports=False,
custom_port_list=["D", "Q", "clk", "vdd", "gnd"],
custom_type_list=["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
clk_pin="clk")
self._dff_buff = _dff_buff(use_custom_ports=False,
custom_buff_ports=["D", "qint", "clk", "vdd", "gnd"],
add_body_contacts=False)
@ -176,7 +176,7 @@ class cell_properties():
@property
def ptx(self):
return self._ptx
@property
def pgate(self):
return self._pgate
@ -184,7 +184,7 @@ class cell_properties():
@property
def dff(self):
return self._dff
@property
def dff_buff(self):
return self._dff_buff
@ -200,7 +200,7 @@ class cell_properties():
@property
def sense_amp(self):
return self._sense_amp
@property
def bitcell_array(self):
return self._bitcell_array

View File

@ -6,7 +6,7 @@
# All rights reserved.
#
class _bank:
def __init__(self, stack, pitch):
# bank
@ -15,8 +15,8 @@ class _bank:
# m2_stack, m3_pitch (sky130)
self.stack = stack
self.pitch = pitch
class _hierarchical_decoder:
def __init__(self,
bus_layer,
@ -60,7 +60,7 @@ class _hierarchical_predecode:
self.output_layer = output_layer
self.vertical_supply = vertical_supply
class _column_mux_array:
def __init__(self,
select_layer,
@ -74,7 +74,7 @@ class _column_mux_array:
self.select_pitch= select_pitch
self.bitline_layer = bitline_layer
class _port_address:
def __init__(self,
supply_offset):
@ -82,7 +82,7 @@ class _port_address:
# special supply offset
self.supply_offset = supply_offset
class _port_data:
def __init__(self,
channel_route_bitlines,
@ -94,7 +94,7 @@ class _port_data:
# en_layer
# m1
# m3 (sky130)
# precharge_array
# en_bar_layer
# m1
@ -110,7 +110,7 @@ class _replica_column:
# even row check (sky130)
self.even_rows = even_rows
class _wordline_driver:
def __init__(self,
vertical_supply):
@ -122,14 +122,14 @@ class _wordline_driver:
# vertical vdd/gnd (sky130)
self.vertical_supply = vertical_supply
class layer_properties():
"""
This contains meta information about the module routing layers. These
can be overriden in the tech.py file.
"""
def __init__(self):
self._bank = _bank(stack="m1_stack",
pitch="m2_pitch")
@ -138,7 +138,7 @@ class layer_properties():
input_layer="m1",
output_layer="m3",
vertical_supply=False)
self._hierarchical_predecode = _hierarchical_predecode(bus_layer="m2",
bus_directions="pref",
bus_space_factor=1,
@ -156,13 +156,13 @@ class layer_properties():
enable_layer="m1")
self._replica_column = _replica_column(even_rows=False)
self._wordline_driver = _wordline_driver(vertical_supply=False)
@property
def bank(self):
return self._bank
@property
def column_mux_array(self):
return self._column_mux_array
@ -174,7 +174,7 @@ class layer_properties():
@property
def hierarchical_predecode(self):
return self._hierarchical_predecode
@property
def port_address(self):
return self._port_address
@ -190,4 +190,4 @@ class layer_properties():
@property
def wordline_driver(self):
return self._wordline_driver

View File

@ -38,7 +38,7 @@ class delay_data():
assert isinstance(other, delay_data)
return delay_data(other.delay + self.delay,
self.slew)

View File

@ -9,22 +9,30 @@ from hierarchy_design import hierarchy_design
from utils import round_to_grid
import contact
from tech import preferred_directions
from tech import cell_properties as props
from globals import OPTS
import re
import debug
class design(hierarchy_design):
"""
This is the same as the hierarchy_design class except it contains
some DRC/layer constants and analytical models for other modules to reuse.
"""
def __init__(self, name):
super().__init__(name)
def __init__(self, name, cell_name=None):
# This allows us to use different GDS/spice circuits for hard cells instead of the default ones
# Except bitcell names are generated automatically by the globals.py setup_bitcells routines
# depending on the number of ports.
if name in props.names:
cell_name = props.names[name]
elif not cell_name:
cell_name = name
super().__init__(name, cell_name)
self.setup_multiport_constants()
def check_pins(self):
for pin_name in self.pins:
pins = self.get_pins(pin_name)
@ -52,7 +60,7 @@ class design(hierarchy_design):
match = re.search(r"minarea_(.*)", rule)
if match:
setattr(design, match.group(0), drc(match.group(0)))
# Single layer spacing rules
match = re.search(r"(.*)_to_(.*)", rule)
if match and match.group(1) == match.group(2):
@ -63,7 +71,7 @@ class design(hierarchy_design):
drc(match.group(0)))
else:
setattr(design, match.group(0), drc(match.group(0)))
match = re.search(r"(.*)_enclose_(.*)", rule)
if match:
setattr(design, match.group(0), drc(match.group(0)))
@ -94,7 +102,7 @@ class design(hierarchy_design):
design.well_enclose_active = max(design.pwell_enclose_active,
design.nwell_enclose_active,
design.active_space)
# These are for debugging previous manual rules
if False:
print("poly_width", design.poly_width)
@ -127,7 +135,7 @@ class design(hierarchy_design):
These are some layer constants used
in many places in the compiler.
"""
from tech import layer_indices
import tech
for layer in layer_indices:
@ -143,17 +151,17 @@ class design(hierarchy_design):
# Skip computing the pitch for active
if layer == "active":
continue
# Add the pitch
setattr(design,
"{}_pitch".format(layer),
design.compute_pitch(layer, True))
# Add the non-preferrd pitch (which has vias in the "wrong" way)
setattr(design,
"{}_nonpref_pitch".format(layer),
design.compute_pitch(layer, False))
if False:
from tech import preferred_directions
print(preferred_directions)
@ -173,9 +181,9 @@ class design(hierarchy_design):
import sys
sys.exit(1)
@staticmethod
@staticmethod
def compute_pitch(layer, preferred=True):
"""
This is the preferred direction pitch
i.e. we take the minimum or maximum contact dimension
@ -195,7 +203,7 @@ class design(hierarchy_design):
@staticmethod
def get_preferred_direction(layer):
return preferred_directions[layer]
@staticmethod
def compute_layer_pitch(layer_stack, preferred):
@ -228,7 +236,7 @@ class design(hierarchy_design):
def setup_multiport_constants(self):
"""
"""
These are contants and lists that aid multiport design.
Ports are always in the order RW, W, R.
Port indices start from 0 and increment.
@ -266,14 +274,14 @@ class design(hierarchy_design):
self.read_ports.append(port_number)
self.readonly_ports.append(port_number)
port_number += 1
def analytical_power(self, corner, load):
""" Get total power of a module """
total_module_power = self.return_power()
for inst in self.insts:
total_module_power += inst.mod.analytical_power(corner, load)
return total_module_power
design.setup_drc_constants()
design.setup_layer_constants()

View File

@ -153,7 +153,7 @@ class geometry:
def center(self):
""" Return the center coordinate """
return vector(self.cx(), self.cy())
class instance(geometry):
"""
@ -227,7 +227,7 @@ class instance(geometry):
self.mod.gds_write_file(self.gds)
# now write an instance of my module/structure
new_layout.addInstance(self.gds,
self.mod.name,
self.mod.cell_name,
offsetInMicrons=self.offset,
mirror=self.mirror,
rotate=self.rotate)
@ -271,9 +271,9 @@ class instance(geometry):
p.transform(self.offset, self.mirror, self.rotate)
new_pins.append(p)
return new_pins
def calculate_transform(self, node):
#set up the rotation matrix
#set up the rotation matrix
angle = math.radians(float(node.rotate))
mRotate = np.array([[math.cos(angle),-math.sin(angle),0.0],
[math.sin(angle),math.cos(angle),0.0],
@ -285,7 +285,7 @@ class instance(geometry):
mTranslate = np.array([[1.0,0.0,translateX],
[0.0,1.0,translateY],
[0.0,0.0,1.0]])
#set up the scale matrix (handles mirror X)
scaleX = 1.0
if(node.mirror == 'MX'):
@ -295,7 +295,7 @@ class instance(geometry):
mScale = np.array([[scaleX,0.0,0.0],
[0.0,scaleY,0.0],
[0.0,0.0,1.0]])
return (mRotate, mScale, mTranslate)
def apply_transform(self, mtransforms, uVector, vVector, origin):
@ -312,13 +312,13 @@ class instance(geometry):
def apply_path_transform(self, path):
uVector = np.array([[1.0],[0.0],[0.0]])
vVector = np.array([[0.0],[1.0],[0.0]])
origin = np.array([[0.0],[0.0],[1.0]])
origin = np.array([[0.0],[0.0],[1.0]])
while(path):
instance = path.pop(-1)
mtransforms = self.calculate_transform(instance)
(uVector, vVector, origin) = self.apply_transform(mtransforms, uVector, vVector, origin)
return (uVector, vVector, origin)
def reverse_transformation_bitcell(self, cell_name):
@ -339,7 +339,7 @@ class instance(geometry):
cell_paths.append(copy.copy(path))
inst_name = path[-1].name
# get the row and col names from the path
row = int(path[-1].name.split('_')[-2][1:])
col = int(path[-1].name.split('_')[-1][1:])
@ -349,7 +349,7 @@ class instance(geometry):
normalized_storage_nets = node.mod.get_normalized_storage_nets_offset()
(normalized_bl_offsets, normalized_br_offsets, bl_names, br_names) = node.mod.get_normalized_bitline_offset()
for offset in range(len(normalized_bl_offsets)):
for port in range(len(bl_names)):
cell_bl_meta.append([bl_names[offset], row, col, port])
@ -369,18 +369,18 @@ class instance(geometry):
Q_bar_y = -1 * Q_bar_y
for pair in range(len(normalized_bl_offsets)):
normalized_bl_offsets[pair] = (normalized_bl_offsets[pair][0],
normalized_bl_offsets[pair] = (normalized_bl_offsets[pair][0],
-1 * normalized_bl_offsets[pair][1])
for pair in range(len(normalized_br_offsets)):
normalized_br_offsets[pair] = (normalized_br_offsets[pair][0],
normalized_br_offsets[pair] = (normalized_br_offsets[pair][0],
-1 * normalized_br_offsets[pair][1])
Q_offsets.append([Q_x, Q_y])
Q_offsets.append([Q_x, Q_y])
Q_bar_offsets.append([Q_bar_x, Q_bar_y])
bl_offsets.append(normalized_bl_offsets)
br_offsets.append(normalized_br_offsets)
@ -402,13 +402,13 @@ class instance(geometry):
def __str__(self):
""" override print function output """
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.cell_name + " " + self.mirror + " R=" + str(self.rotate) + ")"
def __repr__(self):
""" override print function output """
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.cell_name + " " + self.mirror + " R=" + str(self.rotate) + ")"
class path(geometry):
"""Represents a Path"""

View File

@ -2,13 +2,13 @@ import copy
from collections import defaultdict
import debug
class timing_graph():
"""
Implements a directed graph
Nodes are currently just Strings.
"""
def __init__(self):
self.graph = defaultdict(set)
self.all_paths = []
@ -17,7 +17,7 @@ class timing_graph():
def add_edge(self, src_node, dest_node, edge_mod):
"""Adds edge to graph. Nodes added as well if they do not exist.
Module which defines the edge must be provided for timing information."""
src_node = src_node.lower()
dest_node = dest_node.lower()
self.graph[src_node].add(dest_node)
@ -25,99 +25,99 @@ class timing_graph():
def add_node(self, node):
"""Add node to graph with no edges"""
node = node.lower()
if node not in self.graph:
self.graph[node] = set()
def remove_edges(self, node):
"""Helper function to remove edges, useful for removing vdd/gnd"""
node = node.lower()
self.graph[node] = set()
def get_all_paths(self, src_node, dest_node, remove_rail_nodes=True, reduce_paths=True):
"""Traverse all paths from source to destination"""
src_node = src_node.lower()
dest_node = dest_node.lower()
# Remove vdd and gnd by default
# Will require edits if separate supplies are implemented.
if remove_rail_nodes:
# Names are also assumed.
self.remove_edges('vdd')
self.remove_edges('gnd')
# Mark all the vertices as not visited
# Mark all the vertices as not visited
visited = set()
# Create an array to store paths
# Create an array to store paths
path = []
self.all_paths = []
# Call the recursive helper function to print all paths
# Call the recursive helper function to print all paths
self.get_all_paths_util(src_node, dest_node, visited, path)
debug.info(2, "Paths found={}".format(len(self.all_paths)))
if reduce_paths:
self.reduce_paths()
return self.all_paths
def reduce_paths(self):
""" Remove any path that is a subset of another path """
self.all_paths = [p1 for p1 in self.all_paths if not any(set(p1)<=set(p2) for p2 in self.all_paths if p1 is not p2)]
def get_all_paths_util(self, cur_node, dest_node, visited, path):
"""Recursive function to find all paths in a Depth First Search manner"""
# Mark the current node as visited and store in path
# Mark the current node as visited and store in path
visited.add(cur_node)
path.append(cur_node)
# If current vertex is same as destination, then print
# current path[]
# If current vertex is same as destination, then print
# current path[]
if cur_node == dest_node:
self.all_paths.append(copy.deepcopy(path))
else:
# If current vertex is not destination
# Recur for all the vertices adjacent to this vertex
# If current vertex is not destination
# Recur for all the vertices adjacent to this vertex
for node in self.graph[cur_node]:
if node not in visited:
self.get_all_paths_util(node, dest_node, visited, path)
# Remove current vertex from path[] and mark it as unvisited
# Remove current vertex from path[] and mark it as unvisited
path.pop()
visited.remove(cur_node)
def get_timing(self, path, corner, slew, load):
"""Returns the analytical delays in the input path"""
if len(path) == 0:
return []
delays = []
cur_slew = slew
for i in range(len(path) - 1):
path_edge_mod = self.edge_mods[(path[i], path[i + 1])]
# On the output of the current stage, get COUT from all other mods connected
cout = 0
for node in self.graph[path[i + 1]]:
output_edge_mod = self.edge_mods[(path[i + 1], node)]
cout+=output_edge_mod.get_cin()
# If at the last output, include the final output load
# If at the last output, include the final output load
if i == len(path) - 2:
cout += load
delays.append(path_edge_mod.analytical_delay(corner, cur_slew, cout))
cur_slew = delays[-1].slew
return delays
def __str__(self):
""" override print function output """
@ -132,4 +132,4 @@ class timing_graph():
""" override print function output """
return str(self)

View File

@ -20,9 +20,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
"""
name_map = []
def __init__(self, name):
self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds"
self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp"
def __init__(self, name, cell_name):
self.gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds"
self.sp_file = OPTS.openram_tech + "sp_lib/" + cell_name + ".sp"
# If we have a separate lvs directory, then all the lvs files
# should be in there (all or nothing!)
@ -33,7 +33,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
lvs_dir = OPTS.openram_tech + lvs_subdir + "/"
if os.path.exists(lvs_dir):
self.lvs_file = lvs_dir + name + ".sp"
self.lvs_file = lvs_dir + cell_name + ".sp"
else:
self.lvs_file = self.sp_file
@ -41,8 +41,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
self.lvs_errors = "skipped"
self.name = name
hierarchy_spice.spice.__init__(self, name)
hierarchy_layout.layout.__init__(self, name)
self.cell_name = cell_name
hierarchy_spice.spice.__init__(self, name, cell_name)
hierarchy_layout.layout.__init__(self, name, cell_name)
self.init_graph_params()
def get_layout_pins(self, inst):
@ -55,7 +56,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
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, force_check=False):
"""Checks both DRC and LVS for a module"""
import verify
@ -76,23 +77,23 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
self.lvs_write(tempspice)
self.gds_write(tempgds)
# Final verification option does not allow nets to be connected by label.
self.drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification)
self.lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
self.drc_errors = verify.run_drc(self.cell_name, tempgds, extract=True, final_verification=final_verification)
self.lvs_errors = verify.run_lvs(self.cell_name, tempgds, tempspice, final_verification=final_verification)
# 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(self.drc_errors == 0,
"DRC failed for {0} with {1} error(s)".format(self.name,
"DRC failed for {0} with {1} error(s)".format(self.cell_name,
self.drc_errors))
debug.check(self.lvs_errors == 0,
"LVS failed for {0} with {1} errors(s)".format(self.name,
"LVS failed for {0} with {1} errors(s)".format(self.cell_name,
self.lvs_errors))
if OPTS.purge_temp:
os.remove(tempspice)
os.remove(tempgds)
def DRC(self, final_verification=False):
"""Checks DRC for a module"""
import verify
@ -104,11 +105,11 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only:
return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.cell_name)
self.gds_write(tempgds)
num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification)
num_errors = verify.run_drc(self.cell_name, tempgds, final_verification=final_verification)
debug.check(num_errors == 0,
"DRC failed for {0} with {1} error(s)".format(self.name,
"DRC failed for {0} with {1} error(s)".format(self.cell_name,
num_errors))
if OPTS.purge_temp:
@ -125,30 +126,30 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only:
return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name)
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.cell_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)
debug.check(num_errors == 0,
"LVS failed for {0} with {1} error(s)".format(self.name,
"LVS failed for {0} with {1} error(s)".format(self.cell_name,
num_errors))
if OPTS.purge_temp:
os.remove(tempspice)
os.remove(tempgds)
def init_graph_params(self):
"""
"""
Initializes parameters relevant to the graph creation
"""
# 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):
"""
Recursively create graph from instances in module.
"""
# Translate port names to external nets
if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
@ -162,7 +163,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
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.
@ -195,7 +196,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
else:
converted_conns.append("{}.{}".format(inst_name, conn))
return converted_conns
def add_graph_edges(self, graph, port_nets):
"""
For every input, adds an edge to every output.
@ -211,13 +212,13 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
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]
s = "********** design {0} **********".format(self.name)
s = "********** design {0} **********".format(self.cell_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))
s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts))
@ -231,4 +232,4 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
for i in self.insts:
text+=str(i) + ",\n"
return text

View File

@ -13,6 +13,7 @@ from tech import drc, GDS
from tech import layer as techlayer
from tech import layer_indices
from tech import layer_stacks
from tech import preferred_directions
import os
from globals import OPTS
from vector import vector
@ -30,8 +31,9 @@ class layout():
layout/netlist and perform LVS/DRC.
"""
def __init__(self, name):
def __init__(self, name, cell_name):
self.name = name
self.cell_name = cell_name
self.width = None
self.height = None
self.bounding_box = None
@ -66,13 +68,13 @@ class layout():
def offset_x_coordinates(self):
"""
This function is called after everything is placed to
shift the origin to the furthest left point.
shift the origin to the furthest left point.
Y offset is unchanged.
"""
offset = self.find_lowest_coords()
self.translate_all(offset.scale(1, 0))
return offset
def get_gate_offset(self, x_offset, height, inv_num):
"""
Gets the base offset and y orientation of stacked rows of gates
@ -214,7 +216,7 @@ class layout():
# Contacts are not really instances, so skip them
if "contact" not in mod.name:
# Check that the instance name is unique
debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.name, name))
debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.cell_name, name))
self.inst_names.add(name)
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
@ -299,9 +301,9 @@ class layout():
tx_list.append(i)
except AttributeError:
pass
return tx_list
def get_pin(self, text):
"""
Return the pin or list of pins
@ -315,7 +317,7 @@ class layout():
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.cell_name), -1)
def get_pins(self, text):
"""
@ -612,24 +614,24 @@ class layout():
next_id = 0
curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None)
via = self.add_via_center(layers=curr_stack,
size=size,
offset=offset,
directions=directions,
implant_type=implant_type,
well_type=well_type)
if cur_layer != from_layer:
self.add_min_area_rect_center(cur_layer,
offset,
via.mod.first_layer_width,
via.mod.first_layer_height)
cur_layer = curr_stack[next_id]
return via
def add_min_area_rect_center(self,
layer,
offset,
@ -643,14 +645,14 @@ class layout():
min_area = drc("minarea_{}".format(layer))
if min_area == 0:
return
min_width = drc("minwidth_{}".format(layer))
if preferred_directions[layer] == "V":
height = max(min_area / width, min_width)
else:
width = max(min_area / height, min_width)
self.add_rect_center(layer=layer,
offset=offset,
width=width,
@ -734,7 +736,7 @@ class layout():
height = boundary[1][1] - boundary[0][1]
width = boundary[1][0] - boundary[0][0]
for boundary_layer in boundary_layers:
(layer_number, layer_purpose) = techlayer[boundary_layer]
gds_layout.addBox(layerNumber=layer_number,
@ -886,7 +888,7 @@ class layout():
new_pin = pin_layout(names[i],
[rect.ll(), rect.ur()],
layer)
pins[names[i]] = new_pin
else:
for i in range(len(names)):
@ -904,7 +906,7 @@ class layout():
new_pin = pin_layout(names[i],
[rect.ll(), rect.ur()],
layer)
pins[names[i]] = new_pin
return pins
@ -1042,7 +1044,7 @@ class layout():
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self)
self.add_inst(cr.name, cr)
self.connect_inst([])
def add_boundary(self, ll=vector(0, 0), ur=None):
""" Add boundary for debugging dimensions """
if OPTS.netlist_only:
@ -1107,7 +1109,7 @@ class layout():
width=xmax - xmin,
height=ymax - ymin)
return rect
def copy_power_pins(self, inst, name, add_vias=True):
"""
This will copy a power pin if it is on the lowest power_grid layer.
@ -1166,7 +1168,7 @@ class layout():
bottom = ll.y
right = ur.x
top = ur.y
pin_loc = pin.center()
if side == "left":
peri_pin_loc = vector(left, pin_loc.y)
@ -1184,14 +1186,14 @@ class layout():
self.add_via_stack_center(from_layer=pin.layer,
to_layer=layer,
offset=pin_loc)
self.add_path(layer,
[pin_loc, peri_pin_loc])
return self.add_layout_pin_rect_center(text=name,
layer=layer,
offset=peri_pin_loc)
def add_power_ring(self, bbox):
"""
Create vdd and gnd power rings around an area of the bounding box

View File

@ -10,6 +10,7 @@ import re
import os
import math
import tech
from pprint import pformat
from delay_data import delay_data
from wire_spice_model import wire_spice_model
from power_data import power_data
@ -26,8 +27,9 @@ class spice():
Class consisting of a set of modules and instances of these modules
"""
def __init__(self, name):
def __init__(self, name, cell_name):
self.name = name
self.cell_name = cell_name
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
# Holds subckts/mods for this module
@ -63,7 +65,7 @@ class spice():
self.comments = []
self.comments.append(comment)
def add_pin(self, name, pin_type="INOUT"):
""" Adds a pin to the pins list. Default type is INOUT signal. """
self.pins.append(name)
@ -82,7 +84,7 @@ class spice():
"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,
@ -104,7 +106,7 @@ class spice():
\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)}
def get_pin_type(self, name):
""" Returns the type of the signal pin. """
pin_type = self.pin_type[name]
@ -118,7 +120,7 @@ class spice():
return "INOUT"
else:
return self.pin_type[name]
def get_inputs(self):
""" These use pin types to determine pin lists. These
may be over-ridden by submodules that didn't use pin directions yet."""
@ -164,7 +166,6 @@ class spice():
num_pins = len(self.insts[-1].mod.pins)
num_args = len(args)
if (check and num_pins != num_args):
from pprint import pformat
if num_pins < num_args:
mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins)
arg_pins = args
@ -181,10 +182,9 @@ class spice():
self.conns.append(args)
if check and (len(self.insts)!=len(self.conns)):
from pprint import pformat
insts_string=pformat(self.insts)
conns_string=pformat(self.conns)
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
len(self.insts),
len(self.conns)))
@ -214,7 +214,7 @@ class spice():
f.close()
# find the correct subckt line in the file
subckt = re.compile("^.subckt {}".format(self.name), re.IGNORECASE)
subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE)
subckt_line = list(filter(subckt.search, self.spice))[0]
# parses line into ports and remove subckt
self.pins = subckt_line.split(" ")[2:]
@ -234,12 +234,12 @@ class spice():
# 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 = re.compile("^.subckt {}".format(self.cell_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.
@ -255,14 +255,14 @@ class spice():
if net_formatted in line:
return True
return False
def do_nets_exist(self, nets):
"""For handmade cell, checks sp file contains the storage nodes."""
nets_match = True
for net in nets:
nets_match = nets_match and self.check_net_in_spice(net)
return nets_match
def contains(self, mod, modlist):
for x in modlist:
if x.name == mod.name:
@ -279,7 +279,7 @@ class spice():
return
elif not self.spice:
# If spice isn't defined, we dynamically generate one.
# recursively write the modules
for i in self.mods:
if self.contains(i, usedMODS):
@ -293,18 +293,18 @@ class spice():
return
# write out the first spice line (the subcircuit)
sp.write("\n.SUBCKT {0} {1}\n".format(self.name,
sp.write("\n.SUBCKT {0} {1}\n".format(self.cell_name,
" ".join(self.pins)))
for pin in self.pins:
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):
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.cell_name,
len(self.insts),
len(self.conns)))
debug.error("Instances: \n" + str(self.insts))
@ -330,9 +330,9 @@ class spice():
else:
sp.write("X{0} {1} {2}\n".format(self.insts[i].name,
" ".join(self.conns[i]),
self.insts[i].mod.name))
self.insts[i].mod.cell_name))
sp.write(".ENDS {0}\n".format(self.name))
sp.write(".ENDS {0}\n".format(self.cell_name))
else:
# If spice is a hard module, output the spice file contents.
@ -343,7 +343,7 @@ class spice():
sp.write("\n".join(self.lvs))
else:
sp.write("\n".join(self.spice))
sp.write("\n")
def sp_write(self, spname):
@ -365,19 +365,19 @@ class spice():
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
relative_cap = logical_effort.convert_farad_to_relative_c(load)
stage_effort = self.get_stage_effort(relative_cap)
# If it fails, then keep running with a valid object.
if not stage_effort:
return delay_data(0.0, 0.0)
abs_delay = stage_effort.get_absolute_delay()
corner_delay = self.apply_corners_analytically(abs_delay, corner)
SLEW_APPROXIMATION = 0.1
@ -390,27 +390,27 @@ class spice():
.format(self.__class__.__name__))
debug.warning("Class {0} name {1}"
.format(self.__class__.__name__,
self.name))
self.cell_name))
return None
def get_cin(self):
"""Returns input load in Femto-Farads. All values generated using
relative capacitance function then converted based on tech file parameter."""
# Override this function within a module if a more accurate input capacitance is needed.
# Input/outputs with differing capacitances is not implemented.
relative_cap = self.input_load()
return logical_effort.convert_relative_c_to_farad(relative_cap)
def input_load(self):
"""Inform users undefined relative capacitance functions used for analytical delays."""
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))
self.cell_name))
return 0
def cal_delay_with_rc(self, corner, r, c, slew, swing=0.5):
"""
Calculate the delay of a mosfet by
@ -420,7 +420,7 @@ class spice():
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
# Output slew should be linear to input slew which is described
# as 0.005* slew.
@ -439,7 +439,7 @@ class spice():
volt_mult = self.get_voltage_delay_factor(vdd)
temp_mult = self.get_temp_delay_factor(temp)
return delay * proc_mult * volt_mult * temp_mult
def get_process_delay_factor(self, proc):
"""Returns delay increase estimate based off process
Currently does +/-10 for fast/slow corners."""
@ -452,13 +452,13 @@ class spice():
elif mos_proc == 'S':
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
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.
@ -478,7 +478,7 @@ class spice():
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)
@ -486,16 +486,16 @@ class spice():
proc, vdd, temp = corner
net_vswing = vdd * swing
power_dyn = c * vdd * net_vswing * freq
# 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)
return power_dyn
def return_power(self, dynamic=0.0, leakage=0.0):
return power_data(dynamic, leakage)
@ -519,7 +519,7 @@ class spice():
if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set):
aliases.append(net)
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).
@ -541,7 +541,7 @@ class spice():
return True
mod_set.add(subinst.mod)
return False
def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod):
"""
Utility function for checking single net alias.

View File

@ -41,7 +41,7 @@ class lef:
self.lef_write_obstructions()
self.lef_write_footer()
self.lef.close()
def lef_write_header(self):
""" Header of LEF file """
self.lef.write("VERSION 5.4 ;\n")
@ -51,7 +51,7 @@ class lef:
self.lef.write("UNITS\n")
self.lef.write(" DATABASE MICRONS {0} ;\n".format(self.lef_units))
self.lef.write("END UNITS\n")
self.lef.write("{0}MACRO {1}\n".format(self.indent,self.name))
self.indent += " "
self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent))
@ -59,25 +59,25 @@ class lef:
round(self.width,self.round_grid),
round(self.height,self.round_grid)))
self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent))
def lef_write_footer(self):
self.lef.write("{0}END {1}\n".format(self.indent,self.name))
self.indent = self.indent[:-3]
self.lef.write("END LIBRARY\n")
def lef_write_pin(self, name):
pin_dir = self.get_pin_dir(name)
pin_type = self.get_pin_type(name)
self.lef.write("{0}PIN {1}\n".format(self.indent,name))
self.indent += " "
self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent,pin_dir))
if pin_type in ["POWER","GROUND"]:
self.lef.write("{0}USE {1} ; \n".format(self.indent,pin_type))
self.lef.write("{0}SHAPE ABUTMENT ; \n".format(self.indent))
self.lef.write("{0}PORT\n".format(self.indent))
self.indent += " "
@ -86,7 +86,7 @@ class lef:
for pin in pin_list:
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,pin.layer))
self.lef_write_shape(pin.rect)
# End the PORT
self.indent = self.indent[:-3]
self.lef.write("{0}END\n".format(self.indent))
@ -94,7 +94,7 @@ class lef:
# End the PIN
self.indent = self.indent[:-3]
self.lef.write("{0}END {1}\n".format(self.indent,name))
def lef_write_obstructions(self):
""" Write all the obstructions on each layer """
self.lef.write("{0}OBS\n".format(self.indent))
@ -111,16 +111,16 @@ class lef:
self.lef.write("{0}END\n".format(self.indent))
def lef_write_shape(self, rect):
if len(rect) == 2:
if len(rect) == 2:
""" Write a LEF rectangle """
self.lef.write("{0}RECT ".format(self.indent))
self.lef.write("{0}RECT ".format(self.indent))
for item in rect:
# print(rect)
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid)))
self.lef.write(" ;\n")
else:
else:
""" Write a LEF polygon """
self.lef.write("{0}POLYGON ".format(self.indent))
self.lef.write("{0}POLYGON ".format(self.indent))
for item in rect:
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid)))
# for i in range(0,len(rect)):

View File

@ -33,7 +33,7 @@ class pin_layout:
# These are the valid pin layers
valid_layers = { x: layer[x] for x in layer_indices.keys()}
# if it's a string, use the name
if type(layer_name_pp) == str:
self._layer = layer_name_pp
@ -378,7 +378,7 @@ class pin_layout:
from tech import label_purpose
except ImportError:
label_purpose = purpose
newLayout.addBox(layerNumber=layer_num,
purposeNumber=pin_purpose,
offsetInMicrons=self.ll(),

View File

@ -14,12 +14,12 @@ from vector3d import vector3d
from sram_factory import factory
class route(design):
"""
"""
Object route (used by the router module)
Add a route of minimium metal width between a set of points.
The widths are the layer widths of the layer stack.
The widths are the layer widths of the layer stack.
(Vias are in numer of vias.)
The wire must be completely rectilinear and the
The wire must be completely rectilinear and the
z-dimension of the points refers to the layers.
The points are the center of the wire.
This can have non-preferred direction routing.
@ -45,12 +45,12 @@ class route(design):
def setup_layers(self):
(self.horiz_layer_name, self.via_layer, self.vert_layer_name) = self.layer_stack
(self.horiz_layer_width, self.num_vias, self.vert_layer_width) = self.layer_widths
if not self.vert_layer_width:
self.vert_layer_width = drc("minwidth_{0}".format(self.vert_layer_name))
if not self.horiz_layer_width:
self.horiz_layer_width = drc("minwidth_{0}".format(self.horiz_layer_name))
# offset this by 1/2 the via size
self.c=factory.create(module_type="contact",
layer_stack=self.layer_stack,
@ -58,7 +58,7 @@ class route(design):
def create_wires(self):
"""
"""
Add the wire segments of the route.
"""
@ -67,7 +67,7 @@ class route(design):
a, b = tee(iterable)
next(b, None)
return zip(a, b)
plist = list(pairwise(self.path))
for p0,p1 in plist:
if p0.z != p1.z: # via
@ -87,10 +87,10 @@ class route(design):
self.draw_corner_wire(plist[0][0])
self.draw_corner_wire(plist[-1][1])
def get_layer_width(self, layer_zindex):
"""
Return the layer width
Return the layer width
"""
if layer_zindex==0:
return self.horiz_layer_width
@ -109,11 +109,11 @@ class route(design):
return self.vert_layer_name
else:
debug.error("Incorrect layer zindex.",-1)
def draw_wire(self, p0, p1):
"""
This draws a straight wire with layer_minwidth
This draws a straight wire with layer_minwidth
"""
layer_width = self.get_layer_width(p0.z)
@ -145,8 +145,8 @@ class route(design):
offset=vector(offset.x,offset.y),
width=width,
height=height)
def draw_corner_wire(self, p0):
""" This function adds the corner squares since the center
line convention only draws to the center of the corner."""

View File

@ -4,10 +4,12 @@
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import os
import math
import gdsMill
import tech
import math
import globals
import debug
from vector import vector
@ -57,10 +59,11 @@ def auto_measure_libcell(pin_list, name, units, lpp):
Return these as a set of properties including the cell width/height too.
"""
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(cell_gds)
cell_vlsi = _get_gds_reader(units, cell_gds)
# FIXME: This duplicates a lot of functionality of get_gds_size and
# get_gds_pins, it should probably just call those functions?
cell = {}
measure_result = cell_vlsi.getLayoutBorder(lpp[0])
if measure_result:
@ -73,22 +76,47 @@ def auto_measure_libcell(pin_list, name, units, lpp):
return cell
_GDS_READER_CACHE = {}
def _get_gds_reader(units, gds_filename):
gds_absname = os.path.realpath(gds_filename)
k = (units, gds_absname)
try:
return _GDS_READER_CACHE[k]
except KeyError:
debug.info(4, "Creating VLSI layout from {}".format(gds_absname))
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(gds_absname)
_GDS_READER_CACHE[k] = cell_vlsi
return cell_vlsi
_GDS_SIZE_CACHE = {}
def get_gds_size(name, gds_filename, units, lpp):
"""
Open a GDS file and return the size from either the
bounding box or a border layer.
"""
debug.info(4, "Creating VLSI layout for {}".format(name))
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(gds_filename)
k = (name, os.path.realpath(gds_filename), units, lpp)
try:
return _GDS_SIZE_CACHE[k]
except KeyError:
cell_vlsi = _get_gds_reader(units, gds_filename)
measure_result = cell_vlsi.getLayoutBorder(lpp)
if not measure_result:
debug.info(2, "Layout border failed. Trying to measure size for {}".format(name))
measure_result = cell_vlsi.measureSize(name)
# returns width,height
return measure_result
measure_result = cell_vlsi.getLayoutBorder(lpp)
if not measure_result:
debug.info(2, "Layout border failed. Trying to measure size for {}".format(name))
measure_result = cell_vlsi.measureSize(name)
_GDS_SIZE_CACHE[k] = measure_result
# returns width,height
return measure_result
def get_libcell_size(name, units, lpp):
@ -101,27 +129,34 @@ def get_libcell_size(name, units, lpp):
return(get_gds_size(name, cell_gds, units, lpp))
_GDS_PINS_CACHE = {}
def get_gds_pins(pin_names, name, gds_filename, units):
"""
Open a GDS file and find the pins in pin_names as text on a given layer.
Return these as a rectangle layer pair for each pin.
"""
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(gds_filename)
k = (tuple(pin_names), name, os.path.realpath(gds_filename), units)
try:
return dict(_GDS_PINS_CACHE[k])
except KeyError:
cell_vlsi = _get_gds_reader(units, gds_filename)
cell = {}
for pin_name in pin_names:
cell[str(pin_name)] = []
pin_list = cell_vlsi.getPinShape(str(pin_name))
for pin_shape in pin_list:
(lpp, boundary) = pin_shape
rect = [vector(boundary[0], boundary[1]),
vector(boundary[2], boundary[3])]
# this is a list because other cells/designs
# may have must-connect pins
cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp))
return cell
cell = {}
for pin_name in pin_names:
cell[str(pin_name)] = []
pin_list = cell_vlsi.getPinShape(str(pin_name))
for pin_shape in pin_list:
(lpp, boundary) = pin_shape
rect = [vector(boundary[0], boundary[1]),
vector(boundary[2], boundary[3])]
# this is a list because other cells/designs
# may have must-connect pins
cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp))
_GDS_PINS_CACHE[k] = cell
return dict(cell)
def get_libcell_pins(pin_list, name, units):
@ -132,7 +167,3 @@ def get_libcell_pins(pin_list, name, units):
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
return(get_gds_pins(pin_list, name, cell_gds, units))

View File

@ -39,8 +39,8 @@ class vector():
return "v["+str(self.x)+","+str(self.y)+"]"
def __setitem__(self, index, value):
"""
override setitem function
"""
override setitem function
can set value by vector[index]=value
"""
if index==0:
@ -50,10 +50,10 @@ class vector():
else:
self.x=float(value[0])
self.y=float(value[1])
def __getitem__(self, index):
"""
override getitem function
override getitem function
can get value by value=vector[index]
"""
if index==0:
@ -61,7 +61,7 @@ class vector():
elif index==1:
return self.y
else:
return self
return self
def __add__(self, other):
"""
@ -109,7 +109,7 @@ class vector():
"""
Changes the coodrinate to match the grid settings
"""
grid = tech.drc["grid"]
grid = tech.drc["grid"]
# this gets the nearest integer value
off_in_grid = int(round(round((offset / grid), 2), 0))
offset = off_in_grid * grid
@ -150,8 +150,8 @@ class vector():
Override round function
"""
return vector(int(round(self.x)),int(round(self.y)))
def __eq__(self, other):
"""Override the default Equals behavior"""
if isinstance(other, self.__class__):

View File

@ -9,13 +9,13 @@ import debug
import math
class verilog:
"""
"""
Create a behavioral Verilog file for simulation.
This is inherited by the sram_base class.
"""
def __init__(self):
pass
def verilog_write(self,verilog_name):
""" Write a behavioral Verilog model. """
self.vf = open(verilog_name, "w")
@ -67,7 +67,7 @@ class verilog:
self.add_inputs_outputs(port)
self.vf.write("\n")
for port in self.all_ports:
self.register_inputs(port)
@ -79,8 +79,8 @@ class verilog:
self.add_write_block(port)
if port in self.read_ports:
self.add_read_block(port)
self.vf.write("\n")
self.vf.write("\n")
self.vf.write("endmodule\n")
self.vf.close()
@ -91,9 +91,9 @@ class verilog:
"""
self.add_regs(port)
self.add_flops(port)
def add_regs(self, port):
"""
"""
Create the input regs for the given port.
"""
self.vf.write(" reg csb{0}_reg;\n".format(port))
@ -107,7 +107,7 @@ class verilog:
self.vf.write(" reg [DATA_WIDTH-1:0] din{0}_reg;\n".format(port))
if port in self.read_ports:
self.vf.write(" reg [DATA_WIDTH-1:0] dout{0};\n".format(port))
def add_flops(self, port):
"""
Add the flop behavior logic for a port.
@ -125,7 +125,7 @@ class verilog:
self.vf.write(" addr{0}_reg = addr{0};\n".format(port))
if port in self.read_ports:
self.add_write_read_checks(port)
if port in self.write_ports:
self.vf.write(" din{0}_reg = din{0};\n".format(port))
if port in self.read_ports:
@ -150,7 +150,7 @@ class verilog:
self.vf.write(" $display($time,\" Writing %m addr{0}=%b din{0}=%b\",addr{0}_reg,din{0}_reg);\n".format(port))
self.vf.write(" end\n\n")
def add_inputs_outputs(self, port):
"""
@ -203,7 +203,7 @@ class verilog:
else:
self.vf.write(" mem[addr{0}_reg] = din{0}_reg;\n".format(port))
self.vf.write(" end\n")
def add_read_block(self, port):
"""
Add a read port block.
@ -231,12 +231,12 @@ class verilog:
wport_control = "!csb{0} && !web{0}".format(wport)
else:
wport_control = "!csb{0}".format(wport)
self.vf.write(" if ({1} && {3} && (addr{0} == addr{2}))\n".format(wport,wport_control,rport,rport_control))
self.vf.write(" $display($time,\" WARNING: Writing and reading addr{0}=%b and addr{1}=%b simultaneously!\",addr{0},addr{1});\n".format(wport,rport))
def add_write_read_checks(self, rport):
"""
"""
Add a warning if we read from an address that we are currently writing.
Can be fixed if we appropriately size the write drivers to do this .
"""

View File

@ -58,13 +58,13 @@ class wire(wire_path):
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
@ -79,13 +79,13 @@ class wire(wire_path):
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."""

View File

@ -15,7 +15,7 @@ def create_rectilinear_route(my_list):
""" Add intermediate nodes if it isn't rectilinear. Also skip
repeated nodes. Also, convert to vector if the aren't."""
pl = [snap_to_grid(x) for x in my_list]
my_list = []
for index in range(len(pl) - 1):
if pl[index] != pl[index + 1]:
@ -121,7 +121,7 @@ class wire_path():
"""
width = layer_width
height = length
height = length
if orientation == "horizontal":
width = length

View File

@ -12,7 +12,7 @@ class wire_spice_model():
"""
def __init__(self, lump_num, wire_length, wire_width):
self.lump_num = lump_num # the number of segment the wire delay has
self.wire_c = self.cal_wire_c(wire_length, wire_width) # c in each segment
self.wire_c = self.cal_wire_c(wire_length, wire_width) # c in each segment
self.wire_r = self.cal_wire_r(wire_length, wire_width) # r in each segment
def cal_wire_c(self, wire_length, wire_width):
@ -36,7 +36,7 @@ class wire_spice_model():
swing_factor = abs(math.log(1-swing)) # time constant based on swing
sum_factor = (1+self.lump_num) * self.lump_num * 0.5 # sum of the arithmetic sequence
delay = sum_factor * swing_factor * self.wire_r * self.wire_c
delay = sum_factor * swing_factor * self.wire_r * self.wire_c
slew = delay * 2 + slew
result= delay_data(delay, slew)
return result

View File

@ -6,9 +6,8 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
from globals import OPTS
import bitcell_base
@ -20,8 +19,6 @@ class bitcell(bitcell_base.bitcell_base):
library.
"""
# If we have a split WL bitcell, if not be backwards
# compatible in the tech file
pin_names = [props.bitcell.cell_6t.pin.bl,
props.bitcell.cell_6t.pin.br,
props.bitcell.cell_6t.pin.wl,
@ -30,20 +27,12 @@ class bitcell(bitcell_base.bitcell_base):
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
(width, height) = utils.get_libcell_size("cell_6t",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "cell_6t")
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = OPTS.bitcell_name
super().__init__(name, cell_name)
debug.info(2, "Create bitcell")
self.width = bitcell.width
self.height = bitcell.height
self.pin_map = bitcell.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
def get_all_wl_names(self):

View File

@ -6,11 +6,9 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer, parameter, drc
from tech import cell_properties as props
import logical_effort
import bitcell_base
from globals import OPTS
class bitcell_1rw_1r(bitcell_base.bitcell_base):
@ -29,33 +27,25 @@ class bitcell_1rw_1r(bitcell_base.bitcell_base):
props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
(width, height) = utils.get_libcell_size("cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "cell_1rw_1r")
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = OPTS.bitcell_name
super().__init__(name, cell_name)
debug.info(2, "Create bitcell with 1RW and 1R Port")
self.width = bitcell_1rw_1r.width
self.height = bitcell_1rw_1r.height
self.pin_map = bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
pin_names = bitcell_1rw_1r.pin_names
pin_names = self.pin_names
self.bl_names = [pin_names[0], pin_names[2]]
self.br_names = [pin_names[1], pin_names[3]]
self.wl_names = [pin_names[4], pin_names[5]]
def get_bitcell_pins(self, col, row):
"""
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""

View File

@ -6,10 +6,9 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
from globals import OPTS
class bitcell_1w_1r(bitcell_base.bitcell_base):
@ -31,28 +30,20 @@ class bitcell_1w_1r(bitcell_base.bitcell_base):
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
(width, height) = utils.get_libcell_size("cell_1w_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "cell_1w_1r")
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = OPTS.bitcell_name
super().__init__(name, cell_name)
debug.info(2, "Create bitcell with 1W and 1R Port")
self.width = bitcell_1w_1r.width
self.height = bitcell_1w_1r.height
self.pin_map = bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
pin_names = bitcell_1w_1r.pin_names
pin_names = self.pin_names
self.bl_names = [pin_names[0], pin_names[2]]
self.br_names = [pin_names[1], pin_names[3]]
self.wl_names = [pin_names[4], pin_names[5]]
def get_bitcell_pins(self, col, row):
"""
Creates a list of connections in the bitcell,

View File

@ -8,17 +8,29 @@
import debug
import design
import utils
from globals import OPTS
import logical_effort
from tech import parameter, drc, layer
from tech import GDS, parameter, drc, layer
class bitcell_base(design.design):
"""
Base bitcell parameters to be over-riden.
"""
def __init__(self, name):
design.design.__init__(self, name)
cell_size_layer = "boundary"
def __init__(self, name, cell_name, hard_cell=True):
design.design.__init__(self, name, cell_name)
if hard_cell:
(self.width, self.height) = utils.get_libcell_size(cell_name,
GDS["unit"],
layer[self.cell_size_layer])
self.pin_map = utils.get_libcell_pins(self.pin_names,
cell_name,
GDS["unit"])
self.add_pin_types(self.type_list)
def get_stage_effort(self, load):
parasitic_delay = 1
@ -49,13 +61,13 @@ class bitcell_base(design.design):
def input_load(self):
""" Return the relative capacitance of the access transistor gates """
# FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell.
# Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2 * access_tx_cin
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
# This is a handmade cell so the value must be entered
@ -82,7 +94,7 @@ class bitcell_base(design.design):
def get_storage_net_offset(self):
"""
Gets the location of the storage net labels to add top level
Gets the location of the storage net labels to add top level
labels for pex simulation.
"""
# If we generated the bitcell, we already know where Q and Q_bar are
@ -92,7 +104,7 @@ class bitcell_base(design.design):
for text in self.gds.getTexts(layer["m1"]):
if self.storage_nets[i] == text.textString.rstrip('\x00'):
self.storage_net_offsets.append(text.coordinates[0])
for i in range(len(self.storage_net_offsets)):
self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]])
@ -116,7 +128,7 @@ class bitcell_base(design.design):
if bl_names[i] == text.textString.rstrip('\x00'):
self.bl_offsets.append(text.coordinates[0])
found_bl.append(bl_names[i])
continue
for i in range(len(br_names)):
@ -131,16 +143,16 @@ class bitcell_base(design.design):
self.bl_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.bl_offsets[i]])
for i in range(len(self.br_offsets)):
self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]])
self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]])
return(self.bl_offsets, self.br_offsets, found_bl, found_br)
def get_normalized_storage_nets_offset(self):
def get_normalized_storage_nets_offset(self):
"""
Convert storage net offset to be relative to the bottom left corner
of the bitcell. This is useful for making sense of offsets outside
of the bitcell. This is useful for making sense of offsets outside
of the bitcell.
"""
"""
if OPTS.bitcell is not "pbitcell":
normalized_storage_net_offset = self.get_storage_net_offset()

View File

@ -6,8 +6,6 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
@ -21,24 +19,14 @@ class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
props.bitcell.cell_1rw1r.pin.bl1,
props.bitcell.cell_1rw1r.pin.br1,
props.bitcell.cell_1rw1r.pin.vdd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"POWER", "GROUND"]
(width, height) = utils.get_libcell_size("col_cap_cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"col_cap_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""):
def __init__(self, name="col_cap_cell_1rw_1r", cell_name=None):
if not cell_name:
cell_name = name
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "col_cap_cell_1rw_1r")
bitcell_base.bitcell_base.__init__(self, name, cell_name)
debug.info(2, "Create col_cap bitcell 1rw+1r object")
self.width = col_cap_bitcell_1rw_1r.width
self.height = col_cap_bitcell_1rw_1r.height
self.pin_map = col_cap_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.no_instances = True

View File

@ -6,10 +6,9 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
from globals import OPTS
class dummy_bitcell(bitcell_base.bitcell_base):
@ -24,19 +23,12 @@ class dummy_bitcell(bitcell_base.bitcell_base):
props.bitcell.cell_6t.pin.wl,
props.bitcell.cell_6t.pin.vdd,
props.bitcell.cell_6t.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_6t",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_6t", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "dummy_cell_6t")
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = OPTS.dummy_bitcell_name
super().__init__(name, cell_name)
debug.info(2, "Create dummy bitcell")
self.width = dummy_bitcell.width
self.height = dummy_bitcell.height
self.pin_map = dummy_bitcell.pin_map

View File

@ -6,10 +6,9 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
from globals import OPTS
class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base):
@ -27,23 +26,13 @@ class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base):
props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"dummy_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1rw_1r")
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = OPTS.dummy_bitcell_name
super().__init__(name, cell_name)
debug.info(2, "Create dummy bitcell 1rw+1r object")
self.width = dummy_bitcell_1rw_1r.width
self.height = dummy_bitcell_1rw_1r.height
self.pin_map = dummy_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)

View File

@ -6,10 +6,9 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
from globals import OPTS
class dummy_bitcell_1w_1r(bitcell_base.bitcell_base):
@ -29,21 +28,13 @@ class dummy_bitcell_1w_1r(bitcell_base.bitcell_base):
props.bitcell.cell_1w1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_1w_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"dummy_cell_1w_1r",
GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1w_1r")
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = OPTS.dummy_bitcell_name
super().__init__(name, cell_name)
debug.info(2, "Create dummy bitcell 1w+1r object")
self.width = dummy_bitcell_1w_1r.width
self.height = dummy_bitcell_1w_1r.height
self.pin_map = dummy_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)

View File

@ -7,85 +7,88 @@
#
import debug
import design
from tech import drc, spice,parameter
from vector import vector
from globals import OPTS
from sram_factory import factory
class dummy_pbitcell(design.design):
"""
Creates a replica bitcell using pbitcell
"""
def __init__(self, name):
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = name
self.num_rw_ports = OPTS.num_rw_ports
self.num_w_ports = OPTS.num_w_ports
self.num_r_ports = OPTS.num_r_ports
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
design.design.__init__(self, name)
design.design.__init__(self, name, cell_name)
debug.info(1, "create a dummy bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
self.num_w_ports,
self.num_r_ports))
self.create_netlist()
self.create_layout()
self.add_boundary()
def create_netlist(self):
self.add_pins()
self.add_modules()
self.create_modules()
def create_layout(self):
self.place_pbitcell()
self.route_rbc_connections()
self.DRC_LVS()
def add_pins(self):
for port in range(self.total_ports):
self.add_pin("bl{}".format(port))
self.add_pin("br{}".format(port))
for port in range(self.total_ports):
self.add_pin("wl{}".format(port))
self.add_pin("vdd")
self.add_pin("gnd")
def add_modules(self):
self.prbc = factory.create(module_type="pbitcell",dummy_bitcell=True)
self.prbc = factory.create(module_type="pbitcell",
dummy_bitcell=True)
self.add_mod(self.prbc)
self.height = self.prbc.height
self.width = self.prbc.width
def create_modules(self):
self.prbc_inst = self.add_inst(name="pbitcell",
mod=self.prbc)
temp = []
for port in range(self.total_ports):
temp.append("bl{}".format(port))
temp.append("br{}".format(port))
for port in range(self.total_ports):
temp.append("wl{}".format(port))
temp.append("wl{}".format(port))
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def place_pbitcell(self):
self.prbc_inst.place(offset=vector(0,0))
def route_rbc_connections(self):
self.prbc_inst.place(offset=vector(0, 0))
def route_rbc_connections(self):
for port in range(self.total_ports):
self.copy_layout_pin(self.prbc_inst, "bl{}".format(port))
self.copy_layout_pin(self.prbc_inst, "br{}".format(port))
for port in range(self.total_ports):
self.copy_layout_pin(self.prbc_inst, "wl{}".format(port))
self.copy_layout_pin(self.prbc_inst, "wl{}".format(port))
self.copy_layout_pin(self.prbc_inst, "vdd")
self.copy_layout_pin(self.prbc_inst, "gnd")
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
#This module is made using a pbitcell. Get the cin from that module

View File

@ -21,16 +21,18 @@ class pbitcell(bitcell_base.bitcell_base):
with a variable number of read/write, write, and read ports
"""
def __init__(self, name, replica_bitcell=False, dummy_bitcell=False):
def __init__(self, name, cell_name=None, replica_bitcell=False, dummy_bitcell=False):
if not cell_name:
cell_name = name
self.num_rw_ports = OPTS.num_rw_ports
self.num_w_ports = OPTS.num_w_ports
self.num_r_ports = OPTS.num_r_ports
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
self.replica_bitcell = replica_bitcell
self.dummy_bitcell = dummy_bitcell
bitcell_base.bitcell_base.__init__(self, name)
bitcell_base.bitcell_base.__init__(self, name, cell_name, hard_cell=False)
fmt_str = "{0} rw ports, {1} w ports and {2} r ports"
info_string = fmt_str.format(self.num_rw_ports,
self.num_w_ports,
@ -295,7 +297,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.width = -2 * self.leftmost_xpos
self.height = self.topmost_ypos - self.botmost_ypos
self.center_ypos = 0.5 * (self.topmost_ypos + self.botmost_ypos)
def create_storage(self):
"""
Creates the crossed coupled inverters that act
@ -322,7 +324,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.connect_inst(["vdd", self.Q, self.Q_bar, "vdd"])
def place_storage(self):
"""
"""
Places the transistors for the crossed
coupled inverters in the bitcell
"""
@ -406,7 +408,7 @@ class pbitcell(bitcell_base.bitcell_base):
contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \
+ 0.5 * contact.poly.height,
self.cross_couple_upper_ypos)
contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \
- 0.5*contact.poly.height,
self.cross_couple_lower_ypos)
@ -421,8 +423,8 @@ class pbitcell(bitcell_base.bitcell_base):
offset=self.gnd_position,
width=self.width)
self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H"))
vdd_ypos = self.inverter_nmos_ypos \
+ self.inverter_nmos.active_height \
+ self.inverter_gap \
@ -433,7 +435,7 @@ class pbitcell(bitcell_base.bitcell_base):
offset=self.vdd_position,
width=self.width)
self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H"))
def create_readwrite_ports(self):
"""
Creates read/write ports to the bit cell. A differential
@ -461,7 +463,7 @@ class pbitcell(bitcell_base.bitcell_base):
if self.dummy_bitcell:
bl_name += "_noconn"
br_name += "_noconn"
# add read/write transistors
self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k),
mod=self.readwrite_nmos)
@ -662,7 +664,7 @@ class pbitcell(bitcell_base.bitcell_base):
if self.dummy_bitcell:
bl_name += "_noconn"
br_name += "_noconn"
# add read-access transistors
self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k),
mod=self.read_nmos)
@ -897,7 +899,7 @@ class pbitcell(bitcell_base.bitcell_base):
[self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right])
def route_readwrite_access(self):
"""
"""
Routes read/write transistors to the storage
component of the bitcell
"""
@ -917,7 +919,7 @@ class pbitcell(bitcell_base.bitcell_base):
[self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
def route_write_access(self):
"""
"""
Routes read/write transistors to the storage
component of the bitcell
"""
@ -937,7 +939,7 @@ class pbitcell(bitcell_base.bitcell_base):
[self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
def route_read_access(self):
"""
"""
Routes read access transistors to the storage
component of the bitcell
"""
@ -1016,7 +1018,7 @@ class pbitcell(bitcell_base.bitcell_base):
offset=offset,
width=well_width,
height=well_height)
# extend nwell to encompass inverter_pmos
# calculate offset of the left pmos well
if "nwell" in layer:
@ -1024,7 +1026,7 @@ class pbitcell(bitcell_base.bitcell_base):
- 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) \
@ -1099,18 +1101,18 @@ class pbitcell(bitcell_base.bitcell_base):
Q_bar_pos = self.inverter_pmos_right.get_pin("S").center()
vdd_pos = self.inverter_pmos_right.get_pin("D").center()
self.add_path("m1", [Q_bar_pos, vdd_pos])
def get_storage_net_names(self):
"""
Returns names of storage nodes in bitcell in
[non-inverting, inverting] format.
"""
return self.storage_nets
def get_bl_name(self, port=0):
"""Get bl name by port"""
return "bl{}".format(port)
def get_br_name(self, port=0):
"""Get bl name by port"""
return "br{}".format(port)
@ -1119,7 +1121,7 @@ class pbitcell(bitcell_base.bitcell_base):
"""Get wl name by port"""
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port)
def get_stage_effort(self, load):
parasitic_delay = 1
# This accounts for bitline being drained thought the access
@ -1128,7 +1130,7 @@ class pbitcell(bitcell_base.bitcell_base):
# Assumes always a minimum sizes inverter. Could be
# specified in the tech.py file.
cin = 3
# Internal loads due to port configs are halved.
# This is to account for the size already being halved
# for stacked TXs, but internal loads do not see this size
@ -1144,10 +1146,10 @@ class pbitcell(bitcell_base.bitcell_base):
load + read_port_load,
parasitic_delay,
False)
def input_load(self):
""" Return the relative capacitance of the access transistor gates """
# FIXME: This applies to bitline capacitances as well.
# pbitcell uses the different sizing for the port access tx's. Not accounted for in this model.
access_tx_cin = self.readwrite_nmos.get_cin()
@ -1155,10 +1157,10 @@ class pbitcell(bitcell_base.bitcell_base):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph for pbitcell. Only readwrite and read ports."""
if self.dummy_bitcell:
return
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
# Edges added wl->bl, wl->br for every port except write ports
rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names)

View File

@ -5,15 +5,15 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,drc,parameter,cell_properties
import bitcell_base
from tech import GDS, layer
from tech import cell_properties as props
from globals import OPTS
class replica_bitcell(design.design):
class replica_bitcell(bitcell_base.bitcell_base):
"""
A single bit cell (6T, 8T, etc.)
This module implements the single memory cell used in the design. It
@ -26,38 +26,28 @@ class replica_bitcell(design.design):
props.bitcell.cell_6t.pin.vdd,
props.bitcell.cell_6t.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"])
else:
(width,height) = (0,0)
pin_map = []
def __init__(self, name=""):
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = OPTS.replica_bitcell_name
# Ignore the name argument
design.design.__init__(self, "replica_cell_6t")
super().__init__(name, cell_name)
debug.info(2, "Create replica bitcell object")
self.width = replica_bitcell.width
self.height = replica_bitcell.height
self.pin_map = replica_bitcell.pin_map
self.add_pin_types(self.type_list)
def get_stage_effort(self, load):
parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
def input_load(self):
"""Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
return 2*access_tx_cin
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
@ -66,6 +56,6 @@ class replica_bitcell(design.design):
total_power = self.return_power(dynamic, leakage)
return total_power
def build_graph(self, graph, inst_name, port_nets):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)

View File

@ -5,13 +5,13 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,drc,parameter
import bitcell_base
from tech import cell_properties as props
from globals import OPTS
class replica_bitcell_1rw_1r(design.design):
class replica_bitcell_1rw_1r(bitcell_base.bitcell_base):
"""
A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It
@ -26,31 +26,24 @@ class replica_bitcell_1rw_1r(design.design):
props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "replica_cell_1rw_1r")
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = OPTS.replica_bitcell_name
super().__init__(name, cell_name)
debug.info(2, "Create replica bitcell 1rw+1r object")
self.width = replica_bitcell_1rw_1r.width
self.height = replica_bitcell_1rw_1r.height
self.pin_map = replica_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
def get_stage_effort(self, load):
parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
def input_load(self):
"""Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]

View File

@ -5,13 +5,15 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,drc,parameter
import bitcell_base
from tech import cell_properties as props
from globals import OPTS
from tech import GDS, layer
import utils
class replica_bitcell_1w_1r(design.design):
class replica_bitcell_1w_1r(bitcell_base.bitcell_base):
"""
A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It
@ -26,20 +28,14 @@ class replica_bitcell_1w_1r(design.design):
props.bitcell.cell_1w1r.pin.wl1,
props.bitcell.cell_1w1r.pin.vdd,
props.bitcell.cell_1w1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "replica_cell_1w_1r")
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = OPTS.replica_bitcell_name
super().__init__(name, cell_name)
debug.info(2, "Create replica bitcell 1w+1r object")
self.width = replica_bitcell_1w_1r.width
self.height = replica_bitcell_1w_1r.height
self.pin_map = replica_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)
def get_stage_effort(self, load):
parasitic_delay = 1
@ -47,10 +43,10 @@ class replica_bitcell_1w_1r(design.design):
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
def input_load(self):
"""Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]

View File

@ -7,82 +7,85 @@
#
import debug
import design
from tech import drc, spice,parameter
from vector import vector
from globals import OPTS
from sram_factory import factory
class replica_pbitcell(design.design):
"""
Creates a replica bitcell using pbitcell
"""
def __init__(self, name):
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = name
self.num_rw_ports = OPTS.num_rw_ports
self.num_w_ports = OPTS.num_w_ports
self.num_r_ports = OPTS.num_r_ports
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
design.design.__init__(self, name)
design.design.__init__(self, name, cell_name)
debug.info(1, "create a replica bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
self.num_w_ports,
self.num_r_ports))
self.create_netlist()
self.create_layout()
self.add_boundary()
def create_netlist(self):
self.add_pins()
self.add_modules()
self.create_modules()
def create_layout(self):
self.place_pbitcell()
self.route_rbc_connections()
self.DRC_LVS()
def add_pins(self):
for port in range(self.total_ports):
self.add_pin("bl{}".format(port))
self.add_pin("br{}".format(port))
for port in range(self.total_ports):
self.add_pin("wl{}".format(port))
self.add_pin("vdd")
self.add_pin("gnd")
def add_modules(self):
self.prbc = factory.create(module_type="pbitcell",replica_bitcell=True)
self.prbc = factory.create(module_type="pbitcell",
replica_bitcell=True)
self.add_mod(self.prbc)
self.height = self.prbc.height
self.width = self.prbc.width
def create_modules(self):
self.prbc_inst = self.add_inst(name="pbitcell",
mod=self.prbc)
temp = []
for port in range(self.total_ports):
temp.append("bl{}".format(port))
temp.append("br{}".format(port))
for port in range(self.total_ports):
temp.append("wl{}".format(port))
temp.append("wl{}".format(port))
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def place_pbitcell(self):
self.prbc_inst.place(offset=vector(0,0))
def route_rbc_connections(self):
self.prbc_inst.place(offset=vector(0, 0))
def route_rbc_connections(self):
for port in range(self.total_ports):
self.copy_layout_pin(self.prbc_inst, "bl{}".format(port))
self.copy_layout_pin(self.prbc_inst, "br{}".format(port))
for port in range(self.total_ports):
self.copy_layout_pin(self.prbc_inst, "wl{}".format(port))
self.copy_layout_pin(self.prbc_inst, "wl{}".format(port))
self.copy_layout_pin(self.prbc_inst, "vdd")
self.copy_layout_pin(self.prbc_inst, "gnd")

View File

@ -6,8 +6,6 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
@ -22,23 +20,12 @@ class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
pin_names = [props.bitcell.cell_1rw1r.pin.wl0,
props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["INPUT", "INPUT", "GROUND"]
(width, height) = utils.get_libcell_size("row_cap_cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"row_cap_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "row_cap_cell_1rw_1r")
def __init__(self, name="row_cap_cell_1rw_1r", cell_name=None):
if not cell_name:
cell_name = name
bitcell_base.bitcell_base.__init__(self, name, cell_name)
debug.info(2, "Create row_cap bitcell 1rw+1r object")
self.width = row_cap_bitcell_1rw_1r.width
self.height = row_cap_bitcell_1rw_1r.height
self.pin_map = row_cap_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.no_instances = True

View File

@ -30,10 +30,10 @@ if not OPTS.analytical_delay:
else:
(OPTS.spice_name,OPTS.spice_exe) = get_tool("spice",["hspice", "ngspice", "ngspice.exe", "xa"])
# set the input dir for spice files if using ngspice
# set the input dir for spice files if using ngspice
if OPTS.spice_name == "ngspice":
os.environ["NGSPICE_INPUT_DIR"] = "{0}".format(OPTS.openram_temp)
if OPTS.spice_exe == "":
debug.error("No recognizable spice version found. Unable to perform characterization.",1)
else:

View File

@ -11,4 +11,4 @@ from enum import Enum
class bit_polarity(Enum):
NONINVERTING = 0
INVERTING = 1

View File

@ -9,7 +9,7 @@ import re
import debug
from globals import OPTS
def relative_compare(value1,value2,error_tolerance=0.001):
""" This is used to compare relative values for convergence. """
return (abs(value1 - value2) / abs(max(value1,value2)) <= error_tolerance)
@ -37,7 +37,7 @@ def parse_spice_list(filename, key):
return convert_to_float(val.group(1))
else:
return "Failed"
def round_time(time,time_precision=3):
# times are in ns, so this is how many digits of precision
# 3 digits = 1ps
@ -58,10 +58,10 @@ def convert_to_float(number):
"""Converts a string into a (float) number; also converts units(m,u,n,p)"""
if number == "Failed":
return False
# start out with a binary value
float_value = False
try:
try:
# checks if string is a float without letter units
float_value = float(number)
except ValueError:
@ -69,7 +69,7 @@ def convert_to_float(number):
unit = re.search(r"(-?\d+\.?\d*)e(\-?\+?\d+)", number)
if unit != None:
float_value=float(unit.group(1)) * (10 ^ float(unit.group(2)))
# see if it is in spice notation
unit = re.search(r"(-?\d+\.?\d*)(m?u?n?p?f?)", number)
if unit != None:

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@ class functional(simulation):
def __init__(self, sram, spfile, corner, cycles=15):
super().__init__(sram, spfile, corner)
# Seed the characterizer with a constant seed for unit tests
if OPTS.is_unit_test:
random.seed(12345)
@ -50,7 +50,7 @@ class functional(simulation):
self.set_internal_spice_names()
self.q_name, self.qbar_name = self.get_bit_name()
debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name))
# Number of checks can be changed
self.num_cycles = cycles
# This is to have ordered keys for random selection
@ -63,16 +63,16 @@ class functional(simulation):
self.period = feasible_period
# Generate a random sequence of reads and writes
self.create_random_memory_sequence()
# Run SPICE simulation
self.write_functional_stimulus()
self.stim.run_sim()
# read dout values from SPICE simulation. If the values do not fall within the noise margins, return the error.
(success, error) = self.read_stim_results()
if not success:
return (0, error)
# Check read values with written values. If the values do not match, return an error.
return self.check_stim_results()
@ -94,7 +94,7 @@ class functional(simulation):
len(val),
port,
name))
def create_random_memory_sequence(self):
if self.write_size:
rw_ops = ["noop", "write", "partial_write", "read"]
@ -123,7 +123,7 @@ class functional(simulation):
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.check_lengths()
# 2. Read at least once. For multiport, it is important that one
# read cycle uses all RW and R port to read from the same
# address simultaniously. This will test the viablilty of the
@ -138,7 +138,7 @@ class functional(simulation):
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.check_lengths()
# 3. Perform a random sequence of writes and reads on random
# ports, using random addresses and random words and random
# write masks (if applicable)
@ -151,7 +151,7 @@ class functional(simulation):
op = random.choice(w_ops)
else:
op = random.choice(r_ops)
if op == "noop":
self.add_noop_one_port(port)
elif op == "write":
@ -191,10 +191,10 @@ class functional(simulation):
comment = self.gen_cycle_comment("read", word, addr, "0" * self.num_wmasks, port, self.t_current)
self.add_read_one_port(comment, addr, port)
self.add_read_check(word, port)
self.cycle_times.append(self.t_current)
self.t_current += self.period
# Last cycle idle needed to correctly measure the value on the second to last clock edge
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current)
self.add_noop_all_ports(comment)
@ -203,7 +203,7 @@ class functional(simulation):
""" Create the masked data word """
# Start with the new word
new_word = word
# When the write mask's bits are 0, the old data values should appear in the new word
# as to not overwrite the old values
for bit in range(len(wmask)):
@ -211,9 +211,9 @@ class functional(simulation):
lower = bit * self.write_size
upper = lower + self.write_size - 1
new_word = new_word[:lower] + old_word[lower:upper + 1] + new_word[upper + 1:]
return new_word
def add_read_check(self, word, port):
""" Add to the check array to ensure a read works. """
try:
@ -222,7 +222,7 @@ class functional(simulation):
self.check = 0
self.read_check.append([word, "{0}{1}".format(self.dout_name, port), self.t_current + self.period, self.check])
self.check += 1
def read_stim_results(self):
# Extract dout values from spice timing.lis
for (word, dout_port, eo_period, check) in self.read_check:
@ -247,12 +247,12 @@ class functional(simulation):
bit,
value,
eo_period)
return (0, error)
self.read_results.append([sp_read_value, dout_port, eo_period, check])
self.read_results.append([sp_read_value, dout_port, eo_period, check])
return (1, "SUCCESS")
def check_stim_results(self):
for i in range(len(self.read_check)):
if self.read_check[i][0] != self.read_results[i][0]:
@ -307,14 +307,14 @@ class functional(simulation):
random_value = random.randint(0, ((2 ** (self.addr_size - 1) - 1)) + (self.num_spare_rows * self.words_per_row))
addr_bits = self.convert_to_bin(random_value, True)
return addr_bits
def get_data(self):
""" Gets an available address and corresponding word. """
# Used for write masks since they should be writing to previously written addresses
addr = random.choice(list(self.stored_words.keys()))
word = self.stored_words[addr]
return (addr, word)
def convert_to_bin(self, value, is_addr):
""" Converts addr & word to usable binary values. """
new_value = str.replace(bin(value), "0b", "")
@ -324,10 +324,10 @@ class functional(simulation):
expected_value = self.word_size + self.num_spare_cols
for i in range(expected_value - len(new_value)):
new_value = "0" + new_value
# print("Binary Conversion: {} to {}".format(value, new_value))
return new_value
def write_functional_stimulus(self):
""" Writes SPICE stimulus. """
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
@ -341,7 +341,7 @@ class functional(simulation):
# Write Vdd/Gnd statements
self.sf.write("\n* Global Power Supplies\n")
self.stim.write_supply()
# Instantiate the SRAM
self.sf.write("\n* Instantiation of the SRAM\n")
self.stim.inst_model(pins=self.pins,
@ -361,19 +361,19 @@ class functional(simulation):
self.sf.write("* s_en: {}\n".format(self.sen_name))
self.sf.write("* q: {}\n".format(self.q_name))
self.sf.write("* qbar: {}\n".format(self.qbar_name))
# Write debug comments to stim file
self.sf.write("\n\n* Sequence of operations\n")
for comment in self.fn_cycle_comments:
self.sf.write("*{}\n".format(comment))
# Generate data input bits
self.sf.write("\n* Generation of data and address signals\n")
for port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols):
sig_name="{0}{1}_{2} ".format(self.din_name, port, bit)
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05)
# Generate address bits
for port in self.all_ports:
for bit in range(self.addr_size):
@ -384,7 +384,7 @@ class functional(simulation):
self.sf.write("\n * Generation of control signals\n")
for port in self.all_ports:
self.stim.gen_pwl("CSB{}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05)
for port in self.readwrite_ports:
self.stim.gen_pwl("WEB{}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05)
@ -417,7 +417,7 @@ class functional(simulation):
period=self.period,
t_rise=self.slew,
t_fall=self.slew)
# Generate dout value measurements
self.sf.write("\n * Generation of dout measurements\n")
for (word, dout_port, eo_period, check) in self.read_check:
@ -428,10 +428,10 @@ class functional(simulation):
dout="{0}_{1}".format(dout_port, bit),
t_intital=t_intital,
t_final=t_final)
self.stim.write_control(self.cycle_times[-1] + self.period)
self.sf.close()
#FIXME: Similar function to delay.py, refactor this
def get_bit_name(self):
""" Get a bit cell name """
@ -444,4 +444,4 @@ class functional(simulation):
return (q_name, qbar_name)

View File

@ -18,21 +18,21 @@ from globals import OPTS
class lib:
""" lib file generation."""
def __init__(self, out_dir, sram, sp_file, use_model=OPTS.analytical_delay):
self.out_dir = out_dir
self.sram = sram
self.sp_file = sp_file
self.sp_file = sp_file
self.use_model = use_model
self.set_port_indices()
self.prepare_tables()
self.create_corners()
self.characterize_corners()
def set_port_indices(self):
"""Copies port information set in the SRAM instance"""
self.total_port_num = len(self.sram.all_ports)
@ -40,7 +40,7 @@ class lib:
self.readwrite_ports = self.sram.readwrite_ports
self.read_ports = self.sram.read_ports
self.write_ports = self.sram.write_ports
def prepare_tables(self):
""" Determine the load/slews if they aren't specified in the config file. """
# These are the parameters to determine the table sizes
@ -48,12 +48,12 @@ class lib:
self.load = tech.spice["dff_in_cap"]
self.loads = self.load_scales * self.load
debug.info(1, "Loads: {0}".format(self.loads))
self.slew_scales = np.array(OPTS.slew_scales)
self.slew = tech.spice["rise_time"]
self.slews = self.slew_scales * self.slew
debug.info(1, "Slews: {0}".format(self.slews))
def create_corners(self):
""" Create corners for characterization. """
# Get the corners from the options file
@ -71,7 +71,7 @@ class lib:
min_process = "FF"
nom_process = "TT"
max_process = "SS"
self.corners = []
self.lib_files = []
@ -103,15 +103,15 @@ class lib:
temp)
self.corner_name = self.corner_name.replace(".","p") # Remove decimals
lib_name = self.out_dir+"{}.lib".format(self.corner_name)
# A corner is a tuple of PVT
self.corners.append((proc, volt, temp))
self.lib_files.append(lib_name)
def characterize_corners(self):
""" Characterize the list of corners. """
debug.info(1,"Characterizing corners: " + str(self.corners))
debug.info(1,"Characterizing corners: " + str(self.corners))
for (self.corner,lib_name) in zip(self.corners,self.lib_files):
debug.info(1,"Corner: " + str(self.corner))
(self.process, self.voltage, self.temperature) = self.corner
@ -121,7 +121,7 @@ class lib:
self.characterize()
self.lib.close()
self.parse_info(self.corner,lib_name)
def characterize(self):
""" Characterize the current corner. """
@ -130,8 +130,8 @@ class lib:
self.compute_setup_hold()
self.write_header()
# Loop over all ports.
# Loop over all ports.
for port in self.all_ports:
# set the read and write port as inputs.
self.write_data_bus(port)
@ -143,7 +143,7 @@ class lib:
self.write_clk_timing_power(port)
self.write_footer()
def write_footer(self):
""" Write the footer """
self.lib.write(" }\n") #Closing brace for the cell
@ -154,13 +154,13 @@ class lib:
self.lib.write("library ({0}_lib)".format(self.corner_name))
self.lib.write("{\n")
self.lib.write(" delay_model : \"table_lookup\";\n")
self.write_units()
self.write_defaults()
self.write_LUT_templates()
self.lib.write(" default_operating_conditions : OC; \n")
self.write_bus()
self.lib.write("cell ({0})".format(self.sram.name))
@ -182,7 +182,7 @@ class lib:
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")
# 'when' condition unnecessary when cs pin does not turn power to devices
@ -190,15 +190,15 @@ class lib:
self.lib.write(" value : {};\n".format(self.char_sram_results["leakage_power"]))
self.lib.write(" }\n")
self.lib.write(" cell_leakage_power : {};\n".format(self.char_sram_results["leakage_power"]))
def write_units(self):
""" Adds default units for time, voltage, current,...
Valid values are 1mV, 10mV, 100mV, and 1V.
Valid values are 1mV, 10mV, 100mV, and 1V.
For time: Valid values are 1ps, 10ps, 100ps, and 1ns.
For power: Valid values are 1mW, 100uW (for 100mW), 10uW (for 10mW),
For power: Valid values are 1mW, 100uW (for 100mW), 10uW (for 10mW),
1uW (for 1mW), 100nW, 10nW, 1nW, 100pW, 10pW, and 1pW.
"""
"""
self.lib.write(" time_unit : \"1ns\" ;\n")
self.lib.write(" voltage_unit : \"1V\" ;\n")
self.lib.write(" current_unit : \"1mA\" ;\n")
@ -214,7 +214,7 @@ class lib:
def write_defaults(self):
""" Adds default values for slew and capacitance."""
self.lib.write(" input_threshold_pct_fall : 50.0 ;\n")
self.lib.write(" output_threshold_pct_fall : 50.0 ;\n")
self.lib.write(" input_threshold_pct_rise : 50.0 ;\n")
@ -255,7 +255,7 @@ class lib:
formatted_rows = list(map(self.create_list,split_values))
formatted_array = ",\\\n".join(formatted_rows)
return formatted_array
def write_index(self, number, values):
""" Write the index """
quoted_string = self.create_list(values)
@ -267,10 +267,10 @@ class lib:
# indent each newline plus extra spaces for word values
indented_string = quoted_string.replace('\n', '\n' + indent +" ")
self.lib.write("{0}values({1});\n".format(indent,indented_string))
def write_LUT_templates(self):
""" Adds lookup_table format (A 1x1 lookup_table)."""
Tran = ["CELL_TABLE"]
for i in Tran:
self.lib.write(" lu_table_template({0})".format(i))
@ -278,8 +278,8 @@ class lib:
self.lib.write(" variable_1 : input_net_transition;\n")
self.lib.write(" variable_2 : total_output_net_capacitance;\n")
self.write_index(1,self.slews)
# Dividing by 1000 to all cap values since output of .sp is in fF,
# and it needs to be in pF for Innovus.
# Dividing by 1000 to all cap values since output of .sp is in fF,
# and it needs to be in pF for Innovus.
self.write_index(2,self.loads/1000)
self.lib.write(" }\n\n")
@ -292,12 +292,12 @@ class lib:
self.write_index(1,self.slews)
self.write_index(2,self.slews)
self.lib.write(" }\n\n")
# self.lib.write(" lu_table_template(CLK_TRAN) {\n")
# self.lib.write(" variable_1 : constrained_pin_transition;\n")
# self.write_index(1,self.slews)
# self.lib.write(" }\n\n")
# self.lib.write(" lu_table_template(TRAN) {\n")
# self.lib.write(" variable_1 : total_output_net_capacitance;\n")
# self.write_index(1,self.slews)
@ -311,10 +311,10 @@ class lib:
# #self.write_index(1,self.slews)
# self.write_index(1,[self.slews[0]])
# self.lib.write(" }\n\n")
def write_bus(self):
""" Adds format of data and addr bus."""
self.lib.write("\n\n")
self.lib.write(" type (data){\n")
self.lib.write(" base_type : array;\n")
@ -378,11 +378,11 @@ class lib:
self.lib.write(" direction : output; \n")
# This is conservative, but limit to range that we characterized.
self.lib.write(" max_capacitance : {0}; \n".format(max(self.loads)/1000))
self.lib.write(" min_capacitance : {0}; \n".format(min(self.loads)/1000))
self.lib.write(" min_capacitance : {0}; \n".format(min(self.loads)/1000))
self.lib.write(" memory_read(){ \n")
self.lib.write(" address : addr{0}; \n".format(read_port))
self.lib.write(" }\n")
self.lib.write(" pin(dout{0}[{1}:0]){{\n".format(read_port,self.sram.word_size-1))
self.lib.write(" timing(){ \n")
@ -402,7 +402,7 @@ class lib:
self.write_values(self.char_port_results[read_port]["slew_hl"],len(self.loads)," ")
self.lib.write(" }\n") # fall trans
self.lib.write(" }\n") # timing
self.lib.write(" }\n") # pin
self.lib.write(" }\n") # pin
self.lib.write(" }\n\n") # bus
def write_data_bus_input(self, write_port):
@ -416,10 +416,10 @@ class lib:
self.lib.write(" memory_write(){ \n")
self.lib.write(" address : addr{0}; \n".format(write_port))
self.lib.write(" clocked_on : clk{0}; \n".format(write_port))
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" pin(din{0}[{1}:0]){{\n".format(write_port,self.sram.word_size-1))
self.write_FF_setuphold(write_port)
self.lib.write(" }\n") # pin
self.lib.write(" }\n") # pin
self.lib.write(" }\n") #bus
def write_data_bus(self, port):
@ -431,7 +431,7 @@ class lib:
def write_addr_bus(self, port):
""" Adds addr bus timing results."""
self.lib.write(" bus(addr{0}){{\n".format(port))
self.lib.write(" bus_type : addr; \n")
self.lib.write(" direction : input; \n")
@ -439,9 +439,9 @@ class lib:
self.lib.write(" max_transition : {0};\n".format(self.slews[-1]))
self.lib.write(" pin(addr{0}[{1}:0])".format(port,self.sram.addr_size-1))
self.lib.write("{\n")
self.write_FF_setuphold(port)
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" }\n\n")
def write_wmask_bus(self, port):
@ -465,7 +465,7 @@ class lib:
ctrl_pin_names = ["csb{0}".format(port)]
if port in self.readwrite_ports:
ctrl_pin_names.append("web{0}".format(port))
for i in ctrl_pin_names:
self.lib.write(" pin({0})".format(i))
self.lib.write("{\n")
@ -508,12 +508,12 @@ class lib:
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" }\n\n")
def add_clk_control_power(self, port):
"""Writes powers under the clock pin group for a specified port"""
#Web added to read/write ports. Likely to change when control logic finished.
web_name = ""
if port in self.write_ports:
if port in self.read_ports:
web_name = " & !web{0}".format(port)
@ -556,7 +556,7 @@ class lib:
self.lib.write(" values(\"{0:.6e}\");\n".format(read0_power))
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"])
@ -585,7 +585,7 @@ class lib:
self.d = delay(self.sram, self.sp_file, self.corner)
if self.use_model:
char_results = self.d.analytical_delay(self.slews,self.loads)
self.char_sram_results, self.char_port_results = char_results
self.char_sram_results, self.char_port_results = char_results
else:
if (self.sram.num_spare_rows == 0):
probe_address = "1" * self.sram.addr_size
@ -593,8 +593,8 @@ class lib:
probe_address = "0" + "1" * (self.sram.addr_size - 1)
probe_data = self.sram.word_size - 1
char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads)
self.char_sram_results, self.char_port_results = char_results
self.char_sram_results, self.char_port_results = char_results
def compute_setup_hold(self):
""" Do the analysis if we haven't characterized a FF yet """
# Do the analysis if we haven't characterized a FF yet
@ -604,8 +604,8 @@ class lib:
self.times = self.sh.analytical_setuphold(self.slews,self.loads)
else:
self.times = self.sh.analyze(self.slews,self.slews)
def parse_info(self,corner,lib_name):
""" Copies important characterization data to datasheet.info to be added to datasheet """
if OPTS.is_unit_test:
@ -617,9 +617,9 @@ class lib:
proc = subprocess.Popen(['git','rev-parse','HEAD'], cwd=os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/', stdout=subprocess.PIPE)
git_id = str(proc.stdout.read())
try:
git_id = git_id[2:-3]
git_id = git_id[2:-3]
except:
pass
# check if git id is valid
@ -628,7 +628,7 @@ class lib:
git_id = 'Failed to retruieve'
datasheet = open(OPTS.openram_temp +'/datasheet.info', 'a+')
current_time = datetime.date.today()
# write static information to be parser later
datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},".format(
@ -654,10 +654,10 @@ class lib:
# information of checks
# run it only the first time
datasheet.write("{0},{1},".format(self.sram.drc_errors, self.sram.lvs_errors))
# write area
datasheet.write(str(self.sram.width * self.sram.height) + ',')
# write timing information for all ports
for port in self.all_ports:
#din timings
@ -675,7 +675,7 @@ class lib:
min(list(map(round_time,self.times["hold_times_HL"]))),
max(list(map(round_time,self.times["hold_times_HL"])))
))
for port in self.all_ports:
@ -695,7 +695,7 @@ class lib:
min(list(map(round_time,self.char_port_results[port]["slew_hl"]))),
max(list(map(round_time,self.char_port_results[port]["slew_hl"])))
))
for port in self.all_ports:
@ -791,9 +791,9 @@ class lib:
control_str = 'csb0'
for i in range(1, self.total_port_num):
control_str += ' & csb{0}'.format(i)
datasheet.write("{0},{1},{2},".format('leak', control_str, self.char_sram_results["leakage_power"]))
datasheet.write("END\n")
datasheet.close()

View File

@ -17,7 +17,7 @@ class logical_effort():
min_inv_cin = 1+beta
pinv=parameter["min_inv_para_delay"]
tau = parameter['le_tau']
def __init__(self, name, size, cin, cout, parasitic, out_is_rise=True):
self.name = name
self.cin = cin
@ -26,31 +26,31 @@ class logical_effort():
self.electrical_effort = self.cout/self.cin
self.parasitic_scale = parasitic
self.is_rise = out_is_rise
def __str__(self):
return "Name={}, g={}, h={}, p={}*pinv, rise_delay={}".format(self.name,
self.logical_effort,
self.electrical_effort,
self.parasitic_scale,
self.is_rise
)
)
def get_stage_effort(self):
return self.logical_effort*self.electrical_effort
def get_parasitic_delay(self):
return logical_effort.pinv*self.parasitic_scale
def get_stage_delay(self):
return self.get_stage_effort()+self.get_parasitic_delay()
def get_absolute_delay(self):
return logical_effort.tau*self.get_stage_delay()
def calculate_delays(stage_effort_list):
"""Convert stage effort objects to list of delay values"""
return [stage.get_stage_delay() for stage in stage_effort_list]
def calculate_relative_delay(stage_effort_list):
"""Calculates the total delay of a given delay path made of a list of logical effort objects."""
total_rise_delay, total_fall_delay = calculate_relative_rise_fall_delays(stage_effort_list)
@ -62,7 +62,7 @@ def calculate_absolute_delay(stage_effort_list):
for stage in stage_effort_list:
total_delay+=stage.get_absolute_delay()
return total_delay
def calculate_relative_rise_fall_delays(stage_effort_list):
"""Calculates the rise/fall delays of a given delay path made of a list of logical effort objects."""
debug.info(2, "Calculating rise/fall relative delays")
@ -74,11 +74,11 @@ def calculate_relative_rise_fall_delays(stage_effort_list):
else:
total_fall_delay += stage.get_stage_delay()
return total_rise_delay, total_fall_delay
def convert_farad_to_relative_c(c_farad):
"""Converts capacitance in Femto-Farads to relative capacitance."""
return c_farad*parameter['cap_relative_per_ff']
def convert_relative_c_to_farad(c_relative):
"""Converts capacitance in logical effort relative units to Femto-Farads."""
return c_relative/parameter['cap_relative_per_ff']
return c_relative/parameter['cap_relative_per_ff']

View File

@ -19,63 +19,63 @@ class spice_measurement(ABC):
self.measure_scale = measure_scale
self.has_port = has_port #Needed for error checking
#Some meta values used externally. variables are added here for consistency accross the objects
self.meta_str = None
self.meta_str = None
self.meta_add_delay = False
@abstractmethod
def get_measure_function(self):
return None
return None
@abstractmethod
def get_measure_values(self):
return None
def write_measure(self, stim_obj, input_tuple):
measure_func = self.get_measure_function()
if measure_func == None:
debug.error("Did not set measure function",1)
measure_vals = self.get_measure_values(*input_tuple)
measure_func(stim_obj, *measure_vals)
def retrieve_measure(self, port=None):
self.port_error_check(port)
if port != None:
value = parse_spice_list("timing", "{0}{1}".format(self.name.lower(), port))
value = parse_spice_list("timing", "{0}{1}".format(self.name.lower(), port))
else:
value = parse_spice_list("timing", "{0}".format(self.name.lower()))
if type(value)!=float or self.measure_scale == None:
value = parse_spice_list("timing", "{0}".format(self.name.lower()))
if type(value)!=float or self.measure_scale == None:
return value
else:
return value*self.measure_scale
def port_error_check(self, port):
if self.has_port and port == None:
debug.error("Cannot retrieve measurement, port input was expected.",1)
elif not self.has_port and port != None:
debug.error("Unexpected port input received during measure retrieval.",1)
class delay_measure(spice_measurement):
"""Generates a spice measurement for the delay of 50%-to-50% points of two signals."""
def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str,\
trig_vdd=0.5, targ_vdd=0.5, measure_scale=None, has_port=True):
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd)
def get_measure_function(self):
return stimuli.gen_meas_delay
def set_meas_constants(self, trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd):
"""Set the constants for this measurement: signal names, directions, and trigger scales"""
self.trig_dir_str = trig_dir_str
self.targ_dir_str = targ_dir_str
self.trig_val_of_vdd = trig_vdd
self.trig_val_of_vdd = trig_vdd
self.targ_val_of_vdd = targ_vdd
self.trig_name_no_port = trig_name
self.targ_name_no_port = targ_name
#Time delays and ports are variant and needed as inputs when writing the measurement
def get_measure_values(self, trig_td, targ_td, vdd_voltage, port=None):
def get_measure_values(self, trig_td, targ_td, vdd_voltage, port=None):
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
self.port_error_check(port)
trig_val = self.trig_val_of_vdd * vdd_voltage
@ -90,74 +90,74 @@ class delay_measure(spice_measurement):
meas_name = self.name
trig_name = self.trig_name_no_port
targ_name = self.targ_name_no_port
return (meas_name,trig_name,targ_name,trig_val,targ_val,self.trig_dir_str,self.targ_dir_str,trig_td,targ_td)
return (meas_name,trig_name,targ_name,trig_val,targ_val,self.trig_dir_str,self.targ_dir_str,trig_td,targ_td)
class slew_measure(delay_measure):
class slew_measure(delay_measure):
def __init__(self, measure_name, signal_name, slew_dir_str, measure_scale=None, has_port=True):
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
self.set_meas_constants(signal_name, slew_dir_str)
def set_meas_constants(self, signal_name, slew_dir_str):
"""Set the values needed to generate a Spice measurement statement based on the name of the measurement."""
self.trig_dir_str = slew_dir_str
self.targ_dir_str = slew_dir_str
if slew_dir_str == "RISE":
self.trig_val_of_vdd = 0.1
self.trig_val_of_vdd = 0.1
self.targ_val_of_vdd = 0.9
elif slew_dir_str == "FALL":
self.trig_val_of_vdd = 0.9
self.trig_val_of_vdd = 0.9
self.targ_val_of_vdd = 0.1
else:
debug.error("Unrecognised slew measurement direction={}".format(slew_dir_str),1)
self.trig_name_no_port = signal_name
self.targ_name_no_port = signal_name
#Time delays and ports are variant and needed as inputs when writing the measurement
#Time delays and ports are variant and needed as inputs when writing the measurement
class power_measure(spice_measurement):
"""Generates a spice measurement for the average power between two time points."""
def __init__(self, measure_name, power_type="", measure_scale=None, has_port=True):
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
self.set_meas_constants(power_type)
def get_measure_function(self):
return stimuli.gen_meas_power
def set_meas_constants(self, power_type):
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
#Not needed for power simulation
self.power_type = power_type #Expected to be "RISE"/"FALL"
def get_measure_values(self, t_initial, t_final, port=None):
def get_measure_values(self, t_initial, t_final, port=None):
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
self.port_error_check(port)
if port != None:
meas_name = "{}{}".format(self.name, port)
else:
meas_name = self.name
return (meas_name,t_initial,t_final)
class voltage_when_measure(spice_measurement):
return (meas_name,t_initial,t_final)
class voltage_when_measure(spice_measurement):
"""Generates a spice measurement to measure the voltage of a signal based on the voltage of another."""
def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, trig_vdd, measure_scale=None, has_port=True):
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
self.set_meas_constants(trig_name, targ_name, trig_dir_str, trig_vdd)
def get_measure_function(self):
return stimuli.gen_meas_find_voltage
def set_meas_constants(self, trig_name, targ_name, trig_dir_str, trig_vdd):
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
self.trig_dir_str = trig_dir_str
self.trig_val_of_vdd = trig_vdd
self.trig_val_of_vdd = trig_vdd
self.trig_name_no_port = trig_name
self.targ_name_no_port = targ_name
def get_measure_values(self, trig_td, vdd_voltage, port=None):
def get_measure_values(self, trig_td, vdd_voltage, port=None):
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
self.port_error_check(port)
if port != None:
@ -169,25 +169,25 @@ class voltage_when_measure(spice_measurement):
meas_name = self.name
trig_name = self.trig_name_no_port
targ_name = self.targ_name_no_port
trig_voltage = self.trig_val_of_vdd*vdd_voltage
trig_voltage = self.trig_val_of_vdd*vdd_voltage
return (meas_name,trig_name,targ_name,trig_voltage,self.trig_dir_str,trig_td)
class voltage_at_measure(spice_measurement):
class voltage_at_measure(spice_measurement):
"""Generates a spice measurement to measure the voltage at a specific time.
The time is considered variant with different periods."""
def __init__(self, measure_name, targ_name, measure_scale=None, has_port=True):
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
self.set_meas_constants(targ_name)
def get_measure_function(self):
return stimuli.gen_meas_find_voltage_at_time
def set_meas_constants(self, targ_name):
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
self.targ_name_no_port = targ_name
def get_measure_values(self, time_at, port=None):
def get_measure_values(self, time_at, port=None):
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
self.port_error_check(port)
if port != None:
@ -196,6 +196,6 @@ class voltage_at_measure(spice_measurement):
targ_name = self.targ_name_no_port.format(port)
else:
meas_name = self.name
targ_name = self.targ_name_no_port
return (meas_name,targ_name,time_at)
targ_name = self.targ_name_no_port
return (meas_name,targ_name,time_at)

View File

@ -30,14 +30,14 @@ class model_check(delay):
self.period = tech.spice["feasible_period"]
self.create_data_names()
self.custom_delaychain=custom_delaychain
def create_data_names(self):
self.wl_meas_name, self.wl_model_name = "wl_measures", "wl_model"
self.sae_meas_name, self.sae_model_name = "sae_measures", "sae_model"
self.wl_slew_name, self.sae_slew_name = "wl_slews", "sae_slews"
self.bl_meas_name, self.bl_slew_name = "bl_measures", "bl_slews"
self.power_name = "total_power"
def create_measurement_names(self, port):
"""Create measurement names. The names themselves currently define the type of measurement"""
#Create delay measurement names
@ -73,10 +73,10 @@ class model_check(delay):
else:
self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar"]+dc_slew_names
self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen"]+sen_driver_slew_names+["slew_sen"]
self.bitline_meas_names = ["delay_wl_to_bl", "delay_bl_to_dout"]
self.power_meas_names = ['read0_power']
def create_signal_names(self, port):
"""Creates list of the signal names used in the spice file along the wl and sen paths.
Names are re-harded coded here; i.e. the names are hardcoded in most of OpenRAM and are
@ -90,7 +90,7 @@ class model_check(delay):
if self.custom_delaychain:
delay_chain_signal_names = []
else:
delay_chain_signal_names = ["Xsram.Xcontrol{}.Xreplica_bitline.Xdelay_chain.dout_{}".format('{}', stage) for stage in range(1,self.get_num_delay_stages())]
delay_chain_signal_names = ["Xsram.Xcontrol{}.Xreplica_bitline.Xdelay_chain.dout_{}".format('{}', stage) for stage in range(1,self.get_num_delay_stages())]
if len(self.sram.all_ports) > 1:
port_format = '{}'
else:
@ -103,21 +103,21 @@ class model_check(delay):
pre_delay_chain_names = ["Xsram.Xcontrol{}.gated_clk_bar".format('{}')]
if port not in self.sram.readonly_ports:
pre_delay_chain_names+= ["Xsram.Xcontrol{}.Xand2_rbl_in.zb_int".format('{}'), "Xsram.Xcontrol{}.rbl_in".format('{}')]
self.rbl_en_signal_names = pre_delay_chain_names+\
delay_chain_signal_names+\
["Xsram.Xcontrol{}.Xreplica_bitline.delayed_en".format('{}')]
self.sae_signal_names = ["Xsram.Xcontrol{}.Xreplica_bitline.bl0_0".format('{}'), "Xsram.Xcontrol{}.pre_s_en".format('{}')]+\
sen_driver_signals+\
["Xsram.s_en{}".format('{}')]
dout_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit
self.bl_signal_names = ["Xsram.Xbank0.wl{}_{}".format(port_format, self.wordline_row),\
"Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column),\
dout_name]
def create_measurement_objects(self):
"""Create the measurements used for read and write ports"""
self.create_wordline_meas_objs()
@ -125,101 +125,101 @@ class model_check(delay):
self.create_bl_meas_objs()
self.create_power_meas_objs()
self.all_measures = self.wl_meas_objs+self.sae_meas_objs+self.bl_meas_objs+self.power_meas_objs
def create_power_meas_objs(self):
"""Create power measurement object. Only one."""
self.power_meas_objs = []
self.power_meas_objs.append(power_measure(self.power_meas_names[0], "FALL", measure_scale=1e3))
def create_wordline_meas_objs(self):
"""Create the measurements to measure the wordline path from the gated_clk_bar signal"""
self.wl_meas_objs = []
trig_dir = "RISE"
targ_dir = "FALL"
for i in range(1, len(self.wl_signal_names)):
self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1],
self.wl_signal_names[i-1],
self.wl_signal_names[i],
trig_dir,
targ_dir,
self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1],
self.wl_signal_names[i-1],
self.wl_signal_names[i],
trig_dir,
targ_dir,
measure_scale=1e9))
self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i-1],
self.wl_signal_names[i-1],
trig_dir,
self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i-1],
self.wl_signal_names[i-1],
trig_dir,
measure_scale=1e9))
temp_dir = trig_dir
trig_dir = targ_dir
targ_dir = temp_dir
self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[-1], self.wl_signal_names[-1], trig_dir, measure_scale=1e9))
def create_bl_meas_objs(self):
"""Create the measurements to measure the bitline to dout, static stages"""
#Bitline has slightly different measurements, objects appends hardcoded.
self.bl_meas_objs = []
trig_dir, targ_dir = "RISE", "FALL" #Only check read 0
self.bl_meas_objs.append(delay_measure(self.bitline_meas_names[0],
self.bl_signal_names[0],
self.bl_signal_names[-1],
trig_dir,
targ_dir,
self.bl_meas_objs.append(delay_measure(self.bitline_meas_names[0],
self.bl_signal_names[0],
self.bl_signal_names[-1],
trig_dir,
targ_dir,
measure_scale=1e9))
def create_sae_meas_objs(self):
"""Create the measurements to measure the sense amp enable path from the gated_clk_bar signal. The RBL splits this path into two."""
self.sae_meas_objs = []
trig_dir = "RISE"
targ_dir = "FALL"
#Add measurements from gated_clk_bar to RBL
for i in range(1, len(self.rbl_en_signal_names)):
self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1],
self.rbl_en_signal_names[i-1],
self.rbl_en_signal_names[i],
trig_dir,
targ_dir,
self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1],
self.rbl_en_signal_names[i-1],
self.rbl_en_signal_names[i],
trig_dir,
targ_dir,
measure_scale=1e9))
self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i-1],
self.rbl_en_signal_names[i-1],
trig_dir,
self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i-1],
self.rbl_en_signal_names[i-1],
trig_dir,
measure_scale=1e9))
temp_dir = trig_dir
trig_dir = targ_dir
targ_dir = temp_dir
if self.custom_delaychain: #Hack for custom delay chains
self.sae_meas_objs[-2] = delay_measure(self.rbl_delay_meas_names[-1],
self.rbl_en_signal_names[-2],
self.rbl_en_signal_names[-1],
"RISE",
"RISE",
self.sae_meas_objs[-2] = delay_measure(self.rbl_delay_meas_names[-1],
self.rbl_en_signal_names[-2],
self.rbl_en_signal_names[-1],
"RISE",
"RISE",
measure_scale=1e9)
self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[-1],
self.rbl_en_signal_names[-1],
trig_dir,
self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[-1],
self.rbl_en_signal_names[-1],
trig_dir,
measure_scale=1e9))
#Add measurements from rbl_out to sae. Trigger directions do not invert from previous stage due to RBL.
trig_dir = "FALL"
targ_dir = "RISE"
for i in range(1, len(self.sae_signal_names)):
self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1],
self.sae_signal_names[i-1],
self.sae_signal_names[i],
trig_dir,
targ_dir,
self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1],
self.sae_signal_names[i-1],
self.sae_signal_names[i],
trig_dir,
targ_dir,
measure_scale=1e9))
self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i-1],
self.sae_signal_names[i-1],
trig_dir,
self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i-1],
self.sae_signal_names[i-1],
trig_dir,
measure_scale=1e9))
temp_dir = trig_dir
trig_dir = targ_dir
targ_dir = temp_dir
self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[-1],
self.sae_signal_names[-1],
trig_dir,
self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[-1],
self.sae_signal_names[-1],
trig_dir,
measure_scale=1e9))
def write_delay_measures(self):
"""
Write the measure statements to quantify the delay and power results for all targeted ports.
@ -229,12 +229,12 @@ class model_check(delay):
# Output some comments to aid where cycles start and what is happening
for comment in self.cycle_comments:
self.sf.write("* {}\n".format(comment))
for read_port in self.targ_read_ports:
self.write_measures_read_port(read_port)
def get_delay_measure_variants(self, port, measure_obj):
"""Get the measurement values that can either vary from simulation to simulation (vdd, address)
"""Get the measurement values that can either vary from simulation to simulation (vdd, address)
or port to port (time delays)"""
#Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port
#Assuming only read 0 for now
@ -246,15 +246,15 @@ class model_check(delay):
return self.get_power_measure_variants(port, measure_obj, "read")
else:
debug.error("Measurement not recognized by the model checker.",1)
def get_power_measure_variants(self, port, power_obj, operation):
"""Get the measurement values that can either vary port to port (time delays)"""
#Return value is intended to match the power measure format: t_initial, t_final, port
t_initial = self.cycle_times[self.measure_cycles[port]["read0"]]
t_final = self.cycle_times[self.measure_cycles[port]["read0"]+1]
return (t_initial, t_final, port)
def write_measures_read_port(self, port):
"""
Write the measure statements for all nodes along the wordline path.
@ -263,16 +263,16 @@ class model_check(delay):
for measure in self.all_measures:
measure_variant_inp_tuple = self.get_delay_measure_variants(port, measure)
measure.write_measure(self.stim, measure_variant_inp_tuple)
def get_measurement_values(self, meas_objs, port):
"""Gets the delays and slews from a specified port from the spice output file and returns them as lists."""
delay_meas_list = []
delay_meas_list = []
slew_meas_list = []
power_meas_list=[]
for measure in meas_objs:
measure_value = measure.retrieve_measure(port=port)
if type(measure_value) != float:
debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, measure_value),1)
debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, measure_value),1)
if type(measure) is delay_measure:
delay_meas_list.append(measure_value)
elif type(measure)is slew_measure:
@ -282,7 +282,7 @@ class model_check(delay):
else:
debug.error("Measurement object not recognized.",1)
return delay_meas_list, slew_meas_list,power_meas_list
def run_delay_simulation(self):
"""
This tries to simulate a period and checks if the result works. If
@ -291,8 +291,8 @@ class model_check(delay):
include leakage of all cells.
"""
#Sanity Check
debug.check(self.period > 0, "Target simulation period non-positive")
debug.check(self.period > 0, "Target simulation period non-positive")
wl_delay_result = [[] for i in self.all_ports]
wl_slew_result = [[] for i in self.all_ports]
sae_delay_result = [[] for i in self.all_ports]
@ -304,44 +304,44 @@ class model_check(delay):
self.write_delay_stimulus()
self.stim.run_sim() #running sim prodoces spice output file.
#Retrieve the results from the output file
for port in self.targ_read_ports:
for port in self.targ_read_ports:
#Parse and check the voltage measurements
wl_delay_result[port], wl_slew_result[port],_ = self.get_measurement_values(self.wl_meas_objs, port)
sae_delay_result[port], sae_slew_result[port],_ = self.get_measurement_values(self.sae_meas_objs, port)
bl_delay_result[port], bl_slew_result[port],_ = self.get_measurement_values(self.bl_meas_objs, port)
_,__,power_result[port] = self.get_measurement_values(self.power_meas_objs, port)
return (True,wl_delay_result, sae_delay_result, wl_slew_result, sae_slew_result, bl_delay_result, bl_slew_result, power_result)
def get_model_delays(self, port):
"""Get model delays based on port. Currently assumes single RW port."""
return self.sram.control_logic_rw.get_wl_sen_delays()
def get_num_delay_stages(self):
"""Gets the number of stages in the delay chain from the control logic"""
return len(self.sram.control_logic_rw.replica_bitline.delay_fanout_list)
def get_num_delay_fanout_list(self):
"""Gets the number of stages in the delay chain from the control logic"""
return self.sram.control_logic_rw.replica_bitline.delay_fanout_list
def get_num_delay_stage_fanout(self):
"""Gets fanout in each stage in the delay chain. Assumes each stage is the same"""
return self.sram.control_logic_rw.replica_bitline.delay_fanout_list[0]
def get_num_wl_en_driver_stages(self):
"""Gets the number of stages in the wl_en driver from the control logic"""
return self.sram.control_logic_rw.wl_en_driver.num_stages
def get_num_sen_driver_stages(self):
"""Gets the number of stages in the sen driver from the control logic"""
return self.sram.control_logic_rw.s_en_driver.num_stages
def get_num_wl_driver_stages(self):
"""Gets the number of stages in the wordline driver from the control logic"""
return self.sram.bank.wordline_driver.inv.num_stages
return self.sram.bank.wordline_driver.inv.num_stages
def scale_delays(self, delay_list):
"""Takes in a list of measured delays and convert it to simple units to easily compare to model values."""
converted_values = []
@ -350,12 +350,12 @@ class model_check(delay):
for meas_value in delay_list:
total+=meas_value
average = total/len(delay_list)
#Convert values
for meas_value in delay_list:
converted_values.append(meas_value/average)
return converted_values
def min_max_normalization(self, value_list):
"""Re-scales input values on a range from 0-1 where min(list)=0, max(list)=1"""
scaled_values = []
@ -364,14 +364,14 @@ class model_check(delay):
for value in value_list:
scaled_values.append((value-average)/(min_max_diff))
return scaled_values
def calculate_error_l2_norm(self, list_a, list_b):
def calculate_error_l2_norm(self, list_a, list_b):
"""Calculates error between two lists using the l2 norm"""
error_list = []
for val_a, val_b in zip(list_a, list_b):
error_list.append((val_a-val_b)**2)
return error_list
def compare_measured_and_model(self, measured_vals, model_vals):
"""First scales both inputs into similar ranges and then compares the error between both."""
scaled_meas = self.min_max_normalization(measured_vals)
@ -380,7 +380,7 @@ class model_check(delay):
debug.info(1, "Scaled model:\n{}".format(scaled_model))
errors = self.calculate_error_l2_norm(scaled_meas, scaled_model)
debug.info(1, "Errors:\n{}\n".format(errors))
def analyze(self, probe_address, probe_data, slews, loads, port):
"""Measures entire delay path along the wordline and sense amp enable and compare it to the model delays."""
self.load=max(loads)
@ -390,7 +390,7 @@ class model_check(delay):
self.create_measurement_names(port)
self.create_measurement_objects()
data_dict = {}
read_port = self.read_ports[0] #only test the first read port
read_port = port
self.targ_read_ports = [read_port]
@ -398,33 +398,33 @@ class model_check(delay):
debug.info(1,"Model test: corner {}".format(self.corner))
(success, wl_delays, sae_delays, wl_slews, sae_slews, bl_delays, bl_slews, powers)=self.run_delay_simulation()
debug.check(success, "Model measurements Failed: period={}".format(self.period))
debug.info(1,"Measured Wordline delays (ns):\n\t {}".format(wl_delays[read_port]))
debug.info(1,"Measured Wordline slews:\n\t {}".format(wl_slews[read_port]))
debug.info(1,"Measured SAE delays (ns):\n\t {}".format(sae_delays[read_port]))
debug.info(1,"Measured SAE slews:\n\t {}".format(sae_slews[read_port]))
debug.info(1,"Measured Bitline delays (ns):\n\t {}".format(bl_delays[read_port]))
data_dict[self.wl_meas_name] = wl_delays[read_port]
data_dict[self.sae_meas_name] = sae_delays[read_port]
data_dict[self.wl_slew_name] = wl_slews[read_port]
data_dict[self.sae_slew_name] = sae_slews[read_port]
data_dict[self.bl_meas_name] = bl_delays[read_port]
data_dict[self.power_name] = powers[read_port]
if not OPTS.use_tech_delay_chain_size: #Model is not used in this case
wl_model_delays, sae_model_delays = self.get_model_delays(read_port)
debug.info(1,"Wordline model delays:\n\t {}".format(wl_model_delays))
debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays))
data_dict[self.wl_model_name] = wl_model_delays
data_dict[self.sae_model_name] = sae_model_delays
#Some evaluations of the model and measured values
# debug.info(1, "Comparing wordline measurements and model.")
# self.compare_measured_and_model(wl_delays[read_port], wl_model_delays)
# debug.info(1, "Comparing SAE measurements and model")
# self.compare_measured_and_model(sae_delays[read_port], sae_model_delays)
return data_dict
def get_all_signal_names(self):
@ -438,12 +438,12 @@ class model_check(delay):
name_dict[self.bl_meas_name] = self.bitline_meas_names[0:1]
name_dict[self.power_name] = self.power_meas_names
#name_dict[self.wl_slew_name] = self.wl_slew_meas_names
if not OPTS.use_tech_delay_chain_size:
name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured.
name_dict[self.sae_model_name] = name_dict["sae_measures"]
return name_dict

View File

@ -31,7 +31,7 @@ class setup_hold():
self.set_corner(corner)
def set_corner(self,corner):
""" Set the corner values """
self.corner = corner
@ -60,9 +60,9 @@ class setup_hold():
self.write_clock()
self.write_measures(mode=mode,
self.write_measures(mode=mode,
correct_value=correct_value)
self.stim.write_control(4*self.period)
@ -102,14 +102,14 @@ class setup_hold():
data_values=[init_value, start_value, end_value],
period=target_time,
slew=self.constrained_input_slew,
setup=0)
setup=0)
def write_clock(self):
""" Create the clock signal for setup/hold analysis. First period initializes the FF
while the second is used for characterization."""
self.stim.gen_pwl(sig_name="clk",
# initial clk edge is right after the 0 time to initialize a flop
# initial clk edge is right after the 0 time to initialize a flop
# without using .IC on an internal node.
# Return input to value after one period.
# The second pulse is the characterization one at 2*period
@ -117,7 +117,7 @@ class setup_hold():
data_values=[0, 1, 0, 1],
period=2*self.period,
slew=self.constrained_input_slew,
setup=0)
setup=0)
@ -154,7 +154,7 @@ class setup_hold():
targ_dir=dout_rise_or_fall,
trig_td=1.9*self.period,
targ_td=1.9*self.period)
targ_name = "data"
# Start triggers right after initialize value is returned to normal
# at one period
@ -167,14 +167,14 @@ class setup_hold():
targ_dir=din_rise_or_fall,
trig_td=1.2*self.period,
targ_td=1.2*self.period)
def bidir_search(self, correct_value, mode):
""" This will perform a bidirectional search for either setup or hold times.
It starts with the feasible priod and looks a half period beyond or before it
depending on whether we are doing setup or hold.
depending on whether we are doing setup or hold.
"""
# NOTE: The feasible bound is always feasible. This is why they are different for setup and hold.
@ -189,8 +189,8 @@ class setup_hold():
feasible_bound = 2.75*self.period
# Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search!
self.write_stimulus(mode=mode,
target_time=feasible_bound,
self.write_stimulus(mode=mode,
target_time=feasible_bound,
correct_value=correct_value)
self.stim.run_sim()
ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
@ -204,18 +204,18 @@ class setup_hold():
setuphold_time *= -1e9
else:
setuphold_time *= 1e9
passing_setuphold_time = setuphold_time
debug.info(2,"Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
setuphold_time,
feasible_bound,
2*self.period))
#raw_input("Press Enter to continue...")
while True:
target_time = (feasible_bound + infeasible_bound)/2
self.write_stimulus(mode=mode,
target_time=target_time,
self.write_stimulus(mode=mode,
target_time=target_time,
correct_value=correct_value)
debug.info(2,"{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
@ -245,7 +245,7 @@ class setup_hold():
if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001):
debug.info(3,"CONVERGE {0} vs {1}".format(feasible_bound,infeasible_bound))
break
debug.info(2,"Converged on {0} time {1}.".format(mode,passing_setuphold_time))
return passing_setuphold_time
@ -261,7 +261,7 @@ class setup_hold():
"""Calculates the setup time for high-to-low transition for a DFF
"""
return self.bidir_search(0, "SETUP")
def hold_LH_time(self):
"""Calculates the hold time for low-to-high transition for a DFF
"""
@ -283,7 +283,7 @@ class setup_hold():
HL_setup = []
LH_hold = []
HL_hold = []
#For debugging, skips characterization and returns dummy values.
# i = 1.0
# for self.related_input_slew in related_slews:
@ -293,15 +293,15 @@ class setup_hold():
# LH_hold.append(i+2.0)
# HL_hold.append(i+3.0)
# i+=4.0
# times = {"setup_times_LH": LH_setup,
# "setup_times_HL": HL_setup,
# "hold_times_LH": LH_hold,
# "hold_times_HL": HL_hold
# }
# return times
for self.related_input_slew in related_slews:
for self.constrained_input_slew in constrained_slews:
debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,self.constrained_input_slew))
@ -317,7 +317,7 @@ class setup_hold():
HL_setup.append(HL_setup_time)
LH_hold.append(LH_hold_time)
HL_hold.append(HL_hold_time)
times = {"setup_times_LH": LH_setup,
"setup_times_HL": HL_setup,
"hold_times_LH": LH_hold,
@ -332,7 +332,7 @@ class setup_hold():
HL_setup = []
LH_hold = []
HL_hold = []
for self.related_input_slew in related_slews:
for self.constrained_input_slew in constrained_slews:
# convert from ps to ns
@ -340,7 +340,7 @@ class setup_hold():
HL_setup.append(tech.spice["dff_setup"]/1e3)
LH_hold.append(tech.spice["dff_hold"]/1e3)
HL_hold.append(tech.spice["dff_hold"]/1e3)
times = {"setup_times_LH": LH_setup,
"setup_times_HL": HL_setup,
"hold_times_LH": LH_hold,

View File

@ -17,7 +17,7 @@ class simulation():
def __init__(self, sram, spfile, corner):
self.sram = sram
self.name = self.sram.name
self.word_size = self.sram.word_size
self.addr_size = self.sram.addr_size
@ -28,7 +28,7 @@ class simulation():
else:
self.num_spare_cols = self.sram.num_spare_cols
self.sp_file = spfile
self.all_ports = self.sram.all_ports
self.readwrite_ports = self.sram.readwrite_ports
self.read_ports = self.sram.read_ports
@ -53,7 +53,7 @@ class simulation():
self.v_high = self.vdd_voltage - tech.spice["nom_threshold"]
self.v_low = tech.spice["nom_threshold"]
self.gnd_voltage = 0
def create_signal_names(self):
self.addr_name = "a"
self.din_name = "din"
@ -66,12 +66,12 @@ class simulation():
"Number of pins generated for characterization \
do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins,
self.pins))
def set_stimulus_variables(self):
# Clock signals
self.cycle_times = []
self.t_current = 0
# control signals: only one cs_b for entire multiported sram, one we_b for each write port
self.csb_values = {port: [] for port in self.all_ports}
self.web_values = {port: [] for port in self.readwrite_ports}
@ -81,13 +81,13 @@ class simulation():
self.data_value = {port: [] for port in self.write_ports}
self.wmask_value = {port: [] for port in self.write_ports}
self.spare_wen_value = {port: [] for port in self.write_ports}
# Three dimensional list to handle each addr and data bits for each port over the number of checks
self.addr_values = {port: [[] for bit in range(self.addr_size)] for port in self.all_ports}
self.data_values = {port: [[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports}
self.wmask_values = {port: [[] for bit in range(self.num_wmasks)] for port in self.write_ports}
self.spare_wen_values = {port: [[] for bit in range(self.num_spare_cols)] for port in self.write_ports}
# For generating comments in SPICE stimulus
self.cycle_comments = []
self.fn_cycle_comments = []
@ -104,13 +104,13 @@ class simulation():
web_val = 0
elif op != "noop":
debug.error("Could not add control signals for port {0}. Command {1} not recognized".format(port, op), 1)
# Append the values depending on the type of port
self.csb_values[port].append(csb_val)
# If port is in both lists, add rw control signal. Condition indicates its a RW port.
if port in self.readwrite_ports:
self.web_values[port].append(web_val)
def add_data(self, data, port):
""" Add the array of data values """
debug.check(len(data)==(self.word_size + self.num_spare_cols), "Invalid data word size.")
@ -129,7 +129,7 @@ class simulation():
def add_address(self, address, port):
""" Add the array of address values """
debug.check(len(address)==self.addr_size, "Invalid address size.")
self.addr_value[port].append(address)
bit = self.addr_size - 1
for c in address:
@ -170,7 +170,7 @@ class simulation():
else:
debug.error("Non-binary spare enable signal string", 1)
bit -= 1
def add_write(self, comment, address, data, wmask, port):
""" Add the control values for a write cycle. """
debug.check(port in self.write_ports,
@ -182,18 +182,18 @@ class simulation():
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.add_control_one_port(port, "write")
self.add_data(data, port)
self.add_address(address, port)
self.add_wmask(wmask, port)
self.add_wmask(wmask, port)
self.add_spare_wen("1" * self.num_spare_cols, 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 add_read(self, comment, address, port):
""" Add the control values for a read cycle. """
debug.check(port in self.read_ports,
@ -206,8 +206,8 @@ class simulation():
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.add_control_one_port(port, "read")
self.add_address(address, port)
self.add_address(address, port)
# If the port is also a readwrite then add
# the same value as previous cycle
if port in self.write_ports:
@ -220,7 +220,7 @@ class simulation():
except:
self.add_wmask("0" * self.num_wmasks, port)
self.add_spare_wen("0" * self.num_spare_cols, port)
#Add noops to all other ports.
for unselected_port in self.all_ports:
if unselected_port != port:
@ -234,7 +234,7 @@ class simulation():
self.cycle_times.append(self.t_current)
self.t_current += self.period
for port in self.all_ports:
self.add_noop_one_port(port)
@ -251,7 +251,7 @@ class simulation():
self.add_address(address, port)
self.add_wmask(wmask, port)
self.add_spare_wen("1" * self.num_spare_cols, port)
def add_read_one_port(self, comment, address, port):
""" Add the control values for a read cycle. Does not increment the period. """
debug.check(port in self.read_ports,
@ -259,7 +259,7 @@ class simulation():
self.read_ports))
debug.info(2, comment)
self.fn_cycle_comments.append(comment)
self.add_control_one_port(port, "read")
self.add_address(address, port)
@ -275,16 +275,16 @@ class simulation():
except:
self.add_wmask("0" * self.num_wmasks, port)
self.add_spare_wen("0" * self.num_spare_cols, port)
def add_noop_one_port(self, port):
""" Add the control values for a noop to a single port. Does not increment the period. """
""" Add the control values for a noop to a single port. Does not increment the period. """
self.add_control_one_port(port, "noop")
try:
self.add_address(self.addr_value[port][-1], port)
except:
self.add_address("0" * self.addr_size, port)
# If the port is also a readwrite then add
# the same value as previous cycle
if port in self.write_ports:
@ -297,7 +297,7 @@ class simulation():
except:
self.add_wmask("0" * self.num_wmasks, port)
self.add_spare_wen("0" * self.num_spare_cols, 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))
@ -324,7 +324,7 @@ class simulation():
time,
time_spacing,
comment))
def gen_cycle_comment(self, op, word, addr, wmask, port, t_current):
if op == "noop":
str = "\tIdle during cycle {0} ({1}ns - {2}ns)"
@ -355,22 +355,22 @@ class simulation():
int(t_current / self.period),
t_current,
t_current + self.period)
return comment
def gen_pin_names(self, port_signal_names, port_info, abits, dbits):
"""Creates the pins names of the SRAM based on the no. of ports."""
# This may seem redundant as the pin names are already defined in the sram. However, it is difficult
# to extract the functionality from the names, so they are recreated. As the order is static, changing
# This may seem redundant as the pin names are already defined in the sram. However, it is difficult
# to extract the functionality from the names, so they are recreated. As the order is static, changing
# the order of the pin names will cause issues here.
pin_names = []
(addr_name, din_name, dout_name) = port_signal_names
(total_ports, write_index, read_index) = port_info
for write_input in write_index:
for i in range(dbits):
pin_names.append("{0}{1}_{2}".format(din_name, write_input, i))
for port in range(total_ports):
for i in range(abits):
pin_names.append("{0}{1}_{2}".format(addr_name, port, i))
@ -389,25 +389,25 @@ class simulation():
for port in write_index:
for bit in range(self.num_wmasks):
pin_names.append("WMASK{0}_{1}".format(port, bit))
if self.num_spare_cols:
for port in write_index:
for bit in range(self.num_spare_cols):
pin_names.append("SPARE_WEN{0}_{1}".format(port, bit))
for read_output in read_index:
for i in range(dbits):
pin_names.append("{0}{1}_{2}".format(dout_name, read_output, i))
pin_names.append("{0}".format("vdd"))
pin_names.append("{0}".format("gnd"))
return pin_names
def add_graph_exclusions(self):
"""
Exclude portions of SRAM from timing graph which are not relevant
"""
# other initializations can only be done during analysis when a bit has been selected
# for testing.
self.sram.bank.graph_exclude_precharge()
@ -415,29 +415,29 @@ class simulation():
self.sram.graph_exclude_data_dff()
self.sram.graph_exclude_ctrl_dffs()
self.sram.bank.bitcell_array.graph_exclude_replica_col_bits()
def set_internal_spice_names(self):
"""
Sets important names for characterization such as Sense amp enable and internal bit nets.
"""
port = self.read_ports[0]
if not OPTS.use_pex:
self.graph.get_all_paths('{}{}'.format("clk", port),
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
sen_with_port = self.get_sen_name(self.graph.all_paths)
if sen_with_port.endswith(str(port)):
self.sen_name = sen_with_port[:-len(str(port))]
else:
self.sen_name = sen_with_port
debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.")
debug.info(2, "s_en name = {}".format(self.sen_name))
bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port)
port_pos = -1 - len(str(self.probe_data)) - len(str(port))
if bl_name_port.endswith(str(port) + "_" + str(self.probe_data)):
self.bl_name = bl_name_port[:port_pos] + "{}" + bl_name_port[port_pos + len(str(port)):]
elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
@ -445,7 +445,7 @@ class simulation():
else:
self.bl_name = bl_name_port
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
if br_name_port.endswith(str(port) + "_" + str(self.probe_data)):
self.br_name = br_name_port[:port_pos] + "{}" + br_name_port[port_pos + len(str(port)):]
elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
@ -457,22 +457,22 @@ class simulation():
else:
self.graph.get_all_paths('{}{}'.format("clk", port),
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
self.sen_name = self.get_sen_name(self.graph.all_paths)
debug.info(2, "s_en name = {}".format(self.sen_name))
self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size - 1)
self.br_name = "br{0}_{1}".format(port, OPTS.word_size - 1)
debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name))
def get_sen_name(self, paths, assumed_port=None):
"""
Gets the signal name associated with the sense amp enable from input paths.
Only expects a single path to contain the sen signal name.
"""
sa_mods = factory.get_mods(OPTS.sense_amp)
# Any sense amp instantiated should be identical, any change to that
# Any sense amp instantiated should be identical, any change to that
# will require some identification to determine the mod desired.
debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.")
enable_name = sa_mods[0].get_enable_name()
@ -480,15 +480,15 @@ class simulation():
if OPTS.use_pex:
sen_name = sen_name.split('.')[-1]
return sen_name
def create_graph(self):
"""
Creates timing graph to generate the timing paths for the SRAM output.
"""
self.sram.clear_exclude_bits() # Removes previous bit exclusions
self.sram.graph_exclude_bits(self.wordline_row, self.bitline_column)
# Generate new graph every analysis as edges might change depending on test bit
self.graph = graph_util.timing_graph()
self.sram_instance_name = "X{}".format(self.sram.name)
@ -498,14 +498,14 @@ class simulation():
"""
Gets the mods as a set which should be excluded while searching for name.
"""
# Exclude the RBL as it contains bitcells which are not in the main bitcell array
# so it makes the search awkward
return set(factory.get_mods(OPTS.replica_bitline))
def get_alias_in_path(self, paths, internal_net, mod, exclusion_set=None):
"""
Finds a single alias for the internal_net in given paths.
Finds a single alias for the internal_net in given paths.
More or less hits cause an error
"""
net_found = False
@ -520,7 +520,7 @@ class simulation():
net_found = True
if not net_found:
debug.error("Could not find {} net in timing paths.".format(internal_net), 1)
return path_net_name
def get_bl_name(self, paths, port):
@ -530,7 +530,7 @@ class simulation():
cell_mod = factory.create(module_type=OPTS.bitcell)
cell_bl = cell_mod.get_bl_name(port)
cell_br = cell_mod.get_br_name(port)
bl_names = []
exclude_set = self.get_bl_name_search_exclusions()
for int_net in [cell_bl, cell_br]:
@ -540,5 +540,5 @@ class simulation():
bl_names[i] = bl_names[i].split('.')[-1]
return bl_names[0], bl_names[1]

View File

@ -31,7 +31,7 @@ class stimuli():
self.tx_length = tech.drc["minlength_channel"]
self.sf = stim_file
(self.process, self.voltage, self.temperature) = corner
found = False
self.device_libraries = []
@ -48,10 +48,10 @@ class stimuli():
pass
if not found:
debug.error("Must define either fet_libraries or fet_models.", -1)
def inst_model(self, pins, model_name):
""" Function to instantiate a generic model with a set of pins """
if OPTS.use_pex:
self.inst_pex_model(pins, model_name)
else:
@ -59,7 +59,7 @@ class stimuli():
for pin in pins:
self.sf.write("{0} ".format(pin))
self.sf.write("{0}\n".format(model_name))
def inst_pex_model(self, pins, model_name):
self.sf.write("X{0} ".format(model_name))
for pin in pins:
@ -99,7 +99,7 @@ class stimuli():
def create_buffer(self, buffer_name, size=[1, 3], beta=2.5):
"""
Generates buffer for top level signals (only for sim
purposes). Size is pair for PMOS, NMOS width multiple.
purposes). Size is pair for PMOS, NMOS width multiple.
"""
self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name,
@ -124,23 +124,23 @@ class stimuli():
self.sf.write(".ENDS test_{0}\n\n".format(buffer_name))
def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
"""
"""
Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
from 50% to 50%.
"""
self.sf.write("* PULSE: period={0}\n".format(period))
pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n"
self.sf.write(pulse_string.format(sig_name,
self.sf.write(pulse_string.format(sig_name,
v1,
v2,
offset,
t_rise,
t_fall,
t_fall,
0.5*period-0.5*t_rise-0.5*t_fall,
period))
def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup):
"""
"""
Generate a PWL stimulus given a signal name and data values at each period.
Automatically creates slews and ensures each data occurs a setup before the clock
edge. The first clk_time should be 0 and is the initial time that corresponds
@ -152,7 +152,7 @@ class stimuli():
str.format(len(clk_times),
len(data_values),
sig_name))
# shift signal times earlier for setup time
times = np.array(clk_times) - setup * period
values = np.array(data_values) * self.voltage
@ -185,7 +185,7 @@ class stimuli():
return 1
else:
debug.error("Invalid value to get an inverse of: {0}".format(value))
def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
""" Creates the .meas statement for the measurement of delay """
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
@ -198,7 +198,7 @@ class stimuli():
targ_val,
targ_dir,
targ_td))
def gen_meas_find_voltage(self, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td):
""" Creates the .meas statement for the measurement of delay """
measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n"
@ -208,7 +208,7 @@ class stimuli():
trig_val,
trig_dir,
trig_td))
def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at):
""" Creates the .meas statement for voltage at time"""
measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n"
@ -227,15 +227,15 @@ class stimuli():
power_exp,
t_initial,
t_final))
def gen_meas_value(self, meas_name, dout, t_intital, t_final):
measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_intital, t_final)
self.sf.write(measure_string)
def write_control(self, end_time, runlvl=4):
""" Write the control cards to run and end the simulation """
# These are guesses...
# These are guesses...
if runlvl==1:
reltol = 0.02 # 2%
elif runlvl==2:
@ -245,7 +245,7 @@ class stimuli():
else:
reltol = 0.001 # 0.1%
timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests.
# UIC is needed for ngspice to converge
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
self.sf.write(".TEMP {}\n".format(self.temperature))
@ -281,9 +281,9 @@ class stimuli():
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]))
includes = self.device_models + [circuit]
for item in list(includes):
if os.path.isfile(item):
self.sf.write(".include \"{0}\"\n".format(item))
@ -305,7 +305,7 @@ class stimuli():
import datetime
start_time = datetime.datetime.now()
debug.check(OPTS.spice_exe != "", "No spice simulator has been found.")
if OPTS.spice_name == "xa":
# Output the xa configurations here. FIXME: Move this to write it once.
xa_cfg = open("{}xa.cfg".format(OPTS.openram_temp), "w")
@ -340,13 +340,13 @@ class stimuli():
spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w')
spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w')
debug.info(3, cmd)
retcode = subprocess.call(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True)
spice_stdout.close()
spice_stderr.close()
if (retcode > valid_retcode):
debug.error("Spice simulation error: " + cmd, -1)
else:
@ -354,4 +354,4 @@ class stimuli():
delta_time = round((end_time - start_time).total_seconds(), 1)
debug.info(2, "*** Spice: {} seconds".format(delta_time))

View File

@ -11,33 +11,33 @@ import re
class trim_spice():
"""
A utility to trim redundant parts of an SRAM spice netlist.
A utility to trim redundant parts of an SRAM spice netlist.
Input is an SRAM spice file. Output is an equivalent netlist
that works for a single address and range of data bits.
"""
def __init__(self, spfile, reduced_spfile):
self.sp_file = spfile
self.reduced_spfile = reduced_spfile
self.reduced_spfile = reduced_spfile
debug.info(1,"Trimming non-critical cells to speed-up characterization: {}.".format(reduced_spfile))
# Load the file into a buffer for performance
sp = open(self.sp_file, "r")
self.spice = sp.readlines()
sp.close()
for i in range(len(self.spice)):
self.spice[i] = self.spice[i].rstrip(" \n")
self.sp_buffer = self.spice
def set_configuration(self, banks, rows, columns, word_size):
""" Set the configuration of SRAM sizes that we are simulating.
Need the: number of banks, number of rows in each bank, number of
Need the: number of banks, number of rows in each bank, number of
columns in each bank, and data word size."""
self.num_banks = banks
self.num_rows = rows
self.num_rows = rows
self.num_columns = columns
self.word_size = word_size
@ -80,8 +80,8 @@ class trim_spice():
debug.info(1,wl_msg)
self.sp_buffer.insert(0, "* It should NOT be used for LVS!!")
self.sp_buffer.insert(0, "* WARNING: This is a TRIMMED NETLIST.")
wl_regex = r"wl\d*_{}".format(wl_address)
bl_regex = r"bl\d*_{}".format(int(self.words_per_row*data_bit + col_address))
self.remove_insts("bitcell_array",[wl_regex,bl_regex])
@ -92,7 +92,7 @@ class trim_spice():
# 3. Keep column muxes basd on BL
self.remove_insts("column_mux_array",[bl_regex])
# 4. Keep write driver based on DATA
data_regex = r"data_{}".format(data_bit)
self.remove_insts("write_driver_array",[data_regex])
@ -100,18 +100,18 @@ class trim_spice():
# 5. Keep wordline driver based on WL
# Need to keep the gater too
#self.remove_insts("wordline_driver",wl_regex)
# 6. Keep precharges based on BL
self.remove_insts("precharge_array",[bl_regex])
# Everything else isn't worth removing. :)
# Finally, write out the buffer as the new reduced file
sp = open(self.reduced_spfile, "w")
sp.write("\n".join(self.sp_buffer))
sp.close()
def remove_insts(self, subckt_name, keep_inst_list):
"""This will remove all of the instances in the list from the named
subckt that DO NOT contain a term in the list. It just does a
@ -121,7 +121,7 @@ class trim_spice():
removed_insts = 0
#Expects keep_inst_list are regex patterns. Compile them here.
compiled_patterns = [re.compile(pattern) for pattern in keep_inst_list]
start_name = ".SUBCKT {}".format(subckt_name)
end_name = ".ENDS {}".format(subckt_name)

View File

@ -6,7 +6,7 @@
# All rights reserved.
#
import design
from tech import GDS, layer, spice, parameter
from tech import GDS, layer, spice
from tech import cell_properties as props
import utils
@ -23,39 +23,42 @@ class dff(design.design):
pin_names = props.dff.custom_port_list
type_list = props.dff.custom_type_list
clk_pin = props.dff.clk_pin
cell_size_layer = "boundary"
(width, height) = utils.get_libcell_size("dff",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"])
def __init__(self, name="dff"):
design.design.__init__(self, name)
super().__init__(name)
self.width = dff.width
self.height = dff.height
self.pin_map = dff.pin_map
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_frequency"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["dff_leakage"]
total_power = self.return_power(power_dyn, power_leak)
return total_power
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
from tech import parameter
c_load = load
c_para = spice["dff_out_cap"]#ff
transition_prob = 0.5
return transition_prob*(c_load + c_para)
return transition_prob*(c_load + c_para)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)

View File

@ -9,28 +9,31 @@ import design
from tech import GDS, layer, spice, parameter
import logical_effort
import utils
import debug
class inv_dec(design.design):
"""
INV for address decoders.
"""
pin_names = ["A", "Z", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("inv_dec",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "inv_dec", GDS["unit"])
def __init__(self, name="inv_dec", height=None):
design.design.__init__(self, name)
cell_size_layer = "boundary"
self.width = inv_dec.width
self.height = inv_dec.height
self.pin_map = inv_dec.pin_map
def __init__(self, name="inv_dec", height=None):
super().__init__(name)
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
def analytical_power(self, corner, load):
@ -39,10 +42,10 @@ class inv_dec(design.design):
freq = spice["default_event_frequency"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["inv_leakage"]
total_power = self.return_power(power_dyn, power_leak)
return total_power
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
@ -57,7 +60,7 @@ class inv_dec(design.design):
units relative to the minimum width of a transistor
"""
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""
Returns an object representing the parameters for delay in tau units.

View File

@ -15,21 +15,25 @@ class nand2_dec(design.design):
"""
2-input NAND decoder for address decoders.
"""
pin_names = ["A", "B", "Z", "vdd", "gnd"]
type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("nand2_dec",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "nand2_dec", GDS["unit"])
def __init__(self, name="nand2_dec", height=None):
design.design.__init__(self, name)
cell_size_layer = "boundary"
self.width = nand2_dec.width
self.height = nand2_dec.height
self.pin_map = nand2_dec.pin_map
def __init__(self, name="nand2_dec", height=None):
super().__init__(name)
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
# FIXME: For now...
@ -39,17 +43,17 @@ class nand2_dec(design.design):
self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_frequency"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["nand2_leakage"]
total_power = self.return_power(power_dyn, power_leak)
return total_power
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
@ -61,7 +65,7 @@ class nand2_dec(design.design):
def input_load(self):
"""Return the relative input capacitance of a single input"""
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""
Returns an object representing the parameters for delay in tau units.
@ -82,4 +86,4 @@ class nand2_dec(design.design):
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -15,21 +15,25 @@ class nand3_dec(design.design):
"""
3-input NAND decoder for address decoders.
"""
pin_names = ["A", "B", "C", "Z", "vdd", "gnd"]
type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("nand3_dec",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "nand3_dec", GDS["unit"])
def __init__(self, name="nand3_dec", height=None):
design.design.__init__(self, name)
cell_size_layer = "boundary"
self.width = nand3_dec.width
self.height = nand3_dec.height
self.pin_map = nand3_dec.pin_map
def __init__(self, name="nand3_dec", height=None):
super().__init__(name)
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
# FIXME: For now...
@ -39,17 +43,17 @@ class nand3_dec(design.design):
self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_frequency"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["nand3_leakage"]
total_power = self.return_power(power_dyn, power_leak)
return total_power
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
@ -61,7 +65,7 @@ class nand3_dec(design.design):
def input_load(self):
"""Return the relative input capacitance of a single input"""
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""
Returns an object representing the parameters for delay in tau units.
@ -82,4 +86,4 @@ class nand3_dec(design.design):
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -15,21 +15,25 @@ class nand4_dec(design.design):
"""
2-input NAND decoder for address decoders.
"""
pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"]
type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("nand4_dec",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "nand4_dec", GDS["unit"])
def __init__(self, name="nand4_dec", height=None):
design.design.__init__(self, name)
cell_size_layer = "boundary"
self.width = nand4_dec.width
self.height = nand4_dec.height
self.pin_map = nand4_dec.pin_map
def __init__(self, name="nand4_dec", height=None):
super().__init__(name)
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
# FIXME: For now...
@ -39,17 +43,17 @@ class nand4_dec(design.design):
self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_frequency"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["nand4_leakage"]
total_power = self.return_power(power_dyn, power_leak)
return total_power
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
@ -61,7 +65,7 @@ class nand4_dec(design.design):
def input_load(self):
"""Return the relative input capacitance of a single input"""
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""
Returns an object representing the parameters for delay in tau units.
@ -82,4 +86,4 @@ class nand4_dec(design.design):
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -10,7 +10,6 @@ import debug
import utils
from tech import GDS, layer, parameter, drc
from tech import cell_properties as props
from globals import OPTS
import logical_effort
@ -28,12 +27,24 @@ class sense_amp(design.design):
props.sense_amp.pin.vdd,
props.sense_amp.pin.gnd]
type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width, height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"])
else:
(width, height) = (0, 0)
pin_map = []
cell_size_layer = "boundary"
def __init__(self, name="sense_amp"):
super().__init__(name)
debug.info(2, "Create sense_amp")
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
def get_bl_names(self):
return props.sense_amp.pin.bl
@ -49,26 +60,17 @@ class sense_amp(design.design):
def en_name(self):
return props.sense_amp.pin.en
def __init__(self, name):
super().__init__(name)
debug.info(2, "Create sense_amp")
self.width = sense_amp.width
self.height = sense_amp.height
self.pin_map = sense_amp.pin_map
self.add_pin_types(self.type_list)
def get_cin(self):
# FIXME: This input load will be applied to both the s_en timing and bitline timing.
# Input load for the bitlines which are connected to the source/drain of a TX. Not the selects.
from tech import spice
# Default is 8x. Per Samira and Hodges-Jackson book:
# "Column-mux transistors driven by the decoder must be sized for optimal speed"
bitline_pmos_size = 8 # FIXME: This should be set somewhere and referenced. Probably in tech file.
return spice["min_tx_drain_c"] * bitline_pmos_size # ff
def get_stage_effort(self, load):
# Delay of the sense amp will depend on the size of the amp and the output load.
parasitic_delay = 1
@ -82,14 +84,14 @@ class sense_amp(design.design):
# Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
return total_power
def get_enable_name(self):
"""Returns name used for enable net"""
# FIXME: A better programmatic solution to designate pins
enable_name = self.en_name
debug.check(enable_name in self.pin_names, "Enable name {} not found in pin list".format(enable_name))
return enable_name
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)

View File

@ -8,43 +8,51 @@
import debug
import design
import utils
from tech import GDS,layer
from tech import GDS, layer
class tri_gate(design.design):
"""
This module implements the tri gate cell used in the design forS
bit-line isolation. It is a hand-made cell, so the layout and
netlist should be available in the technology library.
netlist should be available in the technology library.
"""
pin_names = ["in", "out", "en", "en_bar", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"])
cell_size_layer = "boundary"
unique_id = 1
def __init__(self, name=""):
if name=="":
name = "tri{0}".format(tri_gate.unique_id)
tri_gate.unique_id += 1
design.design.__init__(self, name)
super().__init__(self, name)
debug.info(2, "Create tri_gate")
self.width = tri_gate.width
self.height = tri_gate.height
self.pin_map = tri_gate.pin_map
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
total_power = self.return_power()
return total_power
def get_cin(self):
return 9*spice["min_tx_gate_c"]
def build_graph(self, graph, inst_name, port_nets):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
self.add_graph_edges(graph, port_nets)

View File

@ -8,13 +8,13 @@
import debug
import design
import utils
from globals import OPTS
from tech import GDS,layer
from tech import GDS, layer
from tech import cell_properties as props
class write_driver(design.design):
"""
Tristate write driver to be active during write operations only.
Tristate write driver to be active during write operations only.
This module implements the write driver cell used in the design. It
is a hand-made cell, so the layout and netlist should be available in
the technology library.
@ -28,20 +28,23 @@ class write_driver(design.design):
props.write_driver.pin.gnd]
type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"])
else:
(width,height) = (0,0)
pin_map = []
cell_size_layer = "boundary"
def __init__(self, name):
design.design.__init__(self, name)
super().__init__(name)
debug.info(2, "Create write_driver")
self.width = write_driver.width
self.height = write_driver.height
self.pin_map = write_driver.pin_map
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
def get_bl_names(self):
@ -63,6 +66,6 @@ class write_driver(design.design):
# This is approximated from SCMOS. It has roughly 5 3x transistor gates.
return 5*3
def build_graph(self, graph, inst_name, port_nets):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
self.add_graph_edges(graph, port_nets)

View File

@ -25,16 +25,16 @@ def parse_html(file, comment):
end_tag = comment+'-->'
with open(file, 'r') as f:
file_string = f.read()
with open(file, 'w') as f:
file_string = file_string.replace(start_tag,"")
file_string = file_string.replace(end_tag,"")
f.write(file_string)
def uncomment(comments):
comment_files = []
for datasheet in datasheet_list:

View File

@ -112,7 +112,7 @@ def parse_characterizer_csv(f, pages):
DATETIME = row[col]
col += 1
ANALYTICAL_MODEL = row[col]
col += 1
@ -121,7 +121,7 @@ def parse_characterizer_csv(f, pages):
LVS = row[col]
col += 1
AREA = row[col]
col += 1
@ -565,7 +565,7 @@ def parse_characterizer_csv(f, pages):
for element in row[col_start:col-1]:
sheet.description.append(str(element))
break
# parse initial power and leakage information
# parse initial power and leakage information
while(True):
start = col
if(row[col].startswith('power')):

View File

@ -46,7 +46,7 @@ class design_rules(dict):
def keys(self):
return self.rules.keys()
def add_layer(self, name, width, spacing, area=0):
# Minimum width
self.add("minwidth_{}".format(name), width)
@ -54,7 +54,7 @@ class design_rules(dict):
self.add("{0}_to_{0}".format(name), spacing)
# Minimum area
self.add("minarea_{}".format(name), area)
def add_enclosure(self, name, layer, enclosure, extension=None):
self.add("{0}_enclose_{1}".format(name, layer), enclosure)
# Reserved for asymmetric enclosures
@ -62,4 +62,4 @@ class design_rules(dict):
self.add("{0}_extend_{1}".format(name, layer), extension)
else:
self.add("{0}_extend_{1}".format(name, layer), enclosure)

View File

@ -44,7 +44,7 @@ class drc_lut():
if k1 < k2:
return False
return True

View File

@ -19,7 +19,7 @@ class drc_value():
Return the value.
"""
return self.value

View File

@ -6,7 +6,7 @@ class GdsStreamer:
"""
def __init__(self, workingDirectory = "."):
self.workingDirectory = os.path.abspath(workingDirectory)
def createStreamOutTemplate(self, sourceLibraryName, sourceCellName, gdsDestinationPath):
templateFile = open(self.workingDirectory+"/partStreamOut.tmpl","w")
templateFile.write("streamOutKeys = list(nil\n")
@ -70,7 +70,7 @@ class GdsStreamer:
templateFile = open(self.workingDirectory+"/partStreamIn.tmpl","w")
templateFile.write("streamInKeys = list(nil\n")
templateFile.write("'runDir \".\"\n")
templateFile.write("'inFile \""+inputGdsPath+"\"\n")
templateFile.write("'inFile \""+inputGdsPath+"\"\n")
templateFile.write("'primaryCell \"\"\n")
templateFile.write("'libName \""+sourceLibraryName+"\"\n")
templateFile.write("'techFileName \"\"\n")
@ -88,7 +88,7 @@ class GdsStreamer:
templateFile.write("'convertNode \"ignore\"\n")
templateFile.write("'keepPcell nil\n")
templateFile.write("'replaceBusBitChar nil\n")
templateFile.write("'skipUndefinedLPP nil\n")
templateFile.write("'skipUndefinedLPP nil\n")
templateFile.write("'ignoreBox nil\n")
templateFile.write("'mergeUndefPurposToDrawing nil\n")
templateFile.write("'reportPrecision nil\n")
@ -109,10 +109,10 @@ class GdsStreamer:
templateFile.write("'propSeparator \",\"\n")
templateFile.write("'userSkillFile \"\"\n")
templateFile.write("'rodDir \"\"\n")
templateFile.write("'refLibOrder \"\"\n")
templateFile.write("'refLibOrder \"\"\n")
templateFile.write(")\n")
templateFile.close()
def streamFromCadence(self, cadenceLibraryContainerPath, libraryName, cellName, outputPath):
#change into the cadence directory
outputPath = os.path.abspath(outputPath)
@ -132,7 +132,7 @@ class GdsStreamer:
os.remove(self.workingDirectory+"/partStreamOut.tmpl")
#and go back to whever it was we started from
os.chdir(currentPath)
def streamToCadence(self,cadenceLibraryContainerPath, libraryName, inputPath):
#change into the cadence directory
inputPath = os.path.abspath(inputPath)

View File

@ -11,19 +11,19 @@ class pdfLayout:
self.layout = theLayout
self.layerColors=dict()
self.scale = 1.0
def setScale(self,newScale):
self.scale = float(newScale)
def hexToRgb(self,hexColor):
"""
Takes a hexadecimal color string i.e. "#219E1C" and converts it to an rgb float triplet ranging 0->1
"""
red = int(hexColor[1:3],16)
green = int(hexColor[3:5],16)
blue = int(hexColor[5:7],16)
blue = int(hexColor[5:7],16)
return (float(red)/255,float(green)/255,float(blue)/255)
def randomHexColor(self):
"""
Generates a random color in hex using the format #ABC123
@ -50,26 +50,26 @@ class pdfLayout:
xyPoint = tMatrix * xyPoint
xyCoordinates += [(xyPoint[0],xyPoint[1])]
return xyCoordinates
def drawBoundary(self,boundary,origin,uVector,vVector):
#get the coordinates in the correct coordinate space
coordinates = self.transformCoordinates(boundary.coordinates,origin,uVector,vVector)
#method to draw a boundary with an XY offset
#method to draw a boundary with an XY offset
x=(coordinates[0][0])/self.scale
y=(coordinates[0][1])/self.scale
shape = pyx.path.path(pyx.path.moveto(x, y))
for index in range(1,len(coordinates)):
x=(coordinates[index][0])/self.scale
y=(coordinates[index][1])/self.scale
shape.append(pyx.path.lineto(x,y))
y=(coordinates[index][1])/self.scale
shape.append(pyx.path.lineto(x,y))
self.canvas.stroke(shape, [pyx.style.linewidth.thick])
if(boundary.drawingLayer in self.layerColors):
layerColor = self.hexToRgb(self.layerColors[boundary.drawingLayer])
self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)])
def drawPath(self,path,origin,uVector,vVector):
#method to draw a path with an XY offset
boundaryCoordinates = self.transformCoordinates(path.equivalentBoundaryCoordinates(),origin,uVector,vVector)
def drawPath(self,path,origin,uVector,vVector):
#method to draw a path with an XY offset
boundaryCoordinates = self.transformCoordinates(path.equivalentBoundaryCoordinates(),origin,uVector,vVector)
shape = pyx.path.path(pyx.path.moveto((boundaryCoordinates[0][0])/self.scale,(boundaryCoordinates[0][1])/self.scale))
for coordinate in boundaryCoordinates[1::]:
shape.append(pyx.path.lineto((coordinate[0])/self.scale,(coordinate[1])/self.scale))
@ -77,7 +77,7 @@ class pdfLayout:
if(path.drawingLayer in self.layerColors):
layerColor = self.hexToRgb(self.layerColors[path.drawingLayer])
self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)])
def drawLayout(self):
#use the layout xyTree and structureList
#to draw ONLY the geometry in each structure
@ -89,6 +89,6 @@ class pdfLayout:
self.drawBoundary(boundary,element[1],element[2], element[3])
for path in structureToDraw.paths:
self.drawPath(path,element[1],element[2], element[3])
def writeToFile(self,filename):
self.canvas.writePDFfile(filename)

View File

@ -28,7 +28,7 @@ import unit
#
class bbox_pt:
"""class for bounding boxes
This variant requires points in the constructor, and is used for internal

View File

@ -47,7 +47,7 @@ class canvasitem:
- the PS code corresponding to the canvasitem has to be written in the
stream file, which provides a write(string) method
- writer is the PSwriter used for the output
- context is an instance of pswriter.context which is used for keeping
- context is an instance of pswriter.context which is used for keeping
track of the graphics state (current linewidth, colorspace and font))
- registry is used for tracking resources needed by the canvasitem
- bbox has to be updated to include the bounding box of the canvasitem
@ -63,7 +63,7 @@ class canvasitem:
- writer is the PDFwriter used for the output, which contains properties
like whether streamcompression is used
- context is an instance of pdfwriter.context which is used for keeping
track of the graphics state, in particular for the emulation of PS
track of the graphics state, in particular for the emulation of PS
behaviour regarding fill and stroke styles, for keeping track of the
currently selected font as well as of text regions.
- registry is used for tracking resources needed by the canvasitem
@ -145,8 +145,8 @@ class _canvas(canvasitem):
attr.checkattrs(attrs, [trafo.trafo_pt, clip, style.strokestyle, style.fillstyle])
# We have to reverse the trafos such that the PostScript concat operators
# are in the right order. Correspondingly, we below multiply the current self.trafo
# from the right.
# Note that while for the stroke and fill styles the order doesn't matter at all,
# from the right.
# Note that while for the stroke and fill styles the order doesn't matter at all,
# this is not true for the clip operation.
attrs = attrs[:]
attrs.reverse()

View File

@ -1188,7 +1188,7 @@ class parallel(deformer): # <<<
intsparams = np[nsp_i][nspitem_i].intersect(np[nsp_j][nspitem_j], epsilon)
if intsparams:
for intsparam_i, intsparam_j in intsparams:
if ( (abs(intsparam_i) < epsilon and abs(1-intsparam_j) < epsilon) or
if ( (abs(intsparam_i) < epsilon and abs(1-intsparam_j) < epsilon) or
(abs(intsparam_j) < epsilon and abs(1-intsparam_i) < epsilon) ):
continue
npp_i = normpath.normpathparam(np, nsp_i, float(nspitem_i)+intsparam_i)

View File

@ -467,7 +467,7 @@ class font:
return fontinfo
def __str__(self):
return "font %s designed at %g TeX pts used at %g TeX pts" % (self.name,
return "font %s designed at %g TeX pts used at %g TeX pts" % (self.name,
16.0*self.d/16777216L,
16.0*self.q/16777216L)
__repr__ = __str__
@ -510,7 +510,7 @@ class font:
def _convert_tfm_to_ds(self, length):
return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv * 1000 / self.getsize_pt()
def _convert_tfm_to_pt(self, length):
return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv
@ -528,7 +528,7 @@ class font:
def getitalic_dvi(self, charcode):
return self._convert_tfm_to_dvi(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index])
# routines returning lengths as integers in design size (AFM) units
# routines returning lengths as integers in design size (AFM) units
def getwidth_ds(self, charcode):
return self._convert_tfm_to_ds(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index])
@ -767,7 +767,7 @@ class dvifile:
if fontslant is not None:
fontslant = float(fontslant)
# XXX we currently misuse use self.activefont as metric
# XXX we currently misuse use self.activefont as metric
font = type1font.font(fontbasefontname, fontfilename, fontencoding, fontslant, self.activefont)
self.activetext = type1font.text_pt(self.pos[_POS_H] * self.pyxconv, -self.pos[_POS_V] * self.pyxconv, font)
@ -973,14 +973,14 @@ class dvifile:
den = afile.readuint32()
self.mag = afile.readuint32()
# For the interpretation of the lengths in dvi and tfm files,
# For the interpretation of the lengths in dvi and tfm files,
# three conversion factors are relevant:
# - self.tfmconv: tfm units -> dvi units
# - self.pyxconv: dvi units -> (PostScript) points
# - self.conv: dvi units -> pixels
self.tfmconv = (25400000.0/num)*(den/473628672.0)/16.0
# calculate conv as described in the DVIType docu using
# calculate conv as described in the DVIType docu using
# a given resolution in dpi
self.resolution = 300.0
self.conv = (num/254000.0)*(self.resolution/den)

View File

@ -319,7 +319,7 @@ class epsfile(canvas.canvasitem):
try:
epsfile=open(self.filename,"rb")
except:
raise IOError, "cannot open EPS file '%s'" % self.filename
raise IOError, "cannot open EPS file '%s'" % self.filename
file.write("BeginEPSF\n")
@ -330,7 +330,7 @@ class epsfile(canvas.canvasitem):
self.trafo.processPS(file, writer, context, registry, bbox)
file.write("%%%%BeginDocument: %s\n" % self.filename)
file.write(epsfile.read())
file.write(epsfile.read())
file.write("%%EndDocument\n")
file.write("EndEPSF\n")

View File

@ -166,4 +166,4 @@ def realpolyroots(*cs):
# else:
# rs.append(r)
# return rs
#
#

View File

@ -710,9 +710,9 @@ class arct_pt(pathitem):
# Negative (positive) angles alpha corresponds to a turn to the right (left)
# as seen from currentpoint.
if dx1*dy2-dy1*dx2 > 0:
alpha = acos(dx1*dx2+dy1*dy2)
alpha = acos(dx1*dx2+dy1*dy2)
else:
alpha = -acos(dx1*dx2+dy1*dy2)
alpha = -acos(dx1*dx2+dy1*dy2)
try:
# two tangent points
@ -744,7 +744,7 @@ class arct_pt(pathitem):
return [line, arcn_pt(mx_pt, my_pt, self.r_pt, phi-deltaphi, phi+deltaphi)]
except ZeroDivisionError:
# in the degenerate case, we just return a line as specified by the PS
# in the degenerate case, we just return a line as specified by the PS
# language reference
return [lineto_pt(self.x1_pt, self.y1_pt)]

View File

@ -48,7 +48,7 @@ class pattern(canvas._canvas, attr.exclusiveattr, style.fillstyle):
self.patternbbox = bbox
self.patterntrafo = trafo
def __call__(self, painttype=_marker, tilingtype=_marker, xstep=_marker, ystep=_marker,
def __call__(self, painttype=_marker, tilingtype=_marker, xstep=_marker, ystep=_marker,
bbox=_marker, trafo=_marker):
if painttype is _marker:
painttype = self.painttype

View File

@ -38,7 +38,7 @@ def set(epsilon=None):
def _rmatrix(angle):
phi = math.pi*angle/180.0
return ((math.cos(phi), -math.sin(phi)),
return ((math.cos(phi), -math.sin(phi)),
(math.sin(phi), math.cos(phi)))
def _rvector(angle, x, y):
@ -198,7 +198,7 @@ class trafo(trafo_pt):
epsilon=epsilon)
#
# some standard transformations
# some standard transformations
#
class mirror(trafo):
@ -219,7 +219,7 @@ class rotate_pt(trafo_pt):
class rotate(trafo_pt):
def __init__(self, angle, x=None, y=None, epsilon=_marker):
vector = 0, 0
vector = 0, 0
if x is not None or y is not None:
if x is None or y is None:
raise TrafoException("either specify both x and y or none of them")

View File

@ -26,7 +26,7 @@ scale = { 't':1, 'u':1, 'v':1, 'w':1, 'x':1 }
_default_unit = "cm"
_m = {
_m = {
'm' : 1,
'cm': 0.01,
'mm': 0.001,
@ -51,7 +51,7 @@ def set(uscale=None, vscale=None, wscale=None, xscale=None, defaultunit=None):
def _convert_to(l, dest_unit="m"):
if type(l) in (types.IntType, types.LongType, types.FloatType):
return l * _m[_default_unit] * scale['u'] / _m[dest_unit]
elif not isinstance(l, length):
elif not isinstance(l, length):
l = length(l) # convert to length instance if necessary
return (l.t + l.u*scale['u'] + l.v*scale['v'] + l.w*scale['w'] + l.x*scale['x']) / _m[dest_unit]

View File

@ -144,18 +144,18 @@ def check_versions():
minor_required = 5
if not (major_python_version == major_required and minor_python_version >= minor_required):
debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1)
# FIXME: Check versions of other tools here??
# or, this could be done in each module (e.g. verify, characterizer, etc.)
global OPTS
try:
import coverage
OPTS.coverage = 1
except:
OPTS.coverage = 0
def init_openram(config_file, is_unit_test=True):
""" Initialize the technology, paths, simulators, etc. """
@ -164,7 +164,7 @@ def init_openram(config_file, is_unit_test=True):
debug.info(1, "Initializing OpenRAM...")
setup_paths()
read_config(config_file, is_unit_test)
import_tech()
@ -188,7 +188,10 @@ def init_openram(config_file, is_unit_test=True):
if is_unit_test and CHECKPOINT_OPTS:
OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy()
return
# Setup correct bitcell names
setup_bitcell()
# Import these to find the executables for checkpointing
import characterizer
import verify
@ -197,22 +200,19 @@ def init_openram(config_file, is_unit_test=True):
if not CHECKPOINT_OPTS:
CHECKPOINT_OPTS = copy.copy(OPTS)
def setup_bitcell():
"""
Determine the correct custom or parameterized bitcell for the design.
"""
global OPTS
# If we have non-1rw ports,
# and the user didn't over-ride the bitcell manually,
# figure out the right bitcell to use
if (OPTS.bitcell == "bitcell"):
if (OPTS.num_rw_ports == 1 and OPTS.num_w_ports == 0 and OPTS.num_r_ports == 0):
OPTS.bitcell = "bitcell"
OPTS.replica_bitcell = "replica_bitcell"
OPTS.dummy_bitcell = "dummy_bitcell"
OPTS.bitcell_name = "cell_6t"
else:
ports = ""
if OPTS.num_rw_ports > 0:
@ -225,6 +225,20 @@ def setup_bitcell():
if ports != "":
OPTS.bitcell_suffix = "_" + ports
OPTS.bitcell = "bitcell" + OPTS.bitcell_suffix
OPTS.bitcell_name = "cell" + OPTS.bitcell_suffix
OPTS.dummy_bitcell = "dummy_" + OPTS.bitcell
OPTS.dummy_bitcell_name = "dummy_" + OPTS.bitcell_name
OPTS.replica_bitcell = "replica_" + OPTS.bitcell
OPTS.replica_bitcell_name = "replica_" + OPTS.bitcell_name
elif (OPTS.bitcell == "pbitcell"):
OPTS.bitcell = "pbitcell"
OPTS.bitcell_name = "pbitcell"
OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.dummy_bitcell_name = "dummy_pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.replica_bitcell_name = "replica_pbitcell"
# See if bitcell exists
try:
@ -234,6 +248,11 @@ def setup_bitcell():
# or its custom replica bitcell
# Use the pbitcell (and give a warning if not in unit test mode)
OPTS.bitcell = "pbitcell"
OPTS.bitcell_name = "pbitcell"
OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.dummy_bitcell_name = "dummy_pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.replica_bitcell_name = "replica_pbitcell"
if not OPTS.is_unit_test:
debug.warning("Using the parameterized bitcell which may have suboptimal density.")
debug.info(1, "Using bitcell: {}".format(OPTS.bitcell))
@ -269,7 +288,7 @@ def get_tool(tool_type, preferences, default_name=None):
else:
return(None, "")
def read_config(config_file, is_unit_test=True):
"""
Read the configuration file that defines a few parameters. The
@ -282,14 +301,14 @@ def read_config(config_file, is_unit_test=True):
# it is already not an abs path, make it one
if not os.path.isabs(config_file):
config_file = os.getcwd() + "/" + config_file
# Make it a python file if the base name was only given
config_file = re.sub(r'\.py$', "", config_file)
# Expand the user if it is used
config_file = os.path.expanduser(config_file)
OPTS.config_file = config_file + ".py"
# Add the path to the system path
# so we can import things in the other directory
@ -328,7 +347,7 @@ def read_config(config_file, is_unit_test=True):
# If we are only generating a netlist, we can't do DRC/LVS
if OPTS.netlist_only:
OPTS.check_lvsdrc = False
# If config didn't set output name, make a reasonable default.
if (OPTS.output_name == ""):
ports = ""
@ -343,7 +362,7 @@ def read_config(config_file, is_unit_test=True):
ports,
OPTS.tech_name)
def end_openram():
""" Clean up openram for a proper exit """
cleanup_paths()
@ -353,8 +372,8 @@ def end_openram():
verify.print_drc_stats()
verify.print_lvs_stats()
verify.print_pex_stats()
def cleanup_paths():
"""
We should clean up the temp directory after execution.
@ -376,8 +395,8 @@ def cleanup_paths():
os.remove(i)
else:
shutil.rmtree(i)
def setup_paths():
""" Set up the non-tech related paths. """
debug.info(2, "Setting up paths...")
@ -400,7 +419,7 @@ def setup_paths():
debug.check(os.path.isdir(full_path),
"$OPENRAM_HOME/{0} does not exist: {1}".format(subdir, full_path))
if "__pycache__" not in full_path:
sys.path.append("{0}".format(full_path))
sys.path.append("{0}".format(full_path))
if not OPTS.openram_temp.endswith('/'):
OPTS.openram_temp += "/"
@ -413,9 +432,9 @@ def is_exe(fpath):
def find_exe(check_exe):
"""
"""
Check if the binary exists in any path dir
and return the full path.
and return the full path.
"""
# Check if the preferred spice option exists in the path
for path in os.environ["PATH"].split(os.pathsep):
@ -437,7 +456,7 @@ def init_paths():
except OSError as e:
if e.errno == 17: # errno.EEXIST
os.chmod(OPTS.openram_temp, 0o750)
# Don't delete the output dir, it may have other files!
# make the directory if it doesn't exist
try:
@ -448,7 +467,7 @@ def init_paths():
except:
debug.error("Unable to make output directory.", -1)
def set_default_corner():
""" Set the default corner. """
@ -459,13 +478,13 @@ def set_default_corner():
OPTS.process_corners = ["TT"]
else:
OPTS.process_corners = tech.spice["fet_models"].keys()
if (OPTS.supply_voltages == ""):
if OPTS.nominal_corner_only:
OPTS.supply_voltages = [tech.spice["supply_voltages"][1]]
else:
OPTS.supply_voltages = tech.spice["supply_voltages"]
if (OPTS.temperatures == ""):
if OPTS.nominal_corner_only:
OPTS.temperatures = [tech.spice["temperatures"][1]]
@ -479,8 +498,8 @@ def set_default_corner():
# Load scales are fanout multiples of the default spice input slew
if (OPTS.slew_scales == ""):
OPTS.slew_scales = [0.25, 1, 8]
def import_tech():
""" Dynamically adds the tech directory to the path and imports it. """
global OPTS
@ -501,7 +520,7 @@ def import_tech():
sys.path.append(tech_path)
debug.info(1, "Adding technology path: {}".format(tech_path))
# Import the tech
# Import the tech
try:
tech_mod = __import__(OPTS.tech_name)
except ImportError:
@ -526,7 +545,7 @@ def import_tech():
def print_time(name, now_time, last_time=None, indentation=2):
""" Print a statement about the time delta. """
global OPTS
# Don't print during testing
if not OPTS.is_unit_test or OPTS.debug_level > 0:
if last_time:
@ -537,12 +556,12 @@ def print_time(name, now_time, last_time=None, indentation=2):
def report_status():
"""
"""
Check for valid arguments and report the
info about the SRAM being generated
info about the SRAM being generated
"""
global OPTS
# Check if all arguments are integers for bits, size, banks
if type(OPTS.word_size) != int:
debug.error("{0} is not an integer in config file.".format(OPTS.word_size))
@ -550,7 +569,7 @@ def report_status():
debug.error("{0} is not an integer in config file.".format(OPTS.sram_size))
if type(OPTS.write_size) is not int and OPTS.write_size is not None:
debug.error("{0} is not an integer in config file.".format(OPTS.write_size))
# If a write mask is specified by the user, the mask write size should be the same as
# the word size so that an entire word is written at once.
if OPTS.write_size is not None:
@ -582,10 +601,10 @@ def report_status():
if OPTS.netlist_only:
debug.print_raw("Netlist only mode (no physical design is being done, netlist_only=False to disable).")
if not OPTS.route_supplies:
debug.print_raw("Design supply routing skipped. Supplies will have multiple must-connect pins. (route_supplies=True to enable supply routing).")
if not OPTS.inline_lvsdrc:
debug.print_raw("DRC/LVS/PEX is only run on the top-level design to save run-time (inline_lvsdrc=True to do inline checking).")

View File

@ -18,18 +18,18 @@ class and2_dec(design.design):
This is an AND with configurable drive strength.
"""
def __init__(self, name, size=1, height=None, add_wells=True):
design.design.__init__(self, name)
debug.info(1, "Creating and2_dec {}".format(name))
self.add_comment("size: {}".format(size))
self.size = size
self.height = height
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_pins()
self.create_modules()
@ -38,14 +38,14 @@ class and2_dec(design.design):
def create_modules(self):
self.nand = factory.create(module_type="nand2_dec",
height=self.height)
self.inv = factory.create(module_type="inv_dec",
height=self.height,
size=self.size)
self.add_mod(self.nand)
self.add_mod(self.inv)
def create_layout(self):
if "li" in layer:
@ -54,14 +54,14 @@ class and2_dec(design.design):
self.route_layer = "m1"
self.width = self.nand.width + self.inv.width
self.height = self.nand.height
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.route_supply_rails()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
self.add_pin("A", "INPUT")
self.add_pin("B", "INPUT")
@ -73,7 +73,7 @@ class and2_dec(design.design):
self.nand_inst = self.add_inst(name="pand2_dec_nand",
mod=self.nand)
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
self.inv_inst = self.add_inst(name="pand2_dec_inv",
mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
@ -100,7 +100,7 @@ class and2_dec(design.design):
layer=self.route_layer,
offset=vector(0.5 * self.width, self.height),
width=self.width)
def add_wires(self):
# nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z")
@ -111,7 +111,7 @@ class and2_dec(design.design):
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path(self.route_layer,
[z1_pin.center(), mid1_point, a2_pin.center()])
def add_layout_pins(self):
pin = self.inv_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
@ -127,7 +127,7 @@ class and2_dec(design.design):
offset=pin.center(),
width=pin.width(),
height=pin.height())
def get_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A or B -> Z path"""
stage_effort_list = []
@ -135,13 +135,13 @@ class and2_dec(design.design):
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
stage_effort_list.append(stage1)
last_stage_is_rise = stage1.is_rise
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
stage_effort_list.append(stage2)
return stage_effort_list
def get_cin(self):
"""Return the relative input capacitance of a single input"""
return self.nand.get_cin()

View File

@ -23,7 +23,7 @@ class and3_dec(design.design):
self.add_comment("size: {}".format(size))
self.size = size
self.height = height
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -52,14 +52,14 @@ class and3_dec(design.design):
self.width = self.nand.width + self.inv.width
self.height = self.nand.height
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.route_supply_rails()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
self.add_pin("A", "INPUT")
self.add_pin("B", "INPUT")
@ -72,7 +72,7 @@ class and3_dec(design.design):
self.nand_inst = self.add_inst(name="pand3_dec_nand",
mod=self.nand)
self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"])
self.inv_inst = self.add_inst(name="pand3_dec_inv",
mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
@ -99,7 +99,7 @@ class and3_dec(design.design):
layer=self.route_layer,
offset=vector(0.5 * self.width, self.height),
width=self.width)
def add_wires(self):
# nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z")
@ -136,7 +136,7 @@ class and3_dec(design.design):
slew=nand_delay.slew,
load=load)
return nand_delay + inv_delay
def get_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A or B -> Z path"""
stage_effort_list = []
@ -144,13 +144,13 @@ class and3_dec(design.design):
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
stage_effort_list.append(stage1)
last_stage_is_rise = stage1.is_rise
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
stage_effort_list.append(stage2)
return stage_effort_list
def get_cin(self):
"""Return the relative input capacitance of a single input"""
return self.nand.get_cin()

View File

@ -18,14 +18,14 @@ class and4_dec(design.design):
This is an AND with configurable drive strength.
"""
def __init__(self, name, size=1, height=None, add_wells=True):
design.design.__init__(self, name)
debug.info(1, "Creating and4_dec {}".format(name))
self.add_comment("size: {}".format(size))
self.size = size
self.height = height
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -61,7 +61,7 @@ class and4_dec(design.design):
self.route_supply_rails()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
self.add_pin("A", "INPUT")
self.add_pin("B", "INPUT")
@ -75,7 +75,7 @@ class and4_dec(design.design):
self.nand_inst = self.add_inst(name="pand4_dec_nand",
mod=self.nand)
self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"])
self.inv_inst = self.add_inst(name="pand4_dec_inv",
mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
@ -102,7 +102,7 @@ class and4_dec(design.design):
layer=self.route_layer,
offset=vector(0.5 * self.width, self.height),
width=self.width)
def add_wires(self):
# nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z")
@ -139,7 +139,7 @@ class and4_dec(design.design):
slew=nand_delay.slew,
load=load)
return nand_delay + inv_delay
def get_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A or B -> Z path"""
stage_effort_list = []
@ -147,13 +147,13 @@ class and4_dec(design.design):
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
stage_effort_list.append(stage1)
last_stage_is_rise = stage1.is_rise
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
stage_effort_list.append(stage2)
return stage_effort_list
def get_cin(self):
"""Return the relative input capacitance of a single input"""
return self.nand.get_cin()

View File

@ -31,16 +31,16 @@ class bank(design.design):
self.num_wmasks = int(ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
if not self.num_spare_cols:
self.num_spare_cols = 0
if name == "":
name = "bank_{0}_{1}".format(self.word_size, self.num_words)
super().__init__(name)
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,
self.num_words))
# The local control signals are gated when we have bank select logic,
# so this prefix will be added to all of the input signals to create
# the internal gated signals.
@ -62,12 +62,12 @@ class bank(design.design):
self.add_modules()
self.add_pins() # Must create the replica bitcell array first
self.create_instances()
def create_layout(self):
self.place_instances()
self.setup_routing_constraints()
self.route_layout()
# Can remove the following, but it helps for debug!
# self.add_lvs_correspondence_points()
@ -110,13 +110,13 @@ class bank(design.design):
self.add_pin("wl_en{0}".format(port), "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def route_layout(self):
""" Create routing amoung the modules """
self.route_central_bus()
self.route_unused_wordlines()
for port in self.all_ports:
self.route_bitlines(port)
self.route_rbl(port)
@ -125,7 +125,7 @@ class bank(design.design):
self.route_control_lines(port)
if self.num_banks > 1:
self.route_bank_select(port)
self.route_supplies()
def route_rbl(self, port):
@ -149,7 +149,7 @@ class bank(design.design):
layer="m3",
start=left_right_offset,
end=pin_offset)
def route_bitlines(self, port):
""" Route the bitlines depending on the port type rw, w, or r. """
@ -158,7 +158,7 @@ class bank(design.design):
if port in self.read_ports:
self.route_port_data_out(port)
self.route_port_data_to_bitcell_array(port)
def create_instances(self):
""" Create the instances of the netlist. """
@ -185,7 +185,7 @@ class bank(design.design):
# The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis.
self.bitcell_array_top = self.bitcell_array.height
self.bitcell_array_right = self.bitcell_array.width
# These are the offsets of the main array (excluding dummy and replica rows/cols)
self.main_bitcell_array_top = self.bitcell_array.get_main_array_top()
# Just past the dummy column
@ -213,7 +213,7 @@ class bank(design.design):
"""
port = 0
# UPPER RIGHT QUADRANT
# Bitcell array is placed at (0,0)
self.bitcell_array_offset = vector(0, 0)
@ -258,14 +258,14 @@ class bank(design.design):
"""
port=1
# LOWER LEFT QUADRANT
# Bitcell array is placed at (0,0)
# UPPER LEFT QUADRANT
# Above the bitcell array
self.port_data_offsets[port] = vector(0, self.bitcell_array_top)
# LOWER RIGHT QUADRANT
# To the right of the bitcell array
x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap
@ -294,12 +294,12 @@ class bank(design.design):
else:
y_offset = self.port_address_offsets[port].y
self.bank_select_offsets[port] = vector(x_offset, y_offset)
def place_instances(self):
""" Place the instances. """
self.compute_instance_offsets()
self.place_bitcell_array(self.bitcell_array_offset)
self.place_port_data(self.port_data_offsets)
@ -308,7 +308,7 @@ class bank(design.design):
self.place_column_decoder(self.column_decoder_offsets)
self.place_bank_select(self.bank_select_offsets)
def compute_sizes(self):
""" Computes the required sizes to create the bank """
@ -351,7 +351,7 @@ class bank(design.design):
self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]])
else:
self.control_signals.append(self.input_control_signals[port])
# The central bus is the column address (one hot) and row address (binary)
if self.col_addr_size>0:
@ -379,7 +379,7 @@ class bank(design.design):
local_array_size = OPTS.local_array_size
except AttributeError:
local_array_size = 0
if local_array_size > 0:
# Find the even multiple that satisfies the fanout with equal sized local arrays
total_cols = self.num_cols + self.num_spare_cols
@ -406,11 +406,11 @@ class bank(design.design):
bit_offsets=self.bit_offsets)
self.port_data.append(temp_pre)
self.add_mod(self.port_data[port])
if(self.num_banks > 1):
self.bank_select = factory.create(module_type="bank_select")
self.add_mod(self.bank_select)
def create_bitcell_array(self):
""" Creating Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
@ -426,12 +426,12 @@ class bank(design.design):
temp.extend(self.bitcell_array.get_wordline_names())
if len(self.all_ports) > 1:
temp.append("rbl_wl1")
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def place_bitcell_array(self, offset):
""" Placing Bitcell Array """
self.bitcell_array_inst.place(offset)
@ -470,7 +470,7 @@ class bank(design.design):
def place_port_data(self, offsets):
""" Placing Port Data """
for port in self.all_ports:
# Top one is unflipped, bottom is flipped along X direction
if port % 2 == 1:
@ -481,7 +481,7 @@ class bank(design.design):
def create_port_address(self):
""" Create the hierarchical row decoder """
self.port_address_inst = [None] * len(self.all_ports)
for port in self.all_ports:
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
@ -496,32 +496,32 @@ class bank(design.design):
temp.append("rbl_wl{}".format(port))
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def place_port_address(self, offsets):
""" Place the hierarchical row decoder """
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.")
# The address and control bus will be in between decoder and the main memory array
# This bus will route address bits to the decoder input and column mux inputs.
# The wires are actually routed after we placed the stuff on both sides.
# The predecoder is below the x-axis and the main decoder is above the x-axis
# The address flop and decoder are aligned in the x coord.
for port in self.all_ports:
if port % 2:
mirror = "MY"
else:
mirror = "R0"
self.port_address_inst[port].place(offset=offsets[port], mirror=mirror)
def create_column_decoder(self):
"""
Create a 2:4 or 3:8 column address decoder.
"""
self.dff =factory.create(module_type="dff")
if self.col_addr_size == 0:
return
elif self.col_addr_size == 1:
@ -541,7 +541,7 @@ class bank(design.design):
# No error checking before?
debug.error("Invalid column decoder?", -1)
self.add_mod(self.column_decoder)
self.column_decoder_inst = [None] * len(self.all_ports)
for port in self.all_ports:
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
@ -554,14 +554,14 @@ class bank(design.design):
temp.append("sel{0}_{1}".format(port, bit))
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def place_column_decoder(self, offsets):
"""
Place a 2:4 or 3:8 column address decoder.
"""
if self.col_addr_size == 0:
return
debug.check(len(offsets)>=len(self.all_ports),
"Insufficient offsets to place column decoder.")
@ -571,7 +571,7 @@ class bank(design.design):
else:
mirror = "R0"
self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
def create_bank_select(self):
""" Create the bank select logic. """
@ -582,7 +582,7 @@ class bank(design.design):
for port in self.all_ports:
self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port),
mod=self.bank_select)
temp = []
temp.extend(self.input_control_signals[port])
temp.append("bank_sel{}".format(port))
@ -601,7 +601,7 @@ class bank(design.design):
for port in self.all_ports:
self.bank_select_inst[port].place(offsets[port])
def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
# Copy only the power pins already on the power layer
@ -624,7 +624,7 @@ class bank(design.design):
def route_bank_select(self, port):
""" Route the bank select logic. """
if self.port_id[port] == "rw":
bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"]
@ -634,11 +634,11 @@ class bank(design.design):
else:
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
copy_control_signals = self.input_control_signals[port] + ["bank_sel{}".format(port)]
for signal in range(len(copy_control_signals)):
self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal])
for signal in range(len(gated_bank_sel_signals)):
# Connect the inverter output to the central bus
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
@ -651,7 +651,7 @@ class bank(design.design):
offset=out_pos)
self.add_via_center(layers=self.m2_stack,
offset=out_pos)
def setup_routing_constraints(self):
"""
After the modules are instantiated, find the dimensions for the
@ -660,7 +660,7 @@ class bank(design.design):
self.max_y_offset = max([x.uy() for x in self.insts]) + 3 * self.m1_width
self.min_y_offset = min([x.by() for x in self.insts])
self.max_x_offset = max([x.rx() for x in self.insts]) + 3 * self.m1_width
self.min_x_offset = min([x.lx() for x in self.insts])
@ -668,7 +668,7 @@ class bank(design.design):
ur = vector(self.max_x_offset, self.max_y_offset)
ll = vector(self.min_x_offset, self.min_y_offset)
self.core_bbox = [ll, ur]
self.height = ur.y - ll.y
self.width = ur.x - ll.x
@ -692,7 +692,7 @@ class bank(design.design):
vertical=True,
make_pins=(self.num_banks==1),
pitch=self.m3_pitch)
# Port 1
if len(self.all_ports)==2:
# The other control bus is routed up to two pitches above the bitcell array
@ -731,12 +731,12 @@ class bank(design.design):
inst1_br_name=inst1_br_name,
inst2_bl_name=inst2_bl_name,
inst2_br_name=inst2_br_name)
# Connect the replica bitlines
for (array_name, data_name) in zip(["rbl_bl_{0}_{0}".format(port), "rbl_br_{0}_{0}".format(port)], ["rbl_bl", "rbl_br"]):
self.connect_bitline(inst1, inst2, array_name, data_name)
def route_port_data_out(self, port):
""" Add pins for the port data out """
@ -747,7 +747,7 @@ class bank(design.design):
offset=data_pin.center(),
height=data_pin.height(),
width=data_pin.width())
def route_port_address_in(self, port):
""" Routes the row decoder inputs and supplies """
@ -771,19 +771,19 @@ class bank(design.design):
wmask_name = "bank_wmask_{}".format(row)
bank_wmask_name = "bank_wmask{0}_{1}".format(port, row)
self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name)
for col in range(self.num_spare_cols):
sparecol_name = "bank_spare_wen{}".format(col)
bank_sparecol_name = "bank_spare_wen{0}_{1}".format(port, col)
self.copy_layout_pin(self.port_data_inst[port], sparecol_name, bank_sparecol_name)
def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
"""
Route the bl and br of two modules using the channel router.
"""
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
@ -801,14 +801,14 @@ class bank(design.design):
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
def connect_bitline(self, inst1, inst2, inst1_name, inst2_name):
"""
Connect two pins of two modules.
This assumes that they have sufficient space to create a jog
in the middle between the two modules (if needed).
"""
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
@ -821,17 +821,17 @@ class bank(design.design):
bottom_pin = bottom_inst.get_pin(bottom_name)
top_pin = top_inst.get_pin(top_name)
debug.check(bottom_pin.layer == top_pin.layer, "Pin layers do not match.")
bottom_loc = bottom_pin.uc()
top_loc = top_pin.bc()
yoffset = 0.5 * (top_loc.y + bottom_loc.y)
self.add_path(top_pin.layer,
[bottom_loc,
vector(bottom_loc.x, yoffset),
vector(top_loc.x, yoffset),
top_loc])
def connect_bitlines(self, inst1, inst2,
inst1_bl_name, inst1_br_name,
inst2_bl_name, inst2_br_name):
@ -847,12 +847,12 @@ class bank(design.design):
""" Connect Wordline driver to bitcell array wordline """
self.route_port_address_in(port)
if port % 2:
self.route_port_address_out(port, "right")
else:
self.route_port_address_out(port, "left")
def route_port_address_out(self, port, side="left"):
""" Connecting Wordline driver output to Bitcell WL connection """
@ -866,7 +866,7 @@ class bank(design.design):
else:
driver_wl_pos = driver_wl_pin.lc()
bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name)
if side == "left":
bitcell_wl_pos = bitcell_wl_pin.lc()
port_address_pos = self.port_address_inst[port].rx()
@ -875,7 +875,7 @@ class bank(design.design):
bitcell_wl_pos = bitcell_wl_pin.rc()
port_address_pos = self.port_address_inst[port].lx()
bitcell_array_pos = self.bitcell_array_inst.rx()
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0)
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
if driver_wl_pin.layer != bitcell_wl_pin.layer:
@ -886,7 +886,7 @@ class bank(design.design):
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
else:
self.add_path(bitcell_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_port_address_right(self, port):
""" Connecting Wordline driver output to Bitcell WL connection """
@ -906,7 +906,7 @@ class bank(design.design):
to_layer=bitcell_wl_pin.layer,
offset=mid2)
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
def route_column_address_lines(self, port):
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
@ -914,15 +914,15 @@ class bank(design.design):
stack = getattr(self, layer_props.bank.stack)
pitch = getattr(self, layer_props.bank.pitch)
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
@ -942,7 +942,7 @@ class bank(design.design):
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names]
route_map = list(zip(decode_pins, column_mux_pins))
self.create_vertical_channel_route(route_map,
offset,
@ -964,7 +964,7 @@ class bank(design.design):
# self.add_label(text=wl_name,
# layer="m1",
# offset=wl_pin.center())
# # Add the bitline names
# for i in range(self.num_cols):
# bl_name = "bl_{}".format(i)
@ -1025,10 +1025,10 @@ class bank(design.design):
# Add a path to connect to the array
self.add_path(pin_layer, [left_loc, left_pin_loc])
self.add_path(pin_layer, [right_loc, right_pin_loc])
def route_control_lines(self, port):
""" Route the control lines of the entire bank """
# Make a list of tuples that we will connect.
# From control signal to the module pin
# Connection from the central bus to the main control block crosses
@ -1040,7 +1040,7 @@ class bank(design.design):
if port in self.write_ports:
connection.append((self.prefix + "w_en{}".format(port),
self.port_data_inst[port].get_pin("w_en")))
if port in self.read_ports:
connection.append((self.prefix + "s_en{}".format(port),
self.port_data_inst[port].get_pin("s_en")))
@ -1057,7 +1057,7 @@ class bank(design.design):
self.add_via_stack_center(from_layer=pin.layer,
to_layer="m2",
offset=control_pos)
# clk to wordline_driver
control_signal = self.prefix + "wl_en{}".format(port)
if port % 2:
@ -1081,7 +1081,7 @@ class bank(design.design):
for port in self.read_ports:
if self.port_data[port]:
self.port_data[port].graph_exclude_precharge()
def get_cell_name(self, inst_name, row, col):
"""
Gets the spice name of the target bitcell.
@ -1097,8 +1097,8 @@ class bank(design.design):
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def clear_exclude_bits(self):
"""
"""
Clears the bit exclusions
"""
self.bitcell_array.clear_exclude_bits()

View File

@ -27,7 +27,7 @@ class bank_select(design.design):
super().__init__(name)
self.port = port
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -36,7 +36,7 @@ class bank_select(design.design):
self.add_pins()
self.add_modules()
self.create_instances()
def create_layout(self):
self.calculate_module_offsets()
self.place_instances()
@ -44,12 +44,12 @@ 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
if self.port == "rw":
self.num_control_lines = 4
@ -86,7 +86,7 @@ class bank_select(design.design):
self.nor2 = factory.create(module_type="pnor2", height=height)
self.add_mod(self.nor2)
self.inv4x_nor = factory.create(module_type="pinv", height=height, size=4)
self.add_mod(self.inv4x_nor)
@ -94,15 +94,15 @@ class bank_select(design.design):
self.add_mod(self.nand2)
def calculate_module_offsets(self):
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",
mod=self.inv_sel)
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
@ -119,7 +119,7 @@ class bank_select(design.design):
# These require OR (nor2+inv) gates since they are active low.
# (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.connect_inst([input_name,
@ -127,7 +127,7 @@ class bank_select(design.design):
gated_name + "_temp_bar",
"vdd",
"gnd"])
# They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x_nor))
@ -135,7 +135,7 @@ class bank_select(design.design):
gated_name,
"vdd",
"gnd"])
# the rest are AND (nand2+inv) gates
else:
self.logic_inst.append(self.add_inst(name=name_nand,
@ -155,7 +155,7 @@ class bank_select(design.design):
"gnd"])
def place_instances(self):
# bank select inverter
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
@ -166,27 +166,27 @@ class bank_select(design.design):
logic_inst = self.logic_inst[i]
inv_inst = self.inv_inst[i]
input_name = self.input_control_signals[i]
if i == 0:
y_offset = 0
else:
y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1)
if i % 2:
y_offset += self.inv4x.height
mirror = "MX"
else:
mirror = ""
# These require OR (nor2+inv) gates since they are active low.
# (writes occur on clk low)
if input_name in ("clk_buf"):
logic_inst.place(offset=[self.xoffset_nor, y_offset],
mirror=mirror)
# the rest are AND (nand2+inv) gates
else:
logic_inst.place(offset=[self.xoffset_nand, y_offset],
@ -197,7 +197,7 @@ class bank_select(design.design):
mirror=mirror)
def route_instances(self):
# bank_sel is vertical wire
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")
xoffset_bank_sel = bank_sel_inv_pin.lx()
@ -227,19 +227,19 @@ class bank_select(design.design):
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]
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
out_pin = logic_inst.get_pin("Z")
out_pos = out_pin.center()
@ -248,7 +248,7 @@ class bank_select(design.design):
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_pin = logic_inst.get_pin("B")
logic_pos = logic_pin.center()
@ -304,7 +304,7 @@ class bank_select(design.design):
self.add_layout_pin_rect_center(text=n,
layer="m3",
offset=pin_pos)
# Add vdd/gnd supply rails
gnd_pin = self.inv_inst[num].get_pin("gnd")
left_gnd_pos = vector(0, gnd_pin.cy())
@ -312,7 +312,7 @@ class bank_select(design.design):
layer="m1",
start=left_gnd_pos,
end=gnd_pin.rc())
vdd_pin = self.inv_inst[num].get_pin("vdd")
left_vdd_pos = vector(0, vdd_pin.cy())
self.add_layout_pin_segment_center(text="vdd",

View File

@ -25,11 +25,11 @@ class bitcell_array(bitcell_base_array):
# This will create a default set of bitline/wordline names
self.create_all_bitline_names()
self.create_all_wordline_names()
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
# We don't offset this because we need to align
# the replica bitcell in the control logic
# self.offset_all_coordinates()

View File

@ -47,11 +47,11 @@ class bitcell_base_array(design.design):
"br_{0}_{1}".format(port, col)])
# Make a flat list too
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
def create_all_wordline_names(self, row_size=None):
if row_size == None:
row_size = self.row_size
for row in range(row_size):
for port in self.all_ports:
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
@ -69,7 +69,7 @@ class bitcell_base_array(design.design):
def get_bitcell_pins(self, row, col):
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
indexed by column and row, for instance use in bitcell_array
"""
bitcell_pins = []
for port in self.all_ports:
@ -81,7 +81,7 @@ class bitcell_base_array(design.design):
return bitcell_pins
def get_rbl_wordline_names(self, port=None):
"""
"""
Return the WL for the given RBL port.
"""
if port == None:
@ -102,7 +102,7 @@ class bitcell_base_array(design.design):
return self.all_bitline_names
else:
return self.bitline_names[port]
def get_all_bitline_names(self, port=None):
""" Return ALL the bitline names (including rbl) """
temp = []
@ -121,7 +121,7 @@ class bitcell_base_array(design.design):
return self.all_wordline_names
else:
return self.wordline_names[port]
def get_all_wordline_names(self, port=None):
""" Return all the wordline names """
temp = []
@ -133,7 +133,7 @@ class bitcell_base_array(design.design):
if len(self.all_ports) > 1:
temp.extend(self.get_rbl_wordline_names(1))
return temp
def add_layout_pins(self):
""" Add the layout pins """
bitline_names = self.cell.get_all_bitline_names()
@ -161,7 +161,7 @@ class bitcell_base_array(design.design):
offset=wl_pin.ll().scale(0, 1),
width=self.width,
height=wl_pin.height())
# Copy a vdd/gnd layout pin from every cell
for row in range(self.row_size):
for col in range(self.column_size):

View File

@ -34,7 +34,7 @@ class col_cap_array(bitcell_base_array):
if not end_caps_enabled:
self.create_all_wordline_names()
self.create_all_bitline_names()
self.add_modules()
self.add_pins()
self.create_instances()

View File

@ -46,7 +46,7 @@ class column_mux_array(design.design):
# self.sel_layer = "m1"
# self.sel_pitch = self.m2_pitch
# self.bitline_layer = "m2"
if preferred_directions[self.sel_layer] == "V":
self.via_directions = ("H", "H")
else:
@ -125,7 +125,7 @@ class column_mux_array(design.design):
# Default to single spaced columns
if not self.offsets:
self.offsets = [n * self.mux.width for n in range(self.columns)]
# For every column, add a pass gate
for col_num, xoffset in enumerate(self.offsets[0:self.columns]):
if cell_props.bitcell.mirror.y and (col_num + self.column_offset) % 2:
@ -209,7 +209,7 @@ class column_mux_array(design.design):
br_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("br_out").bc()
bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.sel_pitch)
br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.sel_pitch)
self.add_path(self.sel_layer, [bl_out_offset_begin, bl_out_offset_end])
self.add_path(self.sel_layer, [br_out_offset_begin, br_out_offset_end])

View File

@ -28,7 +28,7 @@ class control_logic(design.design):
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.sram=sram
self.num_rows = num_rows
self.words_per_row = words_per_row
@ -42,21 +42,21 @@ class control_logic(design.design):
self.num_cols = word_size * words_per_row + self.num_spare_cols
self.num_words = num_rows * words_per_row
self.enable_delay_chain_resizing = False
self.inv_parasitic_delay = logical_effort.logical_effort.pinv
# Determines how much larger the sen delay should be. Accounts for possible error in model.
# FIXME: This should be made a parameter
self.wl_timing_tolerance = 1
self.wl_stage_efforts = None
self.sen_stage_efforts = None
if self.port_type == "rw":
self.num_control_signals = 2
else:
self.num_control_signals = 1
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -66,7 +66,7 @@ class control_logic(design.design):
self.add_pins()
self.add_modules()
self.create_instances()
def create_layout(self):
""" Create layout and route between modules """
self.place_instances()
@ -84,16 +84,16 @@ class control_logic(design.design):
def add_modules(self):
""" Add all the required modules """
self.dff = factory.create(module_type="dff_buf")
dff_height = self.dff.height
self.ctrl_dff_array = factory.create(module_type="dff_buf_array",
rows=self.num_control_signals,
columns=1)
self.add_mod(self.ctrl_dff_array)
self.and2 = factory.create(module_type="pand2",
size=12,
height=dff_height)
@ -103,7 +103,7 @@ class control_logic(design.design):
size=self.num_cols,
height=dff_height)
self.add_mod(self.rbl_driver)
# clk_buf drives a flop for every address
addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2)
# plus data flops and control flops
@ -114,13 +114,13 @@ class control_logic(design.design):
self.clk_buf_driver = factory.create(module_type="pdriver",
fanout=clock_fanout,
height=dff_height)
self.add_mod(self.clk_buf_driver)
# 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)
# wl_en drives every row in the bank
self.wl_en_driver = factory.create(module_type="pdriver",
fanout=self.num_rows,
@ -144,7 +144,7 @@ class control_logic(design.design):
size=1,
height=dff_height)
self.add_mod(self.inv)
# p_en_bar drives every column in the bitcell array
# but it is sized the same as the wl_en driver with
# prepended 3 inverter stages to guarantee it is slower and odd polarity
@ -183,14 +183,14 @@ class control_logic(design.design):
# 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_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
@ -201,7 +201,7 @@ class control_logic(design.design):
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
@ -218,7 +218,7 @@ class control_logic(design.design):
stages_close = True
safe_fanout_rise = fanout_rise
safe_fanout_fall = fanout_fall
if stages_fall == stages_rise:
break
elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise):
@ -232,14 +232,14 @@ class control_logic(design.design):
fanout_fall+=1
else:
fanout_rise+=1
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)]
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
@ -249,7 +249,7 @@ class control_logic(design.design):
delay_per_stage = fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
return delay_stages
def setup_signal_busses(self):
""" Setup bus names, determine the size of the busses etc """
@ -265,7 +265,7 @@ class control_logic(design.design):
self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"]
else:
self.dff_output_list = ["cs_bar", "cs"]
# list of output control signals (for making a vertical bus)
if self.port_type == "rw":
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "we_bar", "clk_buf", "cs"]
@ -275,7 +275,7 @@ class control_logic(design.design):
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
# Outputs to the bank
if self.port_type == "rw":
self.output_list = ["s_en", "w_en"]
@ -286,14 +286,14 @@ class control_logic(design.design):
self.output_list.append("p_en_bar")
self.output_list.append("wl_en")
self.output_list.append("clk_buf")
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)
self.input_bus = self.create_vertical_bus("m2",
offset,
self.internal_bus_list,
@ -325,7 +325,7 @@ class control_logic(design.design):
# All of the control logic is placed to the right of the DFFs and bus
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width
row = 0
# Add the logic on the right of the bus
self.place_clk_buf_row(row)
@ -396,7 +396,7 @@ class control_logic(design.design):
# Add to the right of the control rows and routing channel
offset = vector(self.delay_chain.width, y_off)
self.delay_inst.place(offset, mirror="MY")
def route_delay(self):
out_pos = self.delay_inst.get_pin("out").bc()
@ -408,21 +408,21 @@ class control_logic(design.design):
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"])
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)
self.row_end_inst.append(self.clk_buf_inst)
def route_clk_buf(self):
@ -443,17 +443,17 @@ class control_logic(design.design):
self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
mod=self.inv)
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(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"])
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)
self.row_end_inst.append(self.gated_clk_bar_inst)
def route_gated_clk_bar(self):
@ -468,7 +468,7 @@ class control_logic(design.design):
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer=in_pin.layer,
offset=in_pos)
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["B"], ["cs"])
@ -495,9 +495,9 @@ class control_logic(design.design):
x_offset = self.control_x_offset
x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row)
self.row_end_inst.append(self.gated_clk_buf_inst)
def route_gated_clk_buf(self):
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
self.connect_vertical_bus(clkbuf_map,
@ -514,7 +514,7 @@ class control_logic(design.design):
self.add_via_stack_center(from_layer=z_pin.layer,
to_layer="m2",
offset=z_pin.center())
def create_wlen_row(self):
# input pre_p_en, output: wl_en
self.wl_en_inst=self.add_inst(name="buf_wl_en",
@ -531,7 +531,7 @@ class control_logic(design.design):
def route_wlen(self):
wlen_map = zip(["A"], ["gated_clk_bar"])
self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.input_bus)
self.connect_output(self.wl_en_inst, "Z", "wl_en")
def create_pen_row(self):
@ -544,7 +544,7 @@ class control_logic(design.design):
self.p_en_bar_driver_inst=self.add_inst(name="buf_p_en_bar",
mod=self.p_en_bar_driver)
self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"])
def place_pen_row(self, row):
x_offset = self.control_x_offset
@ -568,7 +568,7 @@ class control_logic(design.design):
offset=in_pin.center())
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
def create_sen_row(self):
""" Create the sense enable buffer. """
if self.port_type=="rw":
@ -582,24 +582,24 @@ class control_logic(design.design):
# we also must wait until the bitline has been discharged enough for proper sensing
# hence we use rbl_bl_delay as well.
self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"])
def place_sen_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.s_en_gate_inst, x_offset, row)
self.row_end_inst.append(self.s_en_gate_inst)
def route_sen(self):
if self.port_type=="rw":
input_name = "we_bar"
else:
input_name = "cs"
sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name])
self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.input_bus)
self.connect_output(self.s_en_gate_inst, "Z", "s_en")
def create_rbl_delay_row(self):
@ -612,15 +612,15 @@ class control_logic(design.design):
x_offset = self.control_x_offset
x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row)
self.row_end_inst.append(self.rbl_bl_delay_inv_inst)
def route_rbl_delay(self):
# Connect from delay line
# Connect to rail
self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "rbl_bl_delay_bar")
rbl_map = zip(["A"], ["rbl_bl_delay"])
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.input_bus)
@ -638,26 +638,26 @@ class control_logic(design.design):
mod=self.wen_and)
# Only drive the writes in the second half of the clock cycle during a write operation.
self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"])
def place_wen_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.w_en_gate_inst, x_offset, row)
self.row_end_inst.append(self.w_en_gate_inst)
def route_wen(self):
if self.port_type == "rw":
input_name = "we"
else:
# No we for write-only reports, so use cs
input_name = "cs"
wen_map = zip(["A", "B", "C"], [input_name, "rbl_bl_delay_bar", "gated_clk_bar"])
self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.input_bus)
self.connect_output(self.w_en_gate_inst, "Z", "w_en")
def create_dffs(self):
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array)
@ -669,7 +669,7 @@ class control_logic(design.design):
def place_dffs(self):
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"])
@ -678,7 +678,7 @@ class control_logic(design.design):
else:
dff_out_map = zip(["dout_bar_0"], ["cs"])
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.input_bus, self.m2_stack[::-1])
# Connect the clock rail to the other clock rail
# by routing in the supply rail track to avoid channel conflicts
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
@ -691,7 +691,7 @@ class control_logic(design.design):
self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb")
if (self.port_type == "rw"):
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
def get_offset(self, row):
""" Compute the y-offset and mirroring """
y_off = row * self.and2.height
@ -702,14 +702,14 @@ class control_logic(design.design):
mirror="R0"
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)
out_pos = out_pin.center()
right_pos = out_pos + vector(self.width - out_pin.cx(), 0)
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2",
offset=out_pos)
@ -722,7 +722,7 @@ class control_logic(design.design):
""" Add vdd and gnd to the instance cells """
supply_layer = self.dff.get_pin("vdd").layer
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")
@ -740,13 +740,13 @@ class control_logic(design.design):
pin_loc = vector(max_row_x_loc, pin.rc().y)
self.add_power_pin("gnd", pin_loc, start_layer=pin.layer)
self.add_path(supply_layer, [row_loc, pin_loc])
self.copy_layout_pin(self.delay_inst, "gnd")
self.copy_layout_pin(self.delay_inst, "vdd")
self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
@ -772,10 +772,10 @@ class control_logic(design.design):
offset=pin.ll(),
height=pin.height(),
width=pin.width())
def graph_exclude_dffs(self):
"""Exclude dffs from graph as they do not represent critical path"""
self.graph_inst_exclude.add(self.ctrl_dff_inst)
if self.port_type=="rw" or self.port_type=="w":
self.graph_inst_exclude.add(self.w_en_gate_inst)
@ -799,4 +799,4 @@ class control_logic(design.design):
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2",
offset=out_pos)

View File

@ -1,271 +0,0 @@
# 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 design
import debug
from tech import drc
from sram_factory import factory
from vector import vector
from globals import OPTS
class custom_cell(design.design):
"""
Array of tristate drivers to write to the bitlines through the column mux.
Dynamically generated write driver array of all bitlines.
"""
def __init__(self, name, pins, mod):
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.columns = columns
self.word_size = word_size
self.write_size = write_size
self.column_offset = column_offset
self.words_per_row = int(columns / word_size)
if not num_spare_cols:
self.num_spare_cols = 0
else:
self.num_spare_cols = num_spare_cols
if self.write_size:
self.num_wmasks = int(self.word_size / self.write_size)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self):
bl_name = "bl"
return bl_name
def get_br_name(self):
br_name = "br"
return br_name
@property
def data_name(self):
return "data"
@property
def en_name(self):
return "en"
def create_netlist(self):
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
self.single_col_width = self.bitcell.width
else:
self.width = (self.columns + self.num_spare_cols) * self.driver.width
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()
self.DRC_LVS()
def add_pins(self):
for i in range(self.word_size + self.num_spare_cols):
self.add_pin(self.data_name + "_{0}".format(i), "INPUT")
for i in range(self.word_size + self.num_spare_cols):
self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT")
self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT")
if self.write_size:
for i in range(self.num_wmasks + self.num_spare_cols):
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
elif self.num_spare_cols and not self.write_size:
for i in range(self.num_spare_cols + 1):
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
else:
self.add_pin(self.en_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.driver = factory.create(module_type="write_driver")
self.add_mod(self.driver)
# This is just used for measurements,
# so don't add the module
self.bitcell = factory.create(module_type="bitcell")
def create_write_array(self):
self.driver_insts = {}
w = 0
windex=0
for i in range(0, self.columns, self.words_per_row):
name = "write_driver{}".format(i)
index = int(i / self.words_per_row)
self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver)
if self.write_size:
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name + "_{0}".format(windex), "vdd", "gnd"])
w+=1
# when w equals write size, the next en pin can be connected since we are now at the next wmask bit
if w == self.write_size:
w = 0
windex+=1
elif self.num_spare_cols and not self.write_size:
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name + "_{0}".format(0), "vdd", "gnd"])
else:
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name, "vdd", "gnd"])
for i in range(self.num_spare_cols):
index = self.word_size + i
if self.write_size:
offset = self.num_wmasks
else:
offset = 1
name = "write_driver{}".format(self.columns + i)
self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver)
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name + "_{0}".format(i + offset), "vdd", "gnd"])
def place_write_array(self):
from tech import cell_properties
if self.bitcell.width > self.driver.width:
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)
xoffset = i * self.driver_spacing
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
mirror = "MY"
xoffset = xoffset + self.driver.width
else:
mirror = ""
base = vector(xoffset, 0)
self.driver_insts[index].place(offset=base, mirror=mirror)
# place spare write drivers (if spare columns are specified)
for i in range(self.num_spare_cols):
index = self.word_size + i
xoffset = (self.columns + i) * self.driver_spacing
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
mirror = "MY"
xoffset = xoffset + self.driver.width
else:
mirror = ""
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=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=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=br_pin.layer,
offset=br_pin.ll(),
width=br_pin.width(),
height=br_pin.height())
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(),
directions=("V", "V"),
start_layer=pin.layer)
if self.write_size:
for bit in range(self.num_wmasks):
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)
if (self.words_per_row == 1):
en_gap = self.driver_spacing - en_pin.width()
else:
en_gap = self.driver_spacing
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,
height=en_pin.height())
for i in range(self.num_spare_cols):
inst = self.driver_insts[self.word_size + i]
en_pin = inst.get_pin(inst.mod.en_name)
self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks),
layer="m1",
offset=en_pin.lr() + vector(-drc("minwidth_m1"),0))
elif self.num_spare_cols and not self.write_size:
# shorten enable rail to accomodate those for spare write drivers
inst = self.driver_insts[0]
en_pin = inst.get_pin(inst.mod.en_name)
self.add_layout_pin(text=self.en_name + "_{0}".format(0),
layer="m1",
offset=en_pin.ll(),
width=self.width_regular_cols - self.words_per_row * en_pin.width())
# individual enables for every spare write driver
for i in range(self.num_spare_cols):
inst = self.driver_insts[self.word_size + i]
en_pin = inst.get_pin(inst.mod.en_name)
self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1),
layer="m1",
offset=en_pin.lr() + vector(-drc("minwidth_m1"),0))
else:
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),
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.
return self.driver.get_w_en_cin() * len(self.driver_insts)

View File

@ -24,14 +24,14 @@ class delay_chain(design.design):
super().__init__(name)
debug.info(1, "creating delay chain {0}".format(str(fanout_list)))
self.add_comment("fanouts: {0}".format(str(fanout_list)))
# 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.")
# number of inverters including any fanout loads.
self.fanout_list = fanout_list
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -40,7 +40,7 @@ class delay_chain(design.design):
self.add_modules()
self.add_pins()
self.create_inverters()
def create_layout(self):
# Each stage is a a row
self.height = len(self.fanout_list) * self.inv.height
@ -53,7 +53,7 @@ class delay_chain(design.design):
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
""" Add the pins of the delay chain"""
self.add_pin("in", "INPUT")
@ -86,7 +86,7 @@ class delay_chain(design.design):
else:
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):
@ -95,7 +95,7 @@ class delay_chain(design.design):
# Fanout stage is always driven by driver and output is disconnected
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)
@ -108,19 +108,19 @@ class delay_chain(design.design):
else:
inv_mirror = "R0"
inv_offset = vector(0, stage_num * self.inv.height)
# Add the inverter
cur_driver=self.driver_inst_list[stage_num]
cur_driver.place(offset=inv_offset,
mirror=inv_mirror)
# 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)
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. """
pin1_pos = pin1.center()
@ -131,7 +131,7 @@ class delay_chain(design.design):
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])
def route_inverters(self):
""" Add metal routing for each of the fanout stages """
@ -180,12 +180,12 @@ class delay_chain(design.design):
self.add_power_pin(pin_name,
pin.rc() - vector(self.m1_pitch, 0),
start_layer=pin.layer)
pin = load_list[-2].get_pin(pin_name)
self.add_power_pin(pin_name,
pin.rc() - vector(self.m1_pitch, 0),
start_layer=pin.layer)
def add_layout_pins(self):
# input is A pin of first inverter

View File

@ -27,7 +27,7 @@ class dff_array(design.design):
super().__init__(name)
debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns))
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -36,11 +36,11 @@ class dff_array(design.design):
self.add_modules()
self.add_pins()
self.create_dff_array()
def create_layout(self):
self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height
self.place_dff_array()
self.add_layout_pins()
self.add_boundary()
@ -49,7 +49,7 @@ class dff_array(design.design):
def add_modules(self):
self.dff = factory.create(module_type="dff")
self.add_mod(self.dff)
def add_pins(self):
for row in range(self.rows):
for col in range(self.columns):
@ -86,7 +86,7 @@ class dff_array(design.design):
mirror = "MX"
self.dff_insts[row, col].place(offset=base,
mirror=mirror)
def get_din_name(self, row, col):
if self.columns == 1:
din_name = "din_{0}".format(row)
@ -96,7 +96,7 @@ class dff_array(design.design):
din_name = "din_{0}_{1}".format(row, col)
return din_name
def get_dout_name(self, row, col):
if self.columns == 1:
dout_name = "dout_{0}".format(row)
@ -106,7 +106,7 @@ class dff_array(design.design):
dout_name = "dout_{0}_{1}".format(row, col)
return dout_name
def add_layout_pins(self):
for row in range(self.rows):
for col in range(self.columns):
@ -117,7 +117,7 @@ class dff_array(design.design):
# Continous gnd rail along with label.
gnd_pin=self.dff_insts[row, col].get_pin("gnd")
self.add_power_pin("gnd", gnd_pin.center(), start_layer=gnd_pin.layer)
for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row, col].get_pin("D")

View File

@ -7,7 +7,7 @@
#
import debug
import design
from tech import parameter, layer
from tech import layer
from tech import cell_properties as props
from vector import vector
from globals import OPTS
@ -21,16 +21,15 @@ class dff_buf(design.design):
and qbar. This is to enable driving large fanout loads.
"""
unique_id = 1
def __init__(self, inv1_size=2, inv2_size=4, name=""):
def __init__(self, inv1_size=2, inv2_size=4, name=""):
if name=="":
name = "dff_buf_{0}".format(dff_buf.unique_id)
dff_buf.unique_id += 1
super().__init__(name)
debug.info(1, "Creating {}".format(self.name))
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))
# This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width.
# 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.
@ -39,7 +38,7 @@ class dff_buf(design.design):
self.inv1_size=inv1_size
self.inv2_size=inv2_size
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -57,7 +56,7 @@ class dff_buf(design.design):
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)
@ -71,7 +70,7 @@ 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")
self.add_pin("Q", "OUTPUT")
@ -93,7 +92,7 @@ class dff_buf(design.design):
self.inv1_inst=self.add_inst(name="dff_buf_inv1",
mod=self.inv1)
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"])
@ -119,16 +118,16 @@ class dff_buf(design.design):
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))
def route_wires(self):
if "li" in layer:
self.route_layer = "li"
else:
self.route_layer = "m1"
# Route dff q to inv1 a
q_pin = self.dff_inst.get_pin("Q")
a1_pin = self.inv1_inst.get_pin("A")
@ -143,7 +142,7 @@ class dff_buf(design.design):
a2_pin = self.inv2_inst.get_pin("A")
self.mid_qb_pos = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
self.add_zjog(z1_pin.layer, z1_pin.center(), a2_pin.center())
def add_layout_pins(self):
# Continous vdd rail along with label.
@ -161,7 +160,7 @@ class dff_buf(design.design):
offset=gnd_pin.ll(),
width=self.width,
height=vdd_pin.height())
clk_pin = self.dff_inst.get_pin("clk")
self.add_layout_pin(text="clk",
layer=clk_pin.layer,

View File

@ -19,7 +19,7 @@ class dff_buf_array(design.design):
Unlike the data flops, these are never spaced out.
"""
unique_id = 1
def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""):
self.rows = rows
self.columns = columns
@ -31,10 +31,10 @@ class dff_buf_array(design.design):
debug.info(1, "Creating {}".format(self.name))
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))
self.inv1_size = inv1_size
self.inv2_size = inv2_size
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -110,7 +110,7 @@ class dff_buf_array(design.design):
pass
dff_pitch = self.dff.width + well_spacing + self.well_extend_active
for row in range(self.rows):
for col in range(self.columns):
# name = "Xdff_r{0}_c{1}".format(row, col)
@ -122,7 +122,7 @@ class dff_buf_array(design.design):
mirror = "MX"
self.dff_insts[row, col].place(offset=base,
mirror=mirror)
def get_din_name(self, row, col):
if self.columns == 1:
din_name = "din_{0}".format(row)
@ -132,7 +132,7 @@ class dff_buf_array(design.design):
din_name = "din_{0}_{1}".format(row, col)
return din_name
def get_dout_name(self, row, col):
if self.columns == 1:
dout_name = "dout_{0}".format(row)
@ -142,7 +142,7 @@ class dff_buf_array(design.design):
dout_name = "dout_{0}_{1}".format(row, col)
return dout_name
def get_dout_bar_name(self, row, col):
if self.columns == 1:
dout_bar_name = "dout_bar_{0}".format(row)
@ -158,11 +158,11 @@ class dff_buf_array(design.design):
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.
@ -172,9 +172,9 @@ class dff_buf_array(design.design):
# Continous gnd rail along with label.
gnd_pin=self.dff_insts[row, col].get_pin("gnd")
self.add_power_pin("gnd", gnd_pin.lc(), start_layer=gnd_pin.layer)
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")
@ -200,7 +200,7 @@ class dff_buf_array(design.design):
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

View File

@ -19,7 +19,7 @@ class dff_inv(design.design):
do not have Qbar, so this will create it.
"""
unique_id = 1
def __init__(self, inv_size=2, name=""):
if name=="":
@ -30,7 +30,7 @@ class dff_inv(design.design):
self.add_comment("inv: {0}".format(inv_size))
self.inv_size = inv_size
# This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width.
# 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.
@ -44,7 +44,7 @@ class dff_inv(design.design):
self.add_pins()
self.add_modules()
self.create_modules()
def create_layout(self):
self.width = self.dff.width + self.inv1.width
self.height = self.dff.height
@ -52,10 +52,10 @@ class dff_inv(design.design):
self.place_modules()
self.add_wires()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
self.add_pin("D")
self.add_pin("Q")
@ -67,7 +67,7 @@ class dff_inv(design.design):
def add_modules(self):
self.dff = dff_inv.dff_inv(self.inv_size)
self.add_mod(self.dff)
self.inv1 = factory.create(module_type="pinv",
size=self.inv_size,
height=self.dff.height)
@ -88,8 +88,8 @@ class dff_inv(design.design):
# Place the INV1 to the right
self.inv1_inst.place(vector(self.dff_inst.rx(),0))
def add_wires(self):
# Route dff q to inv1 a
q_pin = self.dff_inst.get_pin("Q")
@ -106,7 +106,7 @@ class dff_inv(design.design):
self.add_via_center(layers=self.m1_stack,
offset=a1_pin.center())
def add_layout_pins(self):
# Continous vdd rail along with label.
@ -124,7 +124,7 @@ class dff_inv(design.design):
offset=gnd_pin.ll(),
width=self.width,
height=vdd_pin.height())
clk_pin = self.dff_inst.get_pin("clk")
self.add_layout_pin(text="clk",
layer=clk_pin.layer,

View File

@ -19,7 +19,7 @@ class dff_inv_array(design.design):
Unlike the data flops, these are never spaced out.
"""
unique_id = 1
def __init__(self, rows, columns, inv_size=2, name=""):
self.rows = rows
self.columns = columns
@ -31,9 +31,9 @@ class dff_inv_array(design.design):
debug.info(1, "Creating {}".format(self.name))
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
self.add_comment("inv1: {0}".format(inv1_size))
self.inv_size = inv_size
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -42,7 +42,7 @@ class dff_inv_array(design.design):
self.add_pins()
self.add_modules()
self.create_dff_array()
def create_layout(self):
self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height
@ -53,14 +53,14 @@ class dff_inv_array(design.design):
self.DRC_LVS()
def add_modules(self):
self.dff = factory.create(module_type="dff")
self.dff = factory.create(module_type="dff")
self.add_mod(self.dff)
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):
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")
@ -70,20 +70,20 @@ class dff_inv_array(design.design):
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 = "Xdff_r{0}_c{1}".format(row,col)
self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff)
self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(row,col),
self.get_dout_bar_name(row,col),
self.get_dout_bar_name(row,col),
"clk",
"vdd",
"gnd"])
def place_dff_array(self):
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)
if (row % 2 == 0):
@ -92,9 +92,9 @@ class dff_inv_array(design.design):
else:
base = vector(col*self.dff.width,(row+1)*self.dff.height)
mirror = "MX"
self.dff_insts[row,col].place(offset=base,
self.dff_insts[row,col].place(offset=base,
mirror=mirror)
def get_din_name(self, row, col):
if self.columns == 1:
din_name = "din_{0}".format(row)
@ -104,7 +104,7 @@ class dff_inv_array(design.design):
din_name = "din_{0}_{1}".format(row,col)
return din_name
def get_dout_name(self, row, col):
if self.columns == 1:
dout_name = "dout_{0}".format(row)
@ -114,7 +114,7 @@ class dff_inv_array(design.design):
dout_name = "dout_{0}_{1}".format(row,col)
return dout_name
def get_dout_bar_name(self, row, col):
if self.columns == 1:
dout_bar_name = "dout_bar_{0}".format(row)
@ -124,10 +124,10 @@ class dff_inv_array(design.design):
dout_bar_name = "dout_bar_{0}_{1}".format(row,col)
return dout_bar_name
def add_layout_pins(self):
for row in range(self.rows):
for col in range(self.columns):
for col in range(self.columns):
# Adds power pin on left of row
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
self.add_power_pin("vdd", vdd_pin.lc())
@ -135,10 +135,10 @@ class dff_inv_array(design.design):
# Adds gnd pin on left of row
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
self.add_power_pin("gnd", gnd_pin.lc())
for row in range(self.rows):
for col in range(self.columns):
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),
@ -163,7 +163,7 @@ class dff_inv_array(design.design):
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
@ -188,4 +188,4 @@ class dff_inv_array(design.design):
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))

View File

@ -1,6 +1,6 @@
# 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.
#
from bitcell_base_array import bitcell_base_array
@ -20,13 +20,13 @@ class dummy_array(bitcell_base_array):
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
""" Create and connect the netlist """
# This will create a default set of bitline/wordline names
self.create_all_bitline_names()
self.create_all_wordline_names()
self.add_modules()
self.add_pins()
self.create_instances()
@ -38,16 +38,16 @@ class dummy_array(bitcell_base_array):
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
""" Add the modules used in this design """
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
self.cell = factory.create(module_type="bitcell")
self.add_mod(self.dummy_cell)
def create_instances(self):
""" Create the module instances used in this design """
self.cell_inst = {}
@ -67,7 +67,7 @@ class dummy_array(bitcell_base_array):
self.add_pin(wl_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_layout_pins(self):
""" Add the layout pins """
@ -86,7 +86,7 @@ class dummy_array(bitcell_base_array):
offset=br_pin.ll().scale(1, 0),
width=br_pin.width(),
height=self.height)
wl_names = self.cell.get_all_wl_names()
for row in range(self.row_size):
for port in self.all_ports:
@ -104,7 +104,7 @@ class dummy_array(bitcell_base_array):
inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
def input_load(self):
# FIXME: This appears to be old code from previous characterization. Needs to be updated.
wl_wire = self.gen_wl_wire()

View File

@ -27,11 +27,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
debug.check(len(self.all_ports)<=2, "Only support dual port or less in global bitcell array.")
self.rbl = [1, 1 if len(self.all_ports)>1 else 0]
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
@ -43,11 +43,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
self.place()
self.route()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
@ -88,7 +88,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
rows=self.row_size,
cols=cols,
rbl=self.rbl)
self.add_mod(la)
self.local_mods.append(la)
@ -108,7 +108,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
self.rbl_bitline_names[0].append("rbl_bl_{}_0".format(port))
for port in self.all_ports:
self.rbl_bitline_names[0].append("rbl_br_{}_0".format(port))
for col in range(self.column_size):
for port in self.all_ports:
self.bitline_names[port].append("bl_{0}_{1}".format(port, col))
@ -120,7 +120,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
self.rbl_bitline_names[1].append("rbl_bl_{}_1".format(port))
for port in self.all_ports:
self.rbl_bitline_names[1].append("rbl_br_{}_1".format(port))
# Make a flat list too
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
# Make a flat list too
@ -130,19 +130,19 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
self.add_pin_list(self.all_bitline_names, "INOUT")
if len(self.all_ports) > 1:
self.add_pin_list(self.rbl_bitline_names[1], "INOUT")
def add_wordline_pins(self):
self.rbl_wordline_names = [[] for x in self.all_ports]
self.wordline_names = [[] for x in self.all_ports]
for bit in self.all_ports:
for port in self.all_ports:
self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit))
self.all_rbl_wordline_names = [x for sl in zip(*self.rbl_wordline_names) for x in sl]
# Regular WLs
for row in range(self.row_size):
for port in self.all_ports:
@ -163,11 +163,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
name = "la_{0}".format(col)
self.local_insts.append(self.add_inst(name=name,
mod=mod))
temp = []
if col == 0:
temp.extend(self.get_rbl_bitline_names(0))
port_inouts = [x for x in mod.get_inouts() if x.startswith("bl") or x.startswith("br")]
for pin_name in port_inouts:
# Offset of the last underscore that defines the bit number
@ -180,18 +180,18 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
# Strip the bit and add the new one
new_name = "{0}_{1}".format(base_name, col + col_value)
temp.append(new_name)
if len(self.all_ports) > 1 and mod == self.local_mods[-1]:
temp.extend(self.get_rbl_bitline_names(1))
for port in self.all_ports:
port_inputs = [x for x in mod.get_inputs() if "wl_{}".format(port) in x]
temp.extend(port_inputs)
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def place(self):
offset = vector(0, 0)
for inst in self.local_insts:
@ -204,7 +204,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
def route(self):
pass
def add_layout_pins(self):
# Regular bitlines
@ -230,11 +230,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
layer=left_pin.layer,
start=left_pin.lc(),
end=right_pin.rc())
# Replica bitlines
self.copy_layout_pin(self.local_insts[0], "rbl_bl_0_0")
self.copy_layout_pin(self.local_insts[0], "rbl_br_0_0")
if len(self.all_ports) > 1:
self.copy_layout_pin(self.local_insts[0], "rbl_bl_1_0")
self.copy_layout_pin(self.local_insts[0], "rbl_br_1_0")
@ -269,10 +269,10 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
return offsets
def graph_exclude_bits(self, targ_row, targ_col):
"""
Excludes bits in column from being added to graph except target
"""
Excludes bits in column from being added to graph except target
"""
# This must find which local array includes the specified column
# Find the summation of columns that is large and take the one before
for i, col in enumerate(self.col_offsets):
@ -303,7 +303,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell."""
# This must find which local array includes the specified column
# Find the summation of columns that is large and take the one before
for i, local_col in enumerate(self.col_offsets):
@ -321,9 +321,9 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
local_col = col - self.col_offsets[i - 1]
return local_array.get_cell_name(inst_name + '.x' + local_inst.name, row, local_col)
def clear_exclude_bits(self):
"""
"""
Clears the bit exclusions
"""
for mod in self.local_mods:
@ -331,6 +331,6 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
def graph_exclude_dffs(self):
"""Exclude dffs from graph as they do not represent critical path"""
self.graph_inst_exclude.add(self.ctrl_dff_inst)

View File

@ -22,14 +22,14 @@ class hierarchical_decoder(design.design):
super().__init__(name)
self.AND_FORMAT = "DEC_AND_{0}"
self.pre2x4_inst = []
self.pre3x8_inst = []
self.pre4x16_inst = []
b = factory.create(module_type="bitcell")
self.cell_height = 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.no_of_pre4x16)=self.determine_predecodes(self.num_inputs)
@ -37,7 +37,7 @@ class hierarchical_decoder(design.design):
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_modules()
self.setup_netlist_constants()
@ -49,33 +49,33 @@ class hierarchical_decoder(design.design):
self.setup_layout_constants()
self.place_pre_decoder()
self.place_row_decoder()
self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space
self.route_inputs()
self.route_outputs()
self.route_decoder_bus()
self.route_vdd_gnd()
self.offset_x_coordinates()
self.width = self.and_inst[0].rx() + 0.5 * self.m1_width
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
self.and2 = factory.create(module_type="and2_dec",
height=self.cell_height)
self.add_mod(self.and2)
self.and3 = factory.create(module_type="and3_dec",
height=self.cell_height)
self.add_mod(self.and3)
# TBD
# self.and4 = factory.create(module_type="and4_dec")
# self.add_mod(self.and4)
self.add_decoders()
def add_decoders(self):
@ -83,7 +83,7 @@ class hierarchical_decoder(design.design):
self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4",
height=self.cell_height)
self.add_mod(self.pre2_4)
self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8",
height=self.cell_height)
self.add_mod(self.pre3_8)
@ -91,11 +91,11 @@ class hierarchical_decoder(design.design):
self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16",
height=self.cell_height)
self.add_mod(self.pre4_16)
def determine_predecodes(self, num_inputs):
"""
"""
Determines the number of 2:4, 3:8 and 4:16 pre-decoders
needed based on the number of inputs
needed based on the number of inputs
"""
if (num_inputs == 2):
return (1, 0, 0)
@ -151,7 +151,7 @@ class hierarchical_decoder(design.design):
lines.append(index)
index = index + 1
self.predec_groups.append(lines)
def setup_layout_constants(self):
""" Calculate the overall dimensions of the hierarchical decoder """
@ -190,7 +190,7 @@ class hierarchical_decoder(design.design):
self.input_layer = layer_props.hierarchical_decoder.input_layer
self.output_layer = layer_props.hierarchical_decoder.output_layer
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
# Two extra pitches between modules on left and right
self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch
self.row_decoder_height = self.and2.height * self.num_outputs
@ -215,7 +215,7 @@ class hierarchical_decoder(design.design):
offset=input_offset,
names=input_bus_names,
length=self.predecoder_height)
self.route_input_to_predecodes()
def route_input_to_predecodes(self):
@ -231,13 +231,13 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.center()
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
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_bus["addr_{}".format(index)].center()
in_name = "in_{}".format(i)
@ -245,13 +245,13 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.center()
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_bus(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre4x16):
for i in range(4):
index = pre_num * 4 + i + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2
input_pos = self.input_bus["addr_{}".format(index)].center()
in_name = "in_{}".format(i)
@ -259,15 +259,15 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.center()
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_bus(decoder_offset, input_offset)
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_stack_center(from_layer=self.bus_layer,
to_layer=self.input_layer,
offset=input_offset)
@ -276,10 +276,10 @@ class hierarchical_decoder(design.design):
offset=output_offset,
directions=self.bus_directions)
self.add_path(self.input_layer, [input_offset, output_offset])
def add_pins(self):
""" Add the module pins """
for i in range(self.num_inputs):
self.add_pin("addr_{0}".format(i), "INPUT")
@ -290,10 +290,10 @@ class hierarchical_decoder(design.design):
def create_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """
for i in range(self.no_of_pre2x4):
self.create_pre2x4(i)
for i in range(self.no_of_pre3x8):
self.create_pre3x8(i)
@ -302,7 +302,7 @@ class hierarchical_decoder(design.design):
def create_pre2x4(self, num):
""" Add a 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2):
index_off1 = index_off2 = 0
else:
@ -355,19 +355,19 @@ class hierarchical_decoder(design.design):
self.pre4x16_inst.append(self.add_inst(name="pre4x16_{0}".format(num),
mod=self.pre4_16))
self.connect_inst(pins)
def place_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """
for i in range(self.no_of_pre2x4):
self.place_pre2x4(i)
for i in range(self.no_of_pre3x8):
self.place_pre3x8(i)
for i in range(self.no_of_pre4x16):
self.place_pre4x16(i)
self.predecode_height = 0
if self.no_of_pre2x4 > 0:
self.predecode_height = self.pre2x4_inst[-1].uy()
@ -378,10 +378,10 @@ class hierarchical_decoder(design.design):
def place_pre2x4(self, num):
""" Place 2x4 predecoder to the left of the origin """
base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
self.pre2x4_inst[num].place(base)
def place_pre3x8(self, num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) \
@ -396,7 +396,7 @@ class hierarchical_decoder(design.design):
+ num * (self.pre4_16.height + self.predecoder_spacing)
offset = vector(-self.pre4_16.width, height)
self.pre4x16_inst[num].place(offset)
def create_row_decoder(self):
""" Create the row-decoder by placing AND2/AND3 and Inverters
and add the primary decoder output pins. """
@ -407,7 +407,7 @@ class hierarchical_decoder(design.design):
""" Add a column of AND gates for final decode """
self.and_inst = []
# Row Decoder AND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_inputs == 5):
for i in range(len(self.predec_groups[0])):
@ -435,7 +435,7 @@ class hierarchical_decoder(design.design):
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])),
@ -456,7 +456,7 @@ class hierarchical_decoder(design.design):
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):
self.place_and_array(and_mod=self.and2)
@ -489,7 +489,7 @@ class hierarchical_decoder(design.design):
for row in range(self.num_outputs):
and_inst = self.and_inst[row]
self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row))
def route_decoder_bus(self):
"""
Creates vertical metal 2 bus to connect predecoder and decoder stages.
@ -522,7 +522,7 @@ class hierarchical_decoder(design.design):
x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch
y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
# FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre3x8):
for i in range(8):
@ -542,7 +542,7 @@ class hierarchical_decoder(design.design):
x_offset = self.pre4x16_inst[pre_num].rx() + self.output_layer_pitch
y_offset = self.pre4x16_inst[pre_num].by() + i * self.cell_height
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
def route_bus_to_decoder(self):
"""
Use the self.predec_groups to determine the connections to the decoder AND gates.
@ -555,7 +555,7 @@ class hierarchical_decoder(design.design):
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]:
@ -596,7 +596,7 @@ class hierarchical_decoder(design.design):
Add a pin for each row of vdd/gnd which are
must-connects next level up.
"""
if layer_props.hierarchical_decoder.vertical_supply:
for n in ["vdd", "gnd"]:
pins = self.and_inst[0].get_pins(n)
@ -636,7 +636,7 @@ class hierarchical_decoder(design.design):
for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst:
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(pre, pin_name)
def route_predecode_bus_outputs(self, rail_name, pin, row):
"""
Connect the routing rail to the given metal1 pin
@ -646,17 +646,17 @@ class hierarchical_decoder(design.design):
pin_pos = pin.center()
rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y)
self.add_path(self.input_layer, [rail_pos, pin_pos])
self.add_via_stack_center(from_layer=self.bus_layer,
to_layer=self.input_layer,
offset=rail_pos,
directions=self.bus_directions)
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.input_layer,
offset=pin_pos,
directions=("H", "H"))
def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset):
"""
Connect the routing rail to the given metal1 pin using a jog

View File

@ -31,10 +31,10 @@ class hierarchical_predecode(design.design):
# If we are pitch matched to the bitcell, it's a predecoder
# otherwise it's a column decoder (out of pgates)
self.column_decoder = (height != b.height)
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
super().__init__(name)
def add_pins(self):
for k in range(self.number_of_inputs):
self.add_pin("in_{0}".format(k), "INPUT")
@ -48,7 +48,7 @@ class hierarchical_predecode(design.design):
debug.check(self.number_of_inputs <= 4,
"Invalid number of predecode inputs: {}".format(self.number_of_inputs))
if self.column_decoder:
and_type = "pand{}".format(self.number_of_inputs)
inv_type = "pinv"
@ -79,7 +79,7 @@ class hierarchical_predecode(design.design):
self.route()
self.add_boundary()
self.DRC_LVS()
def setup_layout_constraints(self):
# Inputs to cells are on input layer
@ -92,7 +92,7 @@ class hierarchical_predecode(design.design):
self.input_layer = layer_props.hierarchical_predecode.input_layer
self.output_layer = layer_props.hierarchical_predecode.output_layer
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
self.height = self.number_of_outputs * self.and_mod.height
# x offset for input inverters
@ -139,7 +139,7 @@ class hierarchical_predecode(design.design):
def place_input_inverters(self):
""" Place the input inverters to invert input signals for the decode stage. """
for inv_num in range(self.number_of_inputs):
if (inv_num % 2 == 0):
y_off = inv_num * (self.inv.height)
mirror = "R0"
@ -149,7 +149,7 @@ class hierarchical_predecode(design.design):
offset = vector(self.x_off_inv_1, y_off)
self.inv_inst[inv_num].place(offset=offset,
mirror=mirror)
def create_and_array(self, connections):
""" Create the AND stage for the decodes """
self.and_inst = []
@ -196,7 +196,7 @@ class hierarchical_predecode(design.design):
pin = top_and_gate.get_pin("D")
else:
debug.error("Too many inputs for predecoder.", -1)
y_offset = pin.cy()
in_pin = "in_{}".format(num)
a_pin = "A_{}".format(num)
@ -222,7 +222,7 @@ class hierarchical_predecode(design.design):
offset=z_pin.ll(),
height=z_pin.height(),
width=z_pin.width())
def route_input_inverters(self):
"""
Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd]
@ -232,7 +232,7 @@ class hierarchical_predecode(design.design):
in_pin = "in_{}".format(inv_num)
inv_out_pin = self.inv_inst[inv_num].get_pin("Z")
# add output so that it is just below the vdd or gnd rail
# since this is where the p/n devices are and there are no
# pins in the and gates.
@ -241,7 +241,7 @@ class hierarchical_predecode(design.design):
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0)
rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset)
self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
self.add_via_stack_center(from_layer=inv_out_pin.layer,
to_layer=self.output_layer,
offset=inv_out_pos)
@ -249,7 +249,7 @@ class hierarchical_predecode(design.design):
to_layer=self.bus_layer,
offset=rail_pos,
directions=self.bus_directions)
# route input
pin = self.inv_inst[inv_num].get_pin("A")
inv_in_pos = pin.center()
@ -267,11 +267,11 @@ class hierarchical_predecode(design.design):
offset=in_pos,
height=via.mod.second_layer_height,
width=via.mod.second_layer_width)
if layer_props.hierarchical_predecode.vertical_supply:
below_rail = vector(self.decode_rails[out_pin].cx(), y_offset - (self.cell_height / 2))
self.add_path(self.bus_layer, [rail_pos, below_rail], width=self.li_width + self.m1_enclose_mcon * 2)
def route_and_to_rails(self):
# This 2D array defines the connection mapping
and_input_line_combination = self.get_and_input_line_combination()
@ -302,7 +302,7 @@ class hierarchical_predecode(design.design):
direction = None
else:
direction = ("H", "H")
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.input_layer,
offset=pin_pos,
@ -334,7 +334,7 @@ class hierarchical_predecode(design.design):
self.add_power_pin(name=n,
loc=pin.uc(),
start_layer=pin.layer)
# In other techs, we are using standard cell decoder cells with horizontal power
else:
for num in range(0, self.number_of_outputs):
@ -354,6 +354,6 @@ class hierarchical_predecode(design.design):
self.add_power_pin(name=n,
loc=pin_pos,
start_layer=and_pin.layer)

View File

@ -25,9 +25,9 @@ class hierarchical_predecode3x8(hierarchical_predecode):
self.add_modules()
self.create_input_inverters()
connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"],
@ -41,7 +41,7 @@ class hierarchical_predecode3x8(hierarchical_predecode):
["Abar_0", "A_1", "Abar_2"],
["A_0", "A_1", "Abar_2"],
["Abar_0", "Abar_1", "A_2"],
["A_0", "Abar_1", "A_2"],
["Abar_0", "A_1", "A_2"],
["A_0", "Abar_1", "A_2"],
["Abar_0", "A_1", "A_2"],
["A_0", "A_1", "A_2"]]
return combination

View File

@ -25,17 +25,17 @@ class hierarchical_predecode4x16(hierarchical_predecode):
self.add_modules()
self.create_input_inverters()
connections=[["inbar_0", "inbar_1", "inbar_2", "inbar_3", "out_0", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "inbar_3", "out_2", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "inbar_3", "out_4", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"],
["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"],
["inbar_0", "inbar_1", "inbar_2", "in_3", "out_8", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "in_3", "out_9", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "in_3", "out_9", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "in_3", "out_10", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "in_3", "out_11", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "in_3", "out_11", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "in_3", "out_12", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "in_3", "out_13", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "in_3", "out_14", "vdd", "gnd"],
@ -50,15 +50,15 @@ class hierarchical_predecode4x16(hierarchical_predecode):
["Abar_0", "A_1", "Abar_2", "Abar_3"],
["A_0", "A_1", "Abar_2", "Abar_3"],
["Abar_0", "Abar_1", "A_2" , "Abar_3"],
["A_0", "Abar_1", "A_2" , "Abar_3"],
["Abar_0", "A_1", "A_2" , "Abar_3"],
["A_0", "Abar_1", "A_2" , "Abar_3"],
["Abar_0", "A_1", "A_2" , "Abar_3"],
["A_0", "A_1", "A_2" , "Abar_3"],
["Abar_0", "Abar_1", "Abar_2", "A_3"],
["A_0", "Abar_1", "Abar_2", "A_3"],
["Abar_0", "A_1", "Abar_2", "A_3"],
["A_0", "A_1", "Abar_2", "A_3"],
["Abar_0", "Abar_1", "A_2", "A_3"],
["A_0", "Abar_1", "A_2", "A_3"],
["Abar_0", "A_1", "A_2", "A_3"],
["A_0", "Abar_1", "A_2", "A_3"],
["Abar_0", "A_1", "A_2", "A_3"],
["A_0", "A_1", "A_2", "A_3"]]
return combination

View File

@ -32,9 +32,9 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
self.rbl = rbl
self.left_rbl = left_rbl
self.right_rbl = right_rbl
debug.check(len(self.all_ports) < 3, "Local bitcell array only supports dual port or less.")
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -42,7 +42,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
# We don't offset this because we need to align
# the replica bitcell in the control logic
# self.offset_all_coordinates()
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
@ -56,7 +56,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
self.add_layout_pins()
self.route()
self.add_boundary()
self.DRC_LVS()
@ -78,7 +78,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
rows=self.rows + 1,
cols=self.cols)
self.add_mod(self.wl_array)
def add_pins(self):
# Outputs from the wordline driver (by port)
self.driver_wordline_outputs = []
@ -87,18 +87,18 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
self.wordline_names = self.bitcell_array.wordline_names
self.all_wordline_names = self.bitcell_array.all_wordline_names
self.bitline_names = self.bitcell_array.bitline_names
self.all_bitline_names = self.bitcell_array.all_bitline_names
self.rbl_wordline_names = self.bitcell_array.rbl_wordline_names
self.all_rbl_wordline_names = self.bitcell_array.all_rbl_wordline_names
self.rbl_bitline_names = self.bitcell_array.rbl_bitline_names
self.all_rbl_bitline_names = self.bitcell_array.all_rbl_bitline_names
self.all_array_wordline_inputs = [x + "i" for x in self.bitcell_array.get_all_wordline_names()]
# Arrays are always:
# bit lines (left to right)
# word lines (bottom to top)
@ -133,10 +133,10 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
else:
temp += self.get_wordline_names(port)[::-1]
self.driver_wordline_outputs.append([x + "i" for x in temp])
temp += self.driver_wordline_outputs[-1]
temp += ["vdd", "gnd"]
self.connect_inst(temp)
self.bitcell_array_inst = self.add_inst(name="array",
@ -180,7 +180,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
for x in self.get_inouts():
self.copy_layout_pin(self.bitcell_array_inst, x)
supply_insts = [*self.wl_insts, self.bitcell_array_inst]
for pin_name in ["vdd", "gnd"]:
for inst in supply_insts:
@ -189,7 +189,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
self.add_power_pin(name=pin_name,
loc=pin.center(),
start_layer=pin.layer)
def route(self):
# Route the global wordlines
@ -198,27 +198,27 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)
else:
wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)[::-1]
wordline_pins = self.wl_array.get_inputs()
for (wl_name, in_pin_name) in zip(wordline_names, wordline_pins):
# wl_pin = self.bitcell_array_inst.get_pin(wl_name)
in_pin = self.wl_insts[port].get_pin(in_pin_name)
y_offset = in_pin.cy()
if port == 0:
y_offset -= 2 * self.m3_pitch
else:
y_offset += 2 * self.m3_pitch
self.add_layout_pin_segment_center(text=wl_name,
layer="m3",
start=vector(self.wl_insts[port].lx(), y_offset),
end=vector(self.wl_insts[port].lx() + self.wl_array.width, y_offset))
mid = vector(in_pin.cx(), y_offset)
self.add_path("m2", [in_pin.center(), mid])
self.add_via_stack_center(from_layer=in_pin.layer,
to_layer="m2",
offset=in_pin.center())
@ -228,7 +228,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
# Route the buffers
for port in self.all_ports:
driver_outputs = self.driver_wordline_outputs[port]
for (driver_name, net_name) in zip(self.wl_insts[port].mod.get_outputs(), driver_outputs):
array_name = net_name[:-1]
out_pin = self.wl_insts[port].get_pin(driver_name)
@ -242,7 +242,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
mid_loc = vector(self.wl_insts[port].lx() - 1.5 * self.m3_pitch, out_loc.y)
in_loc = in_pin.rc()
self.add_path(out_pin.layer, [out_loc, mid_loc, in_loc])
def get_main_array_top(self):
return self.bitcell_array_inst.by() + self.bitcell_array.get_main_array_top()
@ -262,13 +262,13 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
# must add the offset of the instance
offsets = [self.bitcell_array_inst.lx() + x for x in self.bitcell_array.get_column_offsets()]
return offsets
def graph_exclude_bits(self, targ_row=None, targ_col=None):
"""
Excludes bits in column from being added to graph except target
"""
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def graph_exclude_replica_col_bits(self):
"""
Exclude all but replica in the local array.
@ -281,8 +281,8 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col)
def clear_exclude_bits(self):
"""
"""
Clears the bit exclusions
"""
self.bitcell_array.clear_exclude_bits()

View File

@ -19,9 +19,9 @@ from globals import OPTS
class multibank(design.design):
"""
Dynamically generated a single bank including bitcell array,
hierarchical_decoder, precharge, (optional column_mux and column decoder),
hierarchical_decoder, precharge, (optional column_mux and column decoder),
write driver and sense amplifiers.
This module includes the tristate and bank select logic.
This module includes the tristate and bank select logic.
"""
def __init__(self, name, word_size, num_words, words_per_row, num_banks=1):
@ -41,7 +41,7 @@ class multibank(design.design):
self.prefix="gated_"
else:
self.prefix=""
self.compute_sizes()
self.add_pins()
self.add_modules()
@ -50,16 +50,16 @@ class multibank(design.design):
# FIXME: Move this to the add modules function
self.add_bank_select()
self.route_layout()
# Can remove the following, but it helps for debug!
self.add_lvs_correspondence_points()
self.add_lvs_correspondence_points()
# Remember the bank center for further placement
self.bank_center=self.offset_all_coordinates().scale(-1,-1)
self.DRC_LVS()
def add_pins(self):
@ -91,23 +91,23 @@ class multibank(design.design):
#self.route_tri_gate_out()
self.route_sense_amp_out()
self.route_wordline_driver()
self.route_write_driver()
self.route_write_driver()
self.route_row_decoder()
self.route_column_address_lines()
self.route_control_lines()
self.add_control_pins()
if self.num_banks > 1:
self.route_bank_select()
self.route_bank_select()
self.route_supplies()
def create_instances(self):
""" Add modules. The order should not matter! """
# Above the bitcell array
self.add_bitcell_array()
self.add_precharge_array()
# Below the bitcell array
self.add_column_mux_array()
self.add_sense_amp_array()
@ -120,7 +120,7 @@ class multibank(design.design):
self.add_wordline_driver()
self.add_column_decoder()
def compute_sizes(self):
""" Computes the required sizes to create the bank """
@ -139,7 +139,7 @@ class multibank(design.design):
self.supply_rail_width = 4*self.m2_width
# FIXME: This spacing should be width dependent...
self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space
# Number of control lines in the bus
self.num_control_lines = 6
# The order of the control signals on the control bus:
@ -153,7 +153,7 @@ class multibank(design.design):
if self.col_addr_size>0:
self.num_col_addr_lines = 2**self.col_addr_size
else:
self.num_col_addr_lines = 0
self.num_col_addr_lines = 0
# The width of this bus is needed to place other modules (e.g. decoder)
# A width on each side too
@ -169,7 +169,7 @@ class multibank(design.design):
""" Add all the modules using the class loader """
self.tri = self.mod_tri_gate()
self.bitcell = self.mod_bitcell()
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
rows=self.num_rows)
self.add_mod(self.bitcell_array)
@ -178,12 +178,12 @@ class multibank(design.design):
self.add_mod(self.precharge_array)
if self.col_addr_size > 0:
self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols,
self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols,
word_size=self.word_size)
self.add_mod(self.column_mux_array)
self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size,
self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size,
words_per_row=self.words_per_row)
self.add_mod(self.sense_amp_array)
@ -199,8 +199,8 @@ class multibank(design.design):
self.row_decoder = self.mod_decoder(rows=self.num_rows)
self.add_mod(self.row_decoder)
self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols,
self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols,
word_size=self.word_size)
self.add_mod(self.tri_gate_array)
@ -213,12 +213,12 @@ class multibank(design.design):
if(self.num_banks > 1):
self.bank_select = self.mod_bank_select()
self.add_mod(self.bank_select)
def add_bitcell_array(self):
""" Adding Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
mod=self.bitcell_array,
offset=vector(0,0))
temp = []
@ -238,7 +238,7 @@ class multibank(design.design):
# The enclosure is for the well and the spacing is to the bitcell wells
y_offset = self.bitcell_array.height + self.m2_gap
self.precharge_array_inst=self.add_inst(name="precharge_array",
mod=self.precharge_array,
mod=self.precharge_array,
offset=vector(0,y_offset))
temp = []
for i in range(self.num_cols):
@ -255,7 +255,7 @@ class multibank(design.design):
self.column_mux_height = 0
return
y_offset = self.column_mux_height
y_offset = self.column_mux_height
self.col_mux_array_inst=self.add_inst(name="column_mux_array",
mod=self.column_mux_array,
offset=vector(0,y_offset).scale(-1,-1))
@ -287,7 +287,7 @@ class multibank(design.design):
else:
temp.append("bl_out_{0}".format(i))
temp.append("br_out_{0}".format(i))
temp.extend([self.prefix+"s_en", "vdd", "gnd"])
self.connect_inst(temp)
@ -295,16 +295,16 @@ class multibank(design.design):
""" Adding Write Driver """
y_offset = self.sense_amp_array.height + self.column_mux_height \
+ self.m2_gap + self.write_driver_array.height
self.write_driver_array_inst=self.add_inst(name="write_driver_array",
mod=self.write_driver_array,
+ self.m2_gap + self.write_driver_array.height
self.write_driver_array_inst=self.add_inst(name="write_driver_array",
mod=self.write_driver_array,
offset=vector(0,y_offset).scale(-1,-1))
temp = []
for i in range(self.word_size):
temp.append("bank_din_{0}".format(i))
for i in range(self.word_size):
if (self.words_per_row == 1):
for i in range(self.word_size):
if (self.words_per_row == 1):
temp.append("bl_{0}".format(i))
temp.append("br_{0}".format(i))
else:
@ -317,10 +317,10 @@ class multibank(design.design):
""" data tri gate to drive the data bus """
y_offset = self.sense_amp_array.height+self.column_mux_height \
+ self.m2_gap + self.tri_gate_array.height
self.tri_gate_array_inst=self.add_inst(name="tri_gate_array",
mod=self.tri_gate_array,
self.tri_gate_array_inst=self.add_inst(name="tri_gate_array",
mod=self.tri_gate_array,
offset=vector(0,y_offset).scale(-1,-1))
temp = []
for i in range(self.word_size):
temp.append("sa_out_{0}".format(i))
@ -332,16 +332,16 @@ class multibank(design.design):
def add_row_decoder(self):
""" Add the hierarchical row decoder """
# The address and control bus will be in between decoder and the main memory array
# This bus will route address bits to the decoder input and column mux inputs.
# The address and control bus will be in between decoder and the main memory array
# This bus will route address bits to the decoder input and column mux inputs.
# The wires are actually routed after we placed the stuff on both sides.
# The predecoder is below the x-axis and the main decoder is above the x-axis
# The address flop and decoder are aligned in the x coord.
x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
self.row_decoder_inst=self.add_inst(name="row_decoder",
mod=self.row_decoder,
self.row_decoder_inst=self.add_inst(name="row_decoder",
mod=self.row_decoder,
offset=vector(x_offset,0))
temp = []
@ -357,8 +357,8 @@ class multibank(design.design):
# The wordline driver is placed to the right of the main decoder width.
x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch
self.wordline_driver_inst=self.add_inst(name="wordline_driver",
mod=self.wordline_driver,
self.wordline_driver_inst=self.add_inst(name="wordline_driver",
mod=self.wordline_driver,
offset=vector(x_offset,0))
temp = []
@ -371,16 +371,16 @@ class multibank(design.design):
temp.append("gnd")
self.connect_inst(temp)
def add_column_decoder_module(self):
"""
"""
Create a 2:4 or 3:8 column address decoder.
"""
# Place the col decoder right aligned with row decoder
x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width)
y_off = -(self.col_decoder.height + 2*drc("well_to_well"))
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
mod=self.col_decoder,
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
mod=self.col_decoder,
offset=vector(x_off,y_off))
temp = []
@ -390,9 +390,9 @@ class multibank(design.design):
temp.append("sel_{0}".format(j))
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def add_column_decoder(self):
"""
"""
Create a decoder to decode column select lines. This could be an inverter/buffer for 1:2,
2:4 decoder, or 3:8 decoder.
"""
@ -408,7 +408,7 @@ class multibank(design.design):
else:
# No error checking before?
debug.error("Invalid column decoder?",-1)
self.add_column_decoder_module()
@ -417,7 +417,7 @@ class multibank(design.design):
if not self.num_banks > 1:
return
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
if self.col_addr_size > 0:
y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by())
@ -441,7 +441,7 @@ class multibank(design.design):
for inst in self.insts:
self.copy_power_pins(inst,"vdd")
self.copy_power_pins(inst,"gnd")
def route_bank_select(self):
""" Route the bank select logic. """
for input_name in self.input_control_signals+["bank_sel"]:
@ -461,8 +461,8 @@ class multibank(design.design):
self.add_via_center(layers=self.m2_stack,
offset=out_pos,
rotate=90)
def setup_layout_constraints(self):
""" After the modules are instantiated, find the dimensions for the
control bus, power ring, etc. """
@ -472,36 +472,36 @@ class multibank(design.design):
#driver.
# Leave room for the output below the tri gate.
#tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch
write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch
write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch
row_decoder_min_y_offset = self.row_decoder_inst.by()
if self.col_addr_size > 0:
col_decoder_min_y_offset = self.col_decoder_inst.by()
else:
col_decoder_min_y_offset = row_decoder_min_y_offset
if self.num_banks>1:
# The control gating logic is below the decoder
# Min of the control gating logic and tri gate.
self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset)
else:
# Just the min of the decoder logic logic and tri gate.
# Just the min of the decoder logic logic and tri gate.
self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset)
# The max point is always the top of the precharge bitlines
# Add a vdd and gnd power rail above the array
self.max_y_offset = self.precharge_array_inst.uy() + 3*self.m1_width
self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width
self.min_x_offset = self.row_decoder_inst.lx()
self.min_x_offset = self.row_decoder_inst.lx()
# # Create the core bbox for the power rings
ur = vector(self.max_x_offset, self.max_y_offset)
ll = vector(self.min_x_offset, self.min_y_offset)
self.core_bbox = [ll, ur]
self.height = ur.y - ll.y
self.width = ur.x - ll.x
def route_central_bus(self):
""" Create the address, supply, and control signal central bus lines. """
@ -509,7 +509,7 @@ class multibank(design.design):
# Overall central bus width. It includes all the column mux lines,
# and control lines.
# The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs
# 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, 0)
control_bus_length = self.bitcell_array_inst.uy()
self.bus_xoffset = self.create_vertical_bus(layer="m2",
@ -542,7 +542,7 @@ class multibank(design.design):
# Only do this if we have a column mux!
if self.col_addr_size==0:
return
for i in range(self.num_cols):
col_mux_bl = self.col_mux_array_inst.get_pin("bl_{}".format(i)).uc()
col_mux_br = self.col_mux_array_inst.get_pin("br_{}".format(i)).uc()
@ -554,7 +554,7 @@ class multibank(design.design):
vector(bitcell_bl.x,yoffset), bitcell_bl])
self.add_path("m2",[col_mux_br, vector(col_mux_br.x,yoffset),
vector(bitcell_br.x,yoffset), bitcell_br])
def route_sense_amp_to_col_mux_or_bitcell_array(self):
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
@ -570,19 +570,19 @@ class multibank(design.design):
# Sense amp is directly connected to the bitcell array
connect_bl = self.bitcell_array_inst.get_pin("bl_{}".format(i)).bc()
connect_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).bc()
yoffset = 0.5*(sense_amp_bl.y+connect_bl.y)
self.add_path("m2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset),
vector(connect_bl.x,yoffset), connect_bl])
self.add_path("m2",[sense_amp_br, vector(sense_amp_br.x,yoffset),
vector(connect_br.x,yoffset), connect_br])
def route_sense_amp_to_trigate(self):
""" Routing of sense amp output to tri_gate input """
for i in range(self.word_size):
# Connection of data_out of sense amp to data_in
# Connection of data_out of sense amp to data_in
tri_gate_in = self.tri_gate_array_inst.get_pin("in_{}".format(i)).lc()
sa_data_out = self.sense_amp_array_inst.get_pin("data_{}".format(i)).bc()
@ -597,17 +597,17 @@ class multibank(design.design):
for i in range(self.word_size):
data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(i))
self.add_layout_pin_rect_center(text="dout_{}".format(i),
layer=data_pin.layer,
layer=data_pin.layer,
offset=data_pin.center(),
height=data_pin.height(),
width=data_pin.width()),
def route_tri_gate_out(self):
""" Metal 3 routing of tri_gate output data """
for i in range(self.word_size):
data_pin = self.tri_gate_array_inst.get_pin("out_{}".format(i))
self.add_layout_pin_rect_center(text="dout_{}".format(i),
layer=data_pin.layer,
layer=data_pin.layer,
offset=data_pin.center(),
height=data_pin.height(),
width=data_pin.width()),
@ -622,21 +622,21 @@ class multibank(design.design):
decoder_name = "a_{}".format(i)
addr_name = "a_{}".format(addr_idx)
self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name)
def route_write_driver(self):
""" Connecting write driver """
for i in range(self.word_size):
data_name = "data_{}".format(i)
data_name = "data_{}".format(i)
din_name = "bank_din_{}".format(i)
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
def route_wordline_driver(self):
""" Connecting Wordline driver output to Bitcell WL connection """
# we don't care about bends after connecting to the input pin, so let the path code decide.
for i in range(self.num_rows):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
@ -653,23 +653,23 @@ class multibank(design.design):
mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1)
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_column_address_lines(self):
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
return
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.col_decoder_inst, "A", "a[0]")
self.copy_layout_pin(self.col_decoder_inst, "A", "a[0]")
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
@ -679,7 +679,7 @@ class multibank(design.design):
decoder_name = "in_{}".format(i)
addr_name = "a_{}".format(i)
self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name)
# This will do a quick "river route" on two layers.
# When above the top select line it will offset "inward" again to prevent conflicts.
@ -688,7 +688,7 @@ class multibank(design.design):
for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)):
mux_name = "sel_{}".format(i)
mux_addr_pos = self.col_mux_array_inst.get_pin(mux_name).lc()
decode_out_pos = self.col_decoder_inst.get_pin(decode_name).center()
# To get to the edge of the decoder and one track out
@ -700,13 +700,13 @@ class multibank(design.design):
mid2_pos = vector(mid1_pos.x,mux_addr_pos.y)
#self.add_wire(self.m1_stack,[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
self.add_path("m1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
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.
"""
@ -715,9 +715,9 @@ class multibank(design.design):
wl_name = "wl_{}".format(i)
wl_pin = self.bitcell_array_inst.get_pin(wl_name)
self.add_label(text=wl_name,
layer="m1",
layer="m1",
offset=wl_pin.center())
# Add the bitline names
for i in range(self.num_cols):
bl_name = "bl_{}".format(i)
@ -725,35 +725,35 @@ class multibank(design.design):
bl_pin = self.bitcell_array_inst.get_pin(bl_name)
br_pin = self.bitcell_array_inst.get_pin(br_name)
self.add_label(text=bl_name,
layer="m2",
layer="m2",
offset=bl_pin.center())
self.add_label(text=br_name,
layer="m2",
layer="m2",
offset=br_pin.center())
# # Add the data output names to the sense amp output
# # Add the data output names to the sense amp output
# for i in range(self.word_size):
# data_name = "data_{}".format(i)
# data_pin = self.sense_amp_array_inst.get_pin(data_name)
# self.add_label(text="sa_out_{}".format(i),
# layer="m2",
# layer="m2",
# offset=data_pin.center())
# Add labels on the decoder
for i in range(self.word_size):
data_name = "dec_out_{}".format(i)
pin_name = "in_{}".format(i)
pin_name = "in_{}".format(i)
data_pin = self.wordline_driver_inst.get_pin(pin_name)
self.add_label(text=data_name,
layer="m1",
layer="m1",
offset=data_pin.center())
def route_control_lines(self):
""" Route the control lines of the entire bank """
# Make a list of tuples that we will connect.
# From control signal to the module pin
# From control signal to the module pin
# Connection from the central bus to the main control block crosses
# pre-decoder and this connection is in metal3
connection = []
@ -762,7 +762,7 @@ class multibank(design.design):
connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst.get_pin("en").lc()))
connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc()))
connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
for (control_signal, pin_pos) in connection:
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
self.add_path("m1", [control_pos, pin_pos])
@ -781,7 +781,7 @@ class multibank(design.design):
self.add_via_center(layers=self.m1_stack,
offset=control_via_pos,
rotate=90)
def add_control_pins(self):
""" Add the control signal input pins """
@ -791,15 +791,15 @@ class multibank(design.design):
if self.num_banks > 1:
# it's not an input pin if we have multiple banks
self.add_label_pin(text=ctrl,
layer="m2",
offset=vector(x_offset, self.min_y_offset),
width=self.m2_width,
layer="m2",
offset=vector(x_offset, self.min_y_offset),
width=self.m2_width,
height=self.max_y_offset-self.min_y_offset)
else:
self.add_layout_pin(text=ctrl,
layer="m2",
offset=vector(x_offset, self.min_y_offset),
width=self.m2_width,
layer="m2",
offset=vector(x_offset, self.min_y_offset),
width=self.m2_width,
height=self.max_y_offset-self.min_y_offset)
@ -815,8 +815,8 @@ class multibank(design.design):
self.add_via(layers=self.m2_stack,
offset=in_pin + self.m2m3_via_offset,
rotate=90)
def connect_rail_from_left(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pin = inst.get_pin(pin).rc()

View File

@ -27,7 +27,7 @@ class bitcell_array(bitcell_base_array):
# We don't offset this because we need to align
# the replica bitcell in the control logic
# self.offset_all_coordinates()
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
@ -41,7 +41,7 @@ class bitcell_array(bitcell_base_array):
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
@ -58,20 +58,20 @@ class bitcell_array(bitcell_base_array):
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."""
# Dynamic Power from Bitline
bl_wire = self.gen_bl_wire()
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
cell_power = self.cell.analytical_power(corner, load)
# Leakage power grows with entire array and bitlines.
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
cell_power.leakage * self.column_size * self.row_size)
@ -102,7 +102,7 @@ class bitcell_array(bitcell_base_array):
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
@ -111,7 +111,7 @@ class bitcell_array(bitcell_base_array):
if row == targ_row and col == targ_col:
continue
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]

Some files were not shown because too many files have changed in this diff Show More