mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into wlbuffer
This commit is contained in:
commit
71d86f88b0
|
|
@ -12,6 +12,8 @@ import debug
|
||||||
from vector import vector
|
from vector import vector
|
||||||
import tech
|
import tech
|
||||||
import math
|
import math
|
||||||
|
import copy
|
||||||
|
import numpy as np
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from utils import round_to_grid
|
from utils import round_to_grid
|
||||||
|
|
||||||
|
|
@ -269,6 +271,134 @@ class instance(geometry):
|
||||||
p.transform(self.offset, self.mirror, self.rotate)
|
p.transform(self.offset, self.mirror, self.rotate)
|
||||||
new_pins.append(p)
|
new_pins.append(p)
|
||||||
return new_pins
|
return new_pins
|
||||||
|
|
||||||
|
def calculate_transform(self, node):
|
||||||
|
#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],
|
||||||
|
[0.0,0.0,1.0]])
|
||||||
|
|
||||||
|
#set up translation matrix
|
||||||
|
translateX = float(node.offset[0])
|
||||||
|
translateY = float(node.offset[1])
|
||||||
|
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'):
|
||||||
|
scaleY = -1.0
|
||||||
|
else:
|
||||||
|
scaleY = 1.0
|
||||||
|
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):
|
||||||
|
origin = np.dot(mtransforms[0], origin) #rotate
|
||||||
|
uVector = np.dot(mtransforms[0], uVector) #rotate
|
||||||
|
vVector = np.dot(mtransforms[0], vVector) #rotate
|
||||||
|
origin = np.dot(mtransforms[1], origin) #scale
|
||||||
|
uVector = np.dot(mtransforms[1], uVector) #scale
|
||||||
|
vVector = np.dot(mtransforms[1], vVector) #scale
|
||||||
|
origin = np.dot(mtransforms[2], origin)
|
||||||
|
|
||||||
|
return(uVector, vVector, origin)
|
||||||
|
|
||||||
|
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]])
|
||||||
|
|
||||||
|
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):
|
||||||
|
path = [] # path currently follwed in bitcell search
|
||||||
|
cell_paths = [] # saved paths to bitcells
|
||||||
|
origin_offsets = [] # cell to bank offset
|
||||||
|
Q_offsets = [] # Q to cell offet
|
||||||
|
Q_bar_offsets = [] # Q_bar to cell offset
|
||||||
|
bl_offsets = [] # bl to cell offset
|
||||||
|
br_offsets = [] # br to cell offset
|
||||||
|
bl_meta = [] # bl offset metadata (row,col,name)
|
||||||
|
br_meta = [] #br offset metadata (row,col,name)
|
||||||
|
|
||||||
|
def walk_subtree(node):
|
||||||
|
path.append(node)
|
||||||
|
|
||||||
|
if node.mod.name == cell_name:
|
||||||
|
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:])
|
||||||
|
|
||||||
|
cell_bl_meta = []
|
||||||
|
cell_br_meta = []
|
||||||
|
|
||||||
|
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])
|
||||||
|
|
||||||
|
for offset in range(len(normalized_br_offsets)):
|
||||||
|
for port in range(len(br_names)):
|
||||||
|
cell_br_meta.append([br_names[offset], row, col, port])
|
||||||
|
|
||||||
|
Q_x = normalized_storage_nets[0][0]
|
||||||
|
Q_y = normalized_storage_nets[0][1]
|
||||||
|
|
||||||
|
Q_bar_x = normalized_storage_nets[1][0]
|
||||||
|
Q_bar_y = normalized_storage_nets[1][1]
|
||||||
|
|
||||||
|
if node.mirror == 'MX':
|
||||||
|
Q_y = -1 * Q_y
|
||||||
|
Q_bar_y = -1 * Q_bar_y
|
||||||
|
|
||||||
|
for pair in range(len(normalized_bl_offsets)):
|
||||||
|
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],
|
||||||
|
-1 * normalized_br_offsets[pair][1])
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
bl_meta.append(cell_bl_meta)
|
||||||
|
br_meta.append(cell_br_meta)
|
||||||
|
|
||||||
|
elif node.mod.insts is not []:
|
||||||
|
for instance in node.mod.insts:
|
||||||
|
walk_subtree(instance)
|
||||||
|
path.pop(-1)
|
||||||
|
|
||||||
|
walk_subtree(self)
|
||||||
|
for path in cell_paths:
|
||||||
|
vector_spaces = self.apply_path_transform(path)
|
||||||
|
origin = vector_spaces[2]
|
||||||
|
origin_offsets.append([origin[0], origin[1]])
|
||||||
|
|
||||||
|
return(origin_offsets, Q_offsets, Q_bar_offsets, bl_offsets, br_offsets, bl_meta, br_meta)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
|
|
|
||||||
|
|
@ -177,62 +177,6 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
name_dict[si_port.lower()] = mod_info
|
name_dict[si_port.lower()] = mod_info
|
||||||
subinst.mod.build_names(name_dict, subinst_name, subinst_ports)
|
subinst.mod.build_names(name_dict, subinst_name, subinst_ports)
|
||||||
|
|
||||||
def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None):
|
|
||||||
"""Given a list of nets, will compare the internal alias of a mod to determine
|
|
||||||
if the nets have a connection to this mod's net (but not inst).
|
|
||||||
"""
|
|
||||||
if not exclusion_set:
|
|
||||||
exclusion_set = set()
|
|
||||||
try:
|
|
||||||
self.name_dict
|
|
||||||
except AttributeError:
|
|
||||||
self.name_dict = {}
|
|
||||||
self.build_names(self.name_dict, inst_name, port_nets)
|
|
||||||
aliases = []
|
|
||||||
for net in path_nets:
|
|
||||||
net = net.lower()
|
|
||||||
int_net = self.name_dict[net]['int_net']
|
|
||||||
int_mod = self.name_dict[net]['mod']
|
|
||||||
if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set):
|
|
||||||
aliases.append(net)
|
|
||||||
return aliases
|
|
||||||
|
|
||||||
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):
|
|
||||||
"""Checks if the alias_net in input mod is the same as the input net for this mod (self)."""
|
|
||||||
if self in exclusion_set:
|
|
||||||
return False
|
|
||||||
# Check ports of this mod
|
|
||||||
for pin in self.pins:
|
|
||||||
if self.is_net_alias_name_check(known_net, pin, net_alias, mod):
|
|
||||||
return True
|
|
||||||
# Check connections of all other subinsts
|
|
||||||
mod_set = set()
|
|
||||||
for subinst, inst_conns in zip(self.insts, self.conns):
|
|
||||||
for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins):
|
|
||||||
if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod):
|
|
||||||
return True
|
|
||||||
elif inst_conn.lower() == known_net.lower() and subinst.mod not in mod_set:
|
|
||||||
if subinst.mod.is_net_alias(mod_pin, net_alias, mod, exclusion_set):
|
|
||||||
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."""
|
|
||||||
return self == mod and \
|
|
||||||
child_net.lower() == alias_net.lower() and \
|
|
||||||
parent_net.lower() == alias_net.lower()
|
|
||||||
|
|
||||||
def get_mod_net(self, parent_net, child_inst, child_conns):
|
|
||||||
"""
|
|
||||||
Given an instance and net, returns the internal net in the mod
|
|
||||||
corresponding to input net.
|
|
||||||
"""
|
|
||||||
for conn, pin in zip(child_conns, child_inst.mod.pins):
|
|
||||||
if parent_net.lower() == conn.lower():
|
|
||||||
return pin
|
|
||||||
return None
|
|
||||||
|
|
||||||
def translate_nets(self, subinst_ports, port_dict, inst_name):
|
def translate_nets(self, subinst_ports, port_dict, inst_name):
|
||||||
"""Converts connection names to their spice hierarchy equivalent"""
|
"""Converts connection names to their spice hierarchy equivalent"""
|
||||||
converted_conns = []
|
converted_conns = []
|
||||||
|
|
|
||||||
|
|
@ -1324,6 +1324,8 @@ class layout():
|
||||||
pdf.drawLayout()
|
pdf.drawLayout()
|
||||||
pdf.writeToFile(pdf_name)
|
pdf.writeToFile(pdf_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def print_attr(self):
|
def print_attr(self):
|
||||||
"""Prints a list of attributes for the current layout object"""
|
"""Prints a list of attributes for the current layout object"""
|
||||||
debug.info(0,
|
debug.info(0,
|
||||||
|
|
|
||||||
|
|
@ -499,3 +499,48 @@ class spice():
|
||||||
def return_power(self, dynamic=0.0, leakage=0.0):
|
def return_power(self, dynamic=0.0, leakage=0.0):
|
||||||
return power_data(dynamic, leakage)
|
return power_data(dynamic, leakage)
|
||||||
|
|
||||||
|
def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None):
|
||||||
|
"""Given a list of nets, will compare the internal alias of a mod to determine
|
||||||
|
if the nets have a connection to this mod's net (but not inst).
|
||||||
|
"""
|
||||||
|
if not exclusion_set:
|
||||||
|
exclusion_set = set()
|
||||||
|
try:
|
||||||
|
self.name_dict
|
||||||
|
except AttributeError:
|
||||||
|
self.name_dict = {}
|
||||||
|
self.build_names(self.name_dict, inst_name, port_nets)
|
||||||
|
aliases = []
|
||||||
|
for net in path_nets:
|
||||||
|
net = net.lower()
|
||||||
|
int_net = self.name_dict[net]['int_net']
|
||||||
|
int_mod = self.name_dict[net]['mod']
|
||||||
|
if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set):
|
||||||
|
aliases.append(net)
|
||||||
|
return aliases
|
||||||
|
|
||||||
|
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):
|
||||||
|
"""Checks if the alias_net in input mod is the same as the input net for this mod (self)."""
|
||||||
|
if self in exclusion_set:
|
||||||
|
return False
|
||||||
|
# Check ports of this mod
|
||||||
|
for pin in self.pins:
|
||||||
|
if self.is_net_alias_name_check(known_net, pin, net_alias, mod):
|
||||||
|
return True
|
||||||
|
# Check connections of all other subinsts
|
||||||
|
mod_set = set()
|
||||||
|
for subinst, inst_conns in zip(self.insts, self.conns):
|
||||||
|
for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins):
|
||||||
|
if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod):
|
||||||
|
return True
|
||||||
|
elif inst_conn.lower() == known_net.lower() and subinst.mod not in mod_set:
|
||||||
|
if subinst.mod.is_net_alias(mod_pin, net_alias, mod, exclusion_set):
|
||||||
|
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."""
|
||||||
|
return self == mod and \
|
||||||
|
child_net.lower() == alias_net.lower() and \
|
||||||
|
parent_net.lower() == alias_net.lower()
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class bitcell(bitcell_base.bitcell_base):
|
||||||
props.bitcell.cell_6t.pin.vdd,
|
props.bitcell.cell_6t.pin.vdd,
|
||||||
props.bitcell.cell_6t.pin.gnd]
|
props.bitcell.cell_6t.pin.gnd]
|
||||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||||
storage_nets = ['Q', 'Qbar']
|
storage_nets = ['Q', 'Q_bar']
|
||||||
|
|
||||||
(width, height) = utils.get_libcell_size("cell_6t",
|
(width, height) = utils.get_libcell_size("cell_6t",
|
||||||
GDS["unit"],
|
GDS["unit"],
|
||||||
|
|
@ -51,7 +51,7 @@ class bitcell(bitcell_base.bitcell_base):
|
||||||
self.add_pin_types(self.type_list)
|
self.add_pin_types(self.type_list)
|
||||||
self.nets_match = self.do_nets_exist(self.storage_nets)
|
self.nets_match = self.do_nets_exist(self.storage_nets)
|
||||||
|
|
||||||
debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells")
|
# debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells")
|
||||||
|
|
||||||
def get_all_wl_names(self):
|
def get_all_wl_names(self):
|
||||||
""" Creates a list of all wordline pin names """
|
""" Creates a list of all wordline pin names """
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,9 @@
|
||||||
|
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
|
from globals import OPTS
|
||||||
import logical_effort
|
import logical_effort
|
||||||
from tech import parameter, drc
|
from tech import parameter, drc, layer
|
||||||
|
|
||||||
|
|
||||||
class bitcell_base(design.design):
|
class bitcell_base(design.design):
|
||||||
|
|
@ -78,7 +79,85 @@ class bitcell_base(design.design):
|
||||||
fmt_str = "Storage nodes={} not found in spice file."
|
fmt_str = "Storage nodes={} not found in spice file."
|
||||||
debug.info(1, fmt_str.format(self.storage_nets))
|
debug.info(1, fmt_str.format(self.storage_nets))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_storage_net_offset(self):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
if OPTS.bitcell is not "pbitcell":
|
||||||
|
self.storage_net_offsets = []
|
||||||
|
for i in range(len(self.get_storage_net_names())):
|
||||||
|
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]])
|
||||||
|
|
||||||
|
|
||||||
|
return(self.storage_net_offsets)
|
||||||
|
|
||||||
|
def get_bitline_offset(self):
|
||||||
|
|
||||||
|
bl_names = self.get_all_bl_names()
|
||||||
|
br_names = self.get_all_br_names()
|
||||||
|
|
||||||
|
found_bl = []
|
||||||
|
found_br = []
|
||||||
|
|
||||||
|
self.bl_offsets = []
|
||||||
|
self.br_offsets = []
|
||||||
|
|
||||||
|
for i in range(len(bl_names)):
|
||||||
|
for text in self.gds.getTexts(layer["m2"]):
|
||||||
|
if not bl_names[i] in found_bl:
|
||||||
|
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)):
|
||||||
|
for text in self.gds.getTexts(layer["m2"]):
|
||||||
|
if not br_names[i] in found_br:
|
||||||
|
if br_names[i] == text.textString.rstrip('\x00'):
|
||||||
|
self.br_offsets.append(text.coordinates[0])
|
||||||
|
found_br.append(br_names[i])
|
||||||
|
continue
|
||||||
|
|
||||||
|
for i in range(len(self.bl_offsets)):
|
||||||
|
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]])
|
||||||
|
|
||||||
|
return(self.bl_offsets, self.br_offsets, found_bl, found_br)
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
if OPTS.bitcell is not "pbitcell":
|
||||||
|
normalized_storage_net_offset = self.get_storage_net_offset()
|
||||||
|
|
||||||
|
else:
|
||||||
|
net_offset = self.get_storage_net_offset()
|
||||||
|
Q_x = net_offset[0][0] - self.leftmost_xpos
|
||||||
|
Q_y = net_offset[0][1] - self.botmost_ypos
|
||||||
|
Q_bar_x = net_offset[1][0] - self.leftmost_xpos
|
||||||
|
Q_bar_y = net_offset[1][1] - self.botmost_ypos
|
||||||
|
|
||||||
|
normalized_storage_net_offset = [[Q_x,Q_y],[Q_bar_x,Q_bar_y]]
|
||||||
|
|
||||||
|
return normalized_storage_net_offset
|
||||||
|
|
||||||
|
def get_normalized_bitline_offset(self):
|
||||||
|
return self.get_bitline_offset()
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""
|
"""
|
||||||
By default, bitcells won't be part of the graph.
|
By default, bitcells won't be part of the graph.
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.num_w_ports = OPTS.num_w_ports
|
self.num_w_ports = OPTS.num_w_ports
|
||||||
self.num_r_ports = OPTS.num_r_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.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
|
||||||
|
|
||||||
self.replica_bitcell = replica_bitcell
|
self.replica_bitcell = replica_bitcell
|
||||||
self.dummy_bitcell = dummy_bitcell
|
self.dummy_bitcell = dummy_bitcell
|
||||||
|
|
||||||
|
|
@ -152,7 +152,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.Q_bar = "Q_bar"
|
self.Q_bar = "Q_bar"
|
||||||
self.Q = "Q"
|
self.Q = "Q"
|
||||||
self.storage_nets = [self.Q, self.Q_bar]
|
self.storage_nets = [self.Q, self.Q_bar]
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Determine size of transistors and add ptx modules """
|
""" Determine size of transistors and add ptx modules """
|
||||||
# if there are any read/write ports,
|
# if there are any read/write ports,
|
||||||
|
|
@ -352,6 +352,11 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.right_building_edge = right_inverter_xpos \
|
self.right_building_edge = right_inverter_xpos \
|
||||||
+ self.inverter_nmos.active_width
|
+ self.inverter_nmos.active_width
|
||||||
|
|
||||||
|
def add_pex_labels(self, left_inverter_offset, right_inverter_offset):
|
||||||
|
self.add_label("Q", "metal1", left_inverter_offset)
|
||||||
|
self.add_label("Q_bar", "metal1", right_inverter_offset)
|
||||||
|
self.storage_net_offsets = [left_inverter_offset, right_inverter_offset]
|
||||||
|
|
||||||
def route_storage(self):
|
def route_storage(self):
|
||||||
""" Routes inputs and outputs of inverters to cross couple them """
|
""" Routes inputs and outputs of inverters to cross couple them """
|
||||||
# connect input (gate) of inverters
|
# connect input (gate) of inverters
|
||||||
|
|
@ -396,6 +401,16 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x,
|
gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x,
|
||||||
contact_offset_right.y)
|
contact_offset_right.y)
|
||||||
self.add_path("poly", [contact_offset_right, gate_offset_left])
|
self.add_path("poly", [contact_offset_right, gate_offset_left])
|
||||||
|
if OPTS.use_pex:
|
||||||
|
# add labels to cross couple inverter for extracted simulation
|
||||||
|
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)
|
||||||
|
self.add_pex_labels(contact_offset_left_output, contact_offset_right_output)
|
||||||
|
|
||||||
def route_rails(self):
|
def route_rails(self):
|
||||||
""" Adds gnd and vdd rails and connects them to the inverters """
|
""" Adds gnd and vdd rails and connects them to the inverters """
|
||||||
|
|
|
||||||
|
|
@ -136,19 +136,18 @@ class delay(simulation):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.bitline_volt_meas = []
|
self.bitline_volt_meas = []
|
||||||
|
|
||||||
self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO",
|
self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO",
|
||||||
self.bl_name))
|
self.bl_name))
|
||||||
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO
|
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO
|
||||||
self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ZERO",
|
self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ZERO",
|
||||||
self.br_name))
|
self.br_name))
|
||||||
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO
|
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO
|
||||||
|
|
||||||
self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ONE",
|
self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ONE",
|
||||||
self.bl_name))
|
self.bl_name))
|
||||||
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE
|
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE
|
||||||
self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ONE",
|
self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ONE",
|
||||||
self.br_name))
|
self.br_name))
|
||||||
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE
|
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE
|
||||||
return self.bitline_volt_meas
|
return self.bitline_volt_meas
|
||||||
|
|
||||||
|
|
@ -181,8 +180,12 @@ class delay(simulation):
|
||||||
self.dout_volt_meas.append(voltage_at_measure("v_{}".format(meas.name),
|
self.dout_volt_meas.append(voltage_at_measure("v_{}".format(meas.name),
|
||||||
meas.targ_name_no_port))
|
meas.targ_name_no_port))
|
||||||
self.dout_volt_meas[-1].meta_str = meas.meta_str
|
self.dout_volt_meas[-1].meta_str = meas.meta_str
|
||||||
|
|
||||||
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9)
|
if not OPTS.use_pex:
|
||||||
|
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9)
|
||||||
|
else:
|
||||||
|
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name, "FALL", "RISE", measure_scale=1e9)
|
||||||
|
|
||||||
self.sen_meas.meta_str = sram_op.READ_ZERO
|
self.sen_meas.meta_str = sram_op.READ_ZERO
|
||||||
self.sen_meas.meta_add_delay = True
|
self.sen_meas.meta_add_delay = True
|
||||||
|
|
||||||
|
|
@ -228,8 +231,13 @@ class delay(simulation):
|
||||||
storage_names = cell_inst.mod.get_storage_net_names()
|
storage_names = cell_inst.mod.get_storage_net_names()
|
||||||
debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes"
|
debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes"
|
||||||
"supported for characterization. Storage nets={}").format(storage_names))
|
"supported for characterization. Storage nets={}").format(storage_names))
|
||||||
q_name = cell_name+'.'+str(storage_names[0])
|
if not OPTS.use_pex:
|
||||||
qbar_name = cell_name+'.'+str(storage_names[1])
|
q_name = cell_name+'.'+str(storage_names[0])
|
||||||
|
qbar_name = cell_name+'.'+str(storage_names[1])
|
||||||
|
else:
|
||||||
|
bank_num = self.sram.get_bank_num(self.sram.name, bit_row, bit_col)
|
||||||
|
q_name = "bitcell_Q_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col)
|
||||||
|
qbar_name = "bitcell_Q_bar_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col)
|
||||||
|
|
||||||
# Bit measures, measurements times to be defined later. The measurement names must be unique
|
# Bit measures, measurements times to be defined later. The measurement names must be unique
|
||||||
# but they is enforced externally. {} added to names to differentiate between ports allow the
|
# but they is enforced externally. {} added to names to differentiate between ports allow the
|
||||||
|
|
@ -244,18 +252,7 @@ class delay(simulation):
|
||||||
|
|
||||||
self.load = load
|
self.load = load
|
||||||
self.slew = slew
|
self.slew = slew
|
||||||
|
|
||||||
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()
|
|
||||||
self.sram.graph_exclude_addr_dff()
|
|
||||||
self.sram.graph_exclude_data_dff()
|
|
||||||
self.sram.graph_exclude_ctrl_dffs()
|
|
||||||
self.sram.bank.bitcell_array.graph_exclude_replica_col_bits()
|
|
||||||
|
|
||||||
def create_graph(self):
|
def create_graph(self):
|
||||||
"""Creates timing graph to generate the timing paths for the SRAM output."""
|
"""Creates timing graph to generate the timing paths for the SRAM output."""
|
||||||
|
|
||||||
|
|
@ -267,74 +264,6 @@ class delay(simulation):
|
||||||
self.sram_spc_name = "X{}".format(self.sram.name)
|
self.sram_spc_name = "X{}".format(self.sram.name)
|
||||||
self.sram.build_graph(self.graph,self.sram_spc_name,self.pins)
|
self.sram.build_graph(self.graph,self.sram_spc_name,self.pins)
|
||||||
|
|
||||||
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]
|
|
||||||
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
|
|
||||||
self.bl_name = bl_name_port
|
|
||||||
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
|
|
||||||
self.br_name = br_name_port
|
|
||||||
else:
|
|
||||||
self.br_name = br_name_port
|
|
||||||
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
|
|
||||||
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
|
|
||||||
# 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()
|
|
||||||
sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0])
|
|
||||||
|
|
||||||
return sen_name
|
|
||||||
|
|
||||||
def get_bl_name(self, paths, port):
|
|
||||||
"""Gets the signal name associated with the bitlines in the bank."""
|
|
||||||
|
|
||||||
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_found = False
|
|
||||||
# Only a single path should contain a single s_en name. Anything else is an error.
|
|
||||||
bl_names = []
|
|
||||||
exclude_set = self.get_bl_name_search_exclusions()
|
|
||||||
for int_net in [cell_bl, cell_br]:
|
|
||||||
bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set))
|
|
||||||
|
|
||||||
return bl_names[0], bl_names[1]
|
|
||||||
|
|
||||||
|
|
||||||
def get_bl_name_search_exclusions(self):
|
def get_bl_name_search_exclusions(self):
|
||||||
"""Gets the mods as a set which should be excluded while searching for name."""
|
"""Gets the mods as a set which should be excluded while searching for name."""
|
||||||
|
|
||||||
|
|
@ -342,36 +271,6 @@ class delay(simulation):
|
||||||
# so it makes the search awkward
|
# so it makes the search awkward
|
||||||
return set(factory.get_mods(OPTS.replica_bitline))
|
return set(factory.get_mods(OPTS.replica_bitline))
|
||||||
|
|
||||||
def get_primary_cell_mod(self, cell_mods):
|
|
||||||
"""
|
|
||||||
Distinguish bitcell array mod from replica bitline array.
|
|
||||||
Assume there are no replica bitcells in the primary array.
|
|
||||||
"""
|
|
||||||
if len(cell_mods) == 1:
|
|
||||||
return cell_mods[0]
|
|
||||||
rbc_mods = factory.get_mods(OPTS.replica_bitcell)
|
|
||||||
non_rbc_mods = []
|
|
||||||
for bitcell in cell_mods:
|
|
||||||
has_cell = False
|
|
||||||
for replica_cell in rbc_mods:
|
|
||||||
has_cell = has_cell or replica_cell.contains(bitcell, replica_cell.mods)
|
|
||||||
if not has_cell:
|
|
||||||
non_rbc_mods.append(bitcell)
|
|
||||||
if len(non_rbc_mods) != 1:
|
|
||||||
debug.error('Multiple bitcell mods found. Cannot distinguish for characterization',1)
|
|
||||||
return non_rbc_mods[0]
|
|
||||||
|
|
||||||
def are_mod_pins_equal(self, mods):
|
|
||||||
"""Determines if there are pins differences in the input mods"""
|
|
||||||
|
|
||||||
if len(mods) == 0:
|
|
||||||
return True
|
|
||||||
pins = mods[0].pins
|
|
||||||
for mod in mods[1:]:
|
|
||||||
if pins != mod.pins:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None):
|
def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None):
|
||||||
"""
|
"""
|
||||||
Finds a single alias for the int_net in given paths.
|
Finds a single alias for the int_net in given paths.
|
||||||
|
|
@ -422,8 +321,13 @@ class delay(simulation):
|
||||||
|
|
||||||
# instantiate the sram
|
# instantiate the sram
|
||||||
self.sf.write("\n* Instantiation of the SRAM\n")
|
self.sf.write("\n* Instantiation of the SRAM\n")
|
||||||
self.stim.inst_model(pins=self.pins,
|
if not OPTS.use_pex:
|
||||||
model_name=self.sram.name)
|
self.stim.inst_model(pins=self.pins,
|
||||||
|
model_name=self.sram.name)
|
||||||
|
else:
|
||||||
|
self.stim.inst_sram_pex(pins=self.pins,
|
||||||
|
model_name=self.sram.name)
|
||||||
|
|
||||||
self.sf.write("\n* SRAM output loads\n")
|
self.sf.write("\n* SRAM output loads\n")
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ class functional(simulation):
|
||||||
if not self.num_spare_cols:
|
if not self.num_spare_cols:
|
||||||
self.num_spare_cols = 0
|
self.num_spare_cols = 0
|
||||||
|
|
||||||
|
self.probe_address, self.probe_data = '0'*self.addr_size,0
|
||||||
self.set_corner(corner)
|
self.set_corner(corner)
|
||||||
self.set_spice_constants()
|
self.set_spice_constants()
|
||||||
self.set_stimulus_variables()
|
self.set_stimulus_variables()
|
||||||
|
|
@ -47,6 +48,8 @@ class functional(simulation):
|
||||||
self.add_graph_exclusions()
|
self.add_graph_exclusions()
|
||||||
self.create_graph()
|
self.create_graph()
|
||||||
self.set_internal_spice_names()
|
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
|
# Number of checks can be changed
|
||||||
self.num_cycles = 15
|
self.num_cycles = 15
|
||||||
|
|
@ -419,18 +422,6 @@ class functional(simulation):
|
||||||
|
|
||||||
self.stim.write_control(self.cycle_times[-1] + self.period)
|
self.stim.write_control(self.cycle_times[-1] + self.period)
|
||||||
self.sf.close()
|
self.sf.close()
|
||||||
|
|
||||||
# FIXME: refactor to share with delay.py
|
|
||||||
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()
|
|
||||||
self.sram.graph_exclude_addr_dff()
|
|
||||||
self.sram.graph_exclude_data_dff()
|
|
||||||
self.sram.graph_exclude_ctrl_dffs()
|
|
||||||
self.sram.bank.bitcell_array.graph_exclude_replica_col_bits()
|
|
||||||
|
|
||||||
# FIXME: refactor to share with delay.py
|
# FIXME: refactor to share with delay.py
|
||||||
def create_graph(self):
|
def create_graph(self):
|
||||||
|
|
@ -444,25 +435,8 @@ class functional(simulation):
|
||||||
self.graph = graph_util.timing_graph()
|
self.graph = graph_util.timing_graph()
|
||||||
self.sram_spc_name = "X{}".format(self.sram.name)
|
self.sram_spc_name = "X{}".format(self.sram.name)
|
||||||
self.sram.build_graph(self.graph, self.sram_spc_name, self.pins)
|
self.sram.build_graph(self.graph, self.sram_spc_name, self.pins)
|
||||||
|
|
||||||
# FIXME: refactor to share with delay.py
|
#FIXME: Similar function to delay.py, refactor this
|
||||||
def set_internal_spice_names(self):
|
|
||||||
"""Sets important names for characterization such as Sense amp enable and internal bit nets."""
|
|
||||||
|
|
||||||
# For now, only testing these using first read port.
|
|
||||||
port = self.read_ports[0]
|
|
||||||
self.graph.get_all_paths('{}{}'.format("clk", port),
|
|
||||||
'{}{}_{}'.format(self.dout_name, port, 0).lower())
|
|
||||||
|
|
||||||
self.sen_name = self.get_sen_name(self.graph.all_paths)
|
|
||||||
debug.info(2, "s_en name = {}".format(self.sen_name))
|
|
||||||
|
|
||||||
self.bl_name, self.br_name = self.get_bl_name(self.graph.all_paths, port)
|
|
||||||
debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name))
|
|
||||||
|
|
||||||
self.q_name, self.qbar_name = self.get_bit_name()
|
|
||||||
debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name))
|
|
||||||
|
|
||||||
def get_bit_name(self):
|
def get_bit_name(self):
|
||||||
""" Get a bit cell name """
|
""" Get a bit cell name """
|
||||||
(cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0)
|
(cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0)
|
||||||
|
|
@ -473,37 +447,6 @@ class functional(simulation):
|
||||||
qbar_name = cell_name + '.' + str(storage_names[1])
|
qbar_name = cell_name + '.' + str(storage_names[1])
|
||||||
|
|
||||||
return (q_name, qbar_name)
|
return (q_name, qbar_name)
|
||||||
|
|
||||||
# FIXME: refactor to share with delay.py
|
|
||||||
def get_sen_name(self, paths):
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
# 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()
|
|
||||||
sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0])
|
|
||||||
return sen_name
|
|
||||||
|
|
||||||
# FIXME: refactor to share with delay.py
|
|
||||||
def get_bl_name(self, paths, port):
|
|
||||||
"""Gets the signal name associated with the bitlines in the bank."""
|
|
||||||
|
|
||||||
cell_mod = factory.create(module_type=OPTS.bitcell)
|
|
||||||
cell_bl = cell_mod.get_bl_name(port)
|
|
||||||
cell_br = cell_mod.get_br_name(port)
|
|
||||||
|
|
||||||
# Only a single path should contain a single s_en name. Anything else is an error.
|
|
||||||
bl_names = []
|
|
||||||
exclude_set = self.get_bl_name_search_exclusions()
|
|
||||||
for int_net in [cell_bl, cell_br]:
|
|
||||||
bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set))
|
|
||||||
|
|
||||||
return bl_names[0], bl_names[1]
|
|
||||||
|
|
||||||
def get_bl_name_search_exclusions(self):
|
def get_bl_name_search_exclusions(self):
|
||||||
"""Gets the mods as a set which should be excluded while searching for name."""
|
"""Gets the mods as a set which should be excluded while searching for name."""
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from .trim_spice import *
|
||||||
from .charutils import *
|
from .charutils import *
|
||||||
import utils
|
import utils
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
|
||||||
class simulation():
|
class simulation():
|
||||||
|
|
||||||
|
|
@ -408,3 +409,96 @@ class simulation():
|
||||||
pin_names.append("{0}".format("gnd"))
|
pin_names.append("{0}".format("gnd"))
|
||||||
return pin_names
|
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()
|
||||||
|
self.sram.graph_exclude_addr_dff()
|
||||||
|
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
|
||||||
|
self.bl_name = bl_name_port
|
||||||
|
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
|
||||||
|
self.br_name = br_name_port
|
||||||
|
else:
|
||||||
|
self.br_name = br_name_port
|
||||||
|
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
|
||||||
|
debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name))
|
||||||
|
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
|
||||||
|
# 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()
|
||||||
|
sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0])
|
||||||
|
if OPTS.use_pex:
|
||||||
|
sen_name = sen_name.split('.')[-1]
|
||||||
|
return sen_name
|
||||||
|
|
||||||
|
|
||||||
|
def get_bl_name(self, paths, port):
|
||||||
|
"""Gets the signal name associated with the bitlines in the bank."""
|
||||||
|
|
||||||
|
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_found = False
|
||||||
|
# Only a single path should contain a single s_en name. Anything else is an error.
|
||||||
|
bl_names = []
|
||||||
|
exclude_set = self.get_bl_name_search_exclusions()
|
||||||
|
for int_net in [cell_bl, cell_br]:
|
||||||
|
bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set))
|
||||||
|
if OPTS.use_pex:
|
||||||
|
for i in range(len(bl_names)):
|
||||||
|
bl_names[i] = bl_names[i].split('.')[-1]
|
||||||
|
return bl_names[0], bl_names[1]
|
||||||
|
|
@ -56,7 +56,30 @@ class stimuli():
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
self.sf.write("{0} ".format(pin))
|
self.sf.write("{0} ".format(pin))
|
||||||
self.sf.write("{0}\n".format(model_name))
|
self.sf.write("{0}\n".format(model_name))
|
||||||
|
|
||||||
|
def inst_sram_pex(self, pins, model_name):
|
||||||
|
self.sf.write("X{0} ".format(model_name))
|
||||||
|
for pin in pins:
|
||||||
|
self.sf.write("{0} ".format(pin))
|
||||||
|
for bank in range(OPTS.num_banks):
|
||||||
|
row = int(OPTS.num_words / OPTS.words_per_row) - 1
|
||||||
|
col = int(OPTS.word_size * OPTS.words_per_row) - 1
|
||||||
|
self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col))
|
||||||
|
self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col))
|
||||||
|
# can't add all bitcells to top level due to ngspice max port count of 1005
|
||||||
|
# for row in range(int(OPTS.num_words / OPTS.words_per_row)):
|
||||||
|
# for col in range(int(OPTS.word_size * OPTS.words_per_row)):
|
||||||
|
# self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col))
|
||||||
|
# self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col))
|
||||||
|
for bank in range(OPTS.num_banks):
|
||||||
|
for col in range(OPTS.word_size * OPTS.words_per_row):
|
||||||
|
for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports):
|
||||||
|
self.sf.write("bl{0}_{1} ".format(port, col))
|
||||||
|
self.sf.write("br{0}_{1} ".format(port, col))
|
||||||
|
|
||||||
|
|
||||||
|
self.sf.write("s_en{0} ".format(bank))
|
||||||
|
self.sf.write("{0}\n".format(model_name))
|
||||||
|
|
||||||
def create_inverter(self, size=1, beta=2.5):
|
def create_inverter(self, size=1, beta=2.5):
|
||||||
""" Generates inverter for the top level signals (only for sim purposes) """
|
""" Generates inverter for the top level signals (only for sim purposes) """
|
||||||
|
|
|
||||||
|
|
@ -55,12 +55,6 @@ class dff(design.design):
|
||||||
transition_prob = 0.5
|
transition_prob = 0.5
|
||||||
return transition_prob*(c_load + c_para)
|
return transition_prob*(c_load + c_para)
|
||||||
|
|
||||||
def get_clk_cin(self):
|
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
|
|
||||||
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
|
|
||||||
#Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width.
|
|
||||||
return parameter["dff_clk_cin"]
|
|
||||||
|
|
||||||
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."""
|
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
||||||
self.add_graph_edges(graph, port_nets)
|
self.add_graph_edges(graph, port_nets)
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,6 @@ class bank(design.design):
|
||||||
self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1)
|
self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1)
|
||||||
self.bank_array_ur = self.bitcell_array_inst.ur()
|
self.bank_array_ur = self.bitcell_array_inst.ur()
|
||||||
self.bank_array_ul = self.bitcell_array_inst.ul()
|
self.bank_array_ul = self.bitcell_array_inst.ul()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
|
|
@ -1049,42 +1048,6 @@ class bank(design.design):
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=control_pos)
|
offset=control_pos)
|
||||||
|
|
||||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
|
||||||
"""Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline"""
|
|
||||||
# Decoder is assumed to have settled before the negative edge of the clock.
|
|
||||||
# Delay model relies on this assumption
|
|
||||||
stage_effort_list = []
|
|
||||||
wordline_cout = self.bitcell_array.get_wordline_cin() + external_cout
|
|
||||||
stage_effort_list += self.port_address.wordline_driver.determine_wordline_stage_efforts(wordline_cout,
|
|
||||||
inp_is_rise)
|
|
||||||
|
|
||||||
return stage_effort_list
|
|
||||||
|
|
||||||
def get_wl_en_cin(self):
|
|
||||||
"""Get the relative capacitance of all the clk connections in the bank"""
|
|
||||||
# wl_en only used in the wordline driver.
|
|
||||||
return self.port_address.wordline_driver.get_wl_en_cin()
|
|
||||||
|
|
||||||
def get_w_en_cin(self):
|
|
||||||
"""Get the relative capacitance of all the clk connections in the bank"""
|
|
||||||
# wl_en only used in the wordline driver.
|
|
||||||
port = self.write_ports[0]
|
|
||||||
return self.port_data[port].write_driver.get_w_en_cin()
|
|
||||||
|
|
||||||
def get_clk_bar_cin(self):
|
|
||||||
"""Get the relative capacitance of all the clk_bar connections in the bank"""
|
|
||||||
# Current bank only uses clock bar (clk_buf_bar) as an enable for the precharge array.
|
|
||||||
|
|
||||||
# Precharges are the all the same in Mulitport, one is picked
|
|
||||||
port = self.read_ports[0]
|
|
||||||
return self.port_data[port].precharge_array.get_en_cin()
|
|
||||||
|
|
||||||
def get_sen_cin(self):
|
|
||||||
"""Get the relative capacitance of all the sense amp enable connections in the bank"""
|
|
||||||
# Current bank only uses sen as an enable for the sense amps.
|
|
||||||
port = self.read_ports[0]
|
|
||||||
return self.port_data[port].sense_amp_array.get_en_cin()
|
|
||||||
|
|
||||||
def graph_exclude_precharge(self):
|
def graph_exclude_precharge(self):
|
||||||
"""Precharge adds a loop between bitlines, can be excluded to reduce complexity"""
|
"""Precharge adds a loop between bitlines, can be excluded to reduce complexity"""
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
|
|
|
||||||
|
|
@ -104,13 +104,6 @@ class bitcell_array(bitcell_base_array):
|
||||||
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c
|
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c
|
||||||
return bl_wire
|
return bl_wire
|
||||||
|
|
||||||
def get_wordline_cin(self):
|
|
||||||
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
|
||||||
# A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
|
||||||
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):
|
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"""
|
||||||
# Function is not robust with column mux configurations
|
# Function is not robust with column mux configurations
|
||||||
|
|
|
||||||
|
|
@ -156,96 +156,12 @@ class control_logic(design.design):
|
||||||
self.nand2 = factory.create(module_type="pnand2",
|
self.nand2 = factory.create(module_type="pnand2",
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.nand2)
|
self.add_mod(self.nand2)
|
||||||
|
|
||||||
# if (self.port_type == "rw") or (self.port_type == "r"):
|
|
||||||
# from importlib import reload
|
|
||||||
# self.delay_chain_resized = False
|
|
||||||
# c = reload(__import__(OPTS.replica_bitline))
|
|
||||||
# replica_bitline = getattr(c, OPTS.replica_bitline)
|
|
||||||
# bitcell_loads = int(math.ceil(self.num_rows * OPTS.rbl_delay_percentage))
|
|
||||||
# #Use a model to determine the delays with that heuristic
|
|
||||||
# if OPTS.use_tech_delay_chain_size: #Use tech parameters if set.
|
|
||||||
# fanout_list = OPTS.delay_chain_stages*[OPTS.delay_chain_fanout_per_stage]
|
|
||||||
# debug.info(1, "Using tech parameters to size delay chain: fanout_list={}".format(fanout_list))
|
|
||||||
# self.replica_bitline = factory.create(module_type="replica_bitline",
|
|
||||||
# delay_fanout_list=fanout_list,
|
|
||||||
# bitcell_loads=bitcell_loads)
|
|
||||||
# if self.sram != None: #Calculate model value even for specified sizes
|
|
||||||
# self.set_sen_wl_delays()
|
|
||||||
|
|
||||||
# else: #Otherwise, use a heuristic and/or model based sizing.
|
|
||||||
# #First use a heuristic
|
|
||||||
# delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size()
|
|
||||||
# self.replica_bitline = factory.create(module_type="replica_bitline",
|
|
||||||
# delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic,
|
|
||||||
# bitcell_loads=bitcell_loads)
|
|
||||||
# #Resize if necessary, condition depends on resizing method
|
|
||||||
# if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match():
|
|
||||||
# #This resizes to match fall and rise delays, can make the delay chain weird sizes.
|
|
||||||
# stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic)
|
|
||||||
# self.replica_bitline = factory.create(module_type="replica_bitline",
|
|
||||||
# delay_fanout_list=stage_list,
|
|
||||||
# bitcell_loads=bitcell_loads)
|
|
||||||
|
|
||||||
# #This resizes based on total delay.
|
|
||||||
# # delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic)
|
|
||||||
# # self.replica_bitline = factory.create(module_type="replica_bitline",
|
|
||||||
# # delay_fanout_list=[delay_fanout]*delay_stages,
|
|
||||||
# # bitcell_loads=bitcell_loads)
|
|
||||||
|
|
||||||
# self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing
|
|
||||||
# self.delay_chain_resized = True
|
|
||||||
|
|
||||||
debug.check(OPTS.delay_chain_stages % 2,
|
debug.check(OPTS.delay_chain_stages % 2,
|
||||||
"Must use odd number of delay chain stages for inverting delay chain.")
|
"Must use odd number of delay chain stages for inverting delay chain.")
|
||||||
self.delay_chain=factory.create(module_type="delay_chain",
|
self.delay_chain=factory.create(module_type="delay_chain",
|
||||||
fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ])
|
fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ])
|
||||||
self.add_mod(self.delay_chain)
|
self.add_mod(self.delay_chain)
|
||||||
|
|
||||||
def get_heuristic_delay_chain_size(self):
|
|
||||||
"""Use a basic heuristic to determine the size of the delay chain used for the Sense Amp Enable """
|
|
||||||
# FIXME: The minimum was 2 fanout, now it will not pass DRC unless it is 3. Why?
|
|
||||||
delay_fanout = 3 # This can be anything >=3
|
|
||||||
# Model poorly captures delay of the column mux. Be pessismistic for column mux
|
|
||||||
if self.words_per_row >= 2:
|
|
||||||
delay_stages = 8
|
|
||||||
else:
|
|
||||||
delay_stages = 2
|
|
||||||
|
|
||||||
# Read ports have a shorter s_en delay. The model is not accurate enough to catch this difference
|
|
||||||
# on certain sram configs.
|
|
||||||
if self.port_type == "r":
|
|
||||||
delay_stages+=2
|
|
||||||
|
|
||||||
return (delay_stages, delay_fanout)
|
|
||||||
|
|
||||||
def set_sen_wl_delays(self):
|
|
||||||
"""Set delays for wordline and sense amp enable"""
|
|
||||||
self.wl_delay_rise, self.wl_delay_fall = self.get_delays_to_wl()
|
|
||||||
self.sen_delay_rise, self.sen_delay_fall = self.get_delays_to_sen()
|
|
||||||
self.wl_delay = self.wl_delay_rise + self.wl_delay_fall
|
|
||||||
self.sen_delay = self.sen_delay_rise + self.sen_delay_fall
|
|
||||||
|
|
||||||
def does_sen_rise_fall_timing_match(self):
|
|
||||||
"""Compare the relative rise/fall delays of the sense amp enable and wordline"""
|
|
||||||
self.set_sen_wl_delays()
|
|
||||||
# This is not necessarily more reliable than total delay in some cases.
|
|
||||||
if (self.wl_delay_rise * self.wl_timing_tolerance >= self.sen_delay_rise or
|
|
||||||
self.wl_delay_fall * self.wl_timing_tolerance >= self.sen_delay_fall):
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def does_sen_total_timing_match(self):
|
|
||||||
"""Compare the total delays of the sense amp enable and wordline"""
|
|
||||||
self.set_sen_wl_delays()
|
|
||||||
# The sen delay must always be bigger than than the wl
|
|
||||||
# delay. This decides how much larger the sen delay must be
|
|
||||||
# before a re-size is warranted.
|
|
||||||
if self.wl_delay * self.wl_timing_tolerance >= self.sen_delay:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout):
|
def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout):
|
||||||
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
|
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
|
||||||
|
|
@ -333,17 +249,6 @@ class control_logic(design.design):
|
||||||
delay_per_stage = fanout + 1 + self.inv_parasitic_delay
|
delay_per_stage = fanout + 1 + self.inv_parasitic_delay
|
||||||
delay_stages = ceil(required_delay / delay_per_stage)
|
delay_stages = ceil(required_delay / delay_per_stage)
|
||||||
return delay_stages
|
return delay_stages
|
||||||
|
|
||||||
def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall):
|
|
||||||
"""
|
|
||||||
Produces a list of fanouts which determine the size of the delay chain.
|
|
||||||
List length is the number of stages.
|
|
||||||
Assumes the first stage is falling.
|
|
||||||
"""
|
|
||||||
stage_list = []
|
|
||||||
for i in range(total_stages):
|
|
||||||
if i % 2 == 0:
|
|
||||||
stage_list.append()
|
|
||||||
|
|
||||||
def setup_signal_busses(self):
|
def setup_signal_busses(self):
|
||||||
""" Setup bus names, determine the size of the busses etc """
|
""" Setup bus names, determine the size of the busses etc """
|
||||||
|
|
@ -869,138 +774,6 @@ class control_logic(design.design):
|
||||||
offset=pin.ll(),
|
offset=pin.ll(),
|
||||||
height=pin.height(),
|
height=pin.height(),
|
||||||
width=pin.width())
|
width=pin.width())
|
||||||
|
|
||||||
def get_delays_to_wl(self):
|
|
||||||
"""Get the delay (in delay units) of the clk to a wordline in the bitcell array"""
|
|
||||||
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
|
|
||||||
self.wl_stage_efforts = self.get_wordline_stage_efforts()
|
|
||||||
clk_to_wl_rise, clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts)
|
|
||||||
total_delay = clk_to_wl_rise + clk_to_wl_fall
|
|
||||||
debug.info(1,
|
|
||||||
"Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise,
|
|
||||||
clk_to_wl_fall,
|
|
||||||
total_delay))
|
|
||||||
return clk_to_wl_rise, clk_to_wl_fall
|
|
||||||
|
|
||||||
def get_wordline_stage_efforts(self):
|
|
||||||
"""Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts"""
|
|
||||||
stage_effort_list = []
|
|
||||||
|
|
||||||
# Initial direction of gated_clk_bar signal for this path
|
|
||||||
is_clk_bar_rise = True
|
|
||||||
|
|
||||||
# Calculate the load on wl_en within the module and add it to external load
|
|
||||||
external_cout = self.sram.get_wl_en_cin()
|
|
||||||
# First stage is the clock buffer
|
|
||||||
stage_effort_list += self.clk_buf_driver.get_stage_efforts(external_cout, is_clk_bar_rise)
|
|
||||||
last_stage_is_rise = stage_effort_list[-1].is_rise
|
|
||||||
|
|
||||||
# Then ask the sram for the other path delays (from the bank)
|
|
||||||
stage_effort_list += self.sram.get_wordline_stage_efforts(last_stage_is_rise)
|
|
||||||
|
|
||||||
return stage_effort_list
|
|
||||||
|
|
||||||
def get_delays_to_sen(self):
|
|
||||||
"""
|
|
||||||
Get the delay (in delay units) of the clk to a sense amp enable.
|
|
||||||
This does not incorporate the delay of the replica bitline.
|
|
||||||
"""
|
|
||||||
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
|
|
||||||
self.sen_stage_efforts = self.get_sa_enable_stage_efforts()
|
|
||||||
clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts)
|
|
||||||
total_delay = clk_to_sen_rise + clk_to_sen_fall
|
|
||||||
debug.info(1,
|
|
||||||
"Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise,
|
|
||||||
clk_to_sen_fall,
|
|
||||||
total_delay))
|
|
||||||
return clk_to_sen_rise, clk_to_sen_fall
|
|
||||||
|
|
||||||
def get_sa_enable_stage_efforts(self):
|
|
||||||
"""Follows the gated_clk_bar signal to the sense amp enable signal adding each stages stage effort to a list"""
|
|
||||||
stage_effort_list = []
|
|
||||||
|
|
||||||
# Initial direction of clock signal for this path
|
|
||||||
last_stage_rise = True
|
|
||||||
|
|
||||||
# First stage, gated_clk_bar -(and2)-> rbl_in. Only for RW ports.
|
|
||||||
if self.port_type == "rw":
|
|
||||||
stage1_cout = self.replica_bitline.get_en_cin()
|
|
||||||
stage_effort_list += self.and2.get_stage_efforts(stage1_cout, last_stage_rise)
|
|
||||||
last_stage_rise = stage_effort_list[-1].is_rise
|
|
||||||
|
|
||||||
# Replica bitline stage, rbl_in -(rbl)-> pre_s_en
|
|
||||||
stage2_cout = self.sen_and2.get_cin()
|
|
||||||
stage_effort_list += self.replica_bitline.determine_sen_stage_efforts(stage2_cout, last_stage_rise)
|
|
||||||
last_stage_rise = stage_effort_list[-1].is_rise
|
|
||||||
|
|
||||||
# buffer stage, pre_s_en -(buffer)-> s_en
|
|
||||||
stage3_cout = self.sram.get_sen_cin()
|
|
||||||
stage_effort_list += self.s_en_driver.get_stage_efforts(stage3_cout, last_stage_rise)
|
|
||||||
last_stage_rise = stage_effort_list[-1].is_rise
|
|
||||||
|
|
||||||
return stage_effort_list
|
|
||||||
|
|
||||||
def get_wl_sen_delays(self):
|
|
||||||
""" Gets a list of the stages and delays in order of their path. """
|
|
||||||
|
|
||||||
if self.sen_stage_efforts == None or self.wl_stage_efforts == None:
|
|
||||||
debug.error("Model delays not calculated for SRAM.", 1)
|
|
||||||
wl_delays = logical_effort.calculate_delays(self.wl_stage_efforts)
|
|
||||||
sen_delays = logical_effort.calculate_delays(self.sen_stage_efforts)
|
|
||||||
return wl_delays, sen_delays
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load):
|
|
||||||
""" Gets the analytical delay from clk input to wl_en output """
|
|
||||||
|
|
||||||
stage_effort_list = []
|
|
||||||
# Calculate the load on clk_buf_bar
|
|
||||||
# ext_clk_buf_cout = self.sram.get_clk_bar_cin()
|
|
||||||
|
|
||||||
# Operations logic starts on negative edge
|
|
||||||
last_stage_rise = False
|
|
||||||
|
|
||||||
# First stage(s), clk -(pdriver)-> clk_buf.
|
|
||||||
# clk_buf_cout = self.replica_bitline.get_en_cin()
|
|
||||||
clk_buf_cout = 0
|
|
||||||
stage_effort_list += self.clk_buf_driver.get_stage_efforts(clk_buf_cout, last_stage_rise)
|
|
||||||
last_stage_rise = stage_effort_list[-1].is_rise
|
|
||||||
|
|
||||||
# Second stage, clk_buf -(inv)-> clk_bar
|
|
||||||
clk_bar_cout = self.and2.get_cin()
|
|
||||||
stage_effort_list += self.and2.get_stage_efforts(clk_bar_cout, last_stage_rise)
|
|
||||||
last_stage_rise = stage_effort_list[-1].is_rise
|
|
||||||
|
|
||||||
# Third stage clk_bar -(and)-> gated_clk_bar
|
|
||||||
gated_clk_bar_cin = self.get_gated_clk_bar_cin()
|
|
||||||
stage_effort_list.append(self.inv.get_stage_effort(gated_clk_bar_cin, last_stage_rise))
|
|
||||||
last_stage_rise = stage_effort_list[-1].is_rise
|
|
||||||
|
|
||||||
# Stages from gated_clk_bar -------> wordline
|
|
||||||
stage_effort_list += self.get_wordline_stage_efforts()
|
|
||||||
return stage_effort_list
|
|
||||||
|
|
||||||
def get_clk_buf_cin(self):
|
|
||||||
"""
|
|
||||||
Get the loads that are connected to the buffered clock.
|
|
||||||
Includes all the DFFs and some logic.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Control logic internal load
|
|
||||||
int_clk_buf_cap = self.inv.get_cin() + self.ctrl_dff_array.get_clk_cin() + self.and2.get_cin()
|
|
||||||
|
|
||||||
# Control logic external load (in the other parts of the SRAM)
|
|
||||||
ext_clk_buf_cap = self.sram.get_clk_bar_cin()
|
|
||||||
|
|
||||||
return int_clk_buf_cap + ext_clk_buf_cap
|
|
||||||
|
|
||||||
def get_gated_clk_bar_cin(self):
|
|
||||||
"""Get intermediates net gated_clk_bar's capacitance"""
|
|
||||||
|
|
||||||
total_cin = 0
|
|
||||||
total_cin += self.wl_en_driver.get_cin()
|
|
||||||
if self.port_type == 'rw':
|
|
||||||
total_cin += self.and2.get_cin()
|
|
||||||
return total_cin
|
|
||||||
|
|
||||||
def graph_exclude_dffs(self):
|
def graph_exclude_dffs(self):
|
||||||
"""Exclude dffs from graph as they do not represent critical path"""
|
"""Exclude dffs from graph as they do not represent critical path"""
|
||||||
|
|
|
||||||
|
|
@ -210,25 +210,3 @@ class delay_chain(design.design):
|
||||||
layer="m2",
|
layer="m2",
|
||||||
start=mid_point,
|
start=mid_point,
|
||||||
end=mid_point.scale(1, 0))
|
end=mid_point.scale(1, 0))
|
||||||
|
|
||||||
def get_cin(self):
|
|
||||||
"""Get the enable input ralative capacitance"""
|
|
||||||
# Only 1 input to the delay chain which is connected to an inverter.
|
|
||||||
dc_cin = self.inv.get_cin()
|
|
||||||
return dc_cin
|
|
||||||
|
|
||||||
def determine_delayed_en_stage_efforts(self, ext_delayed_en_cout, inp_is_rise=True):
|
|
||||||
"""Get the stage efforts from the en to s_en. Does not compute the delay for the bitline load."""
|
|
||||||
stage_effort_list = []
|
|
||||||
# Add a stage to the list for every stage in delay chain.
|
|
||||||
# Stages only differ in fanout except the last which has an external cout.
|
|
||||||
last_stage_is_rise = inp_is_rise
|
|
||||||
for stage_fanout in self.fanout_list:
|
|
||||||
stage_cout = self.inv.get_cin() * (stage_fanout + 1)
|
|
||||||
if len(stage_effort_list) == len(self.fanout_list) - 1:
|
|
||||||
stage_cout+=ext_delayed_en_cout
|
|
||||||
stage = self.inv.get_stage_effort(stage_cout, last_stage_is_rise)
|
|
||||||
stage_effort_list.append(stage)
|
|
||||||
last_stage_is_rise = stage.is_rise
|
|
||||||
|
|
||||||
return stage_effort_list
|
|
||||||
|
|
|
||||||
|
|
@ -155,9 +155,3 @@ class dff_array(design.design):
|
||||||
self.add_via_stack_center(from_layer=clk_pin.layer,
|
self.add_via_stack_center(from_layer=clk_pin.layer,
|
||||||
to_layer="m3",
|
to_layer="m3",
|
||||||
offset=vector(clk_pin.cx(), clk_ypos))
|
offset=vector(clk_pin.cx(), clk_ypos))
|
||||||
|
|
||||||
def get_clk_cin(self):
|
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""
|
|
||||||
dff_clk_cin = self.dff.get_clk_cin()
|
|
||||||
total_cin = dff_clk_cin * self.rows * self.columns
|
|
||||||
return total_cin
|
|
||||||
|
|
|
||||||
|
|
@ -196,10 +196,3 @@ class dff_buf(design.design):
|
||||||
self.add_via_stack_center(from_layer=a2_pin.layer,
|
self.add_via_stack_center(from_layer=a2_pin.layer,
|
||||||
to_layer="m2",
|
to_layer="m2",
|
||||||
offset=qb_pos)
|
offset=qb_pos)
|
||||||
|
|
||||||
def get_clk_cin(self):
|
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
|
|
||||||
# This is a handmade cell so the value must be entered in the tech.py file or estimated.
|
|
||||||
# Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width.
|
|
||||||
# FIXME: Dff changed in a past commit. The parameter need to be updated.
|
|
||||||
return parameter["dff_clk_cin"]
|
|
||||||
|
|
|
||||||
|
|
@ -227,9 +227,3 @@ class dff_buf_array(design.design):
|
||||||
# Drop a via to the M3 pin
|
# Drop a via to the M3 pin
|
||||||
self.add_via_center(layers=self.m2_stack,
|
self.add_via_center(layers=self.m2_stack,
|
||||||
offset=vector(clk_pin.cx(), clk_ypos))
|
offset=vector(clk_pin.cx(), clk_ypos))
|
||||||
|
|
||||||
def get_clk_cin(self):
|
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""
|
|
||||||
dff_clk_cin = self.dff.get_clk_cin()
|
|
||||||
total_cin = dff_clk_cin * self.rows * self.columns
|
|
||||||
return total_cin
|
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,3 @@ class dff_inv(design.design):
|
||||||
offset=dout_pin.center())
|
offset=dout_pin.center())
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=dout_pin.center())
|
offset=dout_pin.center())
|
||||||
|
|
||||||
def get_clk_cin(self):
|
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
|
|
||||||
return self.dff.get_clk_cin()
|
|
||||||
|
|
|
||||||
|
|
@ -188,10 +188,4 @@ class dff_inv_array(design.design):
|
||||||
height=self.height)
|
height=self.height)
|
||||||
# Drop a via to the M3 pin
|
# Drop a via to the M3 pin
|
||||||
self.add_via_center(layers=self.m2_stack,
|
self.add_via_center(layers=self.m2_stack,
|
||||||
offset=vector(clk_pin.cx(),clk_ypos))
|
offset=vector(clk_pin.cx(),clk_ypos))
|
||||||
|
|
||||||
def get_clk_cin(self):
|
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""
|
|
||||||
dff_clk_cin = self.dff.get_clk_cin()
|
|
||||||
total_cin = dff_clk_cin * self.rows * self.columns
|
|
||||||
return total_cin
|
|
||||||
|
|
|
||||||
|
|
@ -101,16 +101,6 @@ class dummy_array(bitcell_base_array):
|
||||||
self.copy_layout_pin(inst, pin_name)
|
self.copy_layout_pin(inst, pin_name)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
|
# FIXME: This appears to be old code from previous characterization. Needs to be updated.
|
||||||
wl_wire = self.gen_wl_wire()
|
wl_wire = self.gen_wl_wire()
|
||||||
return wl_wire.return_input_cap()
|
return wl_wire.return_input_cap()
|
||||||
|
|
||||||
def get_wordline_cin(self):
|
|
||||||
"""
|
|
||||||
Get the relative input capacitance from the
|
|
||||||
wordline connections in all the bitcell
|
|
||||||
"""
|
|
||||||
# A single wordline is connected to all the bitcells
|
|
||||||
# in a single row meaning the capacitance depends on the # of columns
|
|
||||||
bitcell_wl_cin = self.cell.get_wl_cin()
|
|
||||||
total_cin = bitcell_wl_cin * self.column_size
|
|
||||||
return total_cin
|
|
||||||
|
|
|
||||||
|
|
@ -606,11 +606,3 @@ class hierarchical_decoder(design.design):
|
||||||
to_layer=self.output_layer,
|
to_layer=self.output_layer,
|
||||||
offset=rail_pos,
|
offset=rail_pos,
|
||||||
directions=self.bus_directions)
|
directions=self.bus_directions)
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
if self.determine_predecodes(self.num_inputs)[1]==0:
|
|
||||||
pre = self.pre2_4
|
|
||||||
else:
|
|
||||||
pre = self.pre3_8
|
|
||||||
return pre.input_load()
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,13 +125,3 @@ class precharge_array(design.design):
|
||||||
offset = vector(tempx, 0)
|
offset = vector(tempx, 0)
|
||||||
self.local_insts[i].place(offset=offset, mirror=mirror)
|
self.local_insts[i].place(offset=offset, mirror=mirror)
|
||||||
xoffset = xoffset + self.pc_cell.width
|
xoffset = xoffset + self.pc_cell.width
|
||||||
|
|
||||||
def get_en_cin(self):
|
|
||||||
"""
|
|
||||||
Get the relative capacitance of all the clk connections
|
|
||||||
in the precharge array
|
|
||||||
"""
|
|
||||||
# Assume single port
|
|
||||||
precharge_en_cin = self.pc_cell.get_en_cin()
|
|
||||||
return precharge_en_cin * self.columns
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -489,13 +489,6 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
||||||
return bl_wire
|
return bl_wire
|
||||||
|
|
||||||
def get_wordline_cin(self):
|
|
||||||
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
|
||||||
# A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
|
||||||
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):
|
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"""
|
||||||
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
|
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
|
||||||
|
|
|
||||||
|
|
@ -82,13 +82,6 @@ class sense_amp(design.design):
|
||||||
# Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
|
# 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
|
return total_power
|
||||||
|
|
||||||
def get_en_cin(self):
|
|
||||||
"""Get the relative capacitance of sense amp enable gate cin"""
|
|
||||||
pmos_cin = parameter["sa_en_pmos_size"] / drc("minwidth_tx")
|
|
||||||
nmos_cin = parameter["sa_en_nmos_size"] / drc("minwidth_tx")
|
|
||||||
# sen is connected to 2 pmos isolation TX and 1 nmos per sense amp.
|
|
||||||
return 2 * pmos_cin + nmos_cin
|
|
||||||
|
|
||||||
def get_enable_name(self):
|
def get_enable_name(self):
|
||||||
"""Returns name used for enable net"""
|
"""Returns name used for enable net"""
|
||||||
|
|
|
||||||
|
|
@ -190,18 +190,3 @@ class sense_amp_array(design.design):
|
||||||
self.add_via_stack_center(from_layer=en_pin.layer,
|
self.add_via_stack_center(from_layer=en_pin.layer,
|
||||||
to_layer=self.en_layer,
|
to_layer=self.en_layer,
|
||||||
offset=inst.get_pin(self.amp.en_name).center())
|
offset=inst.get_pin(self.amp.en_name).center())
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
return self.amp.input_load()
|
|
||||||
|
|
||||||
def get_en_cin(self):
|
|
||||||
"""Get the relative capacitance of all the sense amp enable connections in the array"""
|
|
||||||
sense_amp_en_cin = self.amp.get_en_cin()
|
|
||||||
return sense_amp_en_cin * self.word_size
|
|
||||||
|
|
||||||
def get_drain_cin(self):
|
|
||||||
"""Get the relative capacitance of the drain of the PMOS isolation TX"""
|
|
||||||
from tech import parameter
|
|
||||||
# Bitcell drain load being used to estimate PMOS drain load
|
|
||||||
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
|
|
||||||
return drain_load
|
|
||||||
|
|
|
||||||
|
|
@ -230,10 +230,3 @@ class single_level_column_mux_array(design.design):
|
||||||
to_layer=self.sel_layer,
|
to_layer=self.sel_layer,
|
||||||
offset=br_out_offset_begin,
|
offset=br_out_offset_begin,
|
||||||
directions=self.via_directions)
|
directions=self.via_directions)
|
||||||
|
|
||||||
def get_drain_cin(self):
|
|
||||||
"""Get the relative capacitance of the drain of the NMOS pass TX"""
|
|
||||||
from tech import parameter
|
|
||||||
# Bitcell drain load being used to estimate mux NMOS drain load
|
|
||||||
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
|
|
||||||
return drain_load
|
|
||||||
|
|
|
||||||
|
|
@ -159,24 +159,3 @@ class wordline_driver_array(design.design):
|
||||||
layer=self.route_layer,
|
layer=self.route_layer,
|
||||||
start=wl_offset,
|
start=wl_offset,
|
||||||
end=wl_offset - vector(self.m1_width, 0))
|
end=wl_offset - vector(self.m1_width, 0))
|
||||||
|
|
||||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
|
||||||
"""
|
|
||||||
Follows the clk_buf to a wordline signal adding
|
|
||||||
each stages stage effort to a list.
|
|
||||||
"""
|
|
||||||
stage_effort_list = []
|
|
||||||
|
|
||||||
stage1 = self.wl_driver.get_stage_effort(external_cout, inp_is_rise)
|
|
||||||
stage_effort_list.append(stage1)
|
|
||||||
|
|
||||||
return stage_effort_list
|
|
||||||
|
|
||||||
def get_wl_en_cin(self):
|
|
||||||
"""
|
|
||||||
Get the relative capacitance of all
|
|
||||||
the enable connections in the bank
|
|
||||||
"""
|
|
||||||
# The enable is connected to a and2 for every row.
|
|
||||||
total_cin = self.wl_driver.get_cin() * self.rows
|
|
||||||
return total_cin
|
|
||||||
|
|
|
||||||
|
|
@ -265,7 +265,3 @@ class write_driver_array(design.design):
|
||||||
offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1),
|
offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1),
|
||||||
width=self.width)
|
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)
|
|
||||||
|
|
|
||||||
|
|
@ -140,8 +140,3 @@ class write_mask_and_array(design.design):
|
||||||
supply_pin_left = self.and2_insts[0].get_pin(supply)
|
supply_pin_left = self.and2_insts[0].get_pin(supply)
|
||||||
supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply)
|
supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply)
|
||||||
self.add_path(supply_pin_left.layer, [supply_pin_left.lc(), supply_pin_right.rc()])
|
self.add_path(supply_pin_left.layer, [supply_pin_left.lc(), supply_pin_right.rc()])
|
||||||
|
|
||||||
def get_cin(self):
|
|
||||||
"""Get the relative capacitance of all the input connections in the bank"""
|
|
||||||
# The enable is connected to an and2 for every row.
|
|
||||||
return self.and2.get_cin() * len(self.and2_insts)
|
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,8 @@ class options(optparse.Values):
|
||||||
trim_netlist = False
|
trim_netlist = False
|
||||||
# Run with extracted parasitics
|
# Run with extracted parasitics
|
||||||
use_pex = False
|
use_pex = False
|
||||||
|
# Output config with all options
|
||||||
|
output_extended_config = False
|
||||||
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
|
|
|
||||||
|
|
@ -146,21 +146,4 @@ class pand2(pgate.pgate):
|
||||||
offset=pin.center(),
|
offset=pin.center(),
|
||||||
width=pin.width(),
|
width=pin.width(),
|
||||||
height=pin.height())
|
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 = []
|
|
||||||
stage1_cout = self.inv.get_cin()
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
@ -161,21 +161,4 @@ class pand3(pgate.pgate):
|
||||||
slew=nand_delay.slew,
|
slew=nand_delay.slew,
|
||||||
load=load)
|
load=load)
|
||||||
return nand_delay + inv_delay
|
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 = []
|
|
||||||
stage1_cout = self.inv.get_cin()
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
@ -96,21 +96,4 @@ class pbuf(pgate.pgate):
|
||||||
offset=a_pin.center(),
|
offset=a_pin.center(),
|
||||||
width=a_pin.width(),
|
width=a_pin.width(),
|
||||||
height=a_pin.height())
|
height=a_pin.height())
|
||||||
|
|
||||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
|
||||||
"""Get the stage efforts of the A -> Z path"""
|
|
||||||
stage_effort_list = []
|
|
||||||
stage1_cout = self.inv2.get_cin()
|
|
||||||
stage1 = self.inv1.get_stage_effort(stage1_cout, inp_is_rise)
|
|
||||||
stage_effort_list.append(stage1)
|
|
||||||
last_stage_is_rise = stage1.is_rise
|
|
||||||
|
|
||||||
stage2 = self.inv2.get_stage_effort(external_cout, last_stage_is_rise)
|
|
||||||
stage_effort_list.append(stage2)
|
|
||||||
|
|
||||||
return stage_effort_list
|
|
||||||
|
|
||||||
def get_cin(self):
|
|
||||||
"""Returns the relative capacitance of the input"""
|
|
||||||
input_cin = self.inv1.get_cin()
|
|
||||||
return input_cin
|
|
||||||
|
|
@ -168,24 +168,4 @@ class pdriver(pgate.pgate):
|
||||||
def get_sizes(self):
|
def get_sizes(self):
|
||||||
""" Return the relative sizes of the buffers """
|
""" Return the relative sizes of the buffers """
|
||||||
return self.size_list
|
return self.size_list
|
||||||
|
|
||||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
|
||||||
""" Get the stage efforts of the A -> Z path """
|
|
||||||
cout_list = []
|
|
||||||
for prev_inv, inv in zip(self.inv_list, self.inv_list[1:]):
|
|
||||||
cout_list.append(inv.get_cin())
|
|
||||||
|
|
||||||
cout_list.append(external_cout)
|
|
||||||
|
|
||||||
stage_effort_list = []
|
|
||||||
last_inp_is_rise = inp_is_rise
|
|
||||||
for inv, cout in zip(self.inv_list, cout_list):
|
|
||||||
stage = inv.get_stage_effort(cout, last_inp_is_rise)
|
|
||||||
stage_effort_list.append(stage)
|
|
||||||
last_inp_is_rise = stage.is_rise
|
|
||||||
|
|
||||||
return stage_effort_list
|
|
||||||
|
|
||||||
def get_cin(self):
|
|
||||||
""" Returns the relative capacitance of the input """
|
|
||||||
return self.inv_list[0].get_cin()
|
|
||||||
|
|
@ -184,36 +184,3 @@ class pinvbuf(pgate.pgate):
|
||||||
self.add_layout_pin_rect_center(text="A",
|
self.add_layout_pin_rect_center(text="A",
|
||||||
layer=a_pin.layer,
|
layer=a_pin.layer,
|
||||||
offset=a_pin.center())
|
offset=a_pin.center())
|
||||||
|
|
||||||
def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False):
|
|
||||||
"""Get the stage efforts of the clk -> clk_buf path"""
|
|
||||||
stage_effort_list = []
|
|
||||||
stage1_cout = self.inv1.get_cin() + self.inv2.get_cin()
|
|
||||||
stage1 = self.inv.get_stage_effort(stage1_cout, inp_is_rise)
|
|
||||||
stage_effort_list.append(stage1)
|
|
||||||
last_stage_is_rise = stage1.is_rise
|
|
||||||
|
|
||||||
stage2 = self.inv2.get_stage_effort(external_cout, last_stage_is_rise)
|
|
||||||
stage_effort_list.append(stage2)
|
|
||||||
|
|
||||||
return stage_effort_list
|
|
||||||
|
|
||||||
def determine_clk_buf_bar_stage_efforts(self, external_cout, inp_is_rise=False):
|
|
||||||
"""Get the stage efforts of the clk -> clk_buf path"""
|
|
||||||
|
|
||||||
# After (almost) every stage, the direction of the signal inverts.
|
|
||||||
stage_effort_list = []
|
|
||||||
stage1_cout = self.inv1.get_cin() + self.inv2.get_cin()
|
|
||||||
stage1 = self.inv.get_stage_effort(stage1_cout, inp_is_rise)
|
|
||||||
stage_effort_list.append(stage1)
|
|
||||||
last_stage_is_rise = stage_effort_list[-1].is_rise
|
|
||||||
|
|
||||||
stage2_cout = self.inv2.get_cin()
|
|
||||||
stage2 = self.inv1.get_stage_effort(stage2_cout, last_stage_is_rise)
|
|
||||||
stage_effort_list.append(stage2)
|
|
||||||
last_stage_is_rise = stage_effort_list[-1].is_rise
|
|
||||||
|
|
||||||
stage3 = self.inv2.get_stage_effort(external_cout, last_stage_is_rise)
|
|
||||||
stage_effort_list.append(stage3)
|
|
||||||
|
|
||||||
return stage_effort_list
|
|
||||||
|
|
|
||||||
|
|
@ -300,11 +300,4 @@ class precharge(design.design):
|
||||||
self.add_path(self.bitline_layer,
|
self.add_path(self.bitline_layer,
|
||||||
[left_pos, right_pos],
|
[left_pos, right_pos],
|
||||||
width=pmos_pin.height())
|
width=pmos_pin.height())
|
||||||
|
|
||||||
def get_en_cin(self):
|
|
||||||
"""Get the relative capacitance of the enable in the precharge cell"""
|
|
||||||
# The enable connect to three pmos gates
|
|
||||||
# They all use the same size pmos.
|
|
||||||
pmos_cin = self.pmos.get_cin()
|
|
||||||
return 3 * pmos_cin
|
|
||||||
|
|
||||||
|
|
@ -241,18 +241,3 @@ class single_level_column_mux(pgate.pgate):
|
||||||
offset=vector(0, 0),
|
offset=vector(0, 0),
|
||||||
width=self.bitcell.width,
|
width=self.bitcell.width,
|
||||||
height=self.height)
|
height=self.height)
|
||||||
|
|
||||||
def get_stage_effort(self, corner, slew, load):
|
|
||||||
"""
|
|
||||||
Returns relative delay that the column mux.
|
|
||||||
Difficult to convert to LE model.
|
|
||||||
"""
|
|
||||||
parasitic_delay = 1
|
|
||||||
# This is not CMOS, so using this may be incorrect.
|
|
||||||
cin = 2 * self.tx_size
|
|
||||||
return logical_effort.logical_effort("column_mux",
|
|
||||||
self.tx_size,
|
|
||||||
cin,
|
|
||||||
load,
|
|
||||||
parasitic_delay,
|
|
||||||
False)
|
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,18 @@ class sram():
|
||||||
def verilog_write(self, name):
|
def verilog_write(self, name):
|
||||||
self.s.verilog_write(name)
|
self.s.verilog_write(name)
|
||||||
|
|
||||||
|
def extended_config_write(self, name):
|
||||||
|
"""Dump config file with all options.
|
||||||
|
Include defaults and anything changed by input config."""
|
||||||
|
f = open(name, "w")
|
||||||
|
var_dict = dict((name, getattr(OPTS, name)) for name in dir(OPTS) if not name.startswith('__') and not callable(getattr(OPTS, name)))
|
||||||
|
for var_name, var_value in var_dict.items():
|
||||||
|
if isinstance(var_value, str):
|
||||||
|
f.write(str(var_name) + " = " + "\"" + str(var_value) + "\"\n")
|
||||||
|
else:
|
||||||
|
f.write(str(var_name) + " = " + str(var_value)+ "\n")
|
||||||
|
f.close()
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
""" Save all the output files while reporting time to do it as well. """
|
""" Save all the output files while reporting time to do it as well. """
|
||||||
|
|
||||||
|
|
@ -137,3 +149,11 @@ class sram():
|
||||||
debug.print_raw("Verilog: Writing to {0}".format(vname))
|
debug.print_raw("Verilog: Writing to {0}".format(vname))
|
||||||
self.verilog_write(vname)
|
self.verilog_write(vname)
|
||||||
print_time("Verilog", datetime.datetime.now(), start_time)
|
print_time("Verilog", datetime.datetime.now(), start_time)
|
||||||
|
|
||||||
|
# Write out options if specified
|
||||||
|
if OPTS.output_extended_config:
|
||||||
|
start_time = datetime.datetime.now()
|
||||||
|
oname = OPTS.output_path + OPTS.output_name + "_extended.py"
|
||||||
|
debug.print_raw("Extended Config: Writing to {0}".format(oname))
|
||||||
|
self.extended_config_write(oname)
|
||||||
|
print_time("Extended Config", datetime.datetime.now(), start_time)
|
||||||
|
|
@ -626,24 +626,13 @@ class sram_1bank(sram_base):
|
||||||
# Insts located in control logic, exclusion function called here
|
# Insts located in control logic, exclusion function called here
|
||||||
for inst in self.control_logic_insts:
|
for inst in self.control_logic_insts:
|
||||||
inst.mod.graph_exclude_dffs()
|
inst.mod.graph_exclude_dffs()
|
||||||
|
|
||||||
def get_sen_name(self, sram_name, port=0):
|
|
||||||
"""Returns the s_en spice name."""
|
|
||||||
# Naming scheme is hardcoded using this function, should be built into the
|
|
||||||
# graph in someway.
|
|
||||||
sen_name = "s_en{}".format(port)
|
|
||||||
control_conns = self.get_conns(self.control_logic_insts[port])
|
|
||||||
# Sanity checks
|
|
||||||
if sen_name not in control_conns:
|
|
||||||
debug.error("Signal={} not contained in control logic connections={}".format(sen_name,
|
|
||||||
control_conns))
|
|
||||||
if sen_name in self.pins:
|
|
||||||
debug.error("Internal signal={} contained in port list. Name defined by the parent.".format(sen_name))
|
|
||||||
return "X{}.{}".format(sram_name, sen_name)
|
|
||||||
|
|
||||||
def get_cell_name(self, inst_name, row, col):
|
def get_cell_name(self, inst_name, row, col):
|
||||||
"""Gets the spice name of the target bitcell."""
|
"""Gets the spice name of the target bitcell."""
|
||||||
# Sanity check in case it was forgotten
|
# Sanity check in case it was forgotten
|
||||||
if inst_name.find('x') != 0:
|
if inst_name.find('x') != 0:
|
||||||
inst_name = 'x' + inst_name
|
inst_name = 'x'+inst_name
|
||||||
return self.bank_inst.mod.get_cell_name(inst_name + '.x' + self.bank_inst.name, row, col)
|
return self.bank_inst.mod.get_cell_name(inst_name+'.x'+self.bank_inst.name, row, col)
|
||||||
|
|
||||||
|
def get_bank_num(self, inst_name, row, col):
|
||||||
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ from design import design
|
||||||
from verilog import verilog
|
from verilog import verilog
|
||||||
from lef import lef
|
from lef import lef
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
from tech import drc
|
||||||
|
import numpy as np
|
||||||
|
import logical_effort
|
||||||
|
|
||||||
|
|
||||||
class sram_base(design, verilog, lef):
|
class sram_base(design, verilog, lef):
|
||||||
|
|
@ -84,8 +87,71 @@ class sram_base(design, verilog, lef):
|
||||||
for bit in range(self.word_size + self.num_spare_cols):
|
for bit in range(self.word_size + self.num_spare_cols):
|
||||||
self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT")
|
self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT")
|
||||||
|
|
||||||
self.add_pin("vdd", "POWER")
|
self.add_pin("vdd","POWER")
|
||||||
self.add_pin("gnd", "GROUND")
|
self.add_pin("gnd","GROUND")
|
||||||
|
|
||||||
|
def add_global_pex_labels(self):
|
||||||
|
"""
|
||||||
|
Add pex labels at the sram level for spice analysis
|
||||||
|
"""
|
||||||
|
|
||||||
|
# add pex labels for bitcells
|
||||||
|
for bank_num in range(len(self.bank_insts)):
|
||||||
|
bank = self.bank_insts[bank_num]
|
||||||
|
pex_data = bank.reverse_transformation_bitcell(bank.mod.bitcell.name)
|
||||||
|
|
||||||
|
bank_offset = pex_data[0] # offset bank relative to sram
|
||||||
|
Q_offset = pex_data[1] # offset of storage relative to bank
|
||||||
|
Q_bar_offset = pex_data[2] # offset of storage relative to bank
|
||||||
|
bl_offsets = pex_data[3]
|
||||||
|
br_offsets = pex_data[4]
|
||||||
|
bl_meta = pex_data[5]
|
||||||
|
br_meta = pex_data[6]
|
||||||
|
|
||||||
|
bl = []
|
||||||
|
br = []
|
||||||
|
|
||||||
|
storage_layer_name = "m1"
|
||||||
|
bitline_layer_name = "m2"
|
||||||
|
|
||||||
|
for cell in range(len(bank_offset)):
|
||||||
|
Q = [bank_offset[cell][0] + Q_offset[cell][0], bank_offset[cell][1] + Q_offset[cell][1]]
|
||||||
|
Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], bank_offset[cell][1] + Q_bar_offset[cell][1]]
|
||||||
|
OPTS.words_per_row = self.words_per_row
|
||||||
|
self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))) , storage_layer_name, Q)
|
||||||
|
self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))), storage_layer_name, Q_bar)
|
||||||
|
|
||||||
|
for cell in range(len(bl_offsets)):
|
||||||
|
col = bl_meta[cell][0][2]
|
||||||
|
for bitline in range(len(bl_offsets[cell])):
|
||||||
|
bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0], float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]]
|
||||||
|
bl.append([bitline_location, bl_meta[cell][bitline][3], col])
|
||||||
|
|
||||||
|
for cell in range(len(br_offsets)):
|
||||||
|
col = br_meta[cell][0][2]
|
||||||
|
for bitline in range(len(br_offsets[cell])):
|
||||||
|
bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0], float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]]
|
||||||
|
br.append([bitline_location, br_meta[cell][bitline][3], col])
|
||||||
|
|
||||||
|
for i in range(len(bl)):
|
||||||
|
self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]), bitline_layer_name, bl[i][0])
|
||||||
|
|
||||||
|
for i in range(len(br)):
|
||||||
|
self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]), bitline_layer_name, br[i][0])
|
||||||
|
|
||||||
|
# add pex labels for control logic
|
||||||
|
for i in range (len(self.control_logic_insts)):
|
||||||
|
instance = self.control_logic_insts[i]
|
||||||
|
control_logic_offset = instance.offset
|
||||||
|
for output in instance.mod.output_list:
|
||||||
|
pin = instance.mod.get_pin(output)
|
||||||
|
pin.transform([0,0], instance.mirror, instance.rotate)
|
||||||
|
offset = [control_logic_offset[0] + pin.center()[0], control_logic_offset[1] + pin.center()[1]]
|
||||||
|
self.add_layout_pin_rect_center("{0}{1}".format(pin.name,i), storage_layer_name, offset)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
""" Netlist creation """
|
""" Netlist creation """
|
||||||
|
|
@ -124,6 +190,8 @@ class sram_base(design, verilog, lef):
|
||||||
highest_coord = self.find_highest_coords()
|
highest_coord = self.find_highest_coords()
|
||||||
self.width = highest_coord[0]
|
self.width = highest_coord[0]
|
||||||
self.height = highest_coord[1]
|
self.height = highest_coord[1]
|
||||||
|
if OPTS.use_pex:
|
||||||
|
self.add_global_pex_labels()
|
||||||
self.add_boundary(ll=vector(0, 0),
|
self.add_boundary(ll=vector(0, 0),
|
||||||
ur=vector(self.width, self.height))
|
ur=vector(self.width, self.height))
|
||||||
|
|
||||||
|
|
@ -562,50 +630,4 @@ class sram_base(design, verilog, lef):
|
||||||
|
|
||||||
def lvs_write(self, sp_name):
|
def lvs_write(self, sp_name):
|
||||||
self.sp_write(sp_name, lvs_netlist=True)
|
self.sp_write(sp_name, lvs_netlist=True)
|
||||||
|
|
||||||
def get_wordline_stage_efforts(self, inp_is_rise=True):
|
|
||||||
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""
|
|
||||||
stage_effort_list = []
|
|
||||||
|
|
||||||
# Clk_buf originates from the control logic so only the bank is related to the wordline path
|
|
||||||
# No loading on the wordline other than in the bank.
|
|
||||||
external_wordline_cout = 0
|
|
||||||
stage_effort_list += self.bank.determine_wordline_stage_efforts(external_wordline_cout, inp_is_rise)
|
|
||||||
|
|
||||||
return stage_effort_list
|
|
||||||
|
|
||||||
def get_wl_en_cin(self):
|
|
||||||
"""Gets the capacitive load the of clock (clk_buf) for the sram"""
|
|
||||||
# Only the wordline drivers within the bank use this signal
|
|
||||||
return self.bank.get_wl_en_cin()
|
|
||||||
|
|
||||||
def get_w_en_cin(self):
|
|
||||||
"""Gets the capacitive load the of write enable (w_en) for the sram"""
|
|
||||||
# Only the write drivers within the bank use this signal
|
|
||||||
return self.bank.get_w_en_cin()
|
|
||||||
|
|
||||||
def get_p_en_bar_cin(self):
|
|
||||||
"""Gets the capacitive load the of precharge enable (p_en_bar) for the sram"""
|
|
||||||
# Only the precharges within the bank use this signal
|
|
||||||
return self.bank.get_p_en_bar_cin()
|
|
||||||
|
|
||||||
def get_clk_bar_cin(self):
|
|
||||||
"""Gets the capacitive load the of clock (clk_buf_bar) for the sram"""
|
|
||||||
# As clk_buf_bar is an output of the control logic. The cap for that module is not determined here.
|
|
||||||
# Only the precharge cells use this signal (other than the control logic)
|
|
||||||
return self.bank.get_clk_bar_cin()
|
|
||||||
|
|
||||||
def get_sen_cin(self):
|
|
||||||
"""Gets the capacitive load the of sense amp enable for the sram"""
|
|
||||||
# Only the sense_amps use this signal (other than the control logic)
|
|
||||||
return self.bank.get_sen_cin()
|
|
||||||
|
|
||||||
def get_dff_clk_buf_cin(self):
|
|
||||||
"""Get the relative capacitance of the clk_buf signal.
|
|
||||||
Does not get the control logic loading but everything else"""
|
|
||||||
total_cin = 0
|
|
||||||
total_cin += self.row_addr_dff.get_clk_cin()
|
|
||||||
total_cin += self.data_dff.get_clk_cin()
|
|
||||||
if self.col_addr_size > 0:
|
|
||||||
total_cin += self.col_addr_dff.get_clk_cin()
|
|
||||||
return total_cin
|
|
||||||
|
|
@ -109,21 +109,21 @@ def write_magic_script(cell_name, extract=False, final_verification=False):
|
||||||
# f.write(pre + "ext2spice hierarchy on\n")
|
# f.write(pre + "ext2spice hierarchy on\n")
|
||||||
# f.write(pre + "ext2spice scale off\n")
|
# f.write(pre + "ext2spice scale off\n")
|
||||||
# lvs exists in 8.2.79, but be backword compatible for now
|
# lvs exists in 8.2.79, but be backword compatible for now
|
||||||
# f.write(pre + "ext2spice lvs\n")
|
#f.write(pre+"ext2spice lvs\n")
|
||||||
f.write(pre + "ext2spice hierarchy on\n")
|
f.write(pre+"ext2spice hierarchy on\n")
|
||||||
f.write(pre + "ext2spice format ngspice\n")
|
f.write(pre+"ext2spice format ngspice\n")
|
||||||
f.write(pre + "ext2spice cthresh infinite\n")
|
f.write(pre+"ext2spice cthresh infinite\n")
|
||||||
f.write(pre + "ext2spice rthresh infinite\n")
|
f.write(pre+"ext2spice rthresh infinite\n")
|
||||||
f.write(pre + "ext2spice renumber off\n")
|
f.write(pre+"ext2spice renumber off\n")
|
||||||
f.write(pre + "ext2spice scale off\n")
|
f.write(pre+"ext2spice scale off\n")
|
||||||
f.write(pre + "ext2spice blackbox on\n")
|
f.write(pre+"ext2spice blackbox on\n")
|
||||||
f.write(pre + "ext2spice subcircuit top auto\n")
|
f.write(pre+"ext2spice subcircuit top on\n")
|
||||||
f.write(pre + "ext2spice global off\n")
|
f.write(pre+"ext2spice global off\n")
|
||||||
|
|
||||||
# Can choose hspice, ngspice, or spice3,
|
# Can choose hspice, ngspice, or spice3,
|
||||||
# but they all seem compatible enough.
|
# but they all seem compatible enough.
|
||||||
#f.write(pre + "ext2spice format ngspice\n")
|
f.write(pre+"ext2spice format ngspice\n")
|
||||||
f.write(pre + "ext2spice {}\n".format(cell_name))
|
f.write(pre+"ext2spice {}\n".format(cell_name))
|
||||||
f.write("quit -noprompt\n")
|
f.write("quit -noprompt\n")
|
||||||
f.write("EOF\n")
|
f.write("EOF\n")
|
||||||
|
|
||||||
|
|
@ -413,13 +413,13 @@ def write_script_pex_rule(gds_name,cell_name,output):
|
||||||
else:
|
else:
|
||||||
pre = ""
|
pre = ""
|
||||||
f.write(pre+"extract\n".format(cell_name))
|
f.write(pre+"extract\n".format(cell_name))
|
||||||
#f.write(pre+"ext2spice hierarchy on\n")
|
f.write(pre+"ext2spice hierarchy off\n")
|
||||||
#f.write(pre+"ext2spice format ngspice\n")
|
f.write(pre+"ext2spice format ngspice\n")
|
||||||
#f.write(pre+"ext2spice renumber off\n")
|
f.write(pre+"ext2spice renumber off\n")
|
||||||
#f.write(pre+"ext2spice scale off\n")
|
f.write(pre+"ext2spice scale off\n")
|
||||||
#f.write(pre+"ext2spice blackbox on\n")
|
f.write(pre+"ext2spice blackbox on\n")
|
||||||
f.write(pre+"ext2spice subcircuit top on\n")
|
f.write(pre+"ext2spice subcircuit top on\n")
|
||||||
#f.write(pre+"ext2spice global off\n")
|
f.write(pre+"ext2spice global off\n")
|
||||||
f.write(pre+"ext2spice {}\n".format(cell_name))
|
f.write(pre+"ext2spice {}\n".format(cell_name))
|
||||||
f.write("quit -noprompt\n")
|
f.write("quit -noprompt\n")
|
||||||
f.write("eof\n")
|
f.write("eof\n")
|
||||||
|
|
@ -442,31 +442,49 @@ def correct_port(name, output_file_name, ref_file_name):
|
||||||
pex_file = open(output_file_name, "r")
|
pex_file = open(output_file_name, "r")
|
||||||
contents = pex_file.read()
|
contents = pex_file.read()
|
||||||
# locate the start of circuit definition line
|
# locate the start of circuit definition line
|
||||||
match = re.search(".subckt " + str(name) + ".*", contents)
|
match = re.search(r'^\.subckt+[^M]*', contents, re.MULTILINE)
|
||||||
match_index_start = match.start()
|
match_index_start = match.start()
|
||||||
pex_file.seek(match_index_start)
|
match_index_end = match.end()
|
||||||
rest_text = pex_file.read()
|
|
||||||
# locate the end of circuit definition line
|
|
||||||
match = re.search(r'\n', rest_text)
|
|
||||||
match_index_end = match.start()
|
|
||||||
# store the unchanged part of pex file in memory
|
# store the unchanged part of pex file in memory
|
||||||
pex_file.seek(0)
|
pex_file.seek(0)
|
||||||
part1 = pex_file.read(match_index_start)
|
part1 = pex_file.read(match_index_start)
|
||||||
pex_file.seek(match_index_start + match_index_end)
|
pex_file.seek(match_index_end)
|
||||||
part2 = pex_file.read()
|
part2 = pex_file.read()
|
||||||
|
|
||||||
|
bitcell_list = "+ "
|
||||||
|
if OPTS.words_per_row:
|
||||||
|
for bank in range(OPTS.num_banks):
|
||||||
|
for bank in range(OPTS.num_banks):
|
||||||
|
row = int(OPTS.num_words / OPTS.words_per_row) - 1
|
||||||
|
col = int(OPTS.word_size * OPTS.words_per_row) - 1
|
||||||
|
bitcell_list += "bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col)
|
||||||
|
bitcell_list += "bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col)
|
||||||
|
for col in range(OPTS.word_size * OPTS.words_per_row):
|
||||||
|
for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports):
|
||||||
|
bitcell_list += "bl{0}_{1} ".format(bank, col)
|
||||||
|
bitcell_list += "br{0}_{1} ".format(bank, col)
|
||||||
|
bitcell_list += "\n"
|
||||||
|
control_list = "+ "
|
||||||
|
if OPTS.words_per_row:
|
||||||
|
for bank in range(OPTS.num_banks):
|
||||||
|
control_list += "bank_{}/s_en0".format(bank)
|
||||||
|
control_list += '\n'
|
||||||
|
|
||||||
|
part2 = bitcell_list + control_list + part2
|
||||||
|
|
||||||
pex_file.close()
|
pex_file.close()
|
||||||
|
|
||||||
# obtain the correct definition line from the original spice file
|
# obtain the correct definition line from the original spice file
|
||||||
sp_file = open(ref_file_name, "r")
|
sp_file = open(ref_file_name, "r")
|
||||||
contents = sp_file.read()
|
contents = sp_file.read()
|
||||||
circuit_title = re.search(".SUBCKT " + str(name) + ".*\n", contents)
|
circuit_title = re.search(".SUBCKT " + str(name) + ".*", contents)
|
||||||
circuit_title = circuit_title.group()
|
circuit_title = circuit_title.group()
|
||||||
sp_file.close()
|
sp_file.close()
|
||||||
|
|
||||||
# write the new pex file with info in the memory
|
# write the new pex file with info in the memory
|
||||||
output_file = open(output_file_name, "w")
|
output_file = open(output_file_name, "w")
|
||||||
output_file.write(part1)
|
output_file.write(part1)
|
||||||
output_file.write(circuit_title)
|
output_file.write(circuit_title+'\n')
|
||||||
output_file.write(part2)
|
output_file.write(part2)
|
||||||
output_file.close()
|
output_file.close()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
|
|
||||||
.SUBCKT cell_6t bl br wl vdd gnd
|
.SUBCKT cell_6t bl br wl vdd gnd
|
||||||
* Inverter 1
|
* Inverter 1
|
||||||
MM0 Qbar Q gnd gnd NMOS_VTG W=205.00n L=50n
|
MM0 Q_bar Q gnd gnd NMOS_VTG W=205.00n L=50n
|
||||||
MM4 Qbar Q vdd vdd PMOS_VTG W=90n L=50n
|
MM4 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n
|
||||||
|
|
||||||
* Inverer 2
|
* Inverer 2
|
||||||
MM1 Q Qbar gnd gnd NMOS_VTG W=205.00n L=50n
|
MM1 Q Q_bar gnd gnd NMOS_VTG W=205.00n L=50n
|
||||||
MM5 Q Qbar vdd vdd PMOS_VTG W=90n L=50n
|
MM5 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n
|
||||||
|
|
||||||
* Access transistors
|
* Access transistors
|
||||||
MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n
|
MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n
|
||||||
MM2 br wl Qbar gnd NMOS_VTG W=135.00n L=50n
|
MM2 br wl Q_bar gnd NMOS_VTG W=135.00n L=50n
|
||||||
.ENDS cell_6t
|
.ENDS cell_6t
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
|
|
||||||
.SUBCKT dummy_cell_6t bl br wl vdd gnd
|
.SUBCKT dummy_cell_6t bl br wl vdd gnd
|
||||||
* Inverter 1
|
* Inverter 1
|
||||||
MM0 Qbar Q gnd gnd NMOS_VTG W=205.00n L=50n
|
MM0 Q_bar Q gnd gnd NMOS_VTG W=205.00n L=50n
|
||||||
MM4 Qbar Q vdd vdd PMOS_VTG W=90n L=50n
|
MM4 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n
|
||||||
|
|
||||||
* Inverer 2
|
* Inverer 2
|
||||||
MM1 Q Qbar gnd gnd NMOS_VTG W=205.00n L=50n
|
MM1 Q Q_bar gnd gnd NMOS_VTG W=205.00n L=50n
|
||||||
MM5 Q Qbar vdd vdd PMOS_VTG W=90n L=50n
|
MM5 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n
|
||||||
|
|
||||||
* Access transistors
|
* Access transistors
|
||||||
MM3 bl_noconn wl Q gnd NMOS_VTG W=135.00n L=50n
|
MM3 bl_noconn wl Q gnd NMOS_VTG W=135.00n L=50n
|
||||||
MM2 br_noconn wl Qbar gnd NMOS_VTG W=135.00n L=50n
|
MM2 br_noconn wl Q_bar gnd NMOS_VTG W=135.00n L=50n
|
||||||
.ENDS cell_6t
|
.ENDS cell_6t
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
||||||
magic
|
magic
|
||||||
tech scmos
|
tech scmos
|
||||||
timestamp 1542220294
|
timestamp 1577066542
|
||||||
<< nwell >>
|
<< nwell >>
|
||||||
rect 0 46 54 75
|
rect 0 46 54 75
|
||||||
<< pwell >>
|
<< pwell >>
|
||||||
|
|
@ -47,12 +47,17 @@ rect 17 33 21 37
|
||||||
rect 25 29 29 37
|
rect 25 29 29 37
|
||||||
rect 33 33 37 37
|
rect 33 33 37 37
|
||||||
rect 41 33 45 37
|
rect 41 33 45 37
|
||||||
|
rect 9 17 13 21
|
||||||
rect 25 17 29 23
|
rect 25 17 29 23
|
||||||
|
rect 41 17 45 21
|
||||||
<< pdcontact >>
|
<< pdcontact >>
|
||||||
rect 17 54 21 58
|
rect 17 54 21 58
|
||||||
|
rect 25 54 29 58
|
||||||
rect 33 54 37 58
|
rect 33 54 37 58
|
||||||
<< psubstratepcontact >>
|
<< psubstratepcontact >>
|
||||||
rect 25 9 29 13
|
rect 25 9 29 13
|
||||||
|
<< nsubstratencontact >>
|
||||||
|
rect 25 68 29 72
|
||||||
<< polysilicon >>
|
<< polysilicon >>
|
||||||
rect 22 57 24 60
|
rect 22 57 24 60
|
||||||
rect 30 57 32 60
|
rect 30 57 32 60
|
||||||
|
|
@ -103,19 +108,16 @@ rect 0 2 16 6
|
||||||
rect 20 2 34 6
|
rect 20 2 34 6
|
||||||
rect 38 2 54 6
|
rect 38 2 54 6
|
||||||
<< m2contact >>
|
<< m2contact >>
|
||||||
|
rect 25 68 29 72
|
||||||
|
rect 25 54 29 58
|
||||||
rect 2 33 6 37
|
rect 2 33 6 37
|
||||||
rect 48 33 52 37
|
rect 48 33 52 37
|
||||||
rect 16 24 20 28
|
rect 16 24 20 28
|
||||||
rect 34 24 38 28
|
rect 34 24 38 28
|
||||||
rect 16 2 20 6
|
|
||||||
rect 34 2 38 6
|
|
||||||
<< pdm12contact >>
|
|
||||||
rect 25 54 29 58
|
|
||||||
<< ndm12contact >>
|
|
||||||
rect 9 17 13 21
|
rect 9 17 13 21
|
||||||
rect 41 17 45 21
|
rect 41 17 45 21
|
||||||
<< nsm12contact >>
|
rect 16 2 20 6
|
||||||
rect 25 68 29 72
|
rect 34 2 38 6
|
||||||
<< metal2 >>
|
<< metal2 >>
|
||||||
rect 2 37 6 72
|
rect 2 37 6 72
|
||||||
rect 2 0 6 33
|
rect 2 0 6 33
|
||||||
|
|
@ -139,4 +141,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
||||||
rlabel metal2 11 7 11 7 1 bl1
|
rlabel metal2 11 7 11 7 1 bl1
|
||||||
rlabel metal2 43 7 43 7 1 br1
|
rlabel metal2 43 7 43 7 1 br1
|
||||||
rlabel metal2 50 7 50 7 8 br0
|
rlabel metal2 50 7 50 7 8 br0
|
||||||
|
rlabel polycontact 29 49 29 49 1 Q
|
||||||
|
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||||
<< end >>
|
<< end >>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
magic
|
magic
|
||||||
tech scmos
|
tech scmos
|
||||||
timestamp 1542220294
|
timestamp 1577067400
|
||||||
<< nwell >>
|
<< nwell >>
|
||||||
rect 0 46 54 75
|
rect 0 46 54 75
|
||||||
<< pwell >>
|
<< pwell >>
|
||||||
|
|
@ -47,12 +47,17 @@ rect 17 33 21 37
|
||||||
rect 25 29 29 37
|
rect 25 29 29 37
|
||||||
rect 33 33 37 37
|
rect 33 33 37 37
|
||||||
rect 41 33 45 37
|
rect 41 33 45 37
|
||||||
|
rect 9 17 13 21
|
||||||
rect 25 17 29 23
|
rect 25 17 29 23
|
||||||
|
rect 41 17 45 21
|
||||||
<< pdcontact >>
|
<< pdcontact >>
|
||||||
rect 17 54 21 58
|
rect 17 54 21 58
|
||||||
|
rect 25 54 29 58
|
||||||
rect 33 54 37 58
|
rect 33 54 37 58
|
||||||
<< psubstratepcontact >>
|
<< psubstratepcontact >>
|
||||||
rect 25 9 29 13
|
rect 25 9 29 13
|
||||||
|
<< nsubstratencontact >>
|
||||||
|
rect 25 68 29 72
|
||||||
<< polysilicon >>
|
<< polysilicon >>
|
||||||
rect 22 57 24 60
|
rect 22 57 24 60
|
||||||
rect 30 57 32 60
|
rect 30 57 32 60
|
||||||
|
|
@ -103,19 +108,16 @@ rect 0 2 16 6
|
||||||
rect 20 2 34 6
|
rect 20 2 34 6
|
||||||
rect 38 2 54 6
|
rect 38 2 54 6
|
||||||
<< m2contact >>
|
<< m2contact >>
|
||||||
|
rect 25 68 29 72
|
||||||
|
rect 25 54 29 58
|
||||||
rect 2 33 6 37
|
rect 2 33 6 37
|
||||||
rect 48 33 52 37
|
rect 48 33 52 37
|
||||||
rect 16 24 20 28
|
rect 16 24 20 28
|
||||||
rect 34 24 38 28
|
rect 34 24 38 28
|
||||||
rect 16 2 20 6
|
|
||||||
rect 34 2 38 6
|
|
||||||
<< pdm12contact >>
|
|
||||||
rect 25 54 29 58
|
|
||||||
<< ndm12contact >>
|
|
||||||
rect 9 17 13 21
|
rect 9 17 13 21
|
||||||
rect 41 17 45 21
|
rect 41 17 45 21
|
||||||
<< nsm12contact >>
|
rect 16 2 20 6
|
||||||
rect 25 68 29 72
|
rect 34 2 38 6
|
||||||
<< metal2 >>
|
<< metal2 >>
|
||||||
rect 2 37 6 72
|
rect 2 37 6 72
|
||||||
rect 2 0 6 33
|
rect 2 0 6 33
|
||||||
|
|
@ -139,4 +141,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
||||||
rlabel metal2 11 7 11 7 1 bl1
|
rlabel metal2 11 7 11 7 1 bl1
|
||||||
rlabel metal2 43 7 43 7 1 br1
|
rlabel metal2 43 7 43 7 1 br1
|
||||||
rlabel metal2 50 7 50 7 8 br0
|
rlabel metal2 50 7 50 7 8 br0
|
||||||
|
rlabel polycontact 29 49 29 49 1 Q
|
||||||
|
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||||
<< end >>
|
<< end >>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
magic
|
magic
|
||||||
tech scmos
|
tech scmos
|
||||||
timestamp 1560809302
|
timestamp 1577163318
|
||||||
<< nwell >>
|
<< nwell >>
|
||||||
rect -8 35 42 57
|
rect -8 35 42 57
|
||||||
<< pwell >>
|
<< pwell >>
|
||||||
|
|
@ -115,4 +115,6 @@ rlabel m2contact 17 52 17 52 5 vdd
|
||||||
rlabel metal2 8 49 8 49 1 bl
|
rlabel metal2 8 49 8 49 1 bl
|
||||||
rlabel metal2 26 49 26 49 1 br
|
rlabel metal2 26 49 26 49 1 br
|
||||||
rlabel metal1 4 13 4 13 1 wl
|
rlabel metal1 4 13 4 13 1 wl
|
||||||
|
rlabel polycontact 17 37 17 37 1 Q
|
||||||
|
rlabel polycontact 23 28 23 28 1 Q_bar
|
||||||
<< end >>
|
<< end >>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
magic
|
magic
|
||||||
tech scmos
|
tech scmos
|
||||||
timestamp 1562188987
|
timestamp 1577067400
|
||||||
<< nwell >>
|
<< nwell >>
|
||||||
rect 0 46 54 75
|
rect 0 46 54 75
|
||||||
<< pwell >>
|
<< pwell >>
|
||||||
|
|
@ -133,4 +133,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
||||||
rlabel metal2 11 7 11 7 1 bl1
|
rlabel metal2 11 7 11 7 1 bl1
|
||||||
rlabel metal2 43 7 43 7 1 br1
|
rlabel metal2 43 7 43 7 1 br1
|
||||||
rlabel metal2 50 7 50 7 8 br0
|
rlabel metal2 50 7 50 7 8 br0
|
||||||
|
rlabel polycontact 29 49 29 49 1 Q
|
||||||
|
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||||
<< end >>
|
<< end >>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
magic
|
magic
|
||||||
tech scmos
|
tech scmos
|
||||||
timestamp 1562189027
|
timestamp 1577067400
|
||||||
<< nwell >>
|
<< nwell >>
|
||||||
rect 0 46 54 75
|
rect 0 46 54 75
|
||||||
<< pwell >>
|
<< pwell >>
|
||||||
|
|
@ -133,4 +133,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
||||||
rlabel metal2 11 7 11 7 1 bl1
|
rlabel metal2 11 7 11 7 1 bl1
|
||||||
rlabel metal2 43 7 43 7 1 br1
|
rlabel metal2 43 7 43 7 1 br1
|
||||||
rlabel metal2 50 7 50 7 8 br0
|
rlabel metal2 50 7 50 7 8 br0
|
||||||
|
rlabel polycontact 29 49 29 49 1 Q
|
||||||
|
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||||
<< end >>
|
<< end >>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
magic
|
magic
|
||||||
tech scmos
|
tech scmos
|
||||||
timestamp 1560809362
|
timestamp 1577067400
|
||||||
<< nwell >>
|
<< nwell >>
|
||||||
rect -8 35 42 57
|
rect -8 35 42 57
|
||||||
<< pwell >>
|
<< pwell >>
|
||||||
|
|
@ -112,4 +112,6 @@ rlabel m2contact 17 52 17 52 5 vdd
|
||||||
rlabel metal2 8 49 8 49 1 bl
|
rlabel metal2 8 49 8 49 1 bl
|
||||||
rlabel metal2 26 49 26 49 1 br
|
rlabel metal2 26 49 26 49 1 br
|
||||||
rlabel metal1 4 13 4 13 1 wl
|
rlabel metal1 4 13 4 13 1 wl
|
||||||
|
rlabel polycontact 17 37 17 37 1 Q
|
||||||
|
rlabel polycontact 23 28 23 28 1 Q_bar
|
||||||
<< end >>
|
<< end >>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
magic
|
magic
|
||||||
tech scmos
|
tech scmos
|
||||||
timestamp 1542221056
|
timestamp 1577067400
|
||||||
<< nwell >>
|
<< nwell >>
|
||||||
rect 0 46 54 75
|
rect 0 46 54 75
|
||||||
<< pwell >>
|
<< pwell >>
|
||||||
|
|
@ -142,4 +142,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
||||||
rlabel metal2 11 7 11 7 1 bl1
|
rlabel metal2 11 7 11 7 1 bl1
|
||||||
rlabel metal2 43 7 43 7 1 br1
|
rlabel metal2 43 7 43 7 1 br1
|
||||||
rlabel metal2 50 7 50 7 8 br0
|
rlabel metal2 50 7 50 7 8 br0
|
||||||
|
rlabel polycontact 29 49 29 49 1 Q
|
||||||
|
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||||
<< end >>
|
<< end >>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
magic
|
magic
|
||||||
tech scmos
|
tech scmos
|
||||||
timestamp 1542221056
|
timestamp 1577067446
|
||||||
<< nwell >>
|
<< nwell >>
|
||||||
rect 0 46 54 75
|
rect 0 46 54 75
|
||||||
<< pwell >>
|
<< pwell >>
|
||||||
|
|
@ -142,4 +142,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
||||||
rlabel metal2 11 7 11 7 1 bl1
|
rlabel metal2 11 7 11 7 1 bl1
|
||||||
rlabel metal2 43 7 43 7 1 br1
|
rlabel metal2 43 7 43 7 1 br1
|
||||||
rlabel metal2 50 7 50 7 8 br0
|
rlabel metal2 50 7 50 7 8 br0
|
||||||
|
rlabel polycontact 29 49 29 49 1 Q
|
||||||
|
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||||
<< end >>
|
<< end >>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
magic
|
magic
|
||||||
tech scmos
|
tech scmos
|
||||||
timestamp 1560809329
|
timestamp 1577067503
|
||||||
<< nwell >>
|
<< nwell >>
|
||||||
rect -8 35 42 57
|
rect -8 35 42 57
|
||||||
<< pwell >>
|
<< pwell >>
|
||||||
|
|
@ -116,4 +116,6 @@ rlabel m2contact 17 52 17 52 5 vdd
|
||||||
rlabel metal2 8 49 8 49 1 bl
|
rlabel metal2 8 49 8 49 1 bl
|
||||||
rlabel metal2 26 49 26 49 1 br
|
rlabel metal2 26 49 26 49 1 br
|
||||||
rlabel metal1 4 13 4 13 1 wl
|
rlabel metal1 4 13 4 13 1 wl
|
||||||
|
rlabel polycontact 17 37 17 37 1 Q
|
||||||
|
rlabel polycontact 23 28 23 28 1 Q_bar
|
||||||
<< end >>
|
<< end >>
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,15 @@
|
||||||
* SPICE3 file created from cell_6t.ext - technology: scmos
|
* SPICE3 file created from cell_6t.ext - technology: scmos
|
||||||
|
|
||||||
* Inverter 1
|
* Inverter 1
|
||||||
M1000 Q Qbar vdd vdd p w=0.6u l=0.8u
|
M1000 Q Q_bar vdd vdd p w=0.6u l=0.8u
|
||||||
M1002 Q Qbar gnd gnd n w=1.6u l=0.4u
|
M1002 Q Q_bar gnd gnd n w=1.6u l=0.4u
|
||||||
|
|
||||||
* Inverter 2
|
* Inverter 2
|
||||||
M1001 vdd Q Qbar vdd p w=0.6u l=0.8u
|
M1001 vdd Q Q_bar vdd p w=0.6u l=0.8u
|
||||||
M1003 gnd Q Qbar gnd n w=1.6u l=0.4u
|
M1003 gnd Q Q_bar gnd n w=1.6u l=0.4u
|
||||||
|
|
||||||
* Access transistors
|
* Access transistors
|
||||||
M1004 Q wl bl gnd n w=0.8u l=0.4u
|
M1004 Q wl bl gnd n w=0.8u l=0.4u
|
||||||
M1005 Qbar wl br gnd n w=0.8u l=0.4u
|
M1005 Q_bar wl br gnd n w=0.8u l=0.4u
|
||||||
|
|
||||||
.ENDS
|
.ENDS
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,15 @@
|
||||||
.SUBCKT dummy_cell_6t bl br wl vdd gnd
|
.SUBCKT dummy_cell_6t bl br wl vdd gnd
|
||||||
|
|
||||||
* Inverter 1
|
* Inverter 1
|
||||||
M1000 Q Qbar vdd vdd p w=0.6u l=0.8u
|
M1000 Q Q_bar vdd vdd p w=0.6u l=0.8u
|
||||||
M1002 Q Qbar gnd gnd n w=1.6u l=0.4u
|
M1002 Q Q_bar gnd gnd n w=1.6u l=0.4u
|
||||||
|
|
||||||
* Inverter 2
|
* Inverter 2
|
||||||
M1001 vdd Q Qbar vdd p w=0.6u l=0.8u
|
M1001 vdd Q Q_bar vdd p w=0.6u l=0.8u
|
||||||
M1003 gnd Q Qbar gnd n w=1.6u l=0.4u
|
M1003 gnd Q Q_bar gnd n w=1.6u l=0.4u
|
||||||
|
|
||||||
* Access transistors
|
* Access transistors
|
||||||
M1004 Q wl bl_noconn gnd n w=0.8u l=0.4u
|
M1004 Q wl bl_noconn gnd n w=0.8u l=0.4u
|
||||||
M1005 Qbar wl br_noconn gnd n w=0.8u l=0.4u
|
M1005 Q_bar wl br_noconn gnd n w=0.8u l=0.4u
|
||||||
|
|
||||||
.ENDS
|
.ENDS
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue