Merge branch 'dev' into wlbuffer

This commit is contained in:
mrg 2020-09-10 13:05:14 -07:00
commit 71d86f88b0
63 changed files with 639 additions and 935 deletions

View File

@ -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
@ -270,6 +272,134 @@ class instance(geometry):
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 """
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")" return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"

View File

@ -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 = []

View File

@ -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,

View File

@ -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()

View File

@ -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 """

View File

@ -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):
@ -79,6 +80,84 @@ class bitcell_base(design.design):
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.

View File

@ -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 """

View File

@ -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
@ -182,7 +181,11 @@ class delay(simulation):
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
@ -245,17 +253,6 @@ 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):

View File

@ -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
@ -420,18 +423,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):
"""Creates timing graph to generate the timing paths for the SRAM output.""" """Creates timing graph to generate the timing paths for the SRAM output."""
@ -445,24 +436,7 @@ class functional(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)
# 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)
@ -474,37 +448,6 @@ class functional(simulation):
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."""

View File

@ -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]

View File

@ -57,6 +57,29 @@ class stimuli():
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) """

View File

@ -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)

View File

@ -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:

View File

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

View File

@ -157,96 +157,12 @@ class control_logic(design.design):
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"""
from math import ceil from math import ceil
@ -334,17 +250,6 @@ class control_logic(design.design):
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 """
@ -870,138 +775,6 @@ class control_logic(design.design):
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"""

View File

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

View File

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

View File

@ -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"]

View File

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

View File

@ -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()

View File

@ -189,9 +189,3 @@ class dff_inv_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

View File

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

View File

@ -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()

View File

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

View File

@ -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)

View File

@ -83,13 +83,6 @@ class sense_amp(design.design):
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"""
# FIXME: A better programmatic solution to designate pins # FIXME: A better programmatic solution to designate pins

View File

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

View File

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

View File

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

View File

@ -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)

View File

@ -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)

View File

@ -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
################### ###################

View File

@ -147,20 +147,3 @@ class pand2(pgate.pgate):
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()

View File

@ -162,20 +162,3 @@ class pand3(pgate.pgate):
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()

View File

@ -97,20 +97,3 @@ class pbuf(pgate.pgate):
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

View File

@ -169,23 +169,3 @@ class pdriver(pgate.pgate):
""" 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()

View File

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

View File

@ -301,10 +301,3 @@ class precharge(design.design):
[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

View File

@ -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)

View File

@ -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)

View File

@ -627,23 +627,12 @@ class sram_1bank(sram_base):
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;

View File

@ -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))
@ -563,49 +631,3 @@ 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

View File

@ -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()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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