merge in wlbuf and begin work on 32kb memory

This commit is contained in:
jcirimel 2020-10-06 05:03:59 -07:00
commit 888646cdf9
91 changed files with 2950 additions and 2527 deletions

View File

@ -26,6 +26,12 @@ class design(hierarchy_design):
self.setup_layer_constants() self.setup_layer_constants()
self.setup_multiport_constants() self.setup_multiport_constants()
def check_pins(self):
for pin_name in self.pins:
pins = self.get_pins(pin_name)
for pin in pins:
print(pin_name, pin)
def setup_layer_constants(self): def setup_layer_constants(self):
""" """
These are some layer constants used These are some layer constants used

View File

@ -1,13 +1,7 @@
import os, copy import copy
from collections import defaultdict from collections import defaultdict
import gdsMill
import tech
import math
import globals
import debug import debug
from vector import vector
from pin_layout import pin_layout
class timing_graph(): class timing_graph():
""" """
@ -33,7 +27,7 @@ class timing_graph():
"""Add node to graph with no edges""" """Add node to graph with no edges"""
node = node.lower() node = node.lower()
if not node in self.graph: if node not in self.graph:
self.graph[node] = set() self.graph[node] = set()
def remove_edges(self, node): def remove_edges(self, node):
@ -106,20 +100,20 @@ class timing_graph():
delays = [] delays = []
cur_slew = slew cur_slew = slew
for i in range(len(path)-1): for i in range(len(path) - 1):
path_edge_mod = self.edge_mods[(path[i], path[i+1])] path_edge_mod = self.edge_mods[(path[i], path[i + 1])]
# On the output of the current stage, get COUT from all other mods connected # On the output of the current stage, get COUT from all other mods connected
cout = 0 cout = 0
for node in self.graph[path[i+1]]: for node in self.graph[path[i + 1]]:
output_edge_mod = self.edge_mods[(path[i+1], node)] output_edge_mod = self.edge_mods[(path[i + 1], node)]
cout+=output_edge_mod.get_cin() cout+=output_edge_mod.get_cin()
# If at the last output, include the final output load # If at the last output, include the final output load
if i == len(path)-2: if i == len(path) - 2:
cout+=load cout += load
delays.append(path_edge_mod.analytical_delay(corner, slew, cout)) delays.append(path_edge_mod.analytical_delay(corner, cur_slew, cout))
cur_slew = delays[-1].slew cur_slew = delays[-1].slew
return delays return delays
@ -127,4 +121,15 @@ class timing_graph():
def __str__(self): def __str__(self):
""" override print function output """ """ override print function output """
return "Nodes: {}\nEdges:{} ".format(list(self.graph), self.graph) str = ""
for n in self.graph:
str += n + "\n"
for d in self.graph[n]:
str += "\t\t-> " + d + "\n"
return str
def __repr__(self):
""" override print function output """
return str(self)

View File

@ -12,6 +12,7 @@ import os
from globals import OPTS from globals import OPTS
import tech import tech
class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
""" """
Design Class for all modules to inherit the base features. Design Class for all modules to inherit the base features.
@ -137,12 +138,16 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
os.remove(tempgds) os.remove(tempgds)
def init_graph_params(self): def init_graph_params(self):
"""Initializes parameters relevant to the graph creation""" """
Initializes parameters relevant to the graph creation
"""
# Only initializes a set for checking instances which should not be added # Only initializes a set for checking instances which should not be added
self.graph_inst_exclude = set() self.graph_inst_exclude = set()
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Recursively create graph from instances in module.""" """
Recursively create graph from instances in module.
"""
# Translate port names to external nets # Translate port names to external nets
if len(port_nets) != len(self.pins): if len(port_nets) != len(self.pins):
@ -159,7 +164,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
subinst.mod.build_graph(graph, subinst_name, subinst_ports) subinst.mod.build_graph(graph, subinst_name, subinst_ports)
def build_names(self, name_dict, inst_name, port_nets): def build_names(self, name_dict, inst_name, port_nets):
"""Collects all the nets and the parent inst of that net.""" """
Collects all the nets and the parent inst of that net.
"""
# Translate port names to external nets # Translate port names to external nets
if len(port_nets) != len(self.pins): if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
@ -177,64 +184,10 @@ 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 = []
for conn in subinst_ports: for conn in subinst_ports:
if conn in port_dict: if conn in port_dict:
@ -244,8 +197,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
return converted_conns return converted_conns
def add_graph_edges(self, graph, port_nets): def add_graph_edges(self, graph, port_nets):
"""For every input, adds an edge to every output. """
Only intended to be used for gates and other simple modules.""" For every input, adds an edge to every output.
Only intended to be used for gates and other simple modules.
"""
# The final pin names will depend on the spice hierarchy, so # The final pin names will depend on the spice hierarchy, so
# they are passed as an input. # they are passed as an input.
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}

View File

@ -63,6 +63,16 @@ class layout():
self.translate_all(offset) self.translate_all(offset)
return offset return offset
def offset_x_coordinates(self):
"""
This function is called after everything is placed to
shift the origin to the furthest left point.
Y offset is unchanged.
"""
offset = self.find_lowest_coords()
self.translate_all(offset.scale(1, 0))
return offset
def get_gate_offset(self, x_offset, height, inv_num): def get_gate_offset(self, x_offset, height, inv_num):
""" """
Gets the base offset and y orientation of stacked rows of gates Gets the base offset and y orientation of stacked rows of gates
@ -201,6 +211,12 @@ class layout():
def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0): def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
""" Adds an instance of a mod to this module """ """ Adds an instance of a mod to this module """
# Contacts are not really instances, so skip them
if "contact" not in mod.name:
# Check that the instance name is unique
for inst in self.insts:
debug.check(name != inst.name, "Duplicate named instance in {0}: {1}".format(self.name, name))
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
debug.info(3, "adding instance {}".format(self.insts[-1])) debug.info(3, "adding instance {}".format(self.insts[-1]))
# This is commented out for runtime reasons # This is commented out for runtime reasons
@ -1019,7 +1035,7 @@ class layout():
""" """
import channel_route import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self) cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self)
self.add_inst("vc", cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None): def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None):
@ -1028,7 +1044,7 @@ class layout():
""" """
import channel_route import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self) cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self)
self.add_inst("hc", cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
def add_boundary(self, ll=vector(0, 0), ur=None): def add_boundary(self, ll=vector(0, 0), ur=None):

View File

@ -499,3 +499,53 @@ 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

@ -6,6 +6,7 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import math
class verilog: class verilog:
""" """
@ -53,7 +54,7 @@ class verilog:
self.vf.write("\n );\n\n") self.vf.write("\n );\n\n")
if self.write_size: if self.write_size:
self.num_wmasks = int(self.word_size/self.write_size) self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
self.vf.write(" parameter NUM_WMASKS = {0} ;\n".format(self.num_wmasks)) self.vf.write(" parameter NUM_WMASKS = {0} ;\n".format(self.num_wmasks))
self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size)) self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size))
self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.addr_size)) self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.addr_size))
@ -189,9 +190,13 @@ class verilog:
self.vf.write(" if (!csb{0}_reg)\n".format(port)) self.vf.write(" if (!csb{0}_reg)\n".format(port))
if self.write_size: if self.write_size:
remainder_bits = self.word_size % self.write_size
for mask in range(0,self.num_wmasks): for mask in range(0,self.num_wmasks):
lower = mask * self.write_size lower = mask * self.write_size
upper = lower + self.write_size-1 if (remainder_bits and mask == self.num_wmasks - 1):
upper = lower + remainder_bits - 1
else:
upper = lower + self.write_size - 1
self.vf.write(" if (wmask{0}_reg[{1}])\n".format(port,mask)) self.vf.write(" if (wmask{0}_reg[{1}])\n".format(port,mask))
self.vf.write(" mem[addr{0}_reg][{1}:{2}] = din{0}_reg[{1}:{2}];\n".format(port,upper,lower)) self.vf.write(" mem[addr{0}_reg][{1}:{2}] = din{0}_reg[{1}:{2}];\n".format(port,upper,lower))
self.vf.write(" end\n") self.vf.write(" end\n")

View File

@ -5,7 +5,7 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import sys,re,shutil,copy import shutil
import debug import debug
import tech import tech
import math import math
@ -14,13 +14,10 @@ from .trim_spice import *
from .charutils import * from .charutils import *
from .sram_op import * from .sram_op import *
from .bit_polarity import * from .bit_polarity import *
import utils
from globals import OPTS from globals import OPTS
from .simulation import simulation from .simulation import simulation
from .measurements import * from .measurements import *
import logical_effort
import graph_util
from sram_factory import factory
class delay(simulation): class delay(simulation):
""" """
@ -47,10 +44,10 @@ class delay(simulation):
self.targ_write_ports = [] self.targ_write_ports = []
self.period = 0 self.period = 0
if self.write_size: if self.write_size:
self.num_wmasks = int(self.word_size / self.write_size) self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else: else:
self.num_wmasks = 0 self.num_wmasks = 0
self.set_load_slew(0,0) self.set_load_slew(0, 0)
self.set_corner(corner) self.set_corner(corner)
self.create_signal_names() self.create_signal_names()
self.add_graph_exclusions() self.add_graph_exclusions()
@ -59,8 +56,14 @@ class delay(simulation):
""" Create measurement names. The names themselves currently define the type of measurement """ """ Create measurement names. The names themselves currently define the type of measurement """
self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"]
self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power", self.power_meas_names = ["read0_power",
"disabled_read0_power", "disabled_read1_power", "disabled_write0_power", "disabled_write1_power"] "read1_power",
"write0_power",
"write1_power",
"disabled_read0_power",
"disabled_read1_power",
"disabled_write0_power",
"disabled_write1_power"]
# self.voltage_when_names = ["volt_bl", "volt_br"] # self.voltage_when_names = ["volt_bl", "volt_br"]
# self.bitline_delay_names = ["delay_bl", "delay_br"] # self.bitline_delay_names = ["delay_bl", "delay_br"]
@ -69,7 +72,7 @@ class delay(simulation):
self.read_meas_lists = self.create_read_port_measurement_objects() self.read_meas_lists = self.create_read_port_measurement_objects()
self.write_meas_lists = self.create_write_port_measurement_objects() self.write_meas_lists = self.create_write_port_measurement_objects()
self.check_meas_names(self.read_meas_lists+self.write_meas_lists) self.check_meas_names(self.read_meas_lists + self.write_meas_lists)
def check_meas_names(self, measures_lists): def check_meas_names(self, measures_lists):
""" """
@ -80,8 +83,8 @@ class delay(simulation):
for meas_list in measures_lists: for meas_list in measures_lists:
for meas in meas_list: for meas in meas_list:
name = meas.name.lower() name = meas.name.lower()
debug.check(name not in name_set,("SPICE measurements must have unique names. " debug.check(name not in name_set, ("SPICE measurements must have unique names. "
"Duplicate name={}").format(name)) "Duplicate name={}").format(name))
name_set.add(name) name_set.add(name)
def create_read_port_measurement_objects(self): def create_read_port_measurement_objects(self):
@ -89,7 +92,7 @@ class delay(simulation):
self.read_lib_meas = [] self.read_lib_meas = []
self.clk_frmt = "clk{0}" # Unformatted clock name self.clk_frmt = "clk{0}" # Unformatted clock name
targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) # Empty values are the port and probe data bit targ_name = "{0}{1}_{2}".format(self.dout_name, "{}", self.probe_data) # Empty values are the port and probe data bit
self.delay_meas = [] self.delay_meas = []
self.delay_meas.append(delay_measure("delay_lh", self.clk_frmt, targ_name, "RISE", "RISE", measure_scale=1e9)) self.delay_meas.append(delay_measure("delay_lh", self.clk_frmt, targ_name, "RISE", "RISE", measure_scale=1e9))
self.delay_meas[-1].meta_str = sram_op.READ_ONE # Used to index time delay values when measurements written to spice file. self.delay_meas[-1].meta_str = sram_op.READ_ONE # Used to index time delay values when measurements written to spice file.
@ -137,17 +140,17 @@ 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 +185,7 @@ class delay(simulation):
self.dout_volt_meas[-1].meta_str = meas.meta_str self.dout_volt_meas[-1].meta_str = meas.meta_str
if not OPTS.use_pex: if not OPTS.use_pex:
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9) self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name + "{}", "FALL", "RISE", measure_scale=1e9)
else: else:
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name, "FALL", "RISE", measure_scale=1e9) self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name, "FALL", "RISE", measure_scale=1e9)
@ -194,12 +197,12 @@ class delay(simulation):
def create_read_bit_measures(self): def create_read_bit_measures(self):
""" Adds bit measurements for read0 and read1 cycles """ """ Adds bit measurements for read0 and read1 cycles """
self.read_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} self.read_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []}
meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE) meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE)
for cycle in meas_cycles: for cycle in meas_cycles:
meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name)
single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data)
for polarity,meas in single_bit_meas.items(): for polarity, meas in single_bit_meas.items():
meas.meta_str = cycle meas.meta_str = cycle
self.read_bit_meas[polarity].append(meas) self.read_bit_meas[polarity].append(meas)
# Dictionary values are lists, reduce to a single list of measurements # Dictionary values are lists, reduce to a single list of measurements
@ -208,12 +211,12 @@ class delay(simulation):
def create_write_bit_measures(self): def create_write_bit_measures(self):
""" Adds bit measurements for write0 and write1 cycles """ """ Adds bit measurements for write0 and write1 cycles """
self.write_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} self.write_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []}
meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE) meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE)
for cycle in meas_cycles: for cycle in meas_cycles:
meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name)
single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data)
for polarity,meas in single_bit_meas.items(): for polarity, meas in single_bit_meas.items():
meas.meta_str = cycle meas.meta_str = cycle
self.write_bit_meas[polarity].append(meas) self.write_bit_meas[polarity].append(meas)
# Dictionary values are lists, reduce to a single list of measurements # Dictionary values are lists, reduce to a single list of measurements
@ -232,8 +235,8 @@ class delay(simulation):
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))
if not OPTS.use_pex: if not OPTS.use_pex:
q_name = cell_name+'.'+str(storage_names[0]) q_name = cell_name + '.' + str(storage_names[0])
qbar_name = cell_name+'.'+str(storage_names[1]) qbar_name = cell_name + '.' + str(storage_names[1])
else: else:
bank_num = self.sram.get_bank_num(self.sram.name, bit_row, bit_col) 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) q_name = "bitcell_Q_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col)
@ -245,197 +248,33 @@ class delay(simulation):
q_meas = voltage_at_measure("v_q_{}".format(meas_tag), q_name) q_meas = voltage_at_measure("v_q_{}".format(meas_tag), q_name)
qbar_meas = voltage_at_measure("v_qbar_{}".format(meas_tag), qbar_name) qbar_meas = voltage_at_measure("v_qbar_{}".format(meas_tag), qbar_name)
return {bit_polarity.NONINVERTING:q_meas, bit_polarity.INVERTING:qbar_meas} return {bit_polarity.NONINVERTING: q_meas, bit_polarity.INVERTING: qbar_meas}
def set_load_slew(self,load,slew): def set_load_slew(self, load, slew):
""" Set the load and slew """ """ Set the load and slew """
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):
"""Creates timing graph to generate the timing paths for the SRAM output."""
self.sram.bank.bitcell_array.bitcell_array.init_graph_params() # Removes previous bit exclusions
self.sram.bank.bitcell_array.graph_exclude_bits(self.wordline_row, self.bitline_column)
# Generate new graph every analysis as edges might change depending on test bit
self.graph = graph_util.timing_graph()
self.sram_spc_name = "X{}".format(self.sram.name)
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]
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]
def get_bl_name_search_exclusions(self):
"""Gets the mods as a set which should be excluded while searching for name."""
# Exclude the RBL as it contains bitcells which are not in the main bitcell array
# so it makes the search awkward
return set(factory.get_mods(OPTS.replica_bitline))
def get_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):
"""
Finds a single alias for the int_net in given paths.
More or less hits cause an error
"""
net_found = False
for path in paths:
aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, int_net, mod, exclusion_set)
if net_found and len(aliases) >= 1:
debug.error('Found multiple paths with {} net.'.format(int_net),1)
elif len(aliases) > 1:
debug.error('Found multiple {} nets in single path.'.format(int_net),1)
elif not net_found and len(aliases) == 1:
path_net_name = aliases[0]
net_found = True
if not net_found:
debug.error("Could not find {} net in timing paths.".format(int_net),1)
return path_net_name
def check_arguments(self): def check_arguments(self):
"""Checks if arguments given for write_stimulus() meets requirements""" """Checks if arguments given for write_stimulus() meets requirements"""
try: try:
int(self.probe_address, 2) int(self.probe_address, 2)
except ValueError: except ValueError:
debug.error("Probe Address is not of binary form: {0}".format(self.probe_address),1) debug.error("Probe Address is not of binary form: {0}".format(self.probe_address), 1)
if len(self.probe_address) != self.addr_size: if len(self.probe_address) != self.addr_size:
debug.error("Probe Address's number of bits does not correspond to given SRAM",1) debug.error("Probe Address's number of bits does not correspond to given SRAM", 1)
if not isinstance(self.probe_data, int) or self.probe_data>self.word_size or self.probe_data<0: if not isinstance(self.probe_data, int) or self.probe_data>self.word_size or self.probe_data<0:
debug.error("Given probe_data is not an integer to specify a data bit",1) debug.error("Given probe_data is not an integer to specify a data bit", 1)
# Adding port options here which the characterizer cannot handle. Some may be added later like ROM # Adding port options here which the characterizer cannot handle. Some may be added later like ROM
if len(self.read_ports) == 0: if len(self.read_ports) == 0:
debug.error("Characterizer does not currently support SRAMs without read ports.",1) debug.error("Characterizer does not currently support SRAMs without read ports.", 1)
if len(self.write_ports) == 0: if len(self.write_ports) == 0:
debug.error("Characterizer does not currently support SRAMs without write ports.",1) debug.error("Characterizer does not currently support SRAMs without write ports.", 1)
def write_generic_stimulus(self): def write_generic_stimulus(self):
""" Create the instance, supplies, loads, and access transistors. """ """ Create the instance, supplies, loads, and access transistors. """
@ -446,18 +285,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")
if not OPTS.use_pex: self.stim.inst_model(pins=self.pins,
self.stim.inst_model(pins=self.pins, model_name=self.sram.name)
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):
self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port,i,self.dout_name,self.load)) self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port, i, self.dout_name, self.load))
def write_delay_stimulus(self): def write_delay_stimulus(self):
""" """
@ -488,7 +322,6 @@ class delay(simulation):
self.gen_data() self.gen_data()
self.gen_addr() self.gen_addr()
# generate control signals # generate control signals
self.sf.write("\n* Generation of control signals\n") self.sf.write("\n* Generation of control signals\n")
self.gen_control() self.gen_control()
@ -510,7 +343,6 @@ class delay(simulation):
self.sf.close() self.sf.close()
def write_power_stimulus(self, trim): def write_power_stimulus(self, trim):
""" Creates a stimulus file to measure leakage power only. """ Creates a stimulus file to measure leakage power only.
This works on the *untrimmed netlist*. This works on the *untrimmed netlist*.
@ -535,11 +367,11 @@ class delay(simulation):
self.sf.write("\n* Generation of data and address signals\n") self.sf.write("\n* Generation of data and address signals\n")
for write_port in self.write_ports: for write_port in self.write_ports:
for i in range(self.word_size): for i in range(self.word_size):
self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i), self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name, write_port, i),
v_val=0) v_val=0)
for port in self.all_ports: for port in self.all_ports:
for i in range(self.addr_size): for i in range(self.addr_size):
self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name,port, i), self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name, port, i),
v_val=0) v_val=0)
# generate control signals # generate control signals
@ -556,7 +388,7 @@ class delay(simulation):
self.write_power_measures() self.write_power_measures()
# run until the end of the cycle time # run until the end of the cycle time
self.stim.write_control(2*self.period) self.stim.write_control(2 * self.period)
self.sf.close() self.sf.close()
@ -602,7 +434,7 @@ class delay(simulation):
# These measurements have there time further delayed to the neg. edge of the clock. # These measurements have there time further delayed to the neg. edge of the clock.
if delay_obj.meta_add_delay: if delay_obj.meta_add_delay:
meas_cycle_delay += self.period/2 meas_cycle_delay += self.period / 2
return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port)
@ -611,7 +443,7 @@ class delay(simulation):
# Return value is intended to match the power measure format: t_initial, t_final, port # Return value is intended to match the power measure format: t_initial, t_final, port
t_initial = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]] t_initial = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]]
t_final = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]+1] t_final = self.cycle_times[self.measure_cycles[port][power_obj.meta_str] + 1]
return (t_initial, t_final, port) return (t_initial, t_final, port)
@ -624,7 +456,7 @@ class delay(simulation):
# Measurement occurs slightly into the next period so we know that the value # Measurement occurs slightly into the next period so we know that the value
# "stuck" after the end of the period -> current period start + 1.25*period # "stuck" after the end of the period -> current period start + 1.25*period
at_time = meas_cycle+1.25*self.period at_time = meas_cycle + 1.25 * self.period
return (at_time, port) return (at_time, port)
@ -634,7 +466,7 @@ class delay(simulation):
""" """
# Only checking 0 value reads for now. # Only checking 0 value reads for now.
t_trig = meas_cycle_delay = self.cycle_times[self.measure_cycles[port][sram_op.READ_ZERO]] t_trig = self.cycle_times[self.measure_cycles[port][sram_op.READ_ZERO]]
return (t_trig, self.vdd_voltage, port) return (t_trig, self.vdd_voltage, port)
@ -649,7 +481,6 @@ class delay(simulation):
measure_variant_inp_tuple = self.get_measure_variants(port, measure, "read") measure_variant_inp_tuple = self.get_measure_variants(port, measure, "read")
measure.write_measure(self.stim, measure_variant_inp_tuple) measure.write_measure(self.stim, measure_variant_inp_tuple)
def write_delay_measures_write_port(self, port): def write_delay_measures_write_port(self, port):
""" """
Write the measure statements to quantify the power results for a write port. Write the measure statements to quantify the power results for a write port.
@ -682,7 +513,6 @@ class delay(simulation):
self.sf.write("* Write ports {}\n".format(write_port)) self.sf.write("* Write ports {}\n".format(write_port))
self.write_delay_measures_write_port(write_port) self.write_delay_measures_write_port(write_port)
def write_power_measures(self): def write_power_measures(self):
""" """
Write the measure statements to quantify the leakage power only. Write the measure statements to quantify the leakage power only.
@ -692,7 +522,7 @@ class delay(simulation):
# add measure statements for power # add measure statements for power
t_initial = self.period t_initial = self.period
t_final = 2*self.period t_final = 2 * self.period
self.stim.gen_meas_power(meas_name="leakage_power", self.stim.gen_meas_power(meas_name="leakage_power",
t_initial=t_initial, t_initial=t_initial,
t_final=t_final) t_final=t_final)
@ -712,7 +542,7 @@ class delay(simulation):
while True: while True:
time_out -= 1 time_out -= 1
if (time_out <= 0): if (time_out <= 0):
debug.error("Timed out, could not find a feasible period.",2) debug.error("Timed out, could not find a feasible period.", 2)
# Write ports are assumed non-critical to timing, so the first available is used # Write ports are assumed non-critical to timing, so the first available is used
self.targ_write_ports = [self.write_ports[0]] self.targ_write_ports = [self.write_ports[0]]
@ -758,7 +588,6 @@ class delay(simulation):
feasible_delays[self.read_ports[0]] = self.find_feasible_period_one_port(self.read_ports[0]) feasible_delays[self.read_ports[0]] = self.find_feasible_period_one_port(self.read_ports[0])
previous_period = self.period previous_period = self.period
# Loops through all the ports checks if the feasible period works. Everything restarts it if does not. # Loops through all the ports checks if the feasible period works. Everything restarts it if does not.
# Write ports do not produce delays which is why they are not included here. # Write ports do not produce delays which is why they are not included here.
i = 1 i = 1
@ -799,7 +628,7 @@ class delay(simulation):
for port in self.targ_write_ports: for port in self.targ_write_ports:
if not self.check_bit_measures(self.write_bit_meas, port): if not self.check_bit_measures(self.write_bit_meas, port):
return(False,{}) return(False, {})
debug.info(2, "Checking write values for port {}".format(port)) debug.info(2, "Checking write values for port {}".format(port))
write_port_dict = {} write_port_dict = {}
@ -807,22 +636,21 @@ class delay(simulation):
write_port_dict[measure.name] = measure.retrieve_measure(port=port) write_port_dict[measure.name] = measure.retrieve_measure(port=port)
if not check_dict_values_is_float(write_port_dict): if not check_dict_values_is_float(write_port_dict):
debug.error("Failed to Measure Write Port Values:\n\t\t{0}".format(write_port_dict),1) debug.error("Failed to Measure Write Port Values:\n\t\t{0}".format(write_port_dict), 1)
result[port].update(write_port_dict) result[port].update(write_port_dict)
for port in self.targ_read_ports: for port in self.targ_read_ports:
# First, check that the memory has the right values at the right times # First, check that the memory has the right values at the right times
if not self.check_bit_measures(self.read_bit_meas, port): if not self.check_bit_measures(self.read_bit_meas, port):
return(False,{}) return(False, {})
debug.info(2, "Checking read delay values for port {}".format(port)) debug.info(2, "Checking read delay values for port {}".format(port))
# Check sen timing, then bitlines, then general measurements. # Check sen timing, then bitlines, then general measurements.
if not self.check_sen_measure(port): if not self.check_sen_measure(port):
return (False,{}) return (False, {})
if not self.check_read_debug_measures(port): if not self.check_read_debug_measures(port):
return (False,{}) return (False, {})
# Check timing for read ports. Power is only checked if it was read correctly # Check timing for read ports. Power is only checked if it was read correctly
read_port_dict = {} read_port_dict = {}
@ -830,27 +658,26 @@ class delay(simulation):
read_port_dict[measure.name] = measure.retrieve_measure(port=port) read_port_dict[measure.name] = measure.retrieve_measure(port=port)
if not self.check_valid_delays(read_port_dict): if not self.check_valid_delays(read_port_dict):
return (False,{}) return (False, {})
if not check_dict_values_is_float(read_port_dict): if not check_dict_values_is_float(read_port_dict):
debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict),1) debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict), 1)
result[port].update(read_port_dict) result[port].update(read_port_dict)
return (True,result) return (True, result)
def check_sen_measure(self, port): def check_sen_measure(self, port):
"""Checks that the sen occurred within a half-period""" """Checks that the sen occurred within a half-period"""
sen_val = self.sen_meas.retrieve_measure(port=port) sen_val = self.sen_meas.retrieve_measure(port=port)
debug.info(2,"s_en delay={}ns".format(sen_val)) debug.info(2, "s_en delay={}ns".format(sen_val))
if self.sen_meas.meta_add_delay: if self.sen_meas.meta_add_delay:
max_delay = self.period/2 max_delay = self.period / 2
else: else:
max_delay = self.period max_delay = self.period
return not (type(sen_val) != float or sen_val > max_delay) return not (type(sen_val) != float or sen_val > max_delay)
def check_read_debug_measures(self, port): def check_read_debug_measures(self, port):
"""Debug measures that indicate special conditions.""" """Debug measures that indicate special conditions."""
@ -866,20 +693,20 @@ class delay(simulation):
elif self.br_name == meas.targ_name_no_port: elif self.br_name == meas.targ_name_no_port:
br_vals[meas.meta_str] = val br_vals[meas.meta_str] = val
debug.info(2,"{}={}".format(meas.name,val)) debug.info(2, "{}={}".format(meas.name, val))
dout_success = True dout_success = True
bl_success = False bl_success = False
for meas in self.dout_volt_meas: for meas in self.dout_volt_meas:
val = meas.retrieve_measure(port=port) val = meas.retrieve_measure(port=port)
debug.info(2,"{}={}".format(meas.name, val)) debug.info(2, "{}={}".format(meas.name, val))
debug.check(type(val)==float, "Error retrieving numeric measurement: {0} {1}".format(meas.name,val)) debug.check(type(val)==float, "Error retrieving numeric measurement: {0} {1}".format(meas.name, val))
if meas.meta_str == sram_op.READ_ONE and val < self.vdd_voltage*0.1: if meas.meta_str == sram_op.READ_ONE and val < self.vdd_voltage * 0.1:
dout_success = False dout_success = False
debug.info(1, "Debug measurement failed. Value {}V was read on read 1 cycle.".format(val)) debug.info(1, "Debug measurement failed. Value {}V was read on read 1 cycle.".format(val))
bl_success = self.check_bitline_meas(bl_vals[sram_op.READ_ONE], br_vals[sram_op.READ_ONE]) bl_success = self.check_bitline_meas(bl_vals[sram_op.READ_ONE], br_vals[sram_op.READ_ONE])
elif meas.meta_str == sram_op.READ_ZERO and val > self.vdd_voltage*0.9: elif meas.meta_str == sram_op.READ_ZERO and val > self.vdd_voltage * 0.9:
dout_success = False dout_success = False
debug.info(1, "Debug measurement failed. Value {}V was read on read 0 cycle.".format(val)) debug.info(1, "Debug measurement failed. Value {}V was read on read 0 cycle.".format(val))
bl_success = self.check_bitline_meas(br_vals[sram_op.READ_ONE], bl_vals[sram_op.READ_ONE]) bl_success = self.check_bitline_meas(br_vals[sram_op.READ_ONE], bl_vals[sram_op.READ_ONE])
@ -887,11 +714,10 @@ class delay(simulation):
# If the bitlines have a correct value while the output does not then that is a # If the bitlines have a correct value while the output does not then that is a
# sen error. FIXME: there are other checks that can be done to solidfy this conclusion. # sen error. FIXME: there are other checks that can be done to solidfy this conclusion.
if not dout_success and bl_success: if not dout_success and bl_success:
debug.error("Sense amp enable timing error. Increase the delay chain through the configuration file.",1) debug.error("Sense amp enable timing error. Increase the delay chain through the configuration file.", 1)
return dout_success return dout_success
def check_bit_measures(self, bit_measures, port): def check_bit_measures(self, bit_measures, port):
""" """
Checks the measurements which represent the internal storage voltages Checks the measurements which represent the internal storage voltages
@ -901,27 +727,27 @@ class delay(simulation):
for polarity, meas_list in bit_measures.items(): for polarity, meas_list in bit_measures.items():
for meas in meas_list: for meas in meas_list:
val = meas.retrieve_measure(port=port) val = meas.retrieve_measure(port=port)
debug.info(2,"{}={}".format(meas.name, val)) debug.info(2, "{}={}".format(meas.name, val))
if type(val) != float: if type(val) != float:
continue continue
meas_cycle = meas.meta_str meas_cycle = meas.meta_str
# Loose error conditions. Assume it's not metastable but account for noise during reads. # Loose error conditions. Assume it's not metastable but account for noise during reads.
if (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.NONINVERTING) or\ if (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.NONINVERTING) or\
(meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.INVERTING): (meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.INVERTING):
success = val < self.vdd_voltage/2 success = val < self.vdd_voltage / 2
elif (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.INVERTING) or\ elif (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.INVERTING) or\
(meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.NONINVERTING): (meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.NONINVERTING):
success = val > self.vdd_voltage/2 success = val > self.vdd_voltage / 2
elif (meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.INVERTING) or\ elif (meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.INVERTING) or\
(meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.NONINVERTING): (meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.NONINVERTING):
success = val > self.vdd_voltage/2 success = val > self.vdd_voltage / 2
elif (meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.INVERTING) or\ elif (meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.INVERTING) or\
(meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.NONINVERTING): (meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.NONINVERTING):
success = val < self.vdd_voltage/2 success = val < self.vdd_voltage / 2
if not success: if not success:
debug.info(1,("Wrong value detected on probe bit during read/write cycle. " debug.info(1, ("Wrong value detected on probe bit during read/write cycle. "
"Check writes and control logic for bugs.\n measure={}, op={}, " "Check writes and control logic for bugs.\n measure={}, op={}, "
"bit_storage={}, V(bit)={}").format(meas.name, meas_cycle.name, polarity.name,val)) "bit_storage={}, V(bit)={}").format(meas.name, meas_cycle.name, polarity.name, val))
return success return success
@ -933,10 +759,10 @@ class delay(simulation):
# The inputs looks at discharge/charged bitline rather than left or right (bl/br) # The inputs looks at discharge/charged bitline rather than left or right (bl/br)
# Performs two checks, discharging bitline is at least 10% away from vdd and there is a # Performs two checks, discharging bitline is at least 10% away from vdd and there is a
# 10% vdd difference between the bitlines. Both need to fail to be considered a s_en error. # 10% vdd difference between the bitlines. Both need to fail to be considered a s_en error.
min_dicharge = v_discharged_bl < self.vdd_voltage*0.9 min_dicharge = v_discharged_bl < self.vdd_voltage * 0.9
min_diff = (v_charged_bl - v_discharged_bl) > self.vdd_voltage*0.1 min_diff = (v_charged_bl - v_discharged_bl) > self.vdd_voltage * 0.1
debug.info(1,"min_dicharge={}, min_diff={}".format(min_dicharge,min_diff)) debug.info(1, "min_dicharge={}, min_diff={}".format(min_dicharge, min_diff))
return (min_dicharge and min_diff) return (min_dicharge and min_diff)
def run_power_simulation(self): def run_power_simulation(self):
@ -948,20 +774,20 @@ class delay(simulation):
self.write_power_stimulus(trim=False) self.write_power_stimulus(trim=False)
self.stim.run_sim() self.stim.run_sim()
leakage_power=parse_spice_list("timing", "leakage_power") leakage_power=parse_spice_list("timing", "leakage_power")
debug.check(leakage_power!="Failed","Could not measure leakage power.") debug.check(leakage_power!="Failed", "Could not measure leakage power.")
debug.info(1, "Leakage power of full array is {0} mW".format(leakage_power*1e3)) debug.info(1, "Leakage power of full array is {0} mW".format(leakage_power * 1e3))
# debug # debug
# sys.exit(1) # sys.exit(1)
self.write_power_stimulus(trim=True) self.write_power_stimulus(trim=True)
self.stim.run_sim() self.stim.run_sim()
trim_leakage_power=parse_spice_list("timing", "leakage_power") trim_leakage_power=parse_spice_list("timing", "leakage_power")
debug.check(trim_leakage_power!="Failed","Could not measure leakage power.") debug.check(trim_leakage_power!="Failed", "Could not measure leakage power.")
debug.info(1, "Leakage power of trimmed array is {0} mW".format(trim_leakage_power*1e3)) debug.info(1, "Leakage power of trimmed array is {0} mW".format(trim_leakage_power * 1e3))
# For debug, you sometimes want to inspect each simulation. # For debug, you sometimes want to inspect each simulation.
# key=raw_input("press return to continue") # key=raw_input("press return to continue")
return (leakage_power*1e3, trim_leakage_power*1e3) return (leakage_power * 1e3, trim_leakage_power * 1e3)
def check_valid_delays(self, result_dict): def check_valid_delays(self, result_dict):
""" Check if the measurements are defined and if they are valid. """ """ Check if the measurements are defined and if they are valid. """
@ -971,30 +797,31 @@ class delay(simulation):
delay_lh = result_dict["delay_lh"] delay_lh = result_dict["delay_lh"]
slew_hl = result_dict["slew_hl"] slew_hl = result_dict["slew_hl"]
slew_lh = result_dict["slew_lh"] slew_lh = result_dict["slew_lh"]
period_load_slew_str = "period {0} load {1} slew {2}".format(self.period,self.load, self.slew) period_load_slew_str = "period {0} load {1} slew {2}".format(self.period, self.load, self.slew)
# if it failed or the read was longer than a period # if it failed or the read was longer than a period
if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float: if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float:
delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh) delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh)
slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh) slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl, slew_lh)
debug.info(2,"Failed simulation (in sec):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str, debug.info(2, "Failed simulation (in sec):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str, delays_str,
slews_str)) slews_str))
return False return False
delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh) delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh)
slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh) slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl, slew_lh)
half_period = self.period/2 # high-to-low delays start at neg. clk edge, so they need to be less than half_period # high-to-low delays start at neg. clk edge, so they need to be less than half_period
half_period = self.period / 2
if abs(delay_hl)>half_period or abs(delay_lh)>self.period or abs(slew_hl)>half_period or abs(slew_lh)>self.period \ if abs(delay_hl)>half_period or abs(delay_lh)>self.period or abs(slew_hl)>half_period or abs(slew_lh)>self.period \
or delay_hl<0 or delay_lh<0 or slew_hl<0 or slew_lh<0: or delay_hl<0 or delay_lh<0 or slew_hl<0 or slew_lh<0:
debug.info(2,"UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str, debug.info(2, "UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str, delays_str,
slews_str)) slews_str))
return False return False
else: else:
debug.info(2,"Successful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str, debug.info(2, "Successful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str, delays_str,
slews_str)) slews_str))
return True return True
@ -1039,7 +866,7 @@ class delay(simulation):
while True: while True:
time_out -= 1 time_out -= 1
if (time_out <= 0): if (time_out <= 0):
debug.error("Timed out, could not converge on minimum period.",2) debug.error("Timed out, could not converge on minimum period.", 2)
self.period = target_period self.period = target_period
debug.info(1, "MinPeriod Search Port {3}: {0}ns (ub: {1} lb: {2})".format(target_period, debug.info(1, "MinPeriod Search Port {3}: {0}ns (ub: {1} lb: {2})".format(target_period,
@ -1060,7 +887,6 @@ class delay(simulation):
target_period = 0.5 * (ub_period + lb_period) target_period = 0.5 * (ub_period + lb_period)
# key=input("press return to continue") # key=input("press return to continue")
def try_period(self, feasible_delays): def try_period(self, feasible_delays):
""" """
This tries to simulate a period and checks if the result This tries to simulate a period and checks if the result
@ -1083,19 +909,19 @@ class delay(simulation):
if self.sram.col_addr_size>0 and "slew" in dname: if self.sram.col_addr_size>0 and "slew" in dname:
continue continue
if not relative_compare(results[port][dname],feasible_delays[port][dname],error_tolerance=0.05): if not relative_compare(results[port][dname], feasible_delays[port][dname], error_tolerance=0.05):
debug.info(2,"Delay too big {0} vs {1}".format(results[port][dname],feasible_delays[port][dname])) debug.info(2, "Delay too big {0} vs {1}".format(results[port][dname], feasible_delays[port][dname]))
return False return False
# key=raw_input("press return to continue") # key=raw_input("press return to continue")
delay_str = ', '.join("{0}={1}ns".format(mname, results[port][mname]) for mname in self.delay_meas_names) delay_str = ', '.join("{0}={1}ns".format(mname, results[port][mname]) for mname in self.delay_meas_names)
debug.info(2,"Successful period {0}, Port {2}, {1}".format(self.period, debug.info(2, "Successful period {0}, Port {2}, {1}".format(self.period,
delay_str, delay_str,
port)) port))
return True return True
def set_probe(self,probe_address, probe_data): def set_probe(self, probe_address, probe_data):
""" """
Probe address and data can be set separately to utilize other Probe address and data can be set separately to utilize other
functions in this characterizer besides analyze. functions in this characterizer besides analyze.
@ -1111,16 +937,16 @@ class delay(simulation):
"""Calculates bitline column number of data bit under test using bit position and mux size""" """Calculates bitline column number of data bit under test using bit position and mux size"""
if self.sram.col_addr_size>0: if self.sram.col_addr_size>0:
col_address = int(probe_address[0:self.sram.col_addr_size],2) col_address = int(probe_address[0:self.sram.col_addr_size], 2)
else: else:
col_address = 0 col_address = 0
bl_column = int(self.sram.words_per_row*probe_data + col_address) bl_column = int(self.sram.words_per_row * probe_data + col_address)
return bl_column return bl_column
def get_address_row_number(self, probe_address): def get_address_row_number(self, probe_address):
"""Calculates wordline row number of data bit under test using address and column mux size""" """Calculates wordline row number of data bit under test using address and column mux size"""
return int(probe_address[self.sram.col_addr_size:],2) return int(probe_address[self.sram.col_addr_size:], 2)
def prepare_netlist(self): def prepare_netlist(self):
""" Prepare a trimmed netlist and regular netlist. """ """ Prepare a trimmed netlist and regular netlist. """
@ -1134,7 +960,7 @@ class delay(simulation):
self.num_cols, self.num_cols,
self.word_size, self.word_size,
self.num_spare_rows) self.num_spare_rows)
self.trimsp.trim(self.probe_address,self.probe_data) self.trimsp.trim(self.probe_address, self.probe_data)
else: else:
# The non-reduced netlist file when it is disabled # The non-reduced netlist file when it is disabled
self.trim_sp_file = "{}sram.sp".format(OPTS.openram_temp) self.trim_sp_file = "{}sram.sp".format(OPTS.openram_temp)
@ -1169,9 +995,9 @@ class delay(simulation):
feasible_delays = self.find_feasible_period() feasible_delays = self.find_feasible_period()
# 2) Finds the minimum period without degrading the delays by X% # 2) Finds the minimum period without degrading the delays by X%
self.set_load_slew(max(loads),max(slews)) self.set_load_slew(max(loads), max(slews))
min_period = self.find_min_period(feasible_delays) min_period = self.find_min_period(feasible_delays)
debug.check(type(min_period)==float,"Couldn't find minimum period.") debug.check(type(min_period)==float, "Couldn't find minimum period.")
debug.info(1, "Min Period Found: {0}ns".format(min_period)) debug.info(1, "Min Period Found: {0}ns".format(min_period))
char_sram_data["min_period"] = round_time(min_period) char_sram_data["min_period"] = round_time(min_period)
@ -1205,14 +1031,14 @@ class delay(simulation):
self.targ_write_ports = self.write_ports self.targ_write_ports = self.write_ports
for slew in slews: for slew in slews:
for load in loads: for load in loads:
self.set_load_slew(load,slew) self.set_load_slew(load, slew)
# Find the delay, dynamic power, and leakage power of the trimmed array. # Find the delay, dynamic power, and leakage power of the trimmed array.
(success, delay_results) = self.run_delay_simulation() (success, delay_results) = self.run_delay_simulation()
debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load)) debug.check(success, "Couldn't run a simulation. slew={0} load={1}\n".format(self.slew, self.load))
debug.info(1, "Simulation Passed: Port {0} slew={1} load={2}".format("All", self.slew,self.load)) debug.info(1, "Simulation Passed: Port {0} slew={1} load={2}".format("All", self.slew, self.load))
# The results has a dict for every port but dicts can be empty (e.g. ports were not targeted). # The results has a dict for every port but dicts can be empty (e.g. ports were not targeted).
for port in self.all_ports: for port in self.all_ports:
for mname,value in delay_results[port].items(): for mname, value in delay_results[port].items():
if "power" in mname: if "power" in mname:
# Subtract partial array leakage and add full array leakage for the power measures # Subtract partial array leakage and add full array leakage for the power measures
measure_data[port][mname].append(value + leakage_offset) measure_data[port][mname].append(value + leakage_offset)
@ -1233,8 +1059,8 @@ class delay(simulation):
elif c=="1": elif c=="1":
inverse_address += "0" inverse_address += "0"
else: else:
debug.error("Non-binary address string",1) debug.error("Non-binary address string", 1)
return inverse_address+column_addr return inverse_address + column_addr
def gen_test_cycles_one_port(self, read_port, write_port): def gen_test_cycles_one_port(self, read_port, write_port):
"""Sets a list of key time-points [ns] of the waveform (each rising edge) """Sets a list of key time-points [ns] of the waveform (each rising edge)
@ -1244,10 +1070,9 @@ class delay(simulation):
inverse_address = self.calculate_inverse_address() inverse_address = self.calculate_inverse_address()
# For now, ignore data patterns and write ones or zeros # For now, ignore data patterns and write ones or zeros
data_ones = "1"*self.word_size data_ones = "1" * self.word_size
data_zeros = "0"*self.word_size data_zeros = "0" * self.word_size
wmask_ones = "1"*self.num_wmasks wmask_ones = "1" * self.num_wmasks
wmask_zeroes = "0"*self.num_wmasks
if self.t_current == 0: if self.t_current == 0:
self.add_noop_all_ports("Idle cycle (no positive clock edge)") self.add_noop_all_ports("Idle cycle (no positive clock edge)")
@ -1263,10 +1088,10 @@ class delay(simulation):
data_zeros, data_zeros,
wmask_ones, wmask_ones,
write_port) write_port)
self.measure_cycles[write_port][sram_op.WRITE_ZERO] = len(self.cycle_times)-1 self.measure_cycles[write_port][sram_op.WRITE_ZERO] = len(self.cycle_times) - 1
self.add_noop_clock_one_port(write_port) self.add_noop_clock_one_port(write_port)
self.measure_cycles[write_port]["disabled_write0"] = len(self.cycle_times)-1 self.measure_cycles[write_port]["disabled_write0"] = len(self.cycle_times) - 1
# This also ensures we will have a H->L transition on the next read # This also ensures we will have a H->L transition on the next read
self.add_read("R data 1 address {} to set dout caps".format(inverse_address), self.add_read("R data 1 address {} to set dout caps".format(inverse_address),
@ -1276,12 +1101,11 @@ class delay(simulation):
self.add_read("R data 0 address {} to check W0 worked".format(self.probe_address), self.add_read("R data 0 address {} to check W0 worked".format(self.probe_address),
self.probe_address, self.probe_address,
read_port) read_port)
self.measure_cycles[read_port][sram_op.READ_ZERO] = len(self.cycle_times)-1 self.measure_cycles[read_port][sram_op.READ_ZERO] = len(self.cycle_times) - 1
self.add_noop_clock_one_port(read_port) self.add_noop_clock_one_port(read_port)
self.measure_cycles[read_port]["disabled_read0"] = len(self.cycle_times) - 1 self.measure_cycles[read_port]["disabled_read0"] = len(self.cycle_times) - 1
self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)") self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)")
self.add_write("W data 1 address {} to write value".format(self.probe_address), self.add_write("W data 1 address {} to write value".format(self.probe_address),
@ -1289,10 +1113,10 @@ class delay(simulation):
data_ones, data_ones,
wmask_ones, wmask_ones,
write_port) write_port)
self.measure_cycles[write_port][sram_op.WRITE_ONE] = len(self.cycle_times)-1 self.measure_cycles[write_port][sram_op.WRITE_ONE] = len(self.cycle_times) - 1
self.add_noop_clock_one_port(write_port) self.add_noop_clock_one_port(write_port)
self.measure_cycles[write_port]["disabled_write1"] = len(self.cycle_times)-1 self.measure_cycles[write_port]["disabled_write1"] = len(self.cycle_times) - 1
self.add_write("W data 0 address {} to clear din caps".format(inverse_address), self.add_write("W data 0 address {} to clear din caps".format(inverse_address),
inverse_address, inverse_address,
@ -1303,7 +1127,6 @@ class delay(simulation):
self.add_noop_clock_one_port(read_port) self.add_noop_clock_one_port(read_port)
self.measure_cycles[read_port]["disabled_read1"] = len(self.cycle_times) - 1 self.measure_cycles[read_port]["disabled_read1"] = len(self.cycle_times) - 1
# This also ensures we will have a L->H transition on the next read # This also ensures we will have a L->H transition on the next read
self.add_read("R data 0 address {} to clear dout caps".format(inverse_address), self.add_read("R data 0 address {} to clear dout caps".format(inverse_address),
inverse_address, inverse_address,
@ -1312,11 +1135,11 @@ class delay(simulation):
self.add_read("R data 1 address {} to check W1 worked".format(self.probe_address), self.add_read("R data 1 address {} to check W1 worked".format(self.probe_address),
self.probe_address, self.probe_address,
read_port) read_port)
self.measure_cycles[read_port][sram_op.READ_ONE] = len(self.cycle_times)-1 self.measure_cycles[read_port][sram_op.READ_ONE] = len(self.cycle_times) - 1
self.add_noop_all_ports("Idle cycle (if read takes >1 cycle))") self.add_noop_all_ports("Idle cycle (if read takes >1 cycle))")
def get_available_port(self,get_read_port): def get_available_port(self, get_read_port):
"""Returns the first accessible read or write port. """ """Returns the first accessible read or write port. """
if get_read_port and len(self.read_ports) > 0: if get_read_port and len(self.read_ports) > 0:
@ -1338,14 +1161,16 @@ class delay(simulation):
# Using this requires setting at least one port to target for simulation. # Using this requires setting at least one port to target for simulation.
if len(self.targ_write_ports) == 0 or len(self.targ_read_ports) == 0: if len(self.targ_write_ports) == 0 or len(self.targ_read_ports) == 0:
debug.error("Write and read port must be specified for characterization.",1) debug.error("Write and read port must be specified for characterization.", 1)
self.set_stimulus_variables() self.set_stimulus_variables()
# Get any available read/write port in case only a single write or read ports is being characterized. # Get any available read/write port in case only a single write or read ports is being characterized.
cur_read_port = self.get_available_port(get_read_port=True) cur_read_port = self.get_available_port(get_read_port=True)
cur_write_port = self.get_available_port(get_read_port=False) cur_write_port = self.get_available_port(get_read_port=False)
debug.check(cur_read_port != None, "Characterizer requires at least 1 read port") debug.check(cur_read_port != None,
debug.check(cur_write_port != None, "Characterizer requires at least 1 write port") "Characterizer requires at least 1 read port")
debug.check(cur_write_port != None,
"Characterizer requires at least 1 write port")
# Create test cycles for specified target ports. # Create test cycles for specified target ports.
write_pos = 0 write_pos = 0
@ -1382,7 +1207,7 @@ class delay(simulation):
debug.warning("In analytical mode, all ports have the timing of the first read port.") debug.warning("In analytical mode, all ports have the timing of the first read port.")
# Probe set to 0th bit, does not matter for analytical delay. # Probe set to 0th bit, does not matter for analytical delay.
self.set_probe('0'*self.addr_size, 0) self.set_probe('0' * self.addr_size, 0)
self.create_graph() self.create_graph()
self.set_internal_spice_names() self.set_internal_spice_names()
self.create_measurement_names() self.create_measurement_names()
@ -1392,13 +1217,13 @@ class delay(simulation):
'{}{}_{}'.format(self.dout_name, port, self.probe_data)) '{}{}_{}'.format(self.dout_name, port, self.probe_data))
# Select the path with the bitline (bl) # Select the path with the bitline (bl)
bl_name,br_name = self.get_bl_name(self.graph.all_paths, port) bl_name, br_name = self.get_bl_name(self.graph.all_paths, port)
bl_path = [path for path in self.graph.all_paths if bl_name in path][0] bl_path = [path for path in self.graph.all_paths if bl_name in path][0]
# Set delay/power for slews and loads # Set delay/power for slews and loads
port_data = self.get_empty_measure_data_dict() port_data = self.get_empty_measure_data_dict()
power = self.analytical_power(slews, loads) power = self.analytical_power(slews, loads)
debug.info(1,'Slew, Load, Delay(ns), Slew(ns)') debug.info(1, 'Slew, Load, Delay(ns), Slew(ns)')
max_delay = 0.0 max_delay = 0.0
for slew in slews: for slew in slews:
for load in loads: for load in loads:
@ -1407,29 +1232,33 @@ class delay(simulation):
total_delay = self.sum_delays(path_delays) total_delay = self.sum_delays(path_delays)
max_delay = max(max_delay, total_delay.delay) max_delay = max(max_delay, total_delay.delay)
debug.info(1,'{}, {}, {}, {}'.format(slew,load,total_delay.delay/1e3, total_delay.slew/1e3)) debug.info(1,
'{}, {}, {}, {}'.format(slew,
load,
total_delay.delay / 1e3,
total_delay.slew / 1e3))
# Delay is only calculated on a single port and replicated for now. # Delay is only calculated on a single port and replicated for now.
for port in self.all_ports: for port in self.all_ports:
for mname in self.delay_meas_names+self.power_meas_names: for mname in self.delay_meas_names + self.power_meas_names:
if "power" in mname: if "power" in mname:
port_data[port][mname].append(power.dynamic) port_data[port][mname].append(power.dynamic)
elif "delay" in mname and port in self.read_ports: elif "delay" in mname and port in self.read_ports:
port_data[port][mname].append(total_delay.delay/1e3) port_data[port][mname].append(total_delay.delay / 1e3)
elif "slew" in mname and port in self.read_ports: elif "slew" in mname and port in self.read_ports:
port_data[port][mname].append(total_delay.slew/1e3) port_data[port][mname].append(total_delay.slew / 1e3)
else: else:
debug.error("Measurement name not recognized: {}".format(mname),1) debug.error("Measurement name not recognized: {}".format(mname), 1)
# Estimate the period as double the delay with margin # Estimate the period as double the delay with margin
period_margin = 0.1 period_margin = 0.1
sram_data = { "min_period":(max_delay/1e3)*2*period_margin, sram_data = {"min_period": (max_delay / 1e3) * 2 * period_margin,
"leakage_power": power.leakage} "leakage_power": power.leakage}
debug.info(2,"SRAM Data:\n{}".format(sram_data)) debug.info(2, "SRAM Data:\n{}".format(sram_data))
debug.info(2,"Port Data:\n{}".format(port_data)) debug.info(2, "Port Data:\n{}".format(port_data))
return (sram_data,port_data) return (sram_data, port_data)
def analytical_power(self, slews, loads): def analytical_power(self, slews, loads):
"""Get the dynamic and leakage power from the SRAM""" """Get the dynamic and leakage power from the SRAM"""
@ -1440,8 +1269,8 @@ class delay(simulation):
# convert from nW to mW # convert from nW to mW
power.dynamic /= 1e6 power.dynamic /= 1e6
power.leakage /= 1e6 power.leakage /= 1e6
debug.info(1,"Dynamic Power: {0} mW".format(power.dynamic)) debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic))
debug.info(1,"Leakage Power: {0} mW".format(power.leakage)) debug.info(1, "Leakage Power: {0} mW".format(power.leakage))
return power return power
def gen_data(self): def gen_data(self):
@ -1449,7 +1278,7 @@ class delay(simulation):
for write_port in self.write_ports: for write_port in self.write_ports:
for i in range(self.word_size): for i in range(self.word_size):
sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i) sig_name="{0}{1}_{2} ".format(self.din_name, write_port, i)
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05)
def gen_addr(self): def gen_addr(self):
@ -1460,7 +1289,7 @@ class delay(simulation):
for port in self.all_ports: for port in self.all_ports:
for i in range(self.addr_size): for i in range(self.addr_size):
sig_name = "{0}{1}_{2}".format(self.addr_name,port,i) sig_name = "{0}{1}_{2}".format(self.addr_name, port, i)
self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][i], self.period, self.slew, 0.05) self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][i], self.period, self.slew, 0.05)
def gen_control(self): def gen_control(self):
@ -1471,11 +1300,10 @@ class delay(simulation):
if port in self.readwrite_ports: if port in self.readwrite_ports:
self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05)
def get_empty_measure_data_dict(self): def get_empty_measure_data_dict(self):
"""Make a dict of lists for each type of delay and power measurement to append results to""" """Make a dict of lists for each type of delay and power measurement to append results to"""
measure_names = self.delay_meas_names + self.power_meas_names measure_names = self.delay_meas_names + self.power_meas_names
# Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. # Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists.
measure_data = [{mname:[] for mname in measure_names} for i in self.all_ports] measure_data = [{mname: [] for mname in measure_names} for i in self.all_ports]
return measure_data return measure_data

View File

@ -8,13 +8,11 @@
import collections import collections
import debug import debug
import random import random
import math
from .stimuli import * from .stimuli import *
from .charutils import * from .charutils import *
from globals import OPTS from globals import OPTS
from .simulation import simulation from .simulation import simulation
# from .delay import delay
import graph_util
from sram_factory import factory
class functional(simulation): class functional(simulation):
@ -23,7 +21,7 @@ class functional(simulation):
for successful SRAM operation. for successful SRAM operation.
""" """
def __init__(self, sram, spfile, corner): def __init__(self, sram, spfile, corner, cycles=15):
super().__init__(sram, spfile, corner) super().__init__(sram, spfile, corner)
# Seed the characterizer with a constant seed for unit tests # Seed the characterizer with a constant seed for unit tests
@ -31,32 +29,37 @@ class functional(simulation):
random.seed(12345) random.seed(12345)
if self.write_size: if self.write_size:
self.num_wmasks = int(self.word_size / self.write_size) self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else: else:
self.num_wmasks = 0 self.num_wmasks = 0
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()
# For the debug signal names # For the debug signal names
self.wordline_row = 0
self.bitline_column = 0
self.create_signal_names() self.create_signal_names()
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 = cycles
# This is to have ordered keys for random selection # This is to have ordered keys for random selection
self.stored_words = collections.OrderedDict() self.stored_words = collections.OrderedDict()
self.read_check = [] self.read_check = []
self.read_results = [] self.read_results = []
def run(self, feasible_period=None): def run(self, feasible_period=None):
if feasible_period: #period defaults to tech.py feasible period otherwise. if feasible_period: # period defaults to tech.py feasible period otherwise.
self.period = feasible_period self.period = feasible_period
# Generate a random sequence of reads and writes # Generate a random sequence of reads and writes
self.create_random_memory_sequence() self.create_random_memory_sequence()
@ -226,17 +229,25 @@ class functional(simulation):
sp_read_value = "" sp_read_value = ""
for bit in range(self.word_size + self.num_spare_cols): for bit in range(self.word_size + self.num_spare_cols):
value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(), bit, check)) value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(), bit, check))
if value > self.v_high: try:
sp_read_value = "1" + sp_read_value value = float(value)
elif value < self.v_low: if value > self.v_high:
sp_read_value = "0" + sp_read_value sp_read_value = "1" + sp_read_value
else: elif value < self.v_low:
error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port, sp_read_value = "0" + sp_read_value
bit, else:
value, error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port,
eo_period, bit,
self.v_low, value,
self.v_high) eo_period,
self.v_low,
self.v_high)
except ValueError:
error ="FAILED: {0}_{1} value {2} at time {3}n is not a float.".format(dout_port,
bit,
value,
eo_period)
return (0, error) return (0, error)
self.read_results.append([sp_read_value, dout_port, eo_period, check]) self.read_results.append([sp_read_value, dout_port, eo_period, check])
@ -245,11 +256,12 @@ class functional(simulation):
def check_stim_results(self): def check_stim_results(self):
for i in range(len(self.read_check)): for i in range(len(self.read_check)):
if self.read_check[i][0] != self.read_results[i][0]: if self.read_check[i][0] != self.read_results[i][0]:
error = "FAILED: {0} value {1} does not match written value {2} read during cycle {3} at time {4}n".format(self.read_results[i][1], str = "FAILED: {0} value {1} does not match written value {2} read during cycle {3} at time {4}n"
self.read_results[i][0], error = str.format(self.read_results[i][1],
self.read_check[i][0], self.read_results[i][0],
int((self.read_results[i][2]-self.period)/self.period), self.read_check[i][0],
self.read_results[i][2]) int((self.read_results[i][2] - self.period) / self.period),
self.read_results[i][2])
return(0, error) return(0, error)
return(1, "SUCCESS") return(1, "SUCCESS")
@ -311,7 +323,7 @@ class functional(simulation):
else: else:
expected_value = self.word_size + self.num_spare_cols expected_value = self.word_size + self.num_spare_cols
for i in range(expected_value - len(new_value)): for i in range(expected_value - len(new_value)):
new_value = "0" + new_value new_value = "0" + new_value
# print("Binary Conversion: {} to {}".format(value, new_value)) # print("Binary Conversion: {} to {}".format(value, new_value))
return new_value return new_value
@ -344,8 +356,8 @@ class functional(simulation):
# Write important signals to stim file # Write important signals to stim file
self.sf.write("\n\n* Important signals for debug\n") self.sf.write("\n\n* Important signals for debug\n")
self.sf.write("* bl: {}\n".format(self.bl_name)) self.sf.write("* bl: {}\n".format(self.bl_name.format(port)))
self.sf.write("* br: {}\n".format(self.br_name)) self.sf.write("* br: {}\n".format(self.br_name.format(port)))
self.sf.write("* s_en: {}\n".format(self.sen_name)) self.sf.write("* s_en: {}\n".format(self.sen_name))
self.sf.write("* q: {}\n".format(self.q_name)) self.sf.write("* q: {}\n".format(self.q_name))
self.sf.write("* qbar: {}\n".format(self.qbar_name)) self.sf.write("* qbar: {}\n".format(self.qbar_name))
@ -420,49 +432,7 @@ 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 #FIXME: Similar function to delay.py, refactor this
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
def create_graph(self):
"""Creates timing graph to generate the timing paths for the SRAM output."""
self.sram.bank.bitcell_array.init_graph_params() # Removes previous bit exclusions
# Does wordline=0 and column=0 just for debug names
self.sram.bank.bitcell_array.graph_exclude_bits(0, 0)
# Generate new graph every analysis as edges might change depending on test bit
self.graph = graph_util.timing_graph()
self.sram_spc_name = "X{}".format(self.sram.name)
self.sram.build_graph(self.graph, self.sram_spc_name, self.pins)
# FIXME: refactor to share with delay.py
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,62 +444,4 @@ 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):
"""Gets the mods as a set which should be excluded while searching for name."""
# Exclude the RBL as it contains bitcells which are not in the main bitcell array
# so it makes the search awkward
return set(factory.get_mods(OPTS.replica_bitline))
def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None):
"""
Finds a single alias for the int_net in given paths.
More or less hits cause an error
"""
net_found = False
for path in paths:
aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, int_net, mod, exclusion_set)
if net_found and len(aliases) >= 1:
debug.error('Found multiple paths with {} net.'.format(int_net), 1)
elif len(aliases) > 1:
debug.error('Found multiple {} nets in single path.'.format(int_net), 1)
elif not net_found and len(aliases) == 1:
path_net_name = aliases[0]
net_found = True
if not net_found:
debug.error("Could not find {} net in timing paths.".format(int_net), 1)
return path_net_name

View File

@ -5,16 +5,13 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import sys,re,shutil
from design import design
import debug import debug
import math import math
import tech import tech
from .stimuli import *
from .trim_spice import *
from .charutils import *
import utils
from globals import OPTS from globals import OPTS
from sram_factory import factory
import graph_util
class simulation(): class simulation():
@ -38,11 +35,11 @@ class simulation():
self.write_ports = self.sram.write_ports self.write_ports = self.sram.write_ports
self.words_per_row = self.sram.words_per_row self.words_per_row = self.sram.words_per_row
if self.write_size: if self.write_size:
self.num_wmasks = int(self.word_size/self.write_size) self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else: else:
self.num_wmasks = 0 self.num_wmasks = 0
def set_corner(self,corner): def set_corner(self, corner):
""" Set the corner values """ """ Set the corner values """
self.corner = corner self.corner = corner
(self.process, self.vdd_voltage, self.temperature) = corner (self.process, self.vdd_voltage, self.temperature) = corner
@ -50,8 +47,8 @@ class simulation():
def set_spice_constants(self): def set_spice_constants(self):
""" sets feasible timing parameters """ """ sets feasible timing parameters """
self.period = tech.spice["feasible_period"] self.period = tech.spice["feasible_period"]
self.slew = tech.spice["rise_time"]*2 self.slew = tech.spice["rise_time"] * 2
self.load = tech.spice["dff_in_cap"]*4 self.load = tech.spice["dff_in_cap"] * 4
self.v_high = self.vdd_voltage - tech.spice["nom_threshold"] self.v_high = self.vdd_voltage - tech.spice["nom_threshold"]
self.v_low = tech.spice["nom_threshold"] self.v_low = tech.spice["nom_threshold"]
@ -61,16 +58,14 @@ class simulation():
self.addr_name = "a" self.addr_name = "a"
self.din_name = "din" self.din_name = "din"
self.dout_name = "dout" self.dout_name = "dout"
self.pins = self.gen_pin_names(port_signal_names=(self.addr_name,self.din_name,self.dout_name), self.pins = self.gen_pin_names(port_signal_names=(self.addr_name, self.din_name, self.dout_name),
port_info=(len(self.all_ports),self.write_ports,self.read_ports), port_info=(len(self.all_ports), self.write_ports, self.read_ports),
abits=self.addr_size, abits=self.addr_size,
dbits=self.word_size + self.num_spare_cols) dbits=self.word_size + self.num_spare_cols)
debug.check(len(self.sram.pins) == len(self.pins), debug.check(len(self.sram.pins) == len(self.pins),
"Number of pins generated for characterization \ "Number of pins generated for characterization \
do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins, do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins,
self.pins)) self.pins))
#This is TODO once multiport control has been finalized.
#self.control_name = "CSB"
def set_stimulus_variables(self): def set_stimulus_variables(self):
# Clock signals # Clock signals
@ -78,20 +73,20 @@ class simulation():
self.t_current = 0 self.t_current = 0
# control signals: only one cs_b for entire multiported sram, one we_b for each write port # control signals: only one cs_b for entire multiported sram, one we_b for each write port
self.csb_values = {port:[] for port in self.all_ports} self.csb_values = {port: [] for port in self.all_ports}
self.web_values = {port:[] for port in self.readwrite_ports} self.web_values = {port: [] for port in self.readwrite_ports}
# Raw values added as a bit vector # Raw values added as a bit vector
self.addr_value = {port:[] for port in self.all_ports} self.addr_value = {port: [] for port in self.all_ports}
self.data_value = {port:[] for port in self.write_ports} self.data_value = {port: [] for port in self.write_ports}
self.wmask_value = {port:[] for port in self.write_ports} self.wmask_value = {port: [] for port in self.write_ports}
self.spare_wen_value = {port:[] for port in self.write_ports} self.spare_wen_value = {port: [] for port in self.write_ports}
# Three dimensional list to handle each addr and data bits for each port over the number of checks # Three dimensional list to handle each addr and data bits for each port over the number of checks
self.addr_values = {port:[[] for bit in range(self.addr_size)] for port in self.all_ports} self.addr_values = {port: [[] for bit in range(self.addr_size)] for port in self.all_ports}
self.data_values = {port:[[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports} self.data_values = {port: [[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports}
self.wmask_values = {port:[[] for bit in range(self.num_wmasks)] for port in self.write_ports} self.wmask_values = {port: [[] for bit in range(self.num_wmasks)] for port in self.write_ports}
self.spare_wen_values = {port:[[] for bit in range(self.num_spare_cols)] for port in self.write_ports} self.spare_wen_values = {port: [[] for bit in range(self.num_spare_cols)] for port in self.write_ports}
# For generating comments in SPICE stimulus # For generating comments in SPICE stimulus
self.cycle_comments = [] self.cycle_comments = []
@ -99,7 +94,7 @@ class simulation():
def add_control_one_port(self, port, op): def add_control_one_port(self, port, op):
"""Appends control signals for operation to a given port""" """Appends control signals for operation to a given port"""
#Determine values to write to port # Determine values to write to port
web_val = 1 web_val = 1
csb_val = 1 csb_val = 1
if op == "read": if op == "read":
@ -108,7 +103,7 @@ class simulation():
csb_val = 0 csb_val = 0
web_val = 0 web_val = 0
elif op != "noop": elif op != "noop":
debug.error("Could not add control signals for port {0}. Command {1} not recognized".format(port,op),1) debug.error("Could not add control signals for port {0}. Command {1} not recognized".format(port, op), 1)
# Append the values depending on the type of port # Append the values depending on the type of port
self.csb_values[port].append(csb_val) self.csb_values[port].append(csb_val)
@ -128,7 +123,7 @@ class simulation():
elif c=="1": elif c=="1":
self.data_values[port][bit].append(1) self.data_values[port][bit].append(1)
else: else:
debug.error("Non-binary data string",1) debug.error("Non-binary data string", 1)
bit -= 1 bit -= 1
def add_address(self, address, port): def add_address(self, address, port):
@ -141,12 +136,11 @@ class simulation():
if c=="0": if c=="0":
self.addr_values[port][bit].append(0) self.addr_values[port][bit].append(0)
elif c=="1": elif c=="1":
self.addr_values[port][bit].append(1) self.addr_values[port][bit].append(1)
else: else:
debug.error("Non-binary address string",1) debug.error("Non-binary address string", 1)
bit -= 1 bit -= 1
def add_wmask(self, wmask, port): def add_wmask(self, wmask, port):
""" Add the array of address values """ """ Add the array of address values """
debug.check(len(wmask) == self.num_wmasks, "Invalid wmask size.") debug.check(len(wmask) == self.num_wmasks, "Invalid wmask size.")
@ -190,9 +184,9 @@ class simulation():
self.t_current += self.period self.t_current += self.period
self.add_control_one_port(port, "write") self.add_control_one_port(port, "write")
self.add_data(data,port) self.add_data(data, port)
self.add_address(address,port) self.add_address(address, port)
self.add_wmask(wmask,port) self.add_wmask(wmask, port)
self.add_spare_wen("1" * self.num_spare_cols, port) self.add_spare_wen("1" * self.num_spare_cols, port)
#Add noops to all other ports. #Add noops to all other ports.
@ -220,11 +214,11 @@ class simulation():
try: try:
self.add_data(self.data_value[port][-1], port) self.add_data(self.data_value[port][-1], port)
except: except:
self.add_data("0"*(self.word_size + self.num_spare_cols), port) self.add_data("0" * (self.word_size + self.num_spare_cols), port)
try: try:
self.add_wmask(self.wmask_value[port][-1], port) self.add_wmask(self.wmask_value[port][-1], port)
except: except:
self.add_wmask("0"*self.num_wmasks, port) self.add_wmask("0" * self.num_wmasks, port)
self.add_spare_wen("0" * self.num_spare_cols, port) self.add_spare_wen("0" * self.num_spare_cols, port)
#Add noops to all other ports. #Add noops to all other ports.
@ -275,11 +269,11 @@ class simulation():
try: try:
self.add_data(self.data_value[port][-1], port) self.add_data(self.data_value[port][-1], port)
except: except:
self.add_data("0"*(self.word_size + self.num_spare_cols), port) self.add_data("0" * (self.word_size + self.num_spare_cols), port)
try: try:
self.add_wmask(self.wmask_value[port][-1], port) self.add_wmask(self.wmask_value[port][-1], port)
except: except:
self.add_wmask("0"*self.num_wmasks, port) self.add_wmask("0" * self.num_wmasks, port)
self.add_spare_wen("0" * self.num_spare_cols, port) self.add_spare_wen("0" * self.num_spare_cols, port)
def add_noop_one_port(self, port): def add_noop_one_port(self, port):
@ -289,7 +283,7 @@ class simulation():
try: try:
self.add_address(self.addr_value[port][-1], port) self.add_address(self.addr_value[port][-1], port)
except: except:
self.add_address("0"*self.addr_size, port) self.add_address("0" * self.addr_size, port)
# If the port is also a readwrite then add # If the port is also a readwrite then add
# the same value as previous cycle # the same value as previous cycle
@ -297,11 +291,11 @@ class simulation():
try: try:
self.add_data(self.data_value[port][-1], port) self.add_data(self.data_value[port][-1], port)
except: except:
self.add_data("0"*(self.word_size + self.num_spare_cols), port) self.add_data("0" * (self.word_size + self.num_spare_cols), port)
try: try:
self.add_wmask(self.wmask_value[port][-1], port) self.add_wmask(self.wmask_value[port][-1], port)
except: except:
self.add_wmask("0"*self.num_wmasks, port) self.add_wmask("0" * self.num_wmasks, port)
self.add_spare_wen("0" * self.num_spare_cols, port) self.add_spare_wen("0" * self.num_spare_cols, port)
def add_noop_clock_one_port(self, port): def add_noop_clock_one_port(self, port):
@ -320,12 +314,11 @@ class simulation():
if unselected_port != port: if unselected_port != port:
self.add_noop_one_port(unselected_port) self.add_noop_one_port(unselected_port)
def append_cycle_comment(self, port, comment): def append_cycle_comment(self, port, comment):
"""Add comment to list to be printed in stimulus file""" """Add comment to list to be printed in stimulus file"""
#Clean up time before appending. Make spacing dynamic as well. #Clean up time before appending. Make spacing dynamic as well.
time = "{0:.2f} ns:".format(self.t_current) time = "{0:.2f} ns:".format(self.t_current)
time_spacing = len(time)+6 time_spacing = len(time) + 6
self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times), self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times),
port, port,
time, time,
@ -334,9 +327,10 @@ class simulation():
def gen_cycle_comment(self, op, word, addr, wmask, port, t_current): def gen_cycle_comment(self, op, word, addr, wmask, port, t_current):
if op == "noop": if op == "noop":
comment = "\tIdle during cycle {0} ({1}ns - {2}ns)".format(int(t_current/self.period), str = "\tIdle during cycle {0} ({1}ns - {2}ns)"
t_current, comment = str.format(int(t_current / self.period),
t_current+self.period) t_current,
t_current + self.period)
elif op == "write": elif op == "write":
comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word, comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
addr, addr,
@ -345,40 +339,41 @@ class simulation():
t_current, t_current,
t_current+self.period) t_current+self.period)
elif op == "partial_write": elif op == "partial_write":
comment = "\tWriting (partial) {0} to address {1} with mask bit {2} (from port {3}) during cycle {4} ({5}ns - {6}ns)".format(word, str = "\tWriting (partial) {0} to address {1} with mask bit {2} (from port {3}) during cycle {4} ({5}ns - {6}ns)"
addr, comment = str.format(word,
wmask, addr,
port, wmask,
int(t_current / self.period), port,
t_current, int(t_current / self.period),
t_current + self.period) t_current,
t_current + self.period)
else: else:
comment = "\tReading {0} from address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word, str = "\tReading {0} from address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)"
addr, comment = str.format(word,
port, addr,
int(t_current/self.period), port,
t_current, int(t_current / self.period),
t_current+self.period) t_current,
t_current + self.period)
return comment return comment
def gen_pin_names(self, port_signal_names, port_info, abits, dbits): def gen_pin_names(self, port_signal_names, port_info, abits, dbits):
"""Creates the pins names of the SRAM based on the no. of ports.""" """Creates the pins names of the SRAM based on the no. of ports."""
#This may seem redundant as the pin names are already defined in the sram. However, it is difficult # This may seem redundant as the pin names are already defined in the sram. However, it is difficult
#to extract the functionality from the names, so they are recreated. As the order is static, changing # to extract the functionality from the names, so they are recreated. As the order is static, changing
#the order of the pin names will cause issues here. # the order of the pin names will cause issues here.
pin_names = [] pin_names = []
(addr_name, din_name, dout_name) = port_signal_names (addr_name, din_name, dout_name) = port_signal_names
(total_ports, write_index, read_index) = port_info (total_ports, write_index, read_index) = port_info
for write_input in write_index: for write_input in write_index:
for i in range(dbits): for i in range(dbits):
pin_names.append("{0}{1}_{2}".format(din_name,write_input, i)) pin_names.append("{0}{1}_{2}".format(din_name, write_input, i))
for port in range(total_ports): for port in range(total_ports):
for i in range(abits): for i in range(abits):
pin_names.append("{0}{1}_{2}".format(addr_name,port,i)) pin_names.append("{0}{1}_{2}".format(addr_name, port, i))
#Control signals not finalized. #Control signals not finalized.
for port in range(total_ports): for port in range(total_ports):
@ -393,18 +388,159 @@ class simulation():
if self.write_size: if self.write_size:
for port in write_index: for port in write_index:
for bit in range(self.num_wmasks): for bit in range(self.num_wmasks):
pin_names.append("WMASK{0}_{1}".format(port,bit)) pin_names.append("WMASK{0}_{1}".format(port, bit))
if self.num_spare_cols: if self.num_spare_cols:
for port in write_index: for port in write_index:
for bit in range(self.num_spare_cols): for bit in range(self.num_spare_cols):
pin_names.append("SPARE_WEN{0}_{1}".format(port,bit)) pin_names.append("SPARE_WEN{0}_{1}".format(port, bit))
for read_output in read_index: for read_output in read_index:
for i in range(dbits): for i in range(dbits):
pin_names.append("{0}{1}_{2}".format(dout_name,read_output, i)) pin_names.append("{0}{1}_{2}".format(dout_name, read_output, i))
pin_names.append("{0}".format("vdd")) pin_names.append("{0}".format("vdd"))
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 create_graph(self):
"""
Creates timing graph to generate the timing paths for the SRAM output.
"""
self.sram.clear_exclude_bits() # Removes previous bit exclusions
self.sram.graph_exclude_bits(self.wordline_row, self.bitline_column)
# Generate new graph every analysis as edges might change depending on test bit
self.graph = graph_util.timing_graph()
self.sram_instance_name = "X{}".format(self.sram.name)
self.sram.build_graph(self.graph, self.sram_instance_name, self.pins)
def get_bl_name_search_exclusions(self):
"""
Gets the mods as a set which should be excluded while searching for name.
"""
# Exclude the RBL as it contains bitcells which are not in the main bitcell array
# so it makes the search awkward
return set(factory.get_mods(OPTS.replica_bitline))
def get_alias_in_path(self, paths, internal_net, mod, exclusion_set=None):
"""
Finds a single alias for the internal_net in given paths.
More or less hits cause an error
"""
net_found = False
for path in paths:
aliases = self.sram.find_aliases(self.sram_instance_name, self.pins, path, internal_net, mod, exclusion_set)
if net_found and len(aliases) >= 1:
debug.error('Found multiple paths with {} net.'.format(internal_net), 1)
elif len(aliases) > 1:
debug.error('Found multiple {} nets in single path.'.format(internal_net), 1)
elif not net_found and len(aliases) == 1:
path_net_name = aliases[0]
net_found = True
if not net_found:
debug.error("Could not find {} net in timing paths.".format(internal_net), 1)
return path_net_name
def get_bl_name(self, paths, port):
"""
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))
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

@ -15,7 +15,6 @@ import tech
import debug import debug
import subprocess import subprocess
import os import os
import sys
import numpy as np import numpy as np
from globals import OPTS from globals import OPTS
@ -40,32 +39,26 @@ class stimuli():
debug.info(2, "Not using spice library") debug.info(2, "Not using spice library")
self.device_models = tech.spice["fet_models"][self.process] self.device_models = tech.spice["fet_models"][self.process]
self.sram_name = "Xsram"
def inst_sram(self, pins, inst_name):
""" Function to instatiate an SRAM subckt. """
self.sf.write("{} ".format(self.sram_name))
for pin in self.sram_pins:
self.sf.write("{0} ".format(pin))
self.sf.write("{0}\n".format(inst_name))
def inst_model(self, pins, model_name): def inst_model(self, pins, model_name):
""" Function to instantiate a generic model with a set of pins """ """ Function to instantiate a generic model with a set of pins """
self.sf.write("X{0} ".format(model_name))
for pin in pins:
self.sf.write("{0} ".format(pin))
self.sf.write("{0}\n".format(model_name))
def inst_sram_pex(self, pins, model_name): if OPTS.use_pex:
self.inst_pex_model(pins, model_name)
else:
self.sf.write("X{0} ".format(model_name))
for pin in pins:
self.sf.write("{0} ".format(pin))
self.sf.write("{0}\n".format(model_name))
def inst_pex_model(self, pins, model_name):
self.sf.write("X{0} ".format(model_name)) self.sf.write("X{0} ".format(model_name))
for pin in pins: for pin in pins:
self.sf.write("{0} ".format(pin)) self.sf.write("{0} ".format(pin))
for bank in range(OPTS.num_banks): for bank in range(OPTS.num_banks):
row = int(OPTS.num_words / OPTS.words_per_row) - 1 row = int(OPTS.num_words / OPTS.words_per_row) - 1
col = int(OPTS.word_size * 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_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)) 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 # 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 row in range(int(OPTS.num_words / OPTS.words_per_row)):
# for col in range(int(OPTS.word_size * OPTS.words_per_row)): # for col in range(int(OPTS.word_size * OPTS.words_per_row)):
@ -77,7 +70,6 @@ class stimuli():
self.sf.write("bl{0}_{1} ".format(port, col)) self.sf.write("bl{0}_{1} ".format(port, col))
self.sf.write("br{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("s_en{0} ".format(bank))
self.sf.write("{0}\n".format(model_name)) self.sf.write("{0}\n".format(model_name))
@ -94,8 +86,7 @@ class stimuli():
self.tx_length)) self.tx_length))
self.sf.write(".ENDS test_inv\n") self.sf.write(".ENDS test_inv\n")
def create_buffer(self, buffer_name, size=[1, 3], beta=2.5):
def create_buffer(self, buffer_name, size=[1,3], beta=2.5):
""" """
Generates buffer for top level signals (only for sim Generates buffer for top level signals (only for sim
purposes). Size is pair for PMOS, NMOS width multiple. purposes). Size is pair for PMOS, NMOS width multiple.
@ -122,8 +113,6 @@ class stimuli():
self.tx_length)) self.tx_length))
self.sf.write(".ENDS test_{0}\n\n".format(buffer_name)) self.sf.write(".ENDS test_{0}\n\n".format(buffer_name))
def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall): def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
""" """
Generates a periodic signal with 50% duty cycle and slew rates. Period is measured Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
@ -140,7 +129,6 @@ class stimuli():
0.5*period-0.5*t_rise-0.5*t_fall, 0.5*period-0.5*t_rise-0.5*t_fall,
period)) period))
def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup): def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup):
""" """
Generate a PWL stimulus given a signal name and data values at each period. Generate a PWL stimulus given a signal name and data values at each period.
@ -149,18 +137,22 @@ class stimuli():
to the initial value. to the initial value.
""" """
# the initial value is not a clock time # the initial value is not a clock time
debug.check(len(clk_times)==len(data_values),"Clock and data value lengths don't match. {0} clock values, {1} data values for {2}".format(len(clk_times), len(data_values), sig_name)) str = "Clock and data value lengths don't match. {0} clock values, {1} data values for {2}"
debug.check(len(clk_times)==len(data_values),
str.format(len(clk_times),
len(data_values),
sig_name))
# shift signal times earlier for setup time # shift signal times earlier for setup time
times = np.array(clk_times) - setup*period times = np.array(clk_times) - setup * period
values = np.array(data_values) * self.voltage values = np.array(data_values) * self.voltage
half_slew = 0.5 * slew half_slew = 0.5 * slew
self.sf.write("* (time, data): {}\n".format(list(zip(clk_times, data_values)))) self.sf.write("* (time, data): {}\n".format(list(zip(clk_times, data_values))))
self.sf.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0])) self.sf.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0]))
for i in range(1,len(times)): for i in range(1, len(times)):
self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew, self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i] - half_slew,
values[i-1], values[i - 1],
times[i]+half_slew, times[i] + half_slew,
values[i])) values[i]))
self.sf.write(")\n") self.sf.write(")\n")
@ -169,9 +161,9 @@ class stimuli():
self.sf.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val)) self.sf.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val))
def get_inverse_voltage(self, value): def get_inverse_voltage(self, value):
if value > 0.5*self.voltage: if value > 0.5 * self.voltage:
return 0 return 0
elif value <= 0.5*self.voltage: elif value <= 0.5 * self.voltage:
return self.voltage return self.voltage
else: else:
debug.error("Invalid value to get an inverse of: {0}".format(value)) debug.error("Invalid value to get an inverse of: {0}".format(value))
@ -184,7 +176,6 @@ class stimuli():
else: else:
debug.error("Invalid value to get an inverse of: {0}".format(value)) debug.error("Invalid value to get an inverse of: {0}".format(value))
def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td): def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
""" Creates the .meas statement for the measurement of delay """ """ Creates the .meas statement for the measurement of delay """
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n" measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
@ -243,10 +234,10 @@ class stimuli():
reltol = 0.005 # 0.5% reltol = 0.005 # 0.5%
else: else:
reltol = 0.001 # 0.1% reltol = 0.001 # 0.1%
timestep = 10 #ps, was 5ps but ngspice was complaining the timestep was too small in certain tests. timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests.
# UIC is needed for ngspice to converge # UIC is needed for ngspice to converge
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep,end_time)) self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
self.sf.write(".TEMP {}\n".format(self.temperature)) self.sf.write(".TEMP {}\n".format(self.temperature))
if OPTS.spice_name == "ngspice": if OPTS.spice_name == "ngspice":
# ngspice sometimes has convergence problems if not using gear method # ngspice sometimes has convergence problems if not using gear method
@ -260,7 +251,7 @@ class stimuli():
# create plots for all signals # create plots for all signals
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n") self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
if OPTS.debug_level>0: if OPTS.debug_level>0:
if OPTS.spice_name in ["hspice","xa"]: if OPTS.spice_name in ["hspice", "xa"]:
self.sf.write(".probe V(*)\n") self.sf.write(".probe V(*)\n")
else: else:
self.sf.write(".plot V(*)\n") self.sf.write(".plot V(*)\n")
@ -271,7 +262,6 @@ class stimuli():
# end the stimulus file # end the stimulus file
self.sf.write(".end\n\n") self.sf.write(".end\n\n")
def write_include(self, circuit): def write_include(self, circuit):
"""Writes include statements, inputs are lists of model files""" """Writes include statements, inputs are lists of model files"""
@ -291,13 +281,12 @@ class stimuli():
else: else:
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item)) debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item))
def write_supply(self): def write_supply(self):
""" Writes supply voltage statements """ """ Writes supply voltage statements """
gnd_node_name = "0" gnd_node_name = "0"
self.sf.write("V{0} {0} {1} {2}\n".format(self.vdd_name, gnd_node_name, self.voltage)) self.sf.write("V{0} {0} {1} {2}\n".format(self.vdd_name, gnd_node_name, self.voltage))
#Adding a commented out supply for simulators where gnd and 0 are not global grounds. # Adding a commented out supply for simulators where gnd and 0 are not global grounds.
self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n") self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n")
self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0)) self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0))
@ -306,7 +295,7 @@ class stimuli():
temp_stim = "{0}stim.sp".format(OPTS.openram_temp) temp_stim = "{0}stim.sp".format(OPTS.openram_temp)
import datetime import datetime
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
debug.check(OPTS.spice_exe!="","No spice simulator has been found.") debug.check(OPTS.spice_exe != "", "No spice simulator has been found.")
if OPTS.spice_name == "xa": if OPTS.spice_name == "xa":
# Output the xa configurations here. FIXME: Move this to write it once. # Output the xa configurations here. FIXME: Move this to write it once.
@ -314,27 +303,32 @@ class stimuli():
xa_cfg.write("set_sim_level -level 7\n") xa_cfg.write("set_sim_level -level 7\n")
xa_cfg.write("set_powernet_level 7 -node vdd\n") xa_cfg.write("set_powernet_level 7 -node vdd\n")
xa_cfg.close() xa_cfg.close()
cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt 2".format(OPTS.spice_exe, cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt {3}".format(OPTS.spice_exe,
temp_stim, temp_stim,
OPTS.openram_temp) OPTS.openram_temp,
OPTS.num_threads)
valid_retcode=0 valid_retcode=0
elif OPTS.spice_name == "hspice": elif OPTS.spice_name == "hspice":
# TODO: Should make multithreading parameter a configuration option # TODO: Should make multithreading parameter a configuration option
cmd = "{0} -mt 2 -i {1} -o {2}timing".format(OPTS.spice_exe, cmd = "{0} -mt {1} -i {2} -o {3}timing".format(OPTS.spice_exe,
temp_stim, OPTS.num_threads,
OPTS.openram_temp) temp_stim,
OPTS.openram_temp)
valid_retcode=0 valid_retcode=0
else: else:
# ngspice 27+ supports threading with "set num_threads=4" in the stimulus file or a .spiceinit # ngspice 27+ supports threading with "set num_threads=4" in the stimulus file or a .spiceinit
# Measurements can't be made with a raw file set in ngspice # Measurements can't be made with a raw file set in ngspice
# -r {2}timing.raw # -r {2}timing.raw
ng_cfg = open("{}.spinit".format(OPTS.openram_temp), "w")
ng_cfg.write("set num_threads={}\n".format(OPTS.num_threads))
ng_cfg.close()
cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe, cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe,
temp_stim, temp_stim,
OPTS.openram_temp) OPTS.openram_temp)
# for some reason, ngspice-25 returns 1 when it only has acceptable warnings # for some reason, ngspice-25 returns 1 when it only has acceptable warnings
valid_retcode=1 valid_retcode=1
spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w') spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w')
spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w') spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w')
@ -348,7 +342,7 @@ class stimuli():
debug.error("Spice simulation error: " + cmd, -1) debug.error("Spice simulation error: " + cmd, -1)
else: else:
end_time = datetime.datetime.now() end_time = datetime.datetime.now()
delta_time = round((end_time-start_time).total_seconds(),1) delta_time = round((end_time - start_time).total_seconds(), 1)
debug.info(2,"*** Spice: {} seconds".format(delta_time)) debug.info(2, "*** Spice: {} seconds".format(delta_time))

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

@ -0,0 +1,136 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California
# All rights reserved.
#
import design
from sram_factory import factory
from globals import OPTS
from tech import cell_properties
class s8_col_cap_array(design.design):
"""
Generate a dummy row/column for the replica array.
"""
def __init__(self, rows, cols, location, column_offset=0, mirror=0, name=""):
super().__init__(name)
self.rows = rows
self.cols = cols
self.location = location
self.column_offset = column_offset
self.mirror = mirror
self.no_instances = True
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.place_array("col_cap_r{0}_c{1}", self.mirror)
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
""" Add the modules used in this design """
if self.location == "top":
self.colend1 = factory.create(module_type="s8_col_end", version = "colend")
self.add_mod(self.colend1)
self.colend2 = factory.create(module_type="s8_col_end", version = "colend_p_cent")
self.add_mod(self.colend2)
elif self.location == "bottom":
self.colend1 = factory.create(module_type="s8_col_end", version = "colenda")
self.add_mod(self.colend1)
self.colend2 = factory.create(module_type="s8_col_end", version = "colenda_p_cent")
self.add_mod(self.colend2)
self.cell = factory.create(module_type="s8_bitcell", version = "opt1")
def create_instances(self):
""" Create the module instances used in this design """
self.cell_inst = {}
self.array_layout = []
alternate_bitcell = 0
for col in range((self.cols * 2 )-1):
row_layout = []
name="rca_{0}".format(col)
# Top/bottom cell are always dummy cells.
# Regular array cells are replica cells (>left_rbl and <rows-right_rbl)
# Replic bit specifies which other bit (in the full range (0,rows) to make a replica cell.
if alternate_bitcell == 0:
row_layout.append(self.colend1)
self.cell_inst[col]=self.add_inst(name=name, mod=self.colend1)
#self.connect_inst(self.get_bitcell_pins(row, 0))
alternate_bitcell = 1
else:
row_layout.append(self.colend2)
self.cell_inst[col]=self.add_inst(name=name,mod=self.colend2)
#self.connect_inst(self.get_bitcell_pins(row, 0))
alternate_bitcell = 0
self.array_layout.append(row_layout)
def get_bitcell_pins(self, row, col):
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
pin_name = cell_properties.bitcell.cell_6t.pin
bitcell_pins = ["{0}_{1}".format(pin_name.bl0, col),
"{0}_{1}".format(pin_name.br0, col),
"vdd"]
return bitcell_pins
def place_array(self, name_template, row_offset=0):
self.height = self.colend1.height
self.width = (self.colend1.width + self.colend2.width) * self.cols - self.colend2.width
yoffset = 0.0
xoffset = 0.0
for row in range(0, len(self.array_layout)):
inst = self.insts[row]
inst.place(offset=[xoffset, yoffset])
xoffset += inst.width
def add_pins(self):
for row in range(self.cols):
for port in self.all_ports:
self.add_pin("bl{}_{}".format(port, row), "OUTPUT")
self.add_pin("br{}_{}".format(port, row), "OUTPUT")
self.add_pin("vpwr", "POWER")
self.add_pin("vgnd", "GROUND")
def add_layout_pins(self):
""" Add the layout pins """
return
row_list = self.cell.get_all_wl_names()
for row in range(1, self.row_size - 1):
for cell_row in row_list:
wl_pin = self.cell_inst[row, 0].get_pin(cell_row)
self.add_layout_pin(text=cell_row + "_{0}".format(row),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1),
width=self.width,
height=wl_pin.height())
# Add vdd/gnd via stacks
for row in range(1, self.row_size - 1):
for col in range(self.column_size):
inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
self.add_power_pin(name=pin.name,
loc=pin.center(),
start_layer=pin.layer)

View File

@ -0,0 +1,52 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,drc,parameter,cell_properties
from tech import cell_properties as props
import bitcell_base
from globals import OPTS
class s8_dummy_bitcell(bitcell_base.bitcell_base):
"""
A single bit cell (6T, 8T, etc.) This module implements the
single memory cell used in the design. It is a hand-made cell, so
the layout and netlist should be available in the technology
library.
"""
if props.compare_ports(props.bitcell.split_wl):
pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT" , "POWER", "GROUND"]
else:
pin_names = [props.bitcell.cell_6t.pin.bl,
props.bitcell.cell_6t.pin.br,
props.bitcell.cell_6t.pin.wl,
props.bitcell.cell_6t.pin.vdd,
props.bitcell.cell_6t.pin.gnd]
def __init__(self, version, name=""):
# Ignore the name argument
if version == "opt1":
self.name = "s8sram_cell_opt1"
self.border_structure = "s8sram_cell"
elif version == "opt1a":
self.name = "s8sram_cell_opt1a"
self.border_structure = "s8sram_cell"
bitcell_base.bitcell_base.__init__(self, self.name)
debug.info(2, "Create dummy bitcell")
(self.width, self.height) = utils.get_libcell_size(self.name,
GDS["unit"],
layer["mem"],
"s8sram_cell\x00")
self.pin_map = utils.get_libcell_pins(self.pin_names, self.name, GDS["unit"])

View File

@ -26,6 +26,9 @@ class s8_internal(design.design):
elif version == "wlstrap_p": elif version == "wlstrap_p":
self.name = "s8sram16x16_wlstrap_p" self.name = "s8sram16x16_wlstrap_p"
self.structure = "s8sram16x16_wlstrap_p_ce" self.structure = "s8sram16x16_wlstrap_p_ce"
elif version == "wlstrapa":
self.name = "s8sram_wlstrapa"
self.structure = "s8sram_wlstrapa_ce"
else: else:
debug.error("Invalid version", -1) debug.error("Invalid version", -1)
design.design.__init__(self, name=self.name) design.design.__init__(self, name=self.name)

View File

@ -0,0 +1,163 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California
# All rights reserved.
#
import design
from sram_factory import factory
from globals import OPTS
from tech import cell_properties
class s8_row_cap_array(design.design):
"""
Generate a dummy row/column for the replica array.
"""
def __init__(self, rows, cols, column_offset=0, mirror=0, name=""):
super().__init__(name)
self.rows = rows
self.cols = cols
self.column_offset = column_offset
self.mirror = mirror
self.no_instances = True
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.place_array("dummy_r{0}_c{1}", self.mirror)
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
""" Add the modules used in this design """
if self.column_offset == 0:
self.top_corner = factory.create(module_type="s8_corner", location = "ul")
self.add_mod(self.top_corner)
self.bottom_corner =factory.create(module_type="s8_corner", location = "ll")
self.add_mod(self.bottom_corner)
else:
self.top_corner = factory.create(module_type="s8_corner", location = "ur")
self.add_mod(self.top_corner)
self.bottom_corner = factory.create(module_type="s8_corner", location = "lr")
self.add_mod(self.bottom_corner)
self.rowend1 = factory.create(module_type="s8_row_end", version = "rowend")
self.add_mod(self.rowend1)
self.rowend2 = factory.create(module_type="s8_row_end", version = "rowenda")
self.add_mod(self.rowend2)
self.cell = factory.create(module_type="s8_bitcell", version = "opt1")
def create_instances(self):
""" Create the module instances used in this design """
self.cell_inst = {}
self.array_layout = []
alternate_bitcell = 0
for row in range(self.rows):
row_layout = []
name="rca_{0}".format(row)
# Top/bottom cell are always dummy cells.
# Regular array cells are replica cells (>left_rbl and <rows-right_rbl)
# Replic bit specifies which other bit (in the full range (0,rows) to make a replica cell.
if (row < self.rows -1 and row >0):
if alternate_bitcell == 0:
row_layout.append(self.rowend1)
self.cell_inst[row]=self.add_inst(name=name, mod=self.rowend1)
#self.connect_inst(self.get_bitcell_pins(row, 0))
alternate_bitcell = 1
else:
row_layout.append(self.rowend2)
self.cell_inst[row]=self.add_inst(name=name,mod=self.rowend2)
#self.connect_inst(self.get_bitcell_pins(row, 0))
alternate_bitcell = 0
elif (row == 0):
row_layout.append(self.bottom_corner)
self.cell_inst[row]=self.add_inst(name=name, mod=self.bottom_corner)
#self.connect_inst(self.get_bitcell_pins_col_cap(row, 0))
elif (row == self.rows - 1):
row_layout.append(self.top_corner)
self.cell_inst[row]=self.add_inst(name=name, mod=self.top_corner)
#self.connect_inst(self.get_bitcell_pins_col_cap(row, 0))
self.array_layout.append(row_layout)
def get_bitcell_pins(self, row, col):
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
pin_name = cell_properties.bitcell.cell_1rw1r.pin
bitcell_pins = ["{0}_{1}".format(pin_name.wl0, row),
"{0}_{1}".format(pin_name.wl1, row),
"gnd"]
return bitcell_pins
def place_array(self, name_template, row_offset=0):
self.width = 0
self.height = 0
for inst in self.insts:
self.height += inst.height
if inst.width > self.width:
self.width = inst.width
yoffset = 0.0
for row in range(0, len(self.array_layout)):
xoffset = 0.0
for col in range(0, len(self.array_layout[row])):
inst = self.insts[col + row*len(self.array_layout[row])]
inst.place(offset=[xoffset, yoffset])
xoffset += inst.width
yoffset += inst.height
def add_pins(self):
for row in range(self.rows):
for port in self.all_ports:
self.add_pin("wl0_{}_{}".format(port, row), "OUTPUT")
self.add_pin("wl1_{}_{}".format(port, row), "OUTPUT")
self.add_pin("vpwr", "POWER")
self.add_pin("vgnd", "GROUND")
def add_layout_pins(self):
""" Add the layout pins """
return
row_list = self.cell.get_all_wl_names()
for row in range(1, self.row_size - 1):
for cell_row in row_list:
wl_pin = self.cell_inst[row, 0].get_pin(cell_row)
self.add_layout_pin(text=cell_row + "_{0}".format(row),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1),
width=self.width,
height=wl_pin.height())
# Add vdd/gnd via stacks
for row in range(1, self.row_size - 1):
for col in range(self.column_size):
inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
self.add_power_pin(name=pin.name,
loc=pin.center(),
start_layer=pin.layer)

View File

@ -0,0 +1,34 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
import design
import utils
from globals import OPTS
from tech import parameter, drc, layer, GDS
class s8_row_end(design.design):
def __init__(self, version, name=""):
super().__init__(name)
pin_names = []
type_list = []
if version == "rowend":
self.name = "s8sram16x16_rowend"
elif version == "rowenda":
self.name = "s8sram16x16_rowenda"
else:
debug.error("Invalid type for row_end", -1)
design.design.__init__(self, name=self.name)
(self.width, self.height) = utils.get_libcell_size(self.name,
GDS["unit"],
layer["mem"],
"s8sram16x16_rowend_ce\x00")
pin_map = utils.get_libcell_pins(pin_names, self.name, GDS["unit"])

View File

@ -8,7 +8,7 @@
import debug import debug
import design import design
from sram_factory import factory from sram_factory import factory
from math import log, ceil from math import log, ceil, floor
from tech import drc, layer from tech import drc, layer
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
@ -27,7 +27,7 @@ class bank(design.design):
self.sram_config = sram_config self.sram_config = sram_config
sram_config.set_local_config(self) sram_config.set_local_config(self)
if self.write_size: if self.write_size:
self.num_wmasks = int(self.word_size / self.write_size) self.num_wmasks = int(ceil(self.word_size / self.write_size))
else: else:
self.num_wmasks = 0 self.num_wmasks = 0
@ -186,16 +186,26 @@ class bank(design.design):
self.bitcell_array_right = self.bitcell_array.width self.bitcell_array_right = self.bitcell_array.width
# These are the offsets of the main array (excluding dummy and replica rows/cols) # These are the offsets of the main array (excluding dummy and replica rows/cols)
self.main_bitcell_array_top = self.bitcell_array.bitcell_array_inst.uy() self.main_bitcell_array_top = self.bitcell_array.get_main_array_top()
# Just past the dummy column # Just past the dummy column
self.main_bitcell_array_left = self.bitcell_array.bitcell_array_inst.lx() self.main_bitcell_array_left = self.bitcell_array.get_main_array_left()
# Just past the dummy column
self.main_bitcell_array_right = self.bitcell_array.get_main_array_right()
# Just past the dummy row and replica row # Just past the dummy row and replica row
self.main_bitcell_array_bottom = self.bitcell_array.bitcell_array_inst.by() self.main_bitcell_array_bottom = self.bitcell_array.get_main_array_bottom()
self.compute_instance_port0_offsets() self.compute_instance_port0_offsets()
if len(self.all_ports)==2: if len(self.all_ports)==2:
self.compute_instance_port1_offsets() self.compute_instance_port1_offsets()
def get_column_offsets(self):
"""
Return an array of the x offsets of all the regular bits
"""
# Assumes bitcell_array is at 0,0
offsets = self.bitcell_array.get_column_offsets()
return offsets
def compute_instance_port0_offsets(self): def compute_instance_port0_offsets(self):
""" """
Compute the instance offsets for port0 on the left/bottom of the bank. Compute the instance offsets for port0 on the left/bottom of the bank.
@ -209,14 +219,14 @@ class bank(design.design):
# LOWER RIGHT QUADRANT # LOWER RIGHT QUADRANT
# Below the bitcell array # Below the bitcell array
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width, 0) self.port_data_offsets[port] = vector(0, 0)
# UPPER LEFT QUADRANT # UPPER LEFT QUADRANT
# To the left of the bitcell array above the predecoders and control logic # To the left of the bitcell array above the predecoders and control logic
x_offset = self.m2_gap + self.port_address.width x_offset = self.m2_gap + self.port_address[port].width
self.port_address_offsets[port] = vector(-x_offset, self.port_address_offsets[port] = vector(-x_offset,
self.main_bitcell_array_bottom) self.main_bitcell_array_bottom)
self.predecoder_height = self.port_address.predecoder_height + self.port_address_offsets[port].y self.predecoder_height = self.port_address[port].predecoder_height + self.port_address_offsets[port].y
# LOWER LEFT QUADRANT # LOWER LEFT QUADRANT
# Place the col decoder left aligned with wordline driver # Place the col decoder left aligned with wordline driver
@ -224,7 +234,7 @@ class bank(design.design):
# control logic to allow control signals to easily pass over in M3 # control logic to allow control signals to easily pass over in M3
# by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs # by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs
# may be routed in M3 or M4 # may be routed in M3 or M4
x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width x_offset = self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width
if self.col_addr_size > 0: if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = 1.25 * self.dff.height + self.column_decoder.height y_offset = 1.25 * self.dff.height + self.column_decoder.height
@ -253,11 +263,11 @@ class bank(design.design):
# UPPER LEFT QUADRANT # UPPER LEFT QUADRANT
# Above the bitcell array # Above the bitcell array
self.port_data_offsets[port] = vector(self.main_bitcell_array_left, self.bitcell_array_top) self.port_data_offsets[port] = vector(0, self.bitcell_array_top)
# LOWER RIGHT QUADRANT # LOWER RIGHT QUADRANT
# To the right of the bitcell array # To the right of the bitcell array
x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap
self.port_address_offsets[port] = vector(x_offset, self.port_address_offsets[port] = vector(x_offset,
self.main_bitcell_array_bottom) self.main_bitcell_array_bottom)
@ -268,7 +278,7 @@ class bank(design.design):
# control logic to allow control signals to easily pass over in M3 # control logic to allow control signals to easily pass over in M3
# by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs # by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs
# may be routed in M3 or M4 # may be routed in M3 or M4
x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width
if self.col_addr_size > 0: if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = self.bitcell_array_top + 1.25 * self.dff.height + self.column_decoder.height y_offset = self.bitcell_array_top + 1.25 * self.dff.height + self.column_decoder.height
@ -356,55 +366,65 @@ class bank(design.design):
def add_modules(self): def add_modules(self):
""" Add all the modules using the class loader """ """ Add all the modules using the class loader """
self.port_address = []
for port in self.all_ports:
self.port_address.append(factory.create(module_type="port_address",
cols=self.num_cols + self.num_spare_cols,
rows=self.num_rows,
port=port))
self.add_mod(self.port_address[port])
try:
local_array_size = OPTS.local_array_size
except AttributeError:
local_array_size = 0
if local_array_size > 0:
# Find the even multiple that satisfies the fanout with equal sized local arrays
total_cols = self.num_cols + self.num_spare_cols
num_lb = floor(total_cols / local_array_size)
final_size = total_cols - num_lb * local_array_size
cols = [local_array_size] * (num_lb - 1)
# Add the odd bits to the last local array
cols.append(local_array_size + final_size)
self.bitcell_array = factory.create(module_type="global_bitcell_array",
cols=cols,
rows=self.num_rows)
else:
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
cols=self.num_cols + self.num_spare_cols,
rows=self.num_rows)
self.add_mod(self.bitcell_array)
self.port_data = [] self.port_data = []
self.bit_offsets = self.get_column_offsets()
for port in self.all_ports: for port in self.all_ports:
temp_pre = factory.create(module_type="port_data", temp_pre = factory.create(module_type="port_data",
sram_config=self.sram_config, sram_config=self.sram_config,
port=port) port=port,
bit_offsets=self.bit_offsets)
self.port_data.append(temp_pre) self.port_data.append(temp_pre)
self.add_mod(self.port_data[port]) self.add_mod(self.port_data[port])
self.port_address = factory.create(module_type="port_address",
cols=self.num_cols + self.num_spare_cols,
rows=self.num_rows)
self.add_mod(self.port_address)
self.num_rbl = len(self.all_ports)
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
cols=self.num_cols + self.num_spare_cols,
rows=self.num_rows,
rbl=[1, 1 if len(self.all_ports)>1 else 0])
self.add_mod(self.bitcell_array)
if(self.num_banks > 1): if(self.num_banks > 1):
self.bank_select = factory.create(module_type="bank_select") self.bank_select = factory.create(module_type="bank_select")
self.add_mod(self.bank_select) self.add_mod(self.bank_select)
def create_bitcell_array(self): def create_bitcell_array(self):
""" Creating Bitcell Array """ """ Creating Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array", self.bitcell_array_inst=self.add_inst(name="bitcell_array",
mod=self.bitcell_array) mod=self.bitcell_array)
# Arrays are always: # Arrays are always:
# word lines (bottom to top) # word lines (bottom to top)
# bit lines (left to right) # bit lines (left to right)
# vdd # vdd
# gnd # gnd
temp = self.bitcell_array.get_inouts()
temp = self.bitcell_array.get_all_bitline_names() temp.append("rbl_wl0")
temp.extend(self.bitcell_array.get_wordline_names())
wordline_names = self.bitcell_array.get_all_wordline_names() if len(self.all_ports) > 1:
temp.append("rbl_wl1")
# Rename the RBL WL to the enable name
for port in self.all_ports:
rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)
wordline_names = [x.replace(rbl_wl_name[port], "wl_en{0}".format(port)) for x in wordline_names]
# Connect the other RBL WL to gnd
wordline_names = ["gnd" if x.startswith("rbl_wl") else x for x in wordline_names]
# Connect the dummy WL to gnd
wordline_names = ["gnd" if x.startswith("dummy") else x for x in wordline_names]
temp.extend(wordline_names)
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
@ -464,13 +484,15 @@ class bank(design.design):
self.port_address_inst = [None] * len(self.all_ports) self.port_address_inst = [None] * len(self.all_ports)
for port in self.all_ports: for port in self.all_ports:
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port), self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
mod=self.port_address) mod=self.port_address[port])
temp = [] temp = []
for bit in range(self.row_addr_size): for bit in range(self.row_addr_size):
temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size)) temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size))
temp.append("wl_en{}".format(port)) temp.append("wl_en{}".format(port))
temp.extend(self.bitcell_array.get_wordline_names(port)) wordline_names = self.bitcell_array.get_wordline_names(port)
temp.extend(wordline_names)
temp.append("rbl_wl{}".format(port))
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
@ -658,7 +680,7 @@ class bank(design.design):
# 2 pitches on the right for vias/jogs to access the inputs # 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - 2 * self.m3_pitch, self.min_y_offset) control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - 2 * self.m3_pitch, self.min_y_offset)
# The control bus is routed up to two pitches below the bitcell array # The control bus is routed up to two pitches below the bitcell array
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch control_bus_length = self.port_data_inst[0].uy() - self.min_y_offset
self.bus_pins[0] = self.create_bus(layer="m2", self.bus_pins[0] = self.create_bus(layer="m2",
offset=control_bus_offset, offset=control_bus_offset,
names=self.control_signals[0], names=self.control_signals[0],
@ -670,7 +692,7 @@ class bank(design.design):
# Port 1 # Port 1
if len(self.all_ports)==2: if len(self.all_ports)==2:
# The other control bus is routed up to two pitches above the bitcell array # The other control bus is routed up to two pitches above the bitcell array
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch control_bus_length = self.max_y_offset - self.port_data_inst[1].by()
control_bus_offset = vector(self.bitcell_array_right + 2.5 * self.m3_pitch, control_bus_offset = vector(self.bitcell_array_right + 2.5 * self.m3_pitch,
self.max_y_offset - control_bus_length) self.max_y_offset - control_bus_length)
# The bus for the right port is reversed so that the rbl_wl is closest to the array # The bus for the right port is reversed so that the rbl_wl is closest to the array
@ -690,7 +712,6 @@ class bank(design.design):
inst1 = self.bitcell_array_inst inst1 = self.bitcell_array_inst
inst1_bl_name = [x for x in self.bitcell_array.get_bitline_names(port) if "bl" in x] inst1_bl_name = [x for x in self.bitcell_array.get_bitline_names(port) if "bl" in x]
inst1_br_name = [x for x in self.bitcell_array.get_bitline_names(port) if "br" in x] inst1_br_name = [x for x in self.bitcell_array.get_bitline_names(port) if "br" in x]
inst2_bl_name = [] inst2_bl_name = []
inst2_br_name = [] inst2_br_name = []
for col in range(self.num_cols): for col in range(self.num_cols):
@ -709,8 +730,7 @@ class bank(design.design):
# Connect the replica bitlines # Connect the replica bitlines
rbl_bl_names = self.bitcell_array.get_rbl_bitline_names(port)[2 * port: 2 * port + 2] for (array_name, data_name) in zip(["rbl_bl_{0}_{0}".format(port), "rbl_br_{0}_{0}".format(port)], ["rbl_bl", "rbl_br"]):
for (array_name, data_name) in zip(rbl_bl_names, ["rbl_bl", "rbl_br"]):
self.connect_bitline(inst1, inst2, array_name, data_name) self.connect_bitline(inst1, inst2, array_name, data_name)
def route_port_data_out(self, port): def route_port_data_out(self, port):
@ -825,33 +845,51 @@ class bank(design.design):
self.route_port_address_in(port) self.route_port_address_in(port)
if port % 2: if port % 2:
self.route_port_address_right(port) self.route_port_address_out(port, "right")
else: else:
self.route_port_address_left(port) self.route_port_address_out(port, "left")
def route_port_address_left(self, port): def route_port_address_out(self, port, side="left"):
""" Connecting Wordline driver output to Bitcell WL connection """ """ Connecting Wordline driver output to Bitcell WL connection """
driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] + ["rbl_wl"]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port)): rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)[port]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port) + [rbl_wl_name]):
# The mid guarantees we exit the input cell to the right. # The mid guarantees we exit the input cell to the right.
driver_wl_pin = self.port_address_inst[port].get_pin(driver_name) driver_wl_pin = self.port_address_inst[port].get_pin(driver_name)
driver_wl_pos = driver_wl_pin.rc() if side == "left":
driver_wl_pos = driver_wl_pin.rc()
else:
driver_wl_pos = driver_wl_pin.lc()
bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name) bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name)
bitcell_wl_pos = bitcell_wl_pin.lc()
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].rx() + 0.5 * self.bitcell_array_inst.lx(), 0) if side == "left":
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1) bitcell_wl_pos = bitcell_wl_pin.lc()
self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) port_address_pos = self.port_address_inst[port].rx()
self.add_via_stack_center(from_layer=driver_wl_pin.layer, bitcell_array_pos = self.bitcell_array_inst.lx()
to_layer=bitcell_wl_pin.layer, else:
offset=bitcell_wl_pos, bitcell_wl_pos = bitcell_wl_pin.rc()
directions=("H", "H")) port_address_pos = self.port_address_inst[port].lx()
bitcell_array_pos = self.bitcell_array_inst.rx()
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0)
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
if driver_wl_pin.layer != bitcell_wl_pin.layer:
self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2])
self.add_via_stack_center(from_layer=driver_wl_pin.layer,
to_layer=bitcell_wl_pin.layer,
offset=mid2)
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
else:
self.add_path(bitcell_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_port_address_right(self, port): def route_port_address_right(self, port):
""" Connecting Wordline driver output to Bitcell WL connection """ """ Connecting Wordline driver output to Bitcell WL connection """
driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] + ["rbl_wl"]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port)): rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)[port]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port) + [rbl_wl_name]):
# The mid guarantees we exit the input cell to the right. # The mid guarantees we exit the input cell to the right.
driver_wl_pin = self.port_address_inst[port].get_pin(driver_name) driver_wl_pin = self.port_address_inst[port].get_pin(driver_name)
driver_wl_pos = driver_wl_pin.lc() driver_wl_pos = driver_wl_pin.lc()
@ -859,11 +897,12 @@ class bank(design.design):
bitcell_wl_pos = bitcell_wl_pin.rc() bitcell_wl_pos = bitcell_wl_pin.rc()
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].lx() + 0.5 * self.bitcell_array_inst.rx(), 0) mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].lx() + 0.5 * self.bitcell_array_inst.rx(), 0)
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1) mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2])
self.add_via_stack_center(from_layer=driver_wl_pin.layer, self.add_via_stack_center(from_layer=driver_wl_pin.layer,
to_layer=bitcell_wl_pin.layer, to_layer=bitcell_wl_pin.layer,
offset=bitcell_wl_pos, offset=mid2)
directions=("H", "H")) self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
def route_column_address_lines(self, port): def route_column_address_lines(self, port):
""" Connecting the select lines of column mux to the address bus """ """ Connecting the select lines of column mux to the address bus """
@ -961,16 +1000,15 @@ class bank(design.design):
def route_unused_wordlines(self): def route_unused_wordlines(self):
""" Connect the unused RBL and dummy wordlines to gnd """ """ Connect the unused RBL and dummy wordlines to gnd """
gnd_wl_names = [] gnd_wl_names = []
return
# Connect unused RBL WL to gnd # Connect unused RBL WL to gnd
# All RBL WL names # All RBL WL names
array_rbl_names = set(self.bitcell_array.get_rbl_wordline_names()) array_rbl_names = set(self.bitcell_array.get_rbl_wordline_names())
dummy_rbl_names = set(self.bitcell_array.get_dummy_wordline_names())
# List of used RBL WL names # List of used RBL WL names
rbl_wl_names = set() rbl_wl_names = set()
for port in self.all_ports: for port in self.all_ports:
rbl_wl_names.add(self.bitcell_array.get_rbl_wordline_names(port)[port]) rbl_wl_names.add(self.bitcell_array.get_rbl_wordline_names(port)[port])
gnd_wl_names = list((array_rbl_names - rbl_wl_names) | dummy_rbl_names) gnd_wl_names = list((array_rbl_names - rbl_wl_names))
for wl_name in gnd_wl_names: for wl_name in gnd_wl_names:
pin = self.bitcell_array_inst.get_pin(wl_name) pin = self.bitcell_array_inst.get_pin(wl_name)
@ -1000,10 +1038,6 @@ class bank(design.design):
connection.append((self.prefix + "p_en_bar{}".format(port), connection.append((self.prefix + "p_en_bar{}".format(port),
self.port_data_inst[port].get_pin("p_en_bar"))) self.port_data_inst[port].get_pin("p_en_bar")))
rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)
connection.append((self.prefix + "wl_en{}".format(port),
self.bitcell_array_inst.get_pin(rbl_wl_name[port])))
if port in self.write_ports: if port in self.write_ports:
connection.append((self.prefix + "w_en{}".format(port), connection.append((self.prefix + "w_en{}".format(port),
self.port_data_inst[port].get_pin("w_en"))) self.port_data_inst[port].get_pin("w_en")))
@ -1029,61 +1063,43 @@ class bank(design.design):
control_signal = self.prefix + "wl_en{}".format(port) control_signal = self.prefix + "wl_en{}".format(port)
if port % 2: if port % 2:
pin_pos = self.port_address_inst[port].get_pin("wl_en").uc() pin_pos = self.port_address_inst[port].get_pin("wl_en").uc()
mid_pos = pin_pos + vector(0, 2 * self.m2_gap) # to route down to the top of the bus control_y_offset = self.bus_pins[port][control_signal].by()
mid_pos = vector(pin_pos.x, control_y_offset + self.m1_pitch)
else: else:
pin_pos = self.port_address_inst[port].get_pin("wl_en").bc() pin_pos = self.port_address_inst[port].get_pin("wl_en").bc()
mid_pos = pin_pos - vector(0, 2 * self.m2_gap) # to route down to the top of the bus control_y_offset = self.bus_pins[port][control_signal].uy()
mid_pos = vector(pin_pos.x, control_y_offset - self.m1_pitch)
control_x_offset = self.bus_pins[port][control_signal].cx() control_x_offset = self.bus_pins[port][control_signal].cx()
control_pos = vector(control_x_offset, mid_pos.y) control_pos = vector(control_x_offset, mid_pos.y)
self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos]) self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos])
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:
if self.port_data[port]: if self.port_data[port]:
self.port_data[port].graph_exclude_precharge() self.port_data[port].graph_exclude_precharge()
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.
"""
return self.bitcell_array_inst.mod.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, return self.bitcell_array_inst.mod.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name,
row, row,
col) col)
def graph_exclude_bits(self, targ_row, targ_col):
"""
Excludes bits in column from being added to graph except target
"""
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bitcell_array.clear_exclude_bits()

View File

@ -5,6 +5,7 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import debug
from bitcell_base_array import bitcell_base_array from bitcell_base_array import bitcell_base_array
from s8_corner import s8_corner from s8_corner import s8_corner
from tech import drc, spice from tech import drc, spice
@ -20,6 +21,12 @@ class bitcell_array(bitcell_base_array):
""" """
def __init__(self, rows, cols, column_offset=0, name=""): def __init__(self, rows, cols, column_offset=0, name=""):
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
# This will create a default set of bitline/wordline names
self.create_all_bitline_names()
self.create_all_wordline_names()
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@ -112,15 +119,10 @@ 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): def graph_exclude_bits(self, targ_row=None, targ_col=None):
"""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 Excludes bits in column from being added to graph except target
bitcell_wl_cin = self.cell.get_wl_cin() """
total_cin = bitcell_wl_cin * self.column_size
return total_cin
def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target"""
# Function is not robust with column mux configurations # Function is not robust with column mux configurations
for row in range(self.row_size): for row in range(self.row_size):
for col in range(self.column_size): for col in range(self.column_size):

View File

@ -19,7 +19,6 @@ class bitcell_base_array(design.design):
def __init__(self, name, rows, cols, column_offset): def __init__(self, name, rows, cols, column_offset):
super().__init__(name) super().__init__(name)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
self.column_size = cols self.column_size = cols
self.row_size = rows self.row_size = rows
@ -34,14 +33,19 @@ class bitcell_base_array(design.design):
self.strap = factory.create(module_type="s8_internal", version="wlstrap") self.strap = factory.create(module_type="s8_internal", version="wlstrap")
self.strap2 = factory.create(module_type="s8_internal", version="wlstrap_p") self.strap2 = factory.create(module_type="s8_internal", version="wlstrap_p")
self.create_all_bitline_names() self.wordline_names = [[] for port in self.all_ports]
self.create_all_wordline_names() self.all_wordline_names = []
self.bitline_names = [[] for port in self.all_ports]
self.all_bitline_names = []
self.rbl_bitline_names = [[] for port in self.all_ports]
self.all_rbl_bitline_names = []
self.rbl_wordline_names = [[] for port in self.all_ports]
self.all_rbl_wordline_names = []
def get_all_bitline_names(self, prefix=""): def get_all_bitline_names(self, prefix=""):
return [prefix + x for x in self.all_bitline_names] return [prefix + x for x in self.all_bitline_names]
def create_all_bitline_names(self): def create_all_bitline_names(self):
self.bitline_names = [[] for port in self.all_ports]
for col in range(self.column_size): for col in range(self.column_size):
for port in self.all_ports: for port in self.all_ports:
self.bitline_names[port].extend(["bl_{0}_{1}".format(port, col), self.bitline_names[port].extend(["bl_{0}_{1}".format(port, col),
@ -49,11 +53,10 @@ class bitcell_base_array(design.design):
# Make a flat list too # Make a flat list too
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl] self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
def get_all_wordline_names(self, prefix=""): # def get_all_wordline_names(self, prefix=""):
return [prefix + x for x in self.all_wordline_names] # return [prefix + x for x in self.all_wordline_names]
def create_all_wordline_names(self): def create_all_wordline_names(self):
self.wordline_names = [[] for port in self.all_ports]
for row in range(self.row_size): for row in range(self.row_size):
for port in self.all_ports: for port in self.all_ports:
if not cell_properties.compare_ports(cell_properties.bitcell.split_wl): if not cell_properties.compare_ports(cell_properties.bitcell.split_wl):
@ -63,18 +66,6 @@ class bitcell_base_array(design.design):
self.wordline_names[port].append("wl1_{0}_{1}".format(port, row)) self.wordline_names[port].append("wl1_{0}_{1}".format(port, row))
self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl] self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl]
def get_bitline_names(self, port=None):
if port == None:
return self.all_bitline_names
else:
return self.bitline_names[port]
def get_wordline_names(self, port=None):
if port == None:
return self.all_wordline_names
else:
return self.wordline_names[port]
def add_pins(self): def add_pins(self):
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
for bl_name in self.get_bitline_names(): for bl_name in self.get_bitline_names():
@ -102,6 +93,60 @@ class bitcell_base_array(design.design):
return bitcell_pins return bitcell_pins
def get_rbl_wordline_names(self, port=None):
"""
Return the WL for the given RBL port.
"""
if port == None:
return self.all_rbl_wordline_names
else:
return self.rbl_wordline_names[port]
def get_rbl_bitline_names(self, port=None):
""" Return all the BL for the given RBL port """
if port == None:
return self.all_rbl_bitline_names
else:
return self.rbl_bitline_names[port]
def get_bitline_names(self, port=None):
""" Return the regular bitlines for the given port or all"""
if port == None:
return self.all_bitline_names
else:
return self.bitline_names[port]
def get_all_bitline_names(self, port=None):
""" Return ALL the bitline names (including rbl) """
temp = []
temp.extend(self.get_rbl_bitline_names(0))
if port == None:
temp.extend(self.all_bitline_names)
else:
temp.extend(self.bitline_names[port])
if len(self.all_ports) > 1:
temp.extend(self.get_rbl_bitline_names(1))
return temp
def get_wordline_names(self, port=None):
""" Return the regular wordline names """
if port == None:
return self.all_wordline_names
else:
return self.wordline_names[port]
def get_all_wordline_names(self, port=None):
""" Return all the wordline names """
temp = []
temp.extend(self.get_rbl_wordline_names(0))
if port == None:
temp.extend(self.all_wordline_names)
else:
temp.extend(self.wordline_names[port])
if len(self.all_ports) > 1:
temp.extend(self.get_rbl_wordline_names(1))
return temp
def add_layout_pins(self): def add_layout_pins(self):
""" Add the layout pins """ """ Add the layout pins """
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
@ -224,3 +269,10 @@ class bitcell_base_array(design.design):
else: else:
from tech import custom_cell_placement from tech import custom_cell_placement
custom_cell_placement(self) custom_cell_placement(self)
def get_column_offsets(self):
"""
Return an array of the x offsets of all the regular bits
"""
offsets = [self.cell_inst[0, col].lx() for col in range(self.column_size)]
return offsets

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 """
@ -869,137 +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"""

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

@ -18,6 +18,10 @@ class dummy_array(bitcell_base_array):
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
self.mirror = mirror self.mirror = mirror
# This will create a default set of bitline/wordline names
self.create_all_bitline_names()
self.create_all_wordline_names()
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -67,13 +71,69 @@ class dummy_array(bitcell_base_array):
from tech import custom_cell_arrangement from tech import custom_cell_arrangement
custom_cell_arrangement(self) custom_cell_arrangement(self)
def add_pins(self):
# bitline pins are not added because they are floating
for wl_name in self.get_wordline_names():
self.add_pin(wl_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_layout_pins(self):
""" Add the layout pins """
# Add the bitline metal, but not as pins since they are going to just be floating
# For some reason, LVS has an issue if we don't add this metal
bitline_names = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for port in self.all_ports:
bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port])
self.add_rect(layer=bl_pin.layer,
offset=bl_pin.ll().scale(1, 0),
width=bl_pin.width(),
height=self.height)
br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1])
self.add_rect(layer=br_pin.layer,
offset=br_pin.ll().scale(1, 0),
width=br_pin.width(),
height=self.height)
wl_names = self.cell.get_all_wl_names()
if not props.compare_ports(props.bitcell_array.use_custom_cell_arrangement):
for row in range(self.row_size):
for port in self.all_ports:
wl_pin = self.cell_inst[row, 0].get_pin(wl_names[port])
self.add_layout_pin(text="wl_{0}_{1}".format(port, row),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1),
width=self.width,
height=wl_pin.height())
else:
for row in range(self.row_size):
for port in self.all_ports:
for wl in range(len(wl_names)):
wl_pin = self.cell_inst[row, 0].get_pin("wl{}".format(wl))
self.add_layout_pin(text="wl{0}_{1}_{2}".format(wl, port, row),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1),
width=self.width,
height=wl_pin.height())
# Copy a vdd/gnd layout pin from every cell
if not props.compare_ports(props.bitcell_array.use_custom_cell_arrangement):
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
else:
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row, col]
for pin_name in ["vpwr", "vgnd"]:
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

@ -5,34 +5,28 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design import bitcell_base_array
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
from vector import vector from vector import vector
import debug import debug
from numpy import cumsum
class global_bitcell_array(design.design): class global_bitcell_array(bitcell_base_array.bitcell_base_array):
""" """
Creates a global bitcell array. Creates a global bitcell array.
Rows is an integer number for all local arrays. Rows is an integer number for all local arrays.
Cols is a list of the array widths. Cols is a list of the array widths.
add_left_rbl and add_right_
""" """
def __init__(self, rows, cols, ports, name=""): def __init__(self, rows, cols, name=""):
# The total of all columns will be the number of columns # The total of all columns will be the number of columns
super().__init__(name=name) super().__init__(name=name, rows=rows, cols=sum(cols), column_offset=0)
self.cols = cols self.column_sizes = cols
self.rows = rows self.col_offsets = [0] + list(cumsum(cols)[:-1])
self.all_ports = ports
debug.check(len(ports)<=2, "Only support dual port or less in global bitcell array.") debug.check(len(self.all_ports)<=2, "Only support dual port or less in global bitcell array.")
self.rbl = [1, 1 if len(self.all_ports)>1 else 0] self.rbl = [1, 1 if len(self.all_ports)>1 else 0]
self.left_rbl = self.rbl[0]
self.right_rbl = self.rbl[1]
# Just used for pin names
self.cell = factory.create(module_type="bitcell")
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@ -48,6 +42,8 @@ class global_bitcell_array(design.design):
self.place() self.place()
self.route()
self.add_layout_pins() self.add_layout_pins()
self.add_boundary() self.add_boundary()
@ -58,21 +54,46 @@ class global_bitcell_array(design.design):
""" Add the modules used in this design """ """ Add the modules used in this design """
self.local_mods = [] self.local_mods = []
for i, cols in enumerate(self.cols): # Special case of a single local array
# Always add the left RBLs to the first subarray and the right RBLs to the last subarray # so it should contain the left and possibly right RBL
if len(self.column_sizes) == 1:
la = factory.create(module_type="local_bitcell_array",
rows=self.row_size,
cols=self.column_sizes[0],
rbl=self.rbl,
left_rbl=[0],
right_rbl=[1] if len(self.all_ports) > 1 else [])
self.add_mod(la)
self.local_mods.append(la)
return
for i, cols in enumerate(self.column_sizes):
# Always add the left RBLs to the first subarray
if i == 0: if i == 0:
la = factory.create(module_type="local_bitcell_array", rows=self.rows, cols=cols, rbl=self.rbl, add_rbl=[self.left_rbl, 0]) la = factory.create(module_type="local_bitcell_array",
elif i == len(self.cols) - 1: rows=self.row_size,
la = factory.create(module_type="local_bitcell_array", rows=self.rows, cols=cols, rbl=self.rbl, add_rbl=[0, self.right_rbl]) cols=cols,
rbl=self.rbl,
left_rbl=[0])
# Add the right RBL to the last subarray
elif i == len(self.column_sizes) - 1 and len(self.all_ports) > 1:
la = factory.create(module_type="local_bitcell_array",
rows=self.row_size,
cols=cols,
rbl=self.rbl,
right_rbl=[1])
# Middle subarrays do not have any RBLs
else: else:
la = factory.create(module_type="local_bitcell_array", rows=self.rows, cols=cols, rbl=self.rbl, add_rbl=[0, 0]) la = factory.create(module_type="local_bitcell_array",
rows=self.row_size,
cols=cols,
rbl=self.rbl)
self.add_mod(la) self.add_mod(la)
self.local_mods.append(la) self.local_mods.append(la)
def add_pins(self): def add_pins(self):
return
self.add_bitline_pins() self.add_bitline_pins()
self.add_wordline_pins() self.add_wordline_pins()
@ -80,60 +101,96 @@ class global_bitcell_array(design.design):
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_bitline_pins(self): def add_bitline_pins(self):
self.bitline_names = [[] for x in self.all_ports]
self.rbl_bitline_names = [[] for x in self.all_ports]
for port in self.all_ports: for port in self.all_ports:
self.add_pin_list(self.replica_bitline_names[port], "INOUT") self.rbl_bitline_names[0].append("rbl_bl_{}_0".format(port))
self.add_pin_list(self.bitline_names, "INOUT") for port in self.all_ports:
self.rbl_bitline_names[0].append("rbl_br_{}_0".format(port))
for col in range(self.column_size):
for port in self.all_ports:
self.bitline_names[port].append("bl_{0}_{1}".format(port, col))
for port in self.all_ports:
self.bitline_names[port].append("br_{0}_{1}".format(port, col))
if len(self.all_ports) > 1:
for port in self.all_ports:
self.rbl_bitline_names[1].append("rbl_bl_{}_1".format(port))
for port in self.all_ports:
self.rbl_bitline_names[1].append("rbl_br_{}_1".format(port))
# Make a flat list too
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
# Make a flat list too
self.all_rbl_bitline_names = [x for sl in zip(*self.rbl_bitline_names) for x in sl]
self.add_pin_list(self.rbl_bitline_names[0], "INOUT")
self.add_pin_list(self.all_bitline_names, "INOUT")
if len(self.all_ports) > 1:
self.add_pin_list(self.rbl_bitline_names[1], "INOUT")
def add_wordline_pins(self): def add_wordline_pins(self):
# All wordline names for all ports self.rbl_wordline_names = [[] for x in self.all_ports]
self.wordline_names = []
# Wordline names for each port
self.wordline_names_by_port = [[] for x in self.all_ports]
# Replica wordlines by port
self.replica_wordline_names = [[] for x in self.all_ports]
# Regular array wordline names self.wordline_names = [[] for x in self.all_ports]
self.bitcell_array_wordline_names = self.bitcell_array.get_all_wordline_names()
self.wordline_names = [] for bit in self.all_ports:
for port in self.all_ports:
self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit))
# Left port WLs self.all_rbl_wordline_names = [x for sl in zip(*self.rbl_wordline_names) for x in sl]
for port in range(self.left_rbl):
# Make names for all RBLs
wl_names=["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()]
# Keep track of the pin that is the RBL
self.replica_wordline_names[port] = wl_names
self.wordline_names.extend(wl_names)
# Regular WLs # Regular WLs
self.wordline_names.extend(self.bitcell_array_wordline_names) for row in range(self.row_size):
for port in self.all_ports:
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
# Right port WLs self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl]
for port in range(self.left_rbl, self.left_rbl + self.right_rbl):
# Make names for all RBLs
wl_names=["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()]
# Keep track of the pin that is the RBL
self.replica_wordline_names[port] = wl_names
self.wordline_names.extend(wl_names)
# Array of all port wl names
for port in range(self.left_rbl + self.right_rbl):
wl_names = ["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()]
self.replica_wordline_names[port] = wl_names
self.add_pin_list(self.wordline_names, "INPUT")
for port in range(self.rbl[0]):
self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
self.add_pin_list(self.all_wordline_names, "INPUT")
for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]):
self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
def create_instances(self): def create_instances(self):
""" Create the module instances used in this design """ """ Create the module instances used in this design """
self.local_insts = [] self.local_insts = []
for i, mod in enumerate(self.local_mods): for col, mod in zip(self.col_offsets, self.local_mods):
name = "la_{0}".format(i) name = "la_{0}".format(col)
self.local_insts.append(self.add_inst(name=name, self.local_insts.append(self.add_inst(name=name,
mod=mod)) mod=mod))
self.connect_inst(mod.pins)
temp = []
if col == 0:
temp.extend(self.get_rbl_bitline_names(0))
port_inouts = [x for x in mod.get_inouts() if x.startswith("bl") or x.startswith("br")]
for pin_name in port_inouts:
# Offset of the last underscore that defines the bit number
bit_index = pin_name.rindex('_')
# col is the bit offset of the local array,
# while col_value is the offset within this array
col_value = int(pin_name[bit_index + 1:])
# Name of signal without the bit
base_name = pin_name[:bit_index]
# Strip the bit and add the new one
new_name = "{0}_{1}".format(base_name, col + col_value)
temp.append(new_name)
if len(self.all_ports) > 1 and mod == self.local_mods[-1]:
temp.extend(self.get_rbl_bitline_names(1))
for port in self.all_ports:
port_inputs = [x for x in mod.get_inputs() if "wl_{}".format(port) in x]
temp.extend(port_inputs)
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def place(self): def place(self):
offset = vector(0, 0) offset = vector(0, 0)
@ -144,5 +201,136 @@ class global_bitcell_array(design.design):
self.height = self.local_mods[0].height self.height = self.local_mods[0].height
self.width = self.local_insts[-1].rx() self.width = self.local_insts[-1].rx()
def add_layout_pins(self): def route(self):
pass pass
def add_layout_pins(self):
# Regular bitlines
for col, inst in zip(self.col_offsets, self.local_insts):
for port in self.all_ports:
port_inouts = [x for x in inst.mod.get_inouts() if x.startswith("bl_{}".format(port)) or x.startswith("br_{}".format(port))]
for pin_name in port_inouts:
# Offset of the last underscore that defines the bit number
bit_index = pin_name.rindex('_')
# col is the bit offset of the local array,
# while col_value is the offset within this array
col_value = int(pin_name[bit_index + 1:])
# Name of signal without the bit
base_name = pin_name[:bit_index]
# Strip the bit and add the new one
new_name = "{0}_{1}".format(base_name, col + col_value)
self.copy_layout_pin(inst, pin_name, new_name)
for wl_name in self.local_mods[0].get_inputs():
left_pin = self.local_insts[0].get_pin(wl_name)
right_pin = self.local_insts[-1].get_pin(wl_name)
self.add_layout_pin_segment_center(text=wl_name,
layer=left_pin.layer,
start=left_pin.lc(),
end=right_pin.rc())
# Replica bitlines
self.copy_layout_pin(self.local_insts[0], "rbl_bl_0_0")
self.copy_layout_pin(self.local_insts[0], "rbl_br_0_0")
if len(self.all_ports) > 1:
self.copy_layout_pin(self.local_insts[0], "rbl_bl_1_0")
self.copy_layout_pin(self.local_insts[0], "rbl_br_1_0")
self.copy_layout_pin(self.local_insts[-1], "rbl_bl_0_1")
self.copy_layout_pin(self.local_insts[-1], "rbl_br_0_1")
self.copy_layout_pin(self.local_insts[-1], "rbl_bl_1_1")
self.copy_layout_pin(self.local_insts[-1], "rbl_br_1_1")
for inst in self.insts:
self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst, "gnd")
def get_main_array_top(self):
return self.local_insts[0].offset.y + self.local_mods[0].get_main_array_top()
def get_main_array_bottom(self):
return self.local_insts[0].offset.y + self.local_mods[0].get_main_array_bottom()
def get_main_array_left(self):
return self.local_insts[0].offset.x + self.local_mods[0].get_main_array_left()
def get_main_array_right(self):
return self.local_insts[-1].offset.x + self.local_mods[-1].get_main_array_right()
def get_column_offsets(self):
"""
Return an array of the x offsets of all the regular bits
"""
offsets = []
for inst in self.local_insts:
offsets.extend(inst.lx() + x for x in inst.mod.get_column_offsets())
return offsets
def graph_exclude_bits(self, targ_row, targ_col):
"""
Excludes bits in column from being added to graph except target
"""
# This must find which local array includes the specified column
# Find the summation of columns that is large and take the one before
for i, col in enumerate(self.col_offsets):
if col > targ_col:
break
else:
i = len(self.local_mods)
# This is the array with the column
local_array = self.local_mods[i - 1]
# We must also translate the global array column number to the local array column number
local_col = targ_col - self.col_offsets[i - 1]
for mod in self.local_mods:
if mod == local_array:
mod.graph_exclude_bits(targ_row, local_col)
else:
# Otherwise, we exclude ALL of the rows/columns
mod.graph_exclude_bits()
def graph_exclude_replica_col_bits(self):
"""
Exclude all but replica in every local array.
"""
for mod in self.local_mods:
mod.graph_exclude_replica_col_bits()
def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell."""
# This must find which local array includes the specified column
# Find the summation of columns that is large and take the one before
for i, local_col in enumerate(self.col_offsets):
if local_col > col:
break
else:
# In this case, we it should be in the last bitcell array
i = len(self.col_offsets)
# This is the local instance
local_inst = self.local_insts[i - 1]
# This is the array with the column
local_array = self.local_mods[i - 1]
# We must also translate the global array column number to the local array column number
local_col = col - self.col_offsets[i - 1]
return local_array.get_cell_name(inst_name + '.x' + local_inst.name, row, local_col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
for mod in self.local_mods:
mod.clear_exclude_bits()
def graph_exclude_dffs(self):
"""Exclude dffs from graph as they do not represent critical path"""
self.graph_inst_exclude.add(self.ctrl_dff_inst)

View File

@ -55,9 +55,9 @@ class hierarchical_decoder(design.design):
self.route_decoder_bus() self.route_decoder_bus()
self.route_vdd_gnd() self.route_vdd_gnd()
self.offset_all_coordinates() self.offset_x_coordinates()
self.width = self.and_inst[0].rx() + self.m1_space self.width = self.and_inst[0].rx() + 0.5 * self.m1_width
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
@ -609,11 +609,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

@ -5,29 +5,33 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design import bitcell_base_array
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
from vector import vector from vector import vector
import debug import debug
class local_bitcell_array(design.design):
class local_bitcell_array(bitcell_base_array.bitcell_base_array):
""" """
A local bitcell array is a bitcell array with a wordline driver. A local bitcell array is a bitcell array with a wordline driver.
This can either be a single aray on its own if there is no hierarchical WL This can either be a single aray on its own if there is no hierarchical WL
or it can be combined into a larger array with hierarchical WL. or it can be combined into a larger array with hierarchical WL.
""" """
def __init__(self, rows, cols, rbl, add_rbl=None, name=""): def __init__(self, rows, cols, rbl, left_rbl=[], right_rbl=[], name=""):
super().__init__(name=name) super().__init__(name=name, rows=rows, cols=cols, column_offset=0)
debug.info(2, "create local array of size {} rows x {} cols words".format(rows, cols)) debug.info(2, "Creating {0} {1}x{2} rbl: {3} left_rbl: {4} right_rbl: {5}".format(name,
rows,
cols,
rbl,
left_rbl,
right_rbl))
self.rows = rows self.rows = rows
self.cols = cols self.cols = cols
self.rbl = rbl self.rbl = rbl
if add_rbl == None: self.left_rbl = left_rbl
self.add_rbl = rbl self.right_rbl = right_rbl
else:
self.add_rbl = add_rbl
debug.check(len(self.all_ports) < 3, "Local bitcell array only supports dual port or less.") debug.check(len(self.all_ports) < 3, "Local bitcell array only supports dual port or less.")
@ -66,7 +70,8 @@ class local_bitcell_array(design.design):
cols=self.cols, cols=self.cols,
rows=self.rows, rows=self.rows,
rbl=self.rbl, rbl=self.rbl,
add_rbl=self.add_rbl) left_rbl=self.left_rbl,
right_rbl=self.right_rbl)
self.add_mod(self.bitcell_array) self.add_mod(self.bitcell_array)
self.wl_array = factory.create(module_type="wordline_buffer_array", self.wl_array = factory.create(module_type="wordline_buffer_array",
@ -75,44 +80,41 @@ class local_bitcell_array(design.design):
self.add_mod(self.wl_array) self.add_mod(self.wl_array)
def add_pins(self): def add_pins(self):
# Inputs to the wordline driver (by port)
self.wordline_names = []
# Outputs from the wordline driver (by port) # Outputs from the wordline driver (by port)
self.driver_wordline_outputs = [] self.driver_wordline_outputs = []
# Inputs to the bitcell array (by port) # Inputs to the bitcell array (by port)
self.array_wordline_inputs = [] self.array_wordline_inputs = []
for port in self.all_ports: self.wordline_names = self.bitcell_array.wordline_names
wordline_inputs = [] self.all_wordline_names = self.bitcell_array.all_wordline_names
if port == 0:
wordline_inputs += [self.bitcell_array.get_rbl_wordline_names(0)[0]]
wordline_inputs += self.bitcell_array.get_wordline_names(port)
if port == 1:
wordline_inputs += [self.bitcell_array.get_rbl_wordline_names(1)[1]]
self.wordline_names.append(wordline_inputs)
self.driver_wordline_outputs.append([x + "i" for x in self.wordline_names[-1]])
self.gnd_wl_names = []
# Connect unused RBL WL to gnd
array_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("rbl")])
dummy_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("dummy")])
rbl_wl_names = set([x for rbl_port_names in self.wordline_names for x in rbl_port_names if x.startswith("rbl")])
self.gnd_wl_names = list((array_rbl_names - rbl_wl_names) | dummy_rbl_names)
self.all_array_wordline_inputs = [x + "i" if x not in self.gnd_wl_names else "gnd" for x in self.bitcell_array.get_all_wordline_names()]
self.bitline_names = self.bitcell_array.bitline_names self.bitline_names = self.bitcell_array.bitline_names
self.all_array_bitline_names = self.bitcell_array.get_all_bitline_names() self.all_bitline_names = self.bitcell_array.all_bitline_names
self.rbl_wordline_names = self.bitcell_array.rbl_wordline_names
self.all_rbl_wordline_names = self.bitcell_array.all_rbl_wordline_names
self.rbl_bitline_names = self.bitcell_array.rbl_bitline_names
self.all_rbl_bitline_names = self.bitcell_array.all_rbl_bitline_names
self.all_array_wordline_inputs = [x + "i" for x in self.bitcell_array.get_all_wordline_names()]
# Arrays are always: # Arrays are always:
# bit lines (left to right) # bit lines (left to right)
# word lines (bottom to top) # word lines (bottom to top)
# vdd # vdd
# gnd # gnd
self.add_pin_list([x for x in self.all_array_bitline_names if not x.startswith("dummy")], "INOUT") for port in self.left_rbl:
self.add_pin_list(self.rbl_bitline_names[port], "INOUT")
self.add_pin_list(self.all_bitline_names, "INOUT")
for port in self.right_rbl:
self.add_pin_list(self.rbl_bitline_names[port], "INOUT")
for port in range(self.rbl[0]):
self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
for port in self.all_ports: for port in self.all_ports:
self.add_pin_list(self.wordline_names[port], "INPUT") self.add_pin_list(self.wordline_names[port], "INPUT")
for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]):
self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
@ -120,15 +122,41 @@ class local_bitcell_array(design.design):
""" Create the module instances used in this design """ """ Create the module instances used in this design """
self.wl_insts = [] self.wl_insts = []
self.driver_wordline_outputs = []
for port in self.all_ports: for port in self.all_ports:
self.wl_insts.append(self.add_inst(name="wl_driver", self.wl_insts.append(self.add_inst(name="wl_driver{}".format(port),
mod=self.wl_array)) mod=self.wl_array))
self.connect_inst(self.wordline_names[port] + self.driver_wordline_outputs[port] + ["vdd", "gnd"]) temp = []
temp += [self.get_rbl_wordline_names(port)[port]]
if port == 0:
temp += self.get_wordline_names(port)
else:
temp += self.get_wordline_names(port)[::-1]
self.driver_wordline_outputs.append([x + "i" for x in temp])
temp += self.driver_wordline_outputs[-1]
temp += ["vdd", "gnd"]
self.connect_inst(temp)
self.bitcell_array_inst = self.add_inst(name="array", self.bitcell_array_inst = self.add_inst(name="array",
mod=self.bitcell_array) mod=self.bitcell_array)
temp = []
for port in self.left_rbl:
temp += self.get_rbl_bitline_names(port)
temp += self.all_bitline_names
for port in self.right_rbl:
temp += self.get_rbl_bitline_names(port)
self.connect_inst(self.all_array_bitline_names + self.all_array_wordline_inputs + ["vdd", "gnd"]) wl_temp = []
for port in range(self.rbl[0]):
wl_temp += [self.get_rbl_wordline_names(port)[port]]
wl_temp += self.get_wordline_names()
for port in range(self.rbl[0], sum(self.rbl)):
wl_temp += [self.get_rbl_wordline_names(port)[port]]
temp += [x + "i" for x in wl_temp]
temp += ["vdd", "gnd"]
self.connect_inst(temp)
def place(self): def place(self):
""" Place the bitcelll array to the right of the wl driver. """ """ Place the bitcelll array to the right of the wl driver. """
@ -142,41 +170,17 @@ class local_bitcell_array(design.design):
if len(self.all_ports) > 1: if len(self.all_ports) > 1:
self.wl_insts[1].place(vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing, self.wl_insts[1].place(vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing,
2 * self.cell.height), 2 * self.cell.height + self.wl_array.height),
mirror="MY") mirror="XY")
self.height = self.bitcell_array.height self.height = self.bitcell_array.height
self.width = self.bitcell_array_inst.rx() self.width = max(self.bitcell_array_inst.rx(), max([x.rx() for x in self.wl_insts]))
def route_unused_wordlines(self):
""" Connect the unused RBL and dummy wordlines to gnd """
for wl_name in self.gnd_wl_names:
pin = self.bitcell_array_inst.get_pin(wl_name)
pin_layer = pin.layer
layer_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer))
left_pin_loc = pin.lc()
right_pin_loc = pin.rc()
# Place the pins a track outside of the array
left_loc = left_pin_loc - vector(layer_pitch, 0)
right_loc = right_pin_loc + vector(layer_pitch, 0)
self.add_power_pin("gnd", left_loc, directions=("H", "H"))
self.add_power_pin("gnd", right_loc, directions=("H", "H"))
# Add a path to connect to the array
self.add_path(pin_layer, [left_loc, left_pin_loc])
self.add_path(pin_layer, [right_loc, right_pin_loc])
def add_layout_pins(self): def add_layout_pins(self):
for x in self.get_inouts(): for x in self.get_inouts():
self.copy_layout_pin(self.bitcell_array_inst, x) self.copy_layout_pin(self.bitcell_array_inst, x)
for port in self.all_ports:
for (x, y) in zip(self.wordline_names[port], self.wl_array.get_inputs()):
self.copy_layout_pin(self.wl_insts[port], y, x)
supply_insts = [*self.wl_insts, self.bitcell_array_inst] supply_insts = [*self.wl_insts, self.bitcell_array_inst]
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
for inst in supply_insts: for inst in supply_insts:
@ -188,8 +192,44 @@ class local_bitcell_array(design.design):
def route(self): def route(self):
# Route the global wordlines
for port in self.all_ports: for port in self.all_ports:
for (driver_name, net_name) in zip(self.wl_insts[port].mod.get_outputs(), self.driver_wordline_outputs[port]): if port == 0:
wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)
else:
wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)[::-1]
wordline_pins = self.wl_array.get_inputs()
for (wl_name, in_pin_name) in zip(wordline_names, wordline_pins):
# wl_pin = self.bitcell_array_inst.get_pin(wl_name)
in_pin = self.wl_insts[port].get_pin(in_pin_name)
y_offset = in_pin.cy()
if port == 0:
y_offset -= 2 * self.m3_pitch
else:
y_offset += 2 * self.m3_pitch
self.add_layout_pin_segment_center(text=wl_name,
layer="m3",
start=vector(self.wl_insts[port].lx(), y_offset),
end=vector(self.wl_insts[port].lx() + self.wl_array.width, y_offset))
mid = vector(in_pin.cx(), y_offset)
self.add_path("m2", [in_pin.center(), mid])
self.add_via_stack_center(from_layer=in_pin.layer,
to_layer="m2",
offset=in_pin.center())
self.add_via_center(self.m2_stack,
offset=mid)
# Route the buffers
for port in self.all_ports:
driver_outputs = self.driver_wordline_outputs[port]
for (driver_name, net_name) in zip(self.wl_insts[port].mod.get_outputs(), driver_outputs):
array_name = net_name[:-1] array_name = net_name[:-1]
out_pin = self.wl_insts[port].get_pin(driver_name) out_pin = self.wl_insts[port].get_pin(driver_name)
in_pin = self.bitcell_array_inst.get_pin(array_name) in_pin = self.bitcell_array_inst.get_pin(array_name)
@ -203,5 +243,46 @@ class local_bitcell_array(design.design):
in_loc = in_pin.rc() in_loc = in_pin.rc()
self.add_path(out_pin.layer, [out_loc, mid_loc, in_loc]) self.add_path(out_pin.layer, [out_loc, mid_loc, in_loc])
self.route_unused_wordlines() def get_main_array_top(self):
return self.bitcell_array_inst.by() + self.bitcell_array.get_main_array_top()
def get_main_array_bottom(self):
return self.bitcell_array_inst.by() + self.bitcell_array.get_main_array_bottom()
def get_main_array_left(self):
return self.bitcell_array_inst.lx() + self.bitcell_array.get_main_array_left()
def get_main_array_right(self):
return self.bitcell_array_inst.lx() + self.bitcell_array.get_main_array_right()
def get_column_offsets(self):
"""
Return an array of the x offsets of all the regular bits
"""
# must add the offset of the instance
offsets = [self.bitcell_array_inst.lx() + x for x in self.bitcell_array.get_column_offsets()]
return offsets
def graph_exclude_bits(self, targ_row=None, targ_col=None):
"""
Excludes bits in column from being added to graph except target
"""
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def graph_exclude_replica_col_bits(self):
"""
Exclude all but replica in the local array.
"""
self.bitcell_array.graph_exclude_replica_col_bits()
def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell."""
return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bitcell_array.clear_exclude_bits()

View File

@ -17,10 +17,11 @@ class port_address(design.design):
Create the address port (row decoder and wordline driver).. Create the address port (row decoder and wordline driver)..
""" """
def __init__(self, cols, rows, name=""): def __init__(self, cols, rows, port, name=""):
self.num_cols = cols self.num_cols = cols
self.num_rows = rows self.num_rows = rows
self.port = port
self.addr_size = ceil(log(self.num_rows, 2)) self.addr_size = ceil(log(self.num_rows, 2))
if name == "": if name == "":
@ -39,6 +40,7 @@ class port_address(design.design):
self.add_modules() self.add_modules()
self.create_row_decoder() self.create_row_decoder()
self.create_wordline_driver() self.create_wordline_driver()
self.create_rbl_driver()
def create_layout(self): def create_layout(self):
if "li" in layer: if "li" in layer:
@ -60,6 +62,8 @@ class port_address(design.design):
for bit in range(self.num_rows): for bit in range(self.num_rows):
self.add_pin("wl_{0}".format(bit), "OUTPUT") self.add_pin("wl_{0}".format(bit), "OUTPUT")
self.add_pin("rbl_wl", "OUTPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
@ -71,10 +75,13 @@ class port_address(design.design):
def route_supplies(self): def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """ """ Propagate all vdd/gnd pins up to this level for all modules """
for inst in self.insts: for inst in [self.wordline_driver_array_inst, self.row_decoder_inst]:
self.copy_power_pins(inst, "vdd") self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst, "gnd") self.copy_power_pins(inst, "gnd")
rbl_vdd_pin = self.rbl_driver_inst.get_pin("vdd")
self.add_power_pin("vdd", rbl_vdd_pin.lc())
def route_pins(self): def route_pins(self):
for row in range(self.addr_size): for row in range(self.addr_size):
decoder_name = "addr_{}".format(row) decoder_name = "addr_{}".format(row)
@ -82,16 +89,16 @@ class port_address(design.design):
for row in range(self.num_rows): for row in range(self.num_rows):
driver_name = "wl_{}".format(row) driver_name = "wl_{}".format(row)
self.copy_layout_pin(self.wordline_driver_inst, driver_name) self.copy_layout_pin(self.wordline_driver_array_inst, driver_name)
self.copy_layout_pin(self.wordline_driver_inst, "en", "wl_en") self.copy_layout_pin(self.rbl_driver_inst, "Z", "rbl_wl")
def route_internal(self): def route_internal(self):
for row in range(self.num_rows): for row in range(self.num_rows):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs # The pre/post is to access the pin from "outside" the cell to avoid DRCs
decoder_out_pin = self.row_decoder_inst.get_pin("decode_{}".format(row)) decoder_out_pin = self.row_decoder_inst.get_pin("decode_{}".format(row))
decoder_out_pos = decoder_out_pin.rc() decoder_out_pos = decoder_out_pin.rc()
driver_in_pin = self.wordline_driver_inst.get_pin("in_{}".format(row)) driver_in_pin = self.wordline_driver_array_inst.get_pin("in_{}".format(row))
driver_in_pos = driver_in_pin.lc() driver_in_pos = driver_in_pin.lc()
self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos, var_offset=0.3) self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos, var_offset=0.3)
@ -103,16 +110,59 @@ class port_address(design.design):
to_layer=self.route_layer, to_layer=self.route_layer,
offset=driver_in_pos) offset=driver_in_pos)
# Route the RBL from the enable input
en_pin = self.wordline_driver_array_inst.get_pin("en")
if self.port == 0:
en_pos = en_pin.bc()
else:
en_pos = en_pin.uc()
rbl_in_pin = self.rbl_driver_inst.get_pin("A")
rbl_in_pos = rbl_in_pin.center()
self.add_via_stack_center(from_layer=rbl_in_pin.layer,
to_layer=en_pin.layer,
offset=rbl_in_pos)
self.add_zjog(layer=en_pin.layer,
start=rbl_in_pos,
end=en_pos,
first_direction="V")
self.add_layout_pin_rect_center(text="wl_en",
layer=en_pin.layer,
offset=rbl_in_pos)
def add_modules(self): def add_modules(self):
self.row_decoder = factory.create(module_type="decoder", self.row_decoder = factory.create(module_type="decoder",
num_outputs=self.num_rows) num_outputs=self.num_rows)
self.add_mod(self.row_decoder) self.add_mod(self.row_decoder)
self.wordline_driver = factory.create(module_type="wordline_driver_array", self.wordline_driver_array = factory.create(module_type="wordline_driver_array",
rows=self.num_rows, rows=self.num_rows,
cols=self.num_cols) cols=self.num_cols)
self.add_mod(self.wordline_driver) self.add_mod(self.wordline_driver_array)
try:
local_array_size = OPTS.local_array_size
driver_size = max(int(self.num_cols / local_array_size), 1)
except AttributeError:
local_array_size = 0
# Defautl to FO4
driver_size = max(int(self.num_cols / 4), 1)
# The polarity must be switched if we have a hierarchical wordline
# to compensate for the local array inverters
b = factory.create(module_type="bitcell")
if local_array_size > 0:
self.rbl_driver = factory.create(module_type="inv_dec",
size=driver_size,
height=b.height)
else:
self.rbl_driver = factory.create(module_type="buf_dec",
size=driver_size,
height=b.height)
self.add_mod(self.rbl_driver)
def create_row_decoder(self): def create_row_decoder(self):
""" Create the hierarchical row decoder """ """ Create the hierarchical row decoder """
@ -128,11 +178,24 @@ class port_address(design.design):
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def create_rbl_driver(self):
""" Create the RBL Wordline Driver """
self.rbl_driver_inst = self.add_inst(name="rbl_driver",
mod=self.rbl_driver)
temp = []
temp.append("wl_en")
temp.append("rbl_wl")
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def create_wordline_driver(self): def create_wordline_driver(self):
""" Create the Wordline Driver """ """ Create the Wordline Driver """
self.wordline_driver_inst = self.add_inst(name="wordline_driver", self.wordline_driver_array_inst = self.add_inst(name="wordline_driver",
mod=self.wordline_driver) mod=self.wordline_driver_array)
temp = [] temp = []
for row in range(self.num_rows): for row in range(self.num_rows):
@ -150,11 +213,23 @@ class port_address(design.design):
""" """
row_decoder_offset = vector(0, 0) row_decoder_offset = vector(0, 0)
wordline_driver_offset = vector(self.row_decoder.width, 0)
self.wordline_driver_inst.place(wordline_driver_offset)
self.row_decoder_inst.place(row_decoder_offset) self.row_decoder_inst.place(row_decoder_offset)
wordline_driver_array_offset = vector(self.row_decoder_inst.rx(), 0)
self.wordline_driver_array_inst.place(wordline_driver_array_offset)
x_offset = self.wordline_driver_array_inst.rx() - self.rbl_driver.width - self.m1_pitch
if self.port == 0:
rbl_driver_offset = vector(x_offset,
0)
self.rbl_driver_inst.place(rbl_driver_offset, "MX")
else:
rbl_driver_offset = vector(x_offset,
self.wordline_driver_array.height)
self.rbl_driver_inst.place(rbl_driver_offset)
# Pass this up # Pass this up
self.predecoder_height = self.row_decoder.predecoder_height self.predecoder_height = self.row_decoder.predecoder_height
self.height = self.row_decoder.height self.height = self.row_decoder.height
self.width = self.wordline_driver_inst.rx() self.width = self.wordline_driver_array_inst.rx()

View File

@ -6,6 +6,7 @@
from tech import drc from tech import drc
import debug import debug
import design import design
import math
from sram_factory import factory from sram_factory import factory
from collections import namedtuple from collections import namedtuple
from vector import vector from vector import vector
@ -18,18 +19,26 @@ class port_data(design.design):
Port 0 always has the RBL on the left while port 1 is on the right. Port 0 always has the RBL on the left while port 1 is on the right.
""" """
def __init__(self, sram_config, port, name=""): def __init__(self, sram_config, port, bit_offsets=None, name=""):
sram_config.set_local_config(self) sram_config.set_local_config(self)
self.port = port self.port = port
if self.write_size is not None: if self.write_size is not None:
self.num_wmasks = int(self.word_size / self.write_size) self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else: else:
self.num_wmasks = 0 self.num_wmasks = 0
if self.num_spare_cols is None: if self.num_spare_cols is None:
self.num_spare_cols = 0 self.num_spare_cols = 0
if not bit_offsets:
bitcell = factory.create(module_type="bitcell")
self.bit_offsets = []
for i in range(self.num_cols + self.num_spare_cols):
self.bit_offsets.append(i * bitcell.width)
else:
self.bit_offsets = bit_offsets
if name == "": if name == "":
name = "port_data_{0}".format(self.port) name = "port_data_{0}".format(self.port)
super().__init__(name) super().__init__(name)
@ -179,8 +188,19 @@ class port_data(design.design):
# Precharge will be shifted left if needed # Precharge will be shifted left if needed
# Column offset is set to port so extra column can be on left or right # Column offset is set to port so extra column can be on left or right
# and mirroring happens correctly # and mirroring happens correctly
# Used for names/dimensions only
self.cell = factory.create(module_type="bitcell")
if self.port == 0:
# Append an offset on the left
precharge_bit_offsets = [self.bit_offsets[0] - self.cell.width] + self.bit_offsets
else:
# Append an offset on the right
precharge_bit_offsets = self.bit_offsets + [self.bit_offsets[-1] + self.cell.width]
self.precharge_array = factory.create(module_type="precharge_array", self.precharge_array = factory.create(module_type="precharge_array",
columns=self.num_cols + self.num_spare_cols + 1, columns=self.num_cols + self.num_spare_cols + 1,
offsets=precharge_bit_offsets,
bitcell_bl=self.bl_names[self.port], bitcell_bl=self.bl_names[self.port],
bitcell_br=self.br_names[self.port], bitcell_br=self.br_names[self.port],
column_offset=self.port - 1) column_offset=self.port - 1)
@ -190,6 +210,7 @@ class port_data(design.design):
# RBLs don't get a sense amp # RBLs don't get a sense amp
self.sense_amp_array = factory.create(module_type="sense_amp_array", self.sense_amp_array = factory.create(module_type="sense_amp_array",
word_size=self.word_size, word_size=self.word_size,
offsets=self.bit_offsets,
words_per_row=self.words_per_row, words_per_row=self.words_per_row,
num_spare_cols=self.num_spare_cols) num_spare_cols=self.num_spare_cols)
self.add_mod(self.sense_amp_array) self.add_mod(self.sense_amp_array)
@ -201,6 +222,7 @@ class port_data(design.design):
self.column_mux_array = factory.create(module_type="column_mux_array", self.column_mux_array = factory.create(module_type="column_mux_array",
columns=self.num_cols, columns=self.num_cols,
word_size=self.word_size, word_size=self.word_size,
offsets=self.bit_offsets,
bitcell_bl=self.bl_names[self.port], bitcell_bl=self.bl_names[self.port],
bitcell_br=self.br_names[self.port]) bitcell_br=self.br_names[self.port])
self.add_mod(self.column_mux_array) self.add_mod(self.column_mux_array)
@ -212,6 +234,7 @@ class port_data(design.design):
self.write_driver_array = factory.create(module_type="write_driver_array", self.write_driver_array = factory.create(module_type="write_driver_array",
columns=self.num_cols, columns=self.num_cols,
word_size=self.word_size, word_size=self.word_size,
offsets=self.bit_offsets,
write_size=self.write_size, write_size=self.write_size,
num_spare_cols=self.num_spare_cols) num_spare_cols=self.num_spare_cols)
self.add_mod(self.write_driver_array) self.add_mod(self.write_driver_array)
@ -219,6 +242,7 @@ class port_data(design.design):
# RBLs don't get a write mask # RBLs don't get a write mask
self.write_mask_and_array = factory.create(module_type="write_mask_and_array", self.write_mask_and_array = factory.create(module_type="write_mask_and_array",
columns=self.num_cols, columns=self.num_cols,
offsets=self.bit_offsets,
word_size=self.word_size, word_size=self.word_size,
write_size=self.write_size) write_size=self.write_size)
self.add_mod(self.write_mask_and_array) self.add_mod(self.write_mask_and_array)
@ -411,21 +435,15 @@ class port_data(design.design):
vertical_port_order.append(self.write_driver_array_inst) vertical_port_order.append(self.write_driver_array_inst)
vertical_port_order.append(self.write_mask_and_array_inst) vertical_port_order.append(self.write_mask_and_array_inst)
# Add one column for the the RBL
if self.port==0:
x_offset = self.bitcell.width
else:
x_offset = 0
vertical_port_offsets = 5 * [None] vertical_port_offsets = 5 * [None]
self.width = x_offset self.width = 0
self.height = 0 self.height = 0
for i, p in enumerate(vertical_port_order): for i, p in enumerate(vertical_port_order):
if p == None: if p == None:
continue continue
self.height += (p.height + self.m2_gap) self.height += (p.height + self.m2_gap)
self.width = max(self.width, p.width) self.width = max(self.width, p.width)
vertical_port_offsets[i] = vector(x_offset, self.height) vertical_port_offsets[i] = vector(0, self.height)
# Reversed order # Reversed order
self.write_mask_and_offset = vertical_port_offsets[4] self.write_mask_and_offset = vertical_port_offsets[4]
@ -433,9 +451,6 @@ class port_data(design.design):
self.sense_amp_offset = vertical_port_offsets[2] self.sense_amp_offset = vertical_port_offsets[2]
self.column_mux_offset = vertical_port_offsets[1] self.column_mux_offset = vertical_port_offsets[1]
self.precharge_offset = vertical_port_offsets[0] self.precharge_offset = vertical_port_offsets[0]
# Shift the precharge left if port 0
if self.precharge_offset and self.port == 0:
self.precharge_offset -= vector(x_offset, 0)
def place_instances(self): def place_instances(self):
""" Place the instances. """ """ Place the instances. """

View File

@ -10,7 +10,7 @@ import debug
from vector import vector from vector import vector
from sram_factory import factory from sram_factory import factory
from globals import OPTS from globals import OPTS
from tech import layer from tech import cell_properties
class precharge_array(design.design): class precharge_array(design.design):
@ -19,12 +19,13 @@ class precharge_array(design.design):
of bit line columns, height is the height of the bit-cell array. of bit line columns, height is the height of the bit-cell array.
""" """
def __init__(self, name, columns, size=1, bitcell_bl="bl", bitcell_br="br", column_offset=0): def __init__(self, name, columns, offsets=None, size=1, bitcell_bl="bl", bitcell_br="br", column_offset=0):
super().__init__(name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br)) self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br))
self.columns = columns self.columns = columns
self.offsets = offsets
self.size = size self.size = size
self.bitcell_bl = bitcell_bl self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br self.bitcell_br = bitcell_br
@ -62,10 +63,11 @@ class precharge_array(design.design):
self.create_insts() self.create_insts()
def create_layout(self): def create_layout(self):
self.width = self.columns * self.pc_cell.width self.place_insts()
self.width = self.offsets[-1] + self.pc_cell.width
self.height = self.pc_cell.height self.height = self.pc_cell.height
self.place_insts()
self.add_layout_pins() self.add_layout_pins()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
@ -112,26 +114,20 @@ class precharge_array(design.design):
def place_insts(self): def place_insts(self):
""" Places precharge array by horizontally tiling the precharge cell""" """ Places precharge array by horizontally tiling the precharge cell"""
from tech import cell_properties
xoffset = 0 # Default to single spaced columns
for i in range(self.columns): if not self.offsets:
tempx = xoffset self.offsets = [n * self.pc_cell.width for n in range(self.columns)]
for i, xoffset in enumerate(self.offsets):
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
mirror = "MY" mirror = "MY"
tempx = tempx + self.pc_cell.width tempx = xoffset + self.pc_cell.width
else: else:
mirror = "" mirror = ""
tempx = xoffset
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
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

@ -21,35 +21,48 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
Requires a regular bitcell array, replica bitcell, and dummy Requires a regular bitcell array, replica bitcell, and dummy
bitcell (Bl/BR disconnected). bitcell (Bl/BR disconnected).
""" """
def __init__(self, rows, cols, rbl, name, add_rbl=None): def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""):
super().__init__(name, rows, cols, column_offset=0) super().__init__(name, rows, cols, column_offset=0)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name,
rows,
cols,
rbl,
left_rbl,
right_rbl))
self.add_comment("rows: {0} cols: {1}".format(rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols))
self.add_comment("rbl: {0} left_rbl: {1} right_rbl: {2}".format(rbl, left_rbl, right_rbl))
self.column_size = cols self.column_size = cols
self.row_size = rows self.row_size = rows
# This is how many RBLs are in all the arrays # This is how many RBLs are in all the arrays
self.rbl = rbl if rbl:
self.left_rbl = rbl[0] self.rbl = rbl
self.right_rbl = rbl[1]
# This is how many RBLs are added to THIS array
if add_rbl == None:
self.add_left_rbl = rbl[0]
self.add_right_rbl = rbl[1]
else: else:
self.add_left_rbl = add_rbl[0] self.rbl=[1, 1 if len(self.all_ports)>1 else 0]
self.add_right_rbl = add_rbl[1] # This specifies which RBL to put on the left or right
for a, b in zip(add_rbl, rbl): # by port number
debug.check(a <= b, # This could be an empty list
"Invalid number of RBLs for port configuration.") if left_rbl != None:
self.left_rbl = left_rbl
else:
self.left_rbl = [0]
# This could be an empty list
if right_rbl != None:
self.right_rbl = right_rbl
else:
self.right_rbl=[1] if len(self.all_ports) > 1 else []
self.rbls = self.left_rbl + self.right_rbl
debug.check(sum(rbl) <= len(self.all_ports), debug.check(sum(self.rbl) == len(self.all_ports),
"Invalid number of RBLs for port configuration.")
debug.check(sum(self.rbl) >= len(self.left_rbl) + len(self.right_rbl),
"Invalid number of RBLs for port configuration.") "Invalid number of RBLs for port configuration.")
# Two dummy rows plus replica even if we don't add the column # Two dummy rows plus replica even if we don't add the column
self.extra_rows = 2 + sum(rbl) self.extra_rows = 2 + sum(self.rbl)
# Two dummy cols plus replica if we add the column # Two dummy cols plus replica if we add the column
self.extra_cols = 2 + self.add_left_rbl + self.add_right_rbl self.extra_cols = 2 + len(self.left_rbl) + len(self.right_rbl)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -89,42 +102,49 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
""" """
# Bitcell array # Bitcell array
self.bitcell_array = factory.create(module_type="bitcell_array", self.bitcell_array = factory.create(module_type="bitcell_array",
column_offset=1 + self.add_left_rbl, column_offset=1 + len(self.left_rbl),
cols=self.column_size, cols=self.column_size,
rows=self.row_size) rows=self.row_size)
self.add_mod(self.bitcell_array) self.add_mod(self.bitcell_array)
# Replica bitlines # Replica bitlines
self.replica_columns = {} self.replica_columns = {}
for bit in range(self.add_left_rbl + self.add_right_rbl):
# Creating left_rbl
if bit < self.add_left_rbl:
# These go from the top (where the bitcell array starts ) down
replica_bit = self.left_rbl - bit
# Creating right_rbl
else:
# These go from the bottom up
replica_bit = self.left_rbl + self.row_size + 1 + bit
# If we have an odd numer on the bottom
column_offset = self.left_rbl + 1
self.replica_columns[bit] = factory.create(module_type="replica_column", for port in self.all_ports:
rows=self.row_size, if port in self.left_rbl:
rbl=self.rbl, # We will always have self.rbl[0] rows of replica wordlines below
column_offset=column_offset, # the array.
replica_bit=replica_bit) # These go from the top (where the bitcell array starts ) down
self.add_mod(self.replica_columns[bit]) replica_bit = self.rbl[0] - port
# If there are bitcell end caps, replace the dummy cells on the edge of the bitcell array with end caps. elif port in self.right_rbl:
# We will always have self.rbl[0] rows of replica wordlines below
# the array.
# These go from the bottom up
replica_bit = self.rbl[0] + self.row_size + port
else:
continue
# If we have an odd numer on the bottom
column_offset = self.rbl[0] + 1
self.replica_columns[port] = factory.create(module_type="replica_column",
rows=self.row_size,
rbl=self.rbl,
column_offset=column_offset,
replica_bit=replica_bit)
self.add_mod(self.replica_columns[port])
try: try:
end_caps_enabled = cell_properties.bitcell.end_caps end_caps_enabled = cell_properties.bitcell.end_caps
except AttributeError: except AttributeError:
end_caps_enabled = False end_caps_enabled = False
# Dummy row
# Dummy row
self.dummy_row = factory.create(module_type="dummy_array", self.dummy_row = factory.create(module_type="dummy_array",
cols=self.column_size, cols=self.column_size,
rows=1, rows=1,
# dummy column + left replica column # dummy column + left replica column
column_offset=1 + self.add_left_rbl, column_offset=1 + len(self.left_rbl),
mirror=0) mirror=0)
self.add_mod(self.dummy_row) self.add_mod(self.dummy_row)
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
@ -134,8 +154,8 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
self.col_cap = factory.create(module_type=col_cap_module_type, self.col_cap = factory.create(module_type=col_cap_module_type,
cols=self.column_size, cols=self.column_size,
rows=1, rows=1,
# dummy column + left replica column(s) # dummy column + left replica column
column_offset=1 + self.add_left_rbl, column_offset=1 + len(self.left_rbl),
mirror=0) mirror=0)
self.add_mod(self.col_cap) self.add_mod(self.col_cap)
@ -155,7 +175,7 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
# + left replica column(s) # + left replica column(s)
# + bitcell columns # + bitcell columns
# + right replica column(s) # + right replica column(s)
column_offset = 1 + self.add_left_rbl + self.column_size + self.add_right_rbl, column_offset = 1 + len(self.left_rbl) + self.column_size + self.rbl[0],
rows=self.row_size + self.extra_rows, rows=self.row_size + self.extra_rows,
mirror=(self.left_rbl + 1) %2) mirror=(self.left_rbl + 1) %2)
self.add_mod(self.row_cap_right) self.add_mod(self.row_cap_right)
@ -166,7 +186,7 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
cols=self.column_size, cols=self.column_size,
rows=1, rows=1,
# dummy column + left replica column(s) # dummy column + left replica column(s)
column_offset=1 + self.add_left_rbl, column_offset=1 + len(self.left_rbl),
mirror=0, mirror=0,
location="top") location="top")
self.add_mod(self.col_cap_top) self.add_mod(self.col_cap_top)
@ -175,7 +195,7 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
cols=self.column_size, cols=self.column_size,
rows=1, rows=1,
# dummy column + left replica column(s) # dummy column + left replica column(s)
column_offset=1 + self.add_left_rbl, column_offset=1 + len(self.left_rbl),
mirror=0, mirror=0,
location="bottom") location="bottom")
self.add_mod(self.col_cap_bottom) self.add_mod(self.col_cap_bottom)
@ -195,7 +215,7 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
# + left replica column(s) # + left replica column(s)
# + bitcell columns # + bitcell columns
# + right replica column(s) # + right replica column(s)
column_offset = 1 + self.add_left_rbl + self.column_size + self.add_right_rbl, column_offset = 1 + len(self.left_rbl) + self.column_size + self.rbl[0],
rows=self.row_size + self.extra_rows, rows=self.row_size + self.extra_rows,
mirror=0) mirror=0)
self.add_mod(self.row_cap_right) self.add_mod(self.row_cap_right)
@ -221,94 +241,81 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_bitline_pins(self): def add_bitline_pins(self):
# Regular bitline names by port # The bit is which port the RBL is for
self.bitline_names = [] for bit in self.rbls:
# Replica bitlines by port for port in self.all_ports:
self.rbl_bitline_names = [] self.rbl_bitline_names[bit].append("rbl_bl_{0}_{1}".format(port, bit))
# Dummy bitlines by left/right for port in self.all_ports:
self.dummy_col_bitline_names = [] self.rbl_bitline_names[bit].append("rbl_br_{0}_{1}".format(port, bit))
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
for loc in ["left", "right"]:
self.dummy_col_bitline_names.append([])
for port in self.all_ports:
bitline_names = ["dummy_{0}_{1}".format(x, loc) for x in self.row_cap_left.get_bitline_names(port)]
self.dummy_col_bitline_names[-1].extend(bitline_names)
self.all_dummy_col_bitline_names = [x for sl in self.dummy_col_bitline_names for x in sl]
for port in range(self.add_left_rbl + self.add_right_rbl):
left_names=["rbl_bl_{0}_{1}".format(x, port) for x in self.all_ports]
right_names=["rbl_br_{0}_{1}".format(x, port) for x in self.all_ports]
bitline_names = [x for t in zip(left_names, right_names) for x in t]
self.rbl_bitline_names.append(bitline_names)
# Make a flat list too # Make a flat list too
self.all_rbl_bitline_names = [x for sl in self.rbl_bitline_names for x in sl] self.all_rbl_bitline_names = [x for sl in self.rbl_bitline_names for x in sl]
for port in self.all_ports: self.bitline_names = self.bitcell_array.bitline_names
bitline_names = self.bitcell_array.get_bitline_names(port)
self.bitline_names.append(bitline_names)
# Make a flat list too # Make a flat list too
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl] self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
self.add_pin_list(self.dummy_col_bitline_names[0], "INOUT") for port in self.left_rbl:
for port in range(self.add_left_rbl): self.add_pin_list(self.rbl_bitline_names[port], "INOUT")
self.add_pin_list(self.rbl_bitline_names[port], "INOUT") self.add_pin_list(self.all_bitline_names, "INOUT")
self.add_pin_list(self.all_bitline_names, "INOUT") for port in self.right_rbl:
for port in range(self.add_left_rbl, self.add_left_rbl + self.add_right_rbl): self.add_pin_list(self.rbl_bitline_names[port], "INOUT")
self.add_pin_list(self.rbl_bitline_names[port], "INOUT")
self.add_pin_list(self.dummy_col_bitline_names[1], "INOUT")
def add_wordline_pins(self): def add_wordline_pins(self):
# Regular wordlines by port # Wordlines to ground
self.wordline_names = [] self.gnd_wordline_names = []
# Replica wordlines by port
self.rbl_wordline_names = []
# Dummy wordlines by bot/top
self.dummy_row_wordline_names = []
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
dummy_row_wordline_names = ["dummy_" + x for x in self.col_cap.get_wordline_names()]
for loc in ["bot", "top"]:
wordline_names = ["{0}_{1}".format(wl_name, loc) for wl_name in dummy_row_wordline_names]
self.dummy_row_wordline_names.append(wordline_names)
self.all_dummy_row_wordline_names = [x for sl in self.dummy_row_wordline_names for x in sl]
for port in range(self.left_rbl + self.right_rbl):
if not cell_properties.compare_ports(cell_properties.bitcell.split_wl):
wordline_names=["rbl_wl_{0}_{1}".format(x, port) for x in self.all_ports]
self.rbl_wordline_names.append(wordline_names)
else:
for x in self.all_ports:
wordline_names = []
wordline_names.append("rbl_wl0_{0}_{1}".format(x, port))
wordline_names.append("rbl_wl1_{0}_{1}".format(x, port))
self.rbl_wordline_names.append(wordline_names)
self.all_rbl_wordline_names = [x for sl in self.rbl_wordline_names for x in sl]
for port in self.all_ports: for port in self.all_ports:
wordline_names = self.bitcell_array.get_wordline_names(port) for bit in self.all_ports:
self.wordline_names.append(wordline_names) if not cell_properties.compare_ports(cell_properties.bitcell.split_wl):
self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl] self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit))
if bit != port:
self.gnd_wordline_names.append("rbl_wl_{0}_{1}".format(port, bit))
else:
self.rbl_wordline_names[port].append("rbl_wl0_{0}_{1}".format(port, bit))
self.rbl_wordline_names[port].append("rbl_wl1_{0}_{1}".format(port, bit))
if bit != port:
self.gnd_wordline_names.append("rbl0_wl_{0}_{1}".format(port, bit))
self.gnd_wordline_names.append("rbl1_wl_{0}_{1}".format(port, bit))
self.all_rbl_wordline_names = [x for sl in self.rbl_wordline_names for x in sl]
self.wordline_names = self.bitcell_array.wordline_names
self.all_wordline_names = self.bitcell_array.all_wordline_names
# All wordlines including dummy and RBL
self.replica_array_wordline_names = []
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
self.replica_array_wordline_names.extend(self.dummy_row_wordline_names[0])
for p in range(self.left_rbl): # All wordlines including dummy and RBL
self.replica_array_wordline_names.extend(self.rbl_wordline_names[p]) self.replica_array_wordline_names = []
self.replica_array_wordline_names.extend(self.all_wordline_names) self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap.get_wordline_names()))
for p in range(self.left_rbl, self.left_rbl + self.right_rbl): for bit in range(self.rbl[0]):
self.replica_array_wordline_names.extend(self.rbl_wordline_names[p]) self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[bit]])
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): self.replica_array_wordline_names.extend(self.all_wordline_names)
self.replica_array_wordline_names.extend(self.dummy_row_wordline_names[1]) for bit in range(self.rbl[1]):
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[self.rbl[0] + bit]])
self.add_pin_list(self.dummy_row_wordline_names[0], "INPUT") self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap.get_wordline_names()))
for port in range(self.left_rbl):
self.add_pin_list(self.rbl_wordline_names[port], "INPUT") for port in range(self.rbl[0]):
self.add_pin_list(self.all_wordline_names, "INPUT") self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
for port in range(self.left_rbl, self.left_rbl + self.right_rbl): self.add_pin_list(self.all_wordline_names, "INPUT")
self.add_pin_list(self.rbl_wordline_names[port], "INPUT") for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]):
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
self.add_pin_list(self.dummy_row_wordline_names[1], "INPUT") else:
# All wordlines including dummy and RBL
self.replica_array_wordline_names = []
self.replica_array_wordline_names.extend(["gnd"] * 2)
for bit in range(self.rbl[0]):
self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[bit]])
self.replica_array_wordline_names.extend(self.all_wordline_names)
for bit in range(self.rbl[1]):
self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[self.rbl[0] + bit]])
self.replica_array_wordline_names.extend(["gnd"] *2)
for port in range(self.rbl[0]):
self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
self.add_pin_list(self.all_wordline_names, "INPUT")
for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]):
self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
def create_instances(self): def create_instances(self):
""" Create the module instances used in this design """ """ Create the module instances used in this design """
@ -331,68 +338,77 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
# Replica columns # Replica columns
self.replica_col_insts = [] self.replica_col_insts = []
for port in range(self.add_left_rbl + self.add_right_rbl): for port in self.all_ports:
self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port), if port in self.rbls:
mod=self.replica_columns[port])) self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port),
self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + self.supplies) mod=self.replica_columns[port]))
self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + self.supplies)
else:
self.replica_col_insts.append(None)
# Dummy rows under the bitcell array (connected with with the replica cell wl) # Dummy rows under the bitcell array (connected with with the replica cell wl)
self.dummy_row_replica_insts = [] self.dummy_row_replica_insts = []
# Note, this is the number of left and right even if we aren't adding the columns to this bitcell array! # Note, this is the number of left and right even if we aren't adding the columns to this bitcell array!
for port in range(self.left_rbl + self.right_rbl): for port in self.all_ports:
self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port), self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port),
mod=self.dummy_row)) mod=self.dummy_row))
self.connect_inst(self.all_bitline_names + self.rbl_wordline_names[port] + self.supplies) self.connect_inst([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[port]] + self.supplies)
# Top/bottom dummy rows or col caps
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
# Top/bottom dummy rows or col caps
self.dummy_row_insts = [] self.dummy_row_insts = []
self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot", self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot",
mod=self.col_cap)) mod=self.col_cap))
self.connect_inst(self.all_bitline_names self.connect_inst(["gnd"] * len(self.col_cap.get_wordline_names()) + self.supplies)
+ self.dummy_row_wordline_names[0]
+ self.supplies)
self.dummy_row_insts.append(self.add_inst(name="dummy_row_top", self.dummy_row_insts.append(self.add_inst(name="dummy_row_top",
mod=self.col_cap)) mod=self.col_cap))
self.connect_inst(self.all_bitline_names self.connect_inst(["gnd"] * len(self.col_cap.get_wordline_names()) + self.supplies)
+ self.dummy_row_wordline_names[1]
+ self.supplies)
# Left/right Dummy columns # Left/right Dummy columns
self.dummy_col_insts = [] self.dummy_col_insts = []
self.dummy_col_insts.append(self.add_inst(name="dummy_col_left", self.dummy_col_insts.append(self.add_inst(name="dummy_col_left",
mod=self.row_cap_left)) mod=self.row_cap_left))
self.connect_inst(self.dummy_col_bitline_names[0] + self.replica_array_wordline_names + self.supplies) self.connect_inst(self.replica_array_wordline_names + self.supplies)
self.dummy_col_insts.append(self.add_inst(name="dummy_col_right", self.dummy_col_insts.append(self.add_inst(name="dummy_col_right",
mod=self.row_cap_right)) mod=self.row_cap_right))
self.connect_inst(self.dummy_col_bitline_names[1] + self.replica_array_wordline_names + self.supplies) self.connect_inst(self.replica_array_wordline_names + self.supplies)
else: else:
# Top/bottom dummy rows or col caps
self.dummy_row_insts = [] self.dummy_row_insts = []
self.dummy_row_insts.append(self.add_inst(name="col_cap_bottom", self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot",
mod=self.col_cap_bottom)) mod=self.col_cap_bottom))
self.connect_inst(self.all_bitline_names self.connect_inst(self.all_bitline_names + self.supplies)
+ self.supplies) self.dummy_row_insts.append(self.add_inst(name="dummy_row_top",
self.dummy_row_insts.append(self.add_inst(name="col_cap_top",
mod=self.col_cap_top)) mod=self.col_cap_top))
self.connect_inst(self.all_bitline_names self.connect_inst(self.all_bitline_names + self.supplies)
+ self.supplies)
# Left/right Dummy columns # Left/right Dummy columns
self.dummy_col_insts = [] self.dummy_col_insts = []
self.dummy_col_insts.append(self.add_inst(name="row_cap_left", self.dummy_col_insts.append(self.add_inst(name="dummy_col_left",
mod=self.row_cap_left)) mod=self.row_cap_left))
self.connect_inst(self.replica_array_wordline_names + self.supplies) self.connect_inst(self.replica_array_wordline_names + self.supplies)
self.dummy_col_insts.append(self.add_inst(name="row_cap_right", self.dummy_col_insts.append(self.add_inst(name="dummy_col_right",
mod=self.row_cap_right)) mod=self.row_cap_right))
self.connect_inst(self.replica_array_wordline_names + self.supplies) self.connect_inst(self.replica_array_wordline_names + self.supplies)
def create_layout(self): def create_layout(self):
# We will need unused wordlines grounded, so we need to know their layer
pin = self.cell.get_pin(self.cell.get_all_wl_names()[0])
pin_layer = pin.layer
self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer))
self.unused_offset = vector(self.unused_pitch, 0)
# Add extra width on the left and right for the unused WLs
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
self.height = (self.row_size + self.extra_rows) * self.dummy_row.height self.height = (self.row_size + self.extra_rows) * self.dummy_row.height
self.width = (self.column_size + self.extra_cols) * self.cell.width self.width = (self.column_size + self.extra_cols) * self.cell.width + 2 * self.unused_pitch
else: else:
self.width = self.row_cap_left.top_corner.width + self.row_cap_right.top_corner.width + (self.col_cap_top.colend1.width + self.col_cap_top.colend2.width) * (self.column_size + self.extra_cols) - self.col_cap_top.colend2.width self.width = self.row_cap_left.width + self.row_cap_right.width + self.col_cap_top.width
for rbl in range(self.rbl[0] + self.rbl[1]):
self.width += self.replica_col_insts[rbl].width
self.height = self.row_cap_left.height self.height = self.row_cap_left.height
# This is a bitcell x bitcell offset to scale # This is a bitcell x bitcell offset to scale
self.bitcell_offset = vector(self.cell.width, self.cell.height) self.bitcell_offset = vector(self.cell.width, self.cell.height)
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
@ -404,54 +420,79 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
self.col_end_offset = vector(self.dummy_row_insts[0].mod.colend1.width, self.dummy_row_insts[0].mod.colend1.height) self.col_end_offset = vector(self.dummy_row_insts[0].mod.colend1.width, self.dummy_row_insts[0].mod.colend1.height)
self.row_end_offset = vector(self.dummy_col_insts[0].mod.rowend1.width, self.dummy_col_insts[0].mod.rowend1.height) self.row_end_offset = vector(self.dummy_col_insts[0].mod.rowend1.width, self.dummy_col_insts[0].mod.rowend1.height)
# Everything is computed with the main array at (0, 0) to start # Everything is computed with the main array at (self.unused_pitch, 0) to start
self.bitcell_array_inst.place(offset=[0, 0]) self.bitcell_array_inst.place(offset=self.unused_offset)
self.add_replica_columns() self.add_replica_columns()
self.add_end_caps() self.add_end_caps()
# Array was at (0, 0) but move everything so it is at the lower left # Array was at (0, 0) but move everything so it is at the lower left
# We move DOWN the number of left RBL even if we didn't add the column to this bitcell array # We move DOWN the number of left RBL even if we didn't add the column to this bitcell array
self.translate_all(self.bitcell_offset.scale(-1 - self.add_left_rbl, -1 - self.left_rbl)) array_offset = self.bitcell_offset.scale(1 + len(self.left_rbl), 1 + self.rbl[0])
self.translate_all(array_offset.scale(-1, -1))
self.add_layout_pins() self.add_layout_pins()
self.route_unused_wordlines()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def get_main_array_top(self):
return self.bitcell_array_inst.uy()
def get_main_array_bottom(self):
return self.bitcell_array_inst.by()
def get_main_array_left(self):
return self.bitcell_array_inst.lx()
def get_main_array_right(self):
return self.bitcell_array_inst.rx()
def get_column_offsets(self):
"""
Return an array of the x offsets of all the regular bits
"""
offsets = [x + self.bitcell_array_inst.lx() for x in self.bitcell_array.get_column_offsets()]
return offsets
def add_replica_columns(self): def add_replica_columns(self):
""" Add replica columns on left and right of array """ """ Add replica columns on left and right of array """
end_caps_enabled = cell_properties.bitcell.end_caps end_caps_enabled = cell_properties.bitcell.end_caps
# Grow from left to right, toward the array # Grow from left to right, toward the array
for bit in range(self.add_left_rbl): for bit, port in enumerate(self.left_rbl):
if not end_caps_enabled: if not end_caps_enabled:
offset = self.bitcell_offset.scale(-self.add_left_rbl + bit, -self.left_rbl - 1) + self.strap_offset.scale(-self.add_left_rbl + bit, 0) offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + self.strap_offset.scale(-len(self.left_rbl) + bit, 0) + self.unused_offset
else: else:
offset = self.bitcell_offset.scale(-self.add_left_rbl + bit, -self.left_rbl - (self.col_end_offset.y/self.cell.height)) + self.strap_offset.scale(-self.add_left_rbl + bit, 0) offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.strap_offset.scale(-len(self.left_rbl) + bit, 0) + self.unused_offset
self.replica_col_insts[bit].place(offset) self.replica_col_insts[bit].place(offset)
# Grow to the right of the bitcell array, array outward # Grow to the right of the bitcell array, array outward
for bit in range(self.add_right_rbl): for bit, port in enumerate(self.right_rbl):
if not end_caps_enabled: if not end_caps_enabled:
offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.left_rbl - 1) + self.strap_offset.scale(bit, -self.left_rbl - 1) offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - 1) + self.strap_offset.scale(bit, -self.rbl[0] - 1)
else: else:
offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.left_rbl - (self.col_end_offset.y/self.cell.height)) + self.strap_offset.scale(bit, -self.left_rbl - 1) offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.strap_offset.scale(bit, -self.rbl[0] - 1)
self.replica_col_insts[self.add_left_rbl + bit].place(offset) self.replica_col_insts[len(self.left_rbl) + bit].place(offset)
# Replica dummy rows # Replica dummy rows
# Add the dummy rows even if we aren't adding the replica column to this bitcell array # Add the dummy rows even if we aren't adding the replica column to this bitcell array
# These grow up, toward the array # These grow up, toward the array
for bit in range(self.left_rbl): for bit in range(self.rbl[0]):
self.dummy_row_replica_insts[bit].place(offset=self.bitcell_offset.scale(0, -self.left_rbl + bit + (-self.left_rbl + bit) % 2), dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) + self.unused_offset
mirror="MX" if (-self.left_rbl + bit) % 2 else "R0") self.dummy_row_replica_insts[bit].place(offset=dummy_offset,
mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0")
# These grow up, away from the array # These grow up, away from the array
for bit in range(self.right_rbl): for bit in range(self.rbl[1]):
self.dummy_row_replica_insts[self.left_rbl + bit].place(offset=self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(), dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul()
mirror="MX" if bit % 2 else "R0") self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset,
mirror="MX" if bit % 2 else "R0")
def add_end_caps(self): def add_end_caps(self):
""" Add dummy cells or end caps around the array """ """ Add dummy cells or end caps around the array """
@ -459,37 +500,37 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
# FIXME: These depend on the array size itself # FIXME: These depend on the array size itself
# Far top dummy row (first row above array is NOT flipped) # Far top dummy row (first row above array is NOT flipped)
flip_dummy = self.right_rbl % 2 flip_dummy = self.rbl[1] % 2
if not end_caps_enabled: if not end_caps_enabled:
dummy_row_offset = self.bitcell_offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul() dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul()
else: else:
dummy_row_offset = self.bitcell_offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul() dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul()
self.dummy_row_insts[1].place(offset=dummy_row_offset, self.dummy_row_insts[1].place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0") mirror="MX" if flip_dummy else "R0")
# FIXME: These depend on the array size itself # FIXME: These depend on the array size itself
# Far bottom dummy row (first row below array IS flipped) # Far bottom dummy row (first row below array IS flipped)
flip_dummy = (self.left_rbl + 1) % 2 flip_dummy = (self.rbl[0] + 1) % 2
if not end_caps_enabled: if not end_caps_enabled:
dummy_row_offset = self.bitcell_offset.scale(0, -self.left_rbl - 1 + flip_dummy) dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset
else: else:
dummy_row_offset = self.bitcell_offset.scale(0, -self.left_rbl - (self.col_end_offset.y/self.cell.height) + flip_dummy) dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - (self.col_end_offset.y/self.cell.height) + flip_dummy) + self.unused_offset
self.dummy_row_insts[0].place(offset=dummy_row_offset, self.dummy_row_insts[0].place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0") mirror="MX" if flip_dummy else "R0")
# Far left dummy col # Far left dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
if not end_caps_enabled: if not end_caps_enabled:
dummy_col_offset = self.bitcell_offset.scale(-self.add_left_rbl - 1, -self.left_rbl - 1) dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -len(self.left_rbl) - 1)
else: else:
dummy_col_offset = self.bitcell_offset.scale(-(self.add_left_rbl*(1+self.strap_offset.x/self.cell.width)) - (self.row_end_offset.x/self.cell.width), -self.left_rbl - (self.col_end_offset.y/self.cell.height)) dummy_col_offset = self.bitcell_offset.scale(-(len(self.left_rbl)*(1+self.strap_offset.x/self.cell.width)) - (self.row_end_offset.x/self.cell.width), -len(self.left_rbl) - (self.col_end_offset.y/self.cell.height))
self.dummy_col_insts[0].place(offset=dummy_col_offset) self.dummy_col_insts[0].place(offset=dummy_col_offset)
# Far right dummy col # Far right dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
if not end_caps_enabled: if not end_caps_enabled:
dummy_col_offset = self.bitcell_offset.scale(self.add_right_rbl*(1+self.strap_offset.x/self.cell.width), -self.left_rbl - 1) + self.bitcell_array_inst.lr() dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl)*(1+self.strap_offset.x/self.cell.width), -self.rbl[0] - 1) + self.bitcell_array_inst.lr()
else: else:
dummy_col_offset = self.bitcell_offset.scale(self.add_right_rbl*(1+self.strap_offset.x/self.cell.width), -self.left_rbl - (self.col_end_offset.y/self.cell.height)) + self.bitcell_array_inst.lr() dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl)*(1+self.strap_offset.x/self.cell.width), -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.bitcell_array_inst.lr()
self.dummy_col_insts[1].place(offset=dummy_col_offset) self.dummy_col_insts[1].place(offset=dummy_col_offset)
@ -506,6 +547,7 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
offset=pin.ll().scale(0, 1), offset=pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=pin.height()) height=pin.height())
for pin_name in self.all_bitline_names: for pin_name in self.all_bitline_names:
pin_list = self.bitcell_array_inst.get_pins(pin_name) pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list: for pin in pin_list:
@ -515,21 +557,12 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
width=pin.width(), width=pin.width(),
height=self.height) height=self.height)
# Dummy wordlines
for (names, inst) in zip(self.dummy_row_wordline_names, self.dummy_row_insts):
for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()):
# It's always a single row
pin = inst.get_pin(pin_name)
self.add_layout_pin(text=wl_name,
layer=pin.layer,
offset=pin.ll().scale(0, 1),
width=self.width,
height=pin.height())
# Replica wordlines (go by the row instead of replica column because we may have to add a pin # Replica wordlines (go by the row instead of replica column because we may have to add a pin
# even though the column is in another local bitcell array) # even though the column is in another local bitcell array)
for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts):
for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()):
if wl_name in self.gnd_wordline_names:
continue
pin = inst.get_pin(pin_name) pin = inst.get_pin(pin_name)
self.add_layout_pin(text=wl_name, self.add_layout_pin(text=wl_name,
layer=pin.layer, layer=pin.layer,
@ -538,14 +571,16 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
height=pin.height()) height=pin.height())
# Replica bitlines # Replica bitlines
for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts): if len(self.rbls) > 0:
for (bl_name, pin_name) in zip(names, self.replica_columns[0].all_bitline_names): for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts):
pin = inst.get_pin(pin_name) pin_names = self.replica_columns[self.rbls[0]].all_bitline_names
self.add_layout_pin(text=bl_name, for (bl_name, pin_name) in zip(names, pin_names):
layer=pin.layer, pin = inst.get_pin(pin_name)
offset=pin.ll().scale(1, 0), self.add_layout_pin(text=bl_name,
width=pin.width(), layer=pin.layer,
height=self.height) offset=pin.ll().scale(1, 0),
width=pin.width(),
height=self.height)
# vdd/gnd are only connected in the perimeter cells # vdd/gnd are only connected in the perimeter cells
# replica column should only have a vdd/gnd in the dummy cell on top/bottom # replica column should only have a vdd/gnd in the dummy cell on top/bottom
@ -557,84 +592,11 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
for pin in pin_list: for pin in pin_list:
self.add_power_pin(name=pin_name, self.add_power_pin(name=pin_name,
loc=pin.center(), loc=pin.center(),
directions=("V", "V"),
start_layer=pin.layer) start_layer=pin.layer)
for inst in self.replica_col_insts: for inst in self.replica_col_insts:
self.copy_layout_pin(inst, pin_name) if inst:
self.copy_layout_pin(inst, pin_name)
def get_rbl_wordline_names(self, port=None):
"""
Return the ACTIVE WL for the given RBL port.
Inactive will be set to gnd.
"""
if port == None:
return self.all_rbl_wordline_names
else:
return self.rbl_wordline_names[port]
def get_rbl_bitline_names(self, port=None):
""" Return the BL for the given RBL port """
if port == None:
return self.all_rbl_bitline_names
else:
return self.rbl_bitline_names[port]
def get_bitline_names(self, port=None):
""" Return the regular bitlines for the given port or all"""
if port == None:
return self.all_bitline_names
else:
return self.bitline_names[port]
def get_all_bitline_names(self):
""" Return ALL the bitline names (including dummy and rbl) """
temp = []
temp.extend(self.get_dummy_bitline_names(0))
if self.add_left_rbl > 0:
temp.extend(self.get_rbl_bitline_names(0))
temp.extend(self.get_bitline_names())
if self.add_right_rbl > 0:
temp.extend(self.get_rbl_bitline_names(self.add_left_rbl))
temp.extend(self.get_dummy_bitline_names(1))
return temp
def get_wordline_names(self, port=None):
""" Return the regular wordline names """
if port == None:
return self.all_wordline_names
else:
return self.wordline_names[port]
def get_all_wordline_names(self, port=None):
""" Return all the wordline names """
temp = []
temp.extend(self.get_dummy_wordline_names(0))
temp.extend(self.get_rbl_wordline_names(0))
if port == None:
temp.extend(self.all_wordline_names)
else:
temp.extend(self.wordline_names[port])
if len(self.all_ports) > 1:
temp.extend(self.get_rbl_wordline_names(1))
temp.extend(self.get_dummy_wordline_names(1))
return temp
def get_dummy_wordline_names(self, port=None):
"""
Return the ACTIVE WL for the given dummy port.
"""
if port == None:
return self.all_dummy_row_wordline_names
else:
return self.dummy_row_wordline_names[port]
def get_dummy_bitline_names(self, port=None):
""" Return the BL for the given dummy port """
if port == None:
return self.all_dummy_col_bitline_names
else:
return self.dummy_col_bitline_names[port]
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW.""" """Power of Bitcell array and bitline in nW."""
@ -653,6 +615,37 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
cell_power.leakage * self.column_size * self.row_size) cell_power.leakage * self.column_size * self.row_size)
return total_power return total_power
def route_unused_wordlines(self):
""" Connect the unused RBL and dummy wordlines to gnd """
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
# This grounds all the dummy row word lines
for inst in self.dummy_row_insts:
for wl_name in self.col_cap.get_wordline_names():
self.ground_pin(inst, wl_name)
# Ground the unused replica wordlines
for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts):
for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()):
if wl_name in self.gnd_wordline_names:
self.ground_pin(inst, pin_name)
def ground_pin(self, inst, name):
pin = inst.get_pin(name)
pin_layer = pin.layer
left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy())
right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy())
# Place the pins a track outside of the array
left_loc = left_pin_loc - vector(self.unused_pitch, 0)
right_loc = right_pin_loc + vector(self.unused_pitch, 0)
self.add_power_pin("gnd", left_loc, directions=("H", "H"))
self.add_power_pin("gnd", right_loc, directions=("H", "H"))
# Add a path to connect to the array
self.add_path(pin_layer, [left_loc, left_pin_loc])
self.add_path(pin_layer, [right_loc, right_pin_loc])
def gen_bl_wire(self): def gen_bl_wire(self):
if OPTS.netlist_only: if OPTS.netlist_only:
height = 0 height = 0
@ -663,23 +656,28 @@ 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): def graph_exclude_bits(self, targ_row=None, targ_col=None):
"""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 Excludes bits in column from being added to graph except target
bitcell_wl_cin = self.cell.get_wl_cin() """
total_cin = bitcell_wl_cin * self.column_size
return total_cin
def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target"""
self.bitcell_array.graph_exclude_bits(targ_row, targ_col) self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def graph_exclude_replica_col_bits(self): def graph_exclude_replica_col_bits(self):
"""Exclude all replica/dummy cells in the replica columns except the replica bit.""" """
Exclude all replica/dummy cells in the replica columns except the replica bit.
"""
for port in range(self.left_rbl + self.right_rbl): for port in self.left_rbl + self.right_rbl:
self.replica_columns[port].exclude_all_but_replica() self.replica_columns[port].exclude_all_but_replica()
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.
"""
return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col) return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bitcell_array.init_graph_params()

View File

@ -4,14 +4,14 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import design from bitcell_base_array import bitcell_base_array
from tech import cell_properties from tech import cell_properties
from sram_factory import factory from sram_factory import factory
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
class replica_column(design.design): class replica_column(bitcell_base_array):
""" """
Generate a replica bitline column for the replica array. Generate a replica bitline column for the replica array.
Rows is the total number of rows i the main array. Rows is the total number of rows i the main array.
@ -21,7 +21,7 @@ class replica_column(design.design):
""" """
def __init__(self, name, rows, rbl, replica_bit, column_offset=0): def __init__(self, name, rows, rbl, replica_bit, column_offset=0):
super().__init__(name) super().__init__(rows=sum(rbl) + rows + 2, cols=1, column_offset=column_offset, name=name)
self.rows = rows self.rows = rows
self.left_rbl = rbl[0] self.left_rbl = rbl[0]
@ -60,38 +60,12 @@ class replica_column(design.design):
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
self.bitline_names = [[] for port in self.all_ports]
col = 0 self.create_all_bitline_names()
for port in self.all_ports: self.create_all_wordline_names()
self.bitline_names[port].append("bl_{0}_{1}".format(port, col))
self.bitline_names[port].append("br_{0}_{1}".format(port, col))
self.all_bitline_names = [x for sl in self.bitline_names for x in sl]
self.add_pin_list(self.all_bitline_names, "OUTPUT") self.add_pin_list(self.all_bitline_names, "OUTPUT")
self.add_pin_list(self.all_wordline_names, "INPUT")
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
self.wordline_names = [[] for port in self.all_ports]
for row in range(self.total_size):
for port in self.all_ports:
if not cell_properties.compare_ports(cell_properties.bitcell.split_wl):
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
else:
self.wordline_names[port].append("wl0_{0}_{1}".format(port, row))
self.wordline_names[port].append("wl1_{0}_{1}".format(port, row))
self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl]
self.add_pin_list(self.all_wordline_names, "INPUT")
else:
self.wordline_names = [[] for port in self.all_ports]
for row in range(self.total_size):
for port in self.all_ports:
if not cell_properties.compare_ports(cell_properties.bitcell.split_wl):
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
else:
if (row > 0 and row < self.total_size-1):
self.wordline_names[port].append("wl0_{0}_{1}".format(port, row))
self.wordline_names[port].append("wl1_{0}_{1}".format(port, row))
self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl]
self.add_pin_list(self.all_wordline_names, "INPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
@ -295,8 +269,10 @@ class replica_column(design.design):
return self.bitline_names[port] return self.bitline_names[port]
def get_bitcell_pins(self, row, col): def get_bitcell_pins(self, row, col):
""" Creates a list of connections in the bitcell, """
indexed by column and row, for instance use in bitcell_array """ Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
bitcell_pins = [] bitcell_pins = []
for port in self.all_ports: for port in self.all_ports:
bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))]) bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))])
@ -307,8 +283,10 @@ class replica_column(design.design):
return bitcell_pins return bitcell_pins
def get_bitcell_pins_col_cap(self, row, col): def get_bitcell_pins_col_cap(self, row, col):
""" Creates a list of connections in the bitcell, """
indexed by column and row, for instance use in bitcell_array """ Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
bitcell_pins = [] bitcell_pins = []
for port in self.all_ports: for port in self.all_ports:
bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))]) bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))])
@ -318,7 +296,9 @@ class replica_column(design.design):
return bitcell_pins return bitcell_pins
def exclude_all_but_replica(self): def exclude_all_but_replica(self):
"""Excludes all bits except the replica cell (self.replica_bit).""" """
Excludes all bits except the replica cell (self.replica_bit).
"""
for row, cell in self.cell_inst.items(): for row, cell in self.cell_inst.items():
if row != self.replica_bit: if row != self.replica_bit:

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

@ -6,12 +6,11 @@
# All rights reserved. # All rights reserved.
# #
import design import design
from tech import drc
from vector import vector from vector import vector
from sram_factory import factory from sram_factory import factory
import debug import debug
from globals import OPTS from globals import OPTS
import logical_effort from tech import cell_properties
class sense_amp_array(design.design): class sense_amp_array(design.design):
@ -20,7 +19,7 @@ class sense_amp_array(design.design):
Dynamically generated sense amp array for all bitlines. Dynamically generated sense amp array for all bitlines.
""" """
def __init__(self, name, word_size, words_per_row, num_spare_cols=None, column_offset=0): def __init__(self, name, word_size, words_per_row, offsets=None, num_spare_cols=None, column_offset=0):
super().__init__(name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
@ -29,6 +28,8 @@ class sense_amp_array(design.design):
self.word_size = word_size self.word_size = word_size
self.words_per_row = words_per_row self.words_per_row = words_per_row
self.num_cols = word_size * words_per_row
self.offsets = offsets
if not num_spare_cols: if not num_spare_cols:
self.num_spare_cols = 0 self.num_spare_cols = 0
else: else:
@ -68,16 +69,15 @@ class sense_amp_array(design.design):
self.create_sense_amp_array() self.create_sense_amp_array()
def create_layout(self): def create_layout(self):
self.height = self.amp.height
if self.bitcell.width > self.amp.width:
self.width = self.bitcell.width * (self.word_size * self.words_per_row + self.num_spare_cols)
else:
self.width = self.amp.width * (self.word_size * self.words_per_row + self.num_spare_cols)
self.place_sense_amp_array() self.place_sense_amp_array()
self.height = self.amp.height
self.width = self.local_insts[-1].rx()
self.add_layout_pins() self.add_layout_pins()
self.route_rails() self.route_rails()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
@ -111,29 +111,32 @@ class sense_amp_array(design.design):
self.en_name, "vdd", "gnd"]) self.en_name, "vdd", "gnd"])
def place_sense_amp_array(self): def place_sense_amp_array(self):
from tech import cell_properties if self.bitcell.width > self.amp.width:
self.amp_spacing = self.bitcell.width
else:
self.amp_spacing = self.amp.width
for i in range(0, self.row_size, self.words_per_row): if not self.offsets:
index = int(i / self.words_per_row) self.offsets = []
xoffset = i * self.bitcell.width for i in range(self.num_cols + self.num_spare_cols):
self.offsets.append(i * self.bitcell.width)
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: for i, xoffset in enumerate(self.offsets[0:self.num_cols:self.words_per_row]):
if cell_properties.bitcell.mirror.y and (i * self.words_per_row + self.column_offset) % 2:
mirror = "MY" mirror = "MY"
xoffset = xoffset + self.amp.width xoffset = xoffset + self.amp_spacing
else: else:
mirror = "" mirror = ""
amp_position = vector(xoffset, 0) amp_position = vector(xoffset, 0)
self.local_insts[index].place(offset=amp_position, mirror=mirror) self.local_insts[i].place(offset=amp_position, mirror=mirror)
# place spare sense amps (will share the same enable as regular sense amps) # place spare sense amps (will share the same enable as regular sense amps)
for i in range(0, self.num_spare_cols): for i, xoffset in enumerate(self.offsets[self.num_cols:]):
index = self.word_size + i index = self.word_size + i
xoffset = ((self.word_size * self.words_per_row) + i) * self.bitcell.width if cell_properties.bitcell.mirror.y and (index + self.column_offset) % 2:
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
mirror = "MY" mirror = "MY"
xoffset = xoffset + self.amp.width xoffset = xoffset + self.amp_width
else: else:
mirror = "" mirror = ""
@ -190,18 +193,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

@ -11,7 +11,7 @@ from tech import layer, preferred_directions
from vector import vector from vector import vector
from sram_factory import factory from sram_factory import factory
from globals import OPTS from globals import OPTS
import logical_effort from tech import cell_properties
class single_level_column_mux_array(design.design): class single_level_column_mux_array(design.design):
@ -20,13 +20,14 @@ class single_level_column_mux_array(design.design):
Array of column mux to read the bitlines through the 6T. Array of column mux to read the bitlines through the 6T.
""" """
def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br", column_offset=0): def __init__(self, name, columns, word_size, offsets=None, bitcell_bl="bl", bitcell_br="br", column_offset=0):
super().__init__(name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br)) self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br))
self.columns = columns self.columns = columns
self.word_size = word_size self.word_size = word_size
self.offsets = offsets
self.words_per_row = int(self.columns / self.word_size) self.words_per_row = int(self.columns / self.word_size)
self.bitcell_bl = bitcell_bl self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br self.bitcell_br = bitcell_br
@ -116,10 +117,12 @@ class single_level_column_mux_array(design.design):
"gnd"]) "gnd"])
def place_array(self): def place_array(self):
from tech import cell_properties # Default to single spaced columns
if not self.offsets:
self.offsets = [n * self.mux.width for n in range(self.columns)]
# For every column, add a pass gate # For every column, add a pass gate
for col_num in range(self.columns): for col_num, xoffset in enumerate(self.offsets[0:self.columns]):
xoffset = col_num * self.mux.width
if cell_properties.bitcell.mirror.y and (col_num + self.column_offset) % 2: if cell_properties.bitcell.mirror.y and (col_num + self.column_offset) % 2:
mirror = "MY" mirror = "MY"
xoffset = xoffset + self.mux.width xoffset = xoffset + self.mux.width
@ -163,7 +166,7 @@ class single_level_column_mux_array(design.design):
self.add_layout_pin(text="sel_{}".format(j), self.add_layout_pin(text="sel_{}".format(j),
layer=self.sel_layer, layer=self.sel_layer,
offset=offset, offset=offset,
width=self.mux.width * self.columns) width=self.mux_inst[-1].rx())
def add_vertical_poly_rail(self): def add_vertical_poly_rail(self):
""" Connect the poly to the address rails """ """ Connect the poly to the address rails """
@ -230,10 +233,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

@ -109,12 +109,13 @@ class wordline_buffer_array(design.design):
def place_drivers(self): def place_drivers(self):
for row in range(self.rows): for row in range(self.rows):
# These are flipped since we always start with an RBL on the bottom
if (row % 2): if (row % 2):
y_offset = self.wl_driver.height * (row + 1)
inst_mirror = "MX"
else:
y_offset = self.wl_driver.height * row y_offset = self.wl_driver.height * row
inst_mirror = "R0" inst_mirror = "R0"
else:
y_offset = self.wl_driver.height * (row + 1)
inst_mirror = "MX"
offset = [0, y_offset] offset = [0, y_offset]

View File

@ -44,7 +44,7 @@ class wordline_driver_array(design.design):
self.place_drivers() self.place_drivers()
self.route_layout() self.route_layout()
self.route_vdd_gnd() self.route_vdd_gnd()
self.offset_all_coordinates() self.offset_x_coordinates()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
@ -60,8 +60,10 @@ class wordline_driver_array(design.design):
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_modules(self): def add_modules(self):
self.wl_driver = factory.create(module_type="wordline_driver", self.wl_driver = factory.create(module_type="wordline_driver",
size=self.cols) cols=self.cols)
self.add_mod(self.wl_driver) self.add_mod(self.wl_driver)
def route_vdd_gnd(self): def route_vdd_gnd(self):
@ -159,24 +161,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

@ -7,10 +7,12 @@
# #
import design import design
import debug import debug
import math
from tech import drc from tech import drc
from sram_factory import factory from sram_factory import factory
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from tech import cell_properties
class write_driver_array(design.design): class write_driver_array(design.design):
@ -19,7 +21,7 @@ class write_driver_array(design.design):
Dynamically generated write driver array of all bitlines. Dynamically generated write driver array of all bitlines.
""" """
def __init__(self, name, columns, word_size, num_spare_cols=None, write_size=None, column_offset=0): def __init__(self, name, columns, word_size, offsets=None, num_spare_cols=None, write_size=None, column_offset=0):
super().__init__(name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
@ -29,6 +31,7 @@ class write_driver_array(design.design):
self.columns = columns self.columns = columns
self.word_size = word_size self.word_size = word_size
self.write_size = write_size self.write_size = write_size
self.offsets = offsets
self.column_offset = column_offset self.column_offset = column_offset
self.words_per_row = int(columns / word_size) self.words_per_row = int(columns / word_size)
if not num_spare_cols: if not num_spare_cols:
@ -37,7 +40,7 @@ class write_driver_array(design.design):
self.num_spare_cols = num_spare_cols self.num_spare_cols = num_spare_cols
if self.write_size: if self.write_size:
self.num_wmasks = int(self.word_size / self.write_size) self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@ -66,17 +69,9 @@ class write_driver_array(design.design):
def create_layout(self): def create_layout(self):
if self.bitcell.width > self.driver.width:
self.width = (self.columns + self.num_spare_cols) * self.bitcell.width
self.width_regular_cols = self.columns * self.bitcell.width
self.single_col_width = self.bitcell.width
else:
self.width = (self.columns + self.num_spare_cols) * self.driver.width
self.width_regular_cols = self.columns * self.driver.width
self.single_col_width = self.driver.width
self.height = self.driver.height
self.place_write_array() self.place_write_array()
self.width = self.driver_insts[-1].rx()
self.height = self.driver.height
self.add_layout_pins() self.add_layout_pins()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
@ -107,14 +102,14 @@ class write_driver_array(design.design):
self.bitcell = factory.create(module_type="bitcell") self.bitcell = factory.create(module_type="bitcell")
def create_write_array(self): def create_write_array(self):
self.driver_insts = {} self.driver_insts = []
w = 0 w = 0
windex=0 windex=0
for i in range(0, self.columns, self.words_per_row): for i in range(0, self.columns, self.words_per_row):
name = "write_driver{}".format(i) name = "write_driver{}".format(i)
index = int(i / self.words_per_row) index = int(i / self.words_per_row)
self.driver_insts[index]=self.add_inst(name=name, self.driver_insts.append(self.add_inst(name=name,
mod=self.driver) mod=self.driver))
if self.write_size: if self.write_size:
self.connect_inst([self.data_name + "_{0}".format(index), self.connect_inst([self.data_name + "_{0}".format(index),
@ -146,8 +141,8 @@ class write_driver_array(design.design):
else: else:
offset = 1 offset = 1
name = "write_driver{}".format(self.columns + i) name = "write_driver{}".format(self.columns + i)
self.driver_insts[index]=self.add_inst(name=name, self.driver_insts.append(self.add_inst(name=name,
mod=self.driver) mod=self.driver))
self.connect_inst([self.data_name + "_{0}".format(index), self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index), self.get_bl_name() + "_{0}".format(index),
@ -155,30 +150,31 @@ class write_driver_array(design.design):
self.en_name + "_{0}".format(i + offset), "vdd", "gnd"]) self.en_name + "_{0}".format(i + offset), "vdd", "gnd"])
def place_write_array(self): def place_write_array(self):
from tech import cell_properties
if self.bitcell.width > self.driver.width: if self.bitcell.width > self.driver.width:
self.driver_spacing = self.bitcell.width self.driver_spacing = self.bitcell.width
else: else:
self.driver_spacing = self.driver.width self.driver_spacing = self.driver.width
for i in range(0, self.columns, self.words_per_row):
index = int(i / self.words_per_row)
xoffset = i * self.driver_spacing
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: if not self.offsets:
self.offsets = []
for i in range(self.columns + self.num_spare_cols):
self.offsets.append(i * self.driver_spacing)
for i, xoffset in enumerate(self.offsets[0:self.columns:self.words_per_row]):
if cell_properties.bitcell.mirror.y and (i * self.words_per_row + self.column_offset) % 2:
mirror = "MY" mirror = "MY"
xoffset = xoffset + self.driver.width xoffset = xoffset + self.driver.width
else: else:
mirror = "" mirror = ""
base = vector(xoffset, 0) base = vector(xoffset, 0)
self.driver_insts[index].place(offset=base, mirror=mirror) self.driver_insts[i].place(offset=base, mirror=mirror)
# place spare write drivers (if spare columns are specified) # place spare write drivers (if spare columns are specified)
for i in range(self.num_spare_cols): for i, xoffset in enumerate(self.offsets[self.columns:]):
index = self.word_size + i index = self.word_size + i
xoffset = (self.columns + i) * self.driver_spacing
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: if cell_properties.bitcell.mirror.y and (index + self.column_offset) % 2:
mirror = "MY" mirror = "MY"
xoffset = xoffset + self.driver.width xoffset = xoffset + self.driver.width
else: else:
@ -243,12 +239,14 @@ class write_driver_array(design.design):
elif self.num_spare_cols and not self.write_size: elif self.num_spare_cols and not self.write_size:
# shorten enable rail to accomodate those for spare write drivers # shorten enable rail to accomodate those for spare write drivers
inst = self.driver_insts[0] left_inst = self.driver_insts[0]
en_pin = inst.get_pin(inst.mod.en_name) left_en_pin = left_inst.get_pin(inst.mod.en_name)
right_inst = self.driver_insts[-self.num_spare_cols - 1]
right_en_pin = right_inst.get_pin(inst.mod.en_name)
self.add_layout_pin(text=self.en_name + "_{0}".format(0), self.add_layout_pin(text=self.en_name + "_{0}".format(0),
layer="m1", layer="m1",
offset=en_pin.ll(), offset=left_en_pin.ll(),
width=self.width_regular_cols - self.words_per_row * en_pin.width()) width=right_en_pin.rx() - left_en_pin.lx())
# individual enables for every spare write driver # individual enables for every spare write driver
for i in range(self.num_spare_cols): for i in range(self.num_spare_cols):
@ -256,7 +254,7 @@ class write_driver_array(design.design):
en_pin = inst.get_pin(inst.mod.en_name) en_pin = inst.get_pin(inst.mod.en_name)
self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1), self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1),
layer="m1", layer="m1",
offset=en_pin.lr() + vector(-drc("minwidth_m1"),0)) offset=en_pin.lr() + vector(-drc("minwidth_m1"), 0))
else: else:
inst = self.driver_insts[0] inst = self.driver_insts[0]
@ -265,7 +263,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

@ -7,6 +7,7 @@
# #
import design import design
import debug import debug
import math
from sram_factory import factory from sram_factory import factory
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
@ -18,7 +19,7 @@ class write_mask_and_array(design.design):
The write mask AND array goes between the write driver array and the sense amp array. The write mask AND array goes between the write driver array and the sense amp array.
""" """
def __init__(self, name, columns, word_size, write_size, column_offset=0): def __init__(self, name, columns, word_size, write_size, offsets=None, column_offset=0):
super().__init__(name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("columns: {0}".format(columns)) self.add_comment("columns: {0}".format(columns))
@ -28,9 +29,10 @@ class write_mask_and_array(design.design):
self.columns = columns self.columns = columns
self.word_size = word_size self.word_size = word_size
self.write_size = write_size self.write_size = write_size
self.offsets = offsets
self.column_offset = column_offset self.column_offset = column_offset
self.words_per_row = int(columns / word_size) self.words_per_row = int(columns / word_size)
self.num_wmasks = int(word_size / write_size) self.num_wmasks = int(math.ceil(word_size / write_size))
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@ -90,12 +92,17 @@ class write_mask_and_array(design.design):
debug.check(self.wmask_en_len >= self.and2.width, debug.check(self.wmask_en_len >= self.and2.width,
"Write mask AND is wider than the corresponding write drivers {0} vs {1}.".format(self.and2.width, "Write mask AND is wider than the corresponding write drivers {0} vs {1}.".format(self.and2.width,
self.wmask_en_len)) self.wmask_en_len))
if not self.offsets:
self.offsets = []
for i in range(self.columns):
self.offsets.append(i * self.driver_spacing)
self.width = self.bitcell.width * self.columns self.width = self.offsets[-1] + self.driver_spacing
self.height = self.and2.height self.height = self.and2.height
write_bits = self.columns / self.num_wmasks
for i in range(self.num_wmasks): for i in range(self.num_wmasks):
base = vector(i * self.wmask_en_len, 0) base = vector(self.offsets[int(i * write_bits)], 0)
self.and2_insts[i].place(base) self.and2_insts[i].place(base)
def add_layout_pins(self): def add_layout_pins(self):
@ -140,8 +147,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

@ -30,6 +30,9 @@ class options(optparse.Values):
num_r_ports = 0 num_r_ports = 0
num_w_ports = 0 num_w_ports = 0
# By default, use local arrays with a max fanout of 16
#local_array_size = 16
# Write mask size, default will be overwritten with word_size if not user specified # Write mask size, default will be overwritten with word_size if not user specified
write_size = None write_size = None
@ -93,6 +96,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
################### ###################
@ -114,6 +119,9 @@ class options(optparse.Values):
# For sky130, we need magic for filtering. # For sky130, we need magic for filtering.
magic_exe = None magic_exe = None
# Number of threads to use
num_threads = 2
# Should we print out the banner at startup # Should we print out the banner at startup
print_banner = True print_banner = True
@ -137,6 +145,7 @@ class options(optparse.Values):
bank_select = "bank_select" bank_select = "bank_select"
bitcell_array = "bitcell_array" bitcell_array = "bitcell_array"
bitcell = "bitcell" bitcell = "bitcell"
buf_dec = "pbuf"
column_mux_array = "single_level_column_mux_array" column_mux_array = "single_level_column_mux_array"
control_logic = "control_logic" control_logic = "control_logic"
decoder = "hierarchical_decoder" decoder = "hierarchical_decoder"

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

@ -18,9 +18,9 @@ class wordline_driver(design.design):
This is an AND (or NAND) with configurable drive strength to drive the wordlines. This is an AND (or NAND) with configurable drive strength to drive the wordlines.
It is matched to the bitcell height. It is matched to the bitcell height.
""" """
def __init__(self, name, size=1, height=None): def __init__(self, name, cols, height=None):
debug.info(1, "Creating wordline_driver {}".format(name)) debug.info(1, "Creating wordline_driver {}".format(name))
self.add_comment("size: {}".format(size)) self.add_comment("cols: {}".format(cols))
super().__init__(name) super().__init__(name)
if height is None: if height is None:
@ -28,7 +28,7 @@ class wordline_driver(design.design):
self.height = b.height self.height = b.height
else: else:
self.height = height self.height = height
self.size = size self.cols = cols
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@ -43,9 +43,24 @@ class wordline_driver(design.design):
self.nand = factory.create(module_type="nand2_dec", self.nand = factory.create(module_type="nand2_dec",
height=self.height) height=self.height)
self.driver = factory.create(module_type="inv_dec", try:
size=self.size, local_array_size = OPTS.local_array_size
height=self.nand.height) driver_size = max(int(self.cols / local_array_size), 1)
except AttributeError:
local_array_size = 0
# Defautl to FO4
driver_size = max(int(self.cols / 4), 1)
# The polarity must be switched if we have a hierarchical wordline
# to compensate for the local array inverters
if local_array_size > 0:
self.driver = factory.create(module_type="buf_dec",
size=driver_size,
height=self.nand.height)
else:
self.driver = factory.create(module_type="inv_dec",
size=driver_size,
height=self.nand.height)
self.add_mod(self.nand) self.add_mod(self.nand)
self.add_mod(self.driver) self.add_mod(self.driver)

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

@ -414,7 +414,7 @@ class sram_1bank(sram_base):
layer_stack=self.m1_stack, layer_stack=self.m1_stack,
parent=self) parent=self)
if add_routes: if add_routes:
self.add_inst("hc", cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
else: else:
self.col_addr_bus_size[port] = cr.height self.col_addr_bus_size[port] = cr.height
@ -470,7 +470,7 @@ class sram_1bank(sram_base):
layer_stack=layer_stack, layer_stack=layer_stack,
parent=self) parent=self)
if add_routes: if add_routes:
self.add_inst("hc", cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
else: else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
@ -482,7 +482,7 @@ class sram_1bank(sram_base):
layer_stack=layer_stack, layer_stack=layer_stack,
parent=self) parent=self)
if add_routes: if add_routes:
self.add_inst("hc", cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
else: else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
@ -542,7 +542,9 @@ class sram_1bank(sram_base):
[data_dff_clk_pos, mid_pos, clk_steiner_pos]) [data_dff_clk_pos, mid_pos, clk_steiner_pos])
def route_control_logic(self): def route_control_logic(self):
""" Route the control logic pins that are not inputs """ """
Route the control logic pins that are not inputs
"""
for port in self.all_ports: for port in self.all_ports:
for signal in self.control_logic_outputs[port]: for signal in self.control_logic_outputs[port]:
@ -567,7 +569,9 @@ class sram_1bank(sram_base):
offset=dest_pin.center()) offset=dest_pin.center())
def route_row_addr_dff(self): def route_row_addr_dff(self):
""" Connect the output of the row flops to the bank pins """ """
Connect the output of the row flops to the bank pins
"""
for port in self.all_ports: for port in self.all_ports:
for bit in range(self.row_addr_size): for bit in range(self.row_addr_size):
flop_name = "dout_{}".format(bit) flop_name = "dout_{}".format(bit)
@ -600,7 +604,9 @@ class sram_1bank(sram_base):
offset=pin.center()) offset=pin.center())
def graph_exclude_data_dff(self): def graph_exclude_data_dff(self):
"""Removes data dff and wmask dff (if applicable) from search graph. """ """
Removes data dff and wmask dff (if applicable) from search graph.
"""
# Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay. # Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay.
for inst in self.data_dff_insts: for inst in self.data_dff_insts:
self.graph_inst_exclude.add(inst) self.graph_inst_exclude.add(inst)
@ -612,7 +618,9 @@ class sram_1bank(sram_base):
self.graph_inst_exclude.add(inst) self.graph_inst_exclude.add(inst)
def graph_exclude_addr_dff(self): def graph_exclude_addr_dff(self):
"""Removes data dff from search graph. """ """
Removes data dff from search graph.
"""
# Address is considered not part of the critical path, subjectively removed # Address is considered not part of the critical path, subjectively removed
for inst in self.row_addr_dff_insts: for inst in self.row_addr_dff_insts:
self.graph_inst_exclude.add(inst) self.graph_inst_exclude.add(inst)
@ -622,31 +630,21 @@ class sram_1bank(sram_base):
self.graph_inst_exclude.add(inst) self.graph_inst_exclude.add(inst)
def graph_exclude_ctrl_dffs(self): def graph_exclude_ctrl_dffs(self):
"""Exclude dffs for CSB, WEB, etc from graph""" """
Exclude dffs for CSB, WEB, etc from graph
"""
# 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): def get_bank_num(self, inst_name, row, col):
return 0; return 0

View File

@ -7,7 +7,7 @@
# #
import datetime import datetime
import debug import debug
from math import log from math import log, ceil
from importlib import reload from importlib import reload
from vector import vector from vector import vector
from globals import OPTS, print_time from globals import OPTS, print_time
@ -15,9 +15,6 @@ 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):
@ -36,16 +33,13 @@ class sram_base(design, verilog, lef):
self.bank_insts = [] self.bank_insts = []
if self.write_size: if self.write_size:
self.num_wmasks = int(self.word_size / self.write_size) self.num_wmasks = int(ceil(self.word_size / self.write_size))
else: else:
self.num_wmasks = 0 self.num_wmasks = 0
if not self.num_spare_cols: if not self.num_spare_cols:
self.num_spare_cols = 0 self.num_spare_cols = 0
# For logical effort delay calculations.
self.all_mods_except_control_done = False
def add_pins(self): def add_pins(self):
""" Add pins for entire SRAM. """ """ Add pins for entire SRAM. """
@ -87,18 +81,20 @@ 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): def add_global_pex_labels(self):
""" """
Add pex labels at the sram level for spice analysis Add pex labels at the sram level for spice analysis
""" """
# add pex labels for bitcells # add pex labels for bitcells
for bank_num in range(len(self.bank_insts)): for bank_num in range(len(self.bank_insts)):
bank = self.bank_insts[bank_num] bank = self.bank_insts[bank_num]
pex_data = bank.reverse_transformation_bitcell(bank.mod.bitcell.name) pex_data = bank.reverse_transformation_bitcell(self.bitcell.name)
bank_offset = pex_data[0] # offset bank relative to sram bank_offset = pex_data[0] # offset bank relative to sram
Q_offset = pex_data[1] # offset of storage relative to bank Q_offset = pex_data[1] # offset of storage relative to bank
@ -112,46 +108,61 @@ class sram_base(design, verilog, lef):
br = [] br = []
storage_layer_name = "m1" storage_layer_name = "m1"
bitline_layer_name = "m2" bitline_layer_name = self.bitcell.get_pin("bl").layer
for cell in range(len(bank_offset)): 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 = [bank_offset[cell][0] + Q_offset[cell][0],
Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], bank_offset[cell][1] + Q_bar_offset[cell][1]] 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 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) row = int(cell % (OPTS.num_words / self.words_per_row))
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) col = int(cell / (OPTS.num_words))
self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num,
row,
col),
storage_layer_name,
Q)
self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num,
row,
col),
storage_layer_name,
Q_bar)
for cell in range(len(bl_offsets)): for cell in range(len(bl_offsets)):
col = bl_meta[cell][0][2] col = bl_meta[cell][0][2]
for bitline in range(len(bl_offsets[cell])): 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]] 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]) bl.append([bitline_location, bl_meta[cell][bitline][3], col])
for cell in range(len(br_offsets)): for cell in range(len(br_offsets)):
col = br_meta[cell][0][2] col = br_meta[cell][0][2]
for bitline in range(len(br_offsets[cell])): 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]] 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]) br.append([bitline_location, br_meta[cell][bitline][3], col])
for i in range(len(bl)): 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]) 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)): 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]) 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 # add pex labels for control logic
for i in range (len(self.control_logic_insts)): for i in range(len(self.control_logic_insts)):
instance = self.control_logic_insts[i] instance = self.control_logic_insts[i]
control_logic_offset = instance.offset control_logic_offset = instance.offset
for output in instance.mod.output_list: for output in instance.mod.output_list:
pin = instance.mod.get_pin(output) pin = instance.mod.get_pin(output)
pin.transform([0,0], instance.mirror, instance.rotate) pin.transform([0, 0], instance.mirror, instance.rotate)
offset = [control_logic_offset[0] + pin.center()[0], control_logic_offset[1] + pin.center()[1]] offset = [control_logic_offset[0] + pin.center()[0],
self.add_layout_pin_rect_center("{0}{1}".format(pin.name,i), storage_layer_name, offset) 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 """
@ -370,10 +381,6 @@ class sram_base(design, verilog, lef):
self.bank_count = 0 self.bank_count = 0
# The control logic can resize itself based on the other modules.
# Requires all other modules added before control logic.
self.all_mods_except_control_done = True
c = reload(__import__(OPTS.control_logic)) c = reload(__import__(OPTS.control_logic))
self.mod_control_logic = getattr(c, OPTS.control_logic) self.mod_control_logic = getattr(c, OPTS.control_logic)
@ -619,6 +626,7 @@ class sram_base(design, verilog, lef):
sp.write("* Column mux: {}:1\n".format(self.words_per_row)) sp.write("* Column mux: {}:1\n".format(self.words_per_row))
sp.write("**************************************************\n") sp.write("**************************************************\n")
# This causes unit test mismatch # This causes unit test mismatch
# sp.write("* Created: {0}\n".format(datetime.datetime.now())) # sp.write("* Created: {0}\n".format(datetime.datetime.now()))
# sp.write("* User: {0}\n".format(getpass.getuser())) # sp.write("* User: {0}\n".format(getpass.getuser()))
# sp.write(".global {0} {1}\n".format(spice["vdd_name"], # sp.write(".global {0} {1}\n".format(spice["vdd_name"],
@ -631,49 +639,14 @@ 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): def graph_exclude_bits(self, targ_row, targ_col):
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline""" """
stage_effort_list = [] Excludes bits in column from being added to graph except target
"""
self.bank.graph_exclude_bits(targ_row, targ_col)
# Clk_buf originates from the control logic so only the bank is related to the wordline path def clear_exclude_bits(self):
# No loading on the wordline other than in the bank. """
external_wordline_cout = 0 Clears the bit exclusions
stage_effort_list += self.bank.determine_wordline_stage_efforts(external_wordline_cout, inp_is_rise) """
self.bank.clear_exclude_bits()
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

@ -25,7 +25,7 @@ class wordline_driver_test(openram_test):
# check wordline driver for single port # check wordline driver for single port
debug.info(2, "Checking driver") debug.info(2, "Checking driver")
tx = factory.create(module_type="wordline_driver") tx = factory.create(module_type="wordline_driver", cols=8)
self.local_check(tx) self.local_check(tx)
globals.end_openram() globals.end_openram()

View File

@ -28,8 +28,7 @@ class replica_bitcell_array_1rw_1r_test(openram_test):
a = factory.create(module_type="replica_bitcell_array", a = factory.create(module_type="replica_bitcell_array",
cols=4, cols=4,
rows=4, rows=4,
rbl=[1, 1], rbl=[1, 1])
add_rbl=[0, 0])
self.local_check(a) self.local_check(a)
debug.info(2, "Testing 4x4 left replica array for cell_1rw_1r") debug.info(2, "Testing 4x4 left replica array for cell_1rw_1r")
@ -37,14 +36,16 @@ class replica_bitcell_array_1rw_1r_test(openram_test):
cols=4, cols=4,
rows=4, rows=4,
rbl=[1, 1], rbl=[1, 1],
add_rbl=[1, 0]) left_rbl=[0])
self.local_check(a) self.local_check(a)
debug.info(2, "Testing 4x4 array left and right replica for cell_1rw_1r") debug.info(2, "Testing 4x4 array left and right replica for cell_1rw_1r")
a = factory.create(module_type="replica_bitcell_array", a = factory.create(module_type="replica_bitcell_array",
cols=4, cols=4,
rows=4, rows=4,
rbl=[1, 1]) rbl=[1, 1],
left_rbl=[0],
right_rbl=[1])
self.local_check(a) self.local_check(a)
@ -55,7 +56,7 @@ class replica_bitcell_array_1rw_1r_test(openram_test):
cols=4, cols=4,
rows=4, rows=4,
rbl=[1, 1], rbl=[1, 1],
add_rbl=[0, 1]) right_rbl=[1])
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -27,7 +27,7 @@ class replica_pbitcell_array_test(openram_test):
OPTS.num_w_ports = 0 OPTS.num_w_ports = 0
debug.info(2, "Testing 4x4 array for pbitcell") debug.info(2, "Testing 4x4 array for pbitcell")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 1], add_rbl=[1, 1]) a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 1], left_rbl=[0], right_rbl=[1])
self.local_check(a) self.local_check(a)
OPTS.bitcell = "pbitcell" OPTS.bitcell = "pbitcell"
@ -39,7 +39,7 @@ class replica_pbitcell_array_test(openram_test):
factory.reset() factory.reset()
debug.info(2, "Testing 4x4 array for pbitcell") debug.info(2, "Testing 4x4 array for pbitcell")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 0], add_rbl=[1, 0]) a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 0], left_rbl=[0])
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -0,0 +1,46 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from sram_factory import factory
import debug
# @unittest.skip("SKIPPING 05_global_bitcell_array_test")
class global_bitcell_array_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
globals.setup_bitcell()
debug.info(2, "Testing 2 x 4x4 global bitcell array for cell_1rw_1r")
a = factory.create(module_type="global_bitcell_array", cols=[4, 4], rows=4)
self.local_check(a)
# debug.info(2, "Testing 4x4 local bitcell array for 6t_cell with replica column")
# a = factory.create(module_type="local_bitcell_array", cols=4, left_rbl=1, rows=4, ports=[0])
# self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -15,21 +15,21 @@ from sram_factory import factory
import debug import debug
@unittest.skip("SKIPPING 05_global_bitcell_array_test") # @unittest.skip("SKIPPING 05_global_bitcell_array_test")
class global_bitcell_array_test(openram_test): class global_bitcell_array_test(openram_test):
def runTest(self): def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file) globals.init_openram(config_file)
debug.info(2, "Testing 2 x 4x4 global bitcell array for 6t_cell without replica") # debug.info(2, "Testing 2 x 4x4 global bitcell array for 6t_cell")
a = factory.create(module_type="global_bitcell_array", cols=[4, 4], rows=4, ports=[0]) # a = factory.create(module_type="global_bitcell_array", cols=[4, 4], rows=4)
self.local_check(a)
# debug.info(2, "Testing 4x4 local bitcell array for 6t_cell with replica column")
# a = factory.create(module_type="local_bitcell_array", cols=4, left_rbl=1, rows=4, ports=[0])
# self.local_check(a) # self.local_check(a)
debug.info(2, "Testing 2 x 4x4 global bitcell array for 6t_cell")
a = factory.create(module_type="global_bitcell_array", cols=[10, 6], rows=4)
self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -28,20 +28,21 @@ class local_bitcell_array_1rw_1r_test(openram_test):
globals.setup_bitcell() globals.setup_bitcell()
debug.info(2, "Testing 4x4 local bitcell array for cell_1rw_1r without replica") debug.info(2, "Testing 4x4 local bitcell array for cell_1rw_1r without replica")
a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 1], add_rbl=[0, 0]) a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 1])
self.local_check(a) self.local_check(a)
debug.info(2, "Testing 4x4 local bitcell array for cell_1rw_1r with replica column") debug.info(2, "Testing 4x4 local bitcell array for cell_1rw_1r with replica column")
a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 1], add_rbl=[1, 0]) a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 1], right_rbl=[1])
self.local_check(a) self.local_check(a)
debug.info(2, "Testing 4x4 local bitcell array for cell_1rw_1r with replica column") debug.info(2, "Testing 4x4 local bitcell array for cell_1rw_1r with replica column")
a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 1], add_rbl=[0, 1]) a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 1], left_rbl=[0])
self.local_check(a) self.local_check(a)
debug.info(2, "Testing 4x4 local bitcell array for cell_1rw_1r with replica column") debug.info(2, "Testing 4x4 local bitcell array for cell_1rw_1r with replica column")
a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 1], add_rbl=[1, 1]) a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 1], left_rbl=[0], right_rbl=[1])
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -23,11 +23,11 @@ class local_bitcell_array_test(openram_test):
globals.init_openram(config_file) globals.init_openram(config_file)
debug.info(2, "Testing 4x4 local bitcell array for 6t_cell without replica") debug.info(2, "Testing 4x4 local bitcell array for 6t_cell without replica")
a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 0], add_rbl=[0, 0]) a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 0])
self.local_check(a) self.local_check(a)
debug.info(2, "Testing 4x4 local bitcell array for 6t_cell with replica column") debug.info(2, "Testing 4x4 local bitcell array for 6t_cell with replica column")
a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 0], add_rbl=[1, 0]) a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 0], left_rbl=[0])
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -27,11 +27,11 @@ class port_address_1rw_1r_test(openram_test):
globals.setup_bitcell() globals.setup_bitcell()
debug.info(1, "Port address 16 rows") debug.info(1, "Port address 16 rows")
a = factory.create("port_address", cols=16, rows=16) a = factory.create("port_address", cols=16, rows=16, port=0)
self.local_check(a) self.local_check(a)
debug.info(1, "Port address 256 rows") debug.info(1, "Port address 256 rows")
a = factory.create("port_address", cols=256, rows=256) a = factory.create("port_address", cols=256, rows=256, port=1)
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -21,11 +21,11 @@ class port_address_test(openram_test):
globals.init_openram(config_file) globals.init_openram(config_file)
debug.info(1, "Port address 16 rows") debug.info(1, "Port address 16 rows")
a = factory.create("port_address", cols=16, rows=16) a = factory.create("port_address", cols=16, rows=16, port=0)
self.local_check(a) self.local_check(a)
debug.info(1, "Port address 512 rows") debug.info(1, "Port address 512 rows")
a = factory.create("port_address", cols=256, rows=512) a = factory.create("port_address", cols=256, rows=512, port=0)
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
class single_bank_1rw_1r_test(openram_test): class single_bank_1rw_1r_test(openram_test):
def runTest(self): def runTest(self):

View File

@ -0,0 +1,74 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class single_bank_1rw_1r_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
from sram_config import sram_config
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
globals.setup_bitcell()
OPTS.local_array_size = 2
c = sram_config(word_size=4,
num_words=16)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create(module_type="bank", sram_config=c)
self.local_check(a)
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create(module_type="bank", sram_config=c)
self.local_check(a)
c.num_words=64
c.words_per_row=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create(module_type="bank", sram_config=c)
self.local_check(a)
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create(module_type="bank", sram_config=c)
self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,53 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
#@unittest.skip("SKIPPING 20_sram_1bank_4mux_test")
class sram_1bank_2mux_global_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
from sram_config import sram_config
OPTS.local_array_size = 8
OPTS.route_supplies = False
c = sram_config(word_size=8,
num_words=32,
num_banks=1)
c.words_per_row=2
c.recompute_sizes()
debug.info(1, "Layout test for {}rw,{}r,{}w sram "
"with {} bit words, {} words, {} words per "
"row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = factory.create(module_type="sram", sram_config=c)
self.local_check(a, final_verification=True)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,110 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys,os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class timing_sram_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
OPTS.spice_name="ngspice"
OPTS.analytical_delay = False
OPTS.netlist_only = True
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
import characterizer
reload(characterizer)
from characterizer import delay
from sram_config import sram_config
OPTS.local_array_size = 2
c = sram_config(word_size=4,
num_words=16,
num_banks=1)
c.words_per_row=1
c.recompute_sizes()
# c = sram_config(word_size=8,
# num_words=32,
# num_banks=1)
# c.words_per_row=2
# c.recompute_sizes()
debug.info(1, "Testing timing for global hierarchical array")
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
probe_address = "1" * s.s.addr_size
probe_data = s.s.word_size - 1
debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data))
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay(s.s, tempspice, corner)
import tech
loads = [tech.spice["dff_in_cap"]*4]
slews = [tech.spice["rise_time"]*2]
data, port_data = d.analyze(probe_address, probe_data, slews, loads)
#Combine info about port into all data
data.update(port_data[0])
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [0.26308339999999997],
'delay_lh': [0.26308339999999997],
'disabled_read0_power': [0.1829355],
'disabled_read1_power': [0.1962055],
'disabled_write0_power': [0.2130763],
'disabled_write1_power': [0.2349011],
'leakage_power': 0.002509793,
'min_period': 0.977,
'read0_power': [0.4028693],
'read1_power': [0.4055884],
'slew_hl': [0.27116019999999996],
'slew_lh': [0.27116019999999996],
'write0_power': [0.44159149999999997],
'write1_power': [0.3856132]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [2.0149939999999997],
'delay_lh': [2.0149939999999997],
'disabled_read0_power': [7.751129],
'disabled_read1_power': [9.025803],
'disabled_write0_power': [9.546656],
'disabled_write1_power': [10.2449],
'leakage_power': 0.004770704,
'min_period': 7.188,
'read0_power': [17.68452],
'read1_power': [18.24353],
'slew_hl': [1.942796],
'slew_lh': [1.942796],
'write0_power': [20.02101],
'write1_power': [15.389470000000001]}
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results
self.assertTrue(len(data.keys())==len(golden_data.keys()))
self.assertTrue(self.check_golden_data(data,golden_data,0.25))
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
class psram_1bank_2mux_func_test(openram_test): class psram_1bank_2mux_func_test(openram_test):
def runTest(self): def runTest(self):
@ -36,7 +37,7 @@ class psram_1bank_2mux_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=2, c = sram_config(word_size=2,
num_words=32, num_words=32,
@ -58,7 +59,7 @@ class psram_1bank_2mux_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
#@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test, third port reads are broken?") #@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test, third port reads are broken?")
class psram_1bank_4mux_func_test(openram_test): class psram_1bank_4mux_func_test(openram_test):
@ -37,7 +38,7 @@ class psram_1bank_4mux_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=2, c = sram_config(word_size=2,
num_words=256, num_words=256,
@ -59,7 +60,7 @@ class psram_1bank_4mux_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
#@unittest.skip("SKIPPING 22_psram_1bank_8mux_func_test") #@unittest.skip("SKIPPING 22_psram_1bank_8mux_func_test")
class psram_1bank_8mux_func_test(openram_test): class psram_1bank_8mux_func_test(openram_test):
@ -37,7 +38,7 @@ class psram_1bank_8mux_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=4, c = sram_config(word_size=4,
num_words=256, num_words=256,
@ -59,7 +60,7 @@ class psram_1bank_8mux_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -8,7 +8,7 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
@ -37,7 +37,7 @@ class psram_1bank_nomux_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=2, c = sram_config(word_size=2,
num_words=32, num_words=32,
@ -59,7 +59,7 @@ class psram_1bank_nomux_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
#@unittest.skip("SKIPPING 22_sram_1bank_2mux_func_test") #@unittest.skip("SKIPPING 22_sram_1bank_2mux_func_test")
class sram_1bank_2mux_func_test(openram_test): class sram_1bank_2mux_func_test(openram_test):
@ -29,7 +30,7 @@ class sram_1bank_2mux_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=4, c = sram_config(word_size=4,
num_words=32, num_words=32,
@ -48,7 +49,7 @@ class sram_1bank_2mux_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -0,0 +1,63 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
#@unittest.skip("SKIPPING 22_sram_1bank_2mux_func_test")
class sram_1bank_2mux_func_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.trim_netlist = False
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
import characterizer
reload(characterizer)
from characterizer import functional
from sram_config import sram_config
OPTS.local_array_size = 8
OPTS.route_supplies = False
c = sram_config(word_size=8,
num_words=32,
num_banks=1)
c.words_per_row=2
c.recompute_sizes()
debug.info(1, "Functional test for sram with "
"{} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
(fail, error) = f.run()
self.assertTrue(fail, error)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
#@unittest.skip("SKIPPING 22_sram_1bank_2mux_sparecols_func_test") #@unittest.skip("SKIPPING 22_sram_1bank_2mux_sparecols_func_test")
class sram_1bank_2mux_sparecols_func_test(openram_test): class sram_1bank_2mux_sparecols_func_test(openram_test):
@ -29,7 +30,7 @@ class sram_1bank_2mux_sparecols_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=4, c = sram_config(word_size=4,
num_words=32, num_words=32,
@ -50,7 +51,7 @@ class sram_1bank_2mux_sparecols_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
#@unittest.skip("SKIPPING 22_sram_1bank_4mux_func_test") #@unittest.skip("SKIPPING 22_sram_1bank_4mux_func_test")
class sram_1bank_4mux_func_test(openram_test): class sram_1bank_4mux_func_test(openram_test):
@ -29,7 +30,7 @@ class sram_1bank_4mux_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=4, c = sram_config(word_size=4,
num_words=128, num_words=128,
@ -48,7 +49,7 @@ class sram_1bank_4mux_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
#@unittest.skip("SKIPPING 22_sram_1bank_8mux_func_test") #@unittest.skip("SKIPPING 22_sram_1bank_8mux_func_test")
class sram_1bank_8mux_func_test(openram_test): class sram_1bank_8mux_func_test(openram_test):
@ -29,7 +30,7 @@ class sram_1bank_8mux_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
if not OPTS.spice_exe: if not OPTS.spice_exe:
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
@ -51,7 +52,7 @@ class sram_1bank_8mux_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
#@unittest.skip("SKIPPING 22_sram_1rw_1r_1bank_nomux_func_test") #@unittest.skip("SKIPPING 22_sram_1rw_1r_1bank_nomux_func_test")
class psram_1bank_nomux_func_test(openram_test): class psram_1bank_nomux_func_test(openram_test):
@ -33,7 +34,7 @@ class psram_1bank_nomux_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=4, c = sram_config(word_size=4,
num_words=32, num_words=32,
@ -52,7 +53,7 @@ class psram_1bank_nomux_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
#@unittest.skip("SKIPPING 22_sram_func_test") #@unittest.skip("SKIPPING 22_sram_func_test")
class sram_1bank_nomux_func_test(openram_test): class sram_1bank_nomux_func_test(openram_test):
@ -47,7 +48,7 @@ class sram_1bank_nomux_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
#@unittest.skip("SKIPPING 22_sram_func_test") #@unittest.skip("SKIPPING 22_sram_func_test")
class sram_1bank_nomux_sparecols_func_test(openram_test): class sram_1bank_nomux_sparecols_func_test(openram_test):
@ -48,7 +49,7 @@ class sram_1bank_nomux_sparecols_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -35,7 +35,7 @@ class sram_wmask_1w_1r_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=8, c = sram_config(word_size=8,
num_words=16, num_words=16,

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
#@unittest.skip("SKIPPING sram_wmask_func_test") #@unittest.skip("SKIPPING sram_wmask_func_test")
class sram_wmask_func_test(openram_test): class sram_wmask_func_test(openram_test):
@ -29,7 +30,7 @@ class sram_wmask_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=8, c = sram_config(word_size=8,
num_words=16, num_words=16,

View File

@ -9,8 +9,8 @@ Run regression tests/pex test on an extracted pinv to ensure pex functionality
with HSPICE. with HSPICE.
""" """
import unittest import unittest
from testutils import header,openram_test from testutils import header, openram_test
import sys,os import sys, os
sys.path.append(os.path.join(sys.path[0],"..")) sys.path.append(os.path.join(sys.path[0],".."))
import globals import globals
from globals import OPTS from globals import OPTS
@ -35,43 +35,43 @@ class hspice_pex_pinv_test(openram_test):
# generate the pinv # generate the pinv
prev_purge_value = OPTS.purge_temp prev_purge_value = OPTS.purge_temp
OPTS.purge_temp = False # force set purge to false to save the sp file # force set purge to false to save the sp file
OPTS.purge_temp = False
debug.info(2, "Checking 1x size inverter") debug.info(2, "Checking 1x size inverter")
tx = pinv.pinv(name="pinv", size=1) tx = pinv.pinv(name="pinv", size=1)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp,tx.name) tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name)
tx.gds_write(tempgds) tx.gds_write(tempgds)
tempsp = "{0}{1}.sp".format(OPTS.openram_temp,tx.name) tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name)
tx.sp_write(tempsp) tx.sp_write(tempsp)
# make sure that the library simulation is successful\ # make sure that the library simulation is successful
sp_delay = self.simulate_delay(test_module = tempsp, sp_delay = self.simulate_delay(test_module=tempsp,
top_level_name = tx.name) top_level_name=tx.name)
if sp_delay is "Failed": if sp_delay == "Failed":
self.fail('Library Spice module did not behave as expected') self.fail('Library Spice module did not behave as expected')
# now generate its pex file # now generate its pex file
pex_file = self.run_pex(tx) pex_file = self.run_pex(tx)
OPTS.purge_temp = prev_purge_value # restore the old purge value OPTS.purge_temp = prev_purge_value # restore the old purge value
# generate simulation for pex, make sure the simulation is successful # generate simulation for pex, make sure the simulation is successful
pex_delay = self.simulate_delay(test_module = pex_file, pex_delay = self.simulate_delay(test_module=pex_file,
top_level_name = tx.name) top_level_name=tx.name)
# make sure the extracted spice simulated # make sure the extracted spice simulated
if pex_delay is "Failed": if pex_delay == "Failed":
self.fail('Pex file did not behave as expected') self.fail('Pex file did not behave as expected')
# if pex data is bigger than original spice file then result is ok # if pex data is bigger than original spice file then result is ok
# However this may not always be true depending on the netlist provided # However this may not always be true depending on the netlist provided
# comment out for now # comment out for now
#debug.info(2,"pex_delay: {0}".format(pex_delay)) # debug.info(2,"pex_delay: {0}".format(pex_delay))
#debug.info(2,"sp_delay: {0}".format(sp_delay)) # debug.info(2,"sp_delay: {0}".format(sp_delay))
#assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\ # assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\
#.format(pex_delay,sp_delay) # .format(pex_delay,sp_delay)
globals.end_openram() globals.end_openram()
def simulate_delay(self, test_module, top_level_name): def simulate_delay(self, test_module, top_level_name):
from characterizer import charutils
from charutils import parse_spice_list from charutils import parse_spice_list
# setup simulation # setup simulation
sim_file = OPTS.openram_temp + "stim.sp" sim_file = OPTS.openram_temp + "stim.sp"
@ -87,43 +87,43 @@ class hspice_pex_pinv_test(openram_test):
from characterizer import measurements, stimuli from characterizer import measurements, stimuli
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
sim_file = open(sim_file, "w") sim_file = open(sim_file, "w")
simulation = stimuli(sim_file,corner) simulation = stimuli(sim_file, corner)
# library files # library files
simulation.write_include(cir_file) simulation.write_include(cir_file)
# supply voltages # supply voltages
simulation.gen_constant(sig_name ="vdd", simulation.gen_constant(sig_name="vdd",
v_val = tech.spice["nom_supply_voltage"]) v_val=tech.spice["nom_supply_voltage"])
simulation.gen_constant(sig_name = "gnd", simulation.gen_constant(sig_name="gnd",
v_val = "0v") v_val="0v")
run_time = tech.spice["feasible_period"] * 4 run_time = tech.spice["feasible_period"] * 4
# input voltage # input voltage
clk_period = tech.spice["feasible_period"] clk_period = tech.spice["feasible_period"]
simulation.gen_pwl(sig_name ="input", simulation.gen_pwl(sig_name="input",
clk_times = [clk_period,clk_period], clk_times=[clk_period, clk_period],
data_values = [1,0], data_values=[1, 0],
period = clk_period, period=clk_period,
slew = 0.001*tech.spice["feasible_period"], slew=0.001 * tech.spice["feasible_period"],
setup = 0) setup=0)
# instantiation of simulated pinv # instantiation of simulated pinv
simulation.inst_model(pins = ["input", "output", "vdd", "gnd"], simulation.inst_model(pins=["input", "output", "vdd", "gnd"],
model_name = top_module_name) model_name=top_module_name)
# delay measurement # delay measurement
delay_measure = measurements.delay_measure(measure_name = "pinv_delay", delay_measure = measurements.delay_measure(measure_name="pinv_delay",
trig_name = "input", trig_name="input",
targ_name = "output", targ_name="output",
trig_dir_str = "FALL", trig_dir_str="FALL",
targ_dir_str = "RISE", targ_dir_str="RISE",
has_port = False) has_port=False)
trig_td = trag_td = 0.01 * run_time trig_td = trag_td = 0.01 * run_time
rest_info = trig_td,trag_td,tech.spice["nom_supply_voltage"] rest_info = trig_td, trag_td, tech.spice["nom_supply_voltage"]
delay_measure.write_measure(simulation, rest_info) delay_measure.write_measure(simulation, rest_info)
simulation.write_control(end_time = run_time) simulation.write_control(end_time=run_time)
sim_file.close() sim_file.close()
return simulation return simulation

View File

@ -34,43 +34,43 @@ class ngspice_pex_pinv_test(openram_test):
# generate the pinv module # generate the pinv module
prev_purge_value = OPTS.purge_temp prev_purge_value = OPTS.purge_temp
OPTS.purge_temp = False # force set purge to false to save the sp file OPTS.purge_temp = False # force set purge to false to save the sp file
debug.info(2, "Checking 1x size inverter") debug.info(2, "Checking 1x size inverter")
tx = pinv.pinv(name="pinv", size=1) tx = pinv.pinv(name="pinv", size=1)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp,tx.name) tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name)
tx.gds_write(tempgds) tx.gds_write(tempgds)
tempsp = "{0}{1}.sp".format(OPTS.openram_temp,tx.name) tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name)
tx.sp_write(tempsp) tx.sp_write(tempsp)
# make sure that the library simulation is successful # make sure that the library simulation is successful
sp_delay = self.simulate_delay(test_module = tempsp, sp_delay = self.simulate_delay(test_module=tempsp,
top_level_name = tx.name) top_level_name=tx.name)
if sp_delay is "Failed": if sp_delay == "Failed":
self.fail('Library Spice module did not behave as expected') self.fail('Library Spice module did not behave as expected')
# now generate its pex file # now generate its pex file
pex_file = self.run_pex(tx) pex_file = self.run_pex(tx)
OPTS.purge_temp = prev_purge_value # restore the old purge value # restore the old purge value
OPTS.purge_temp = prev_purge_value
# generate simulation for pex, make sure the simulation is successful # generate simulation for pex, make sure the simulation is successful
pex_delay = self.simulate_delay(test_module = pex_file, pex_delay = self.simulate_delay(test_module=pex_file,
top_level_name = tx.name) top_level_name=tx.name)
# make sure the extracted spice simulated # make sure the extracted spice simulated
if pex_delay is "Failed": if pex_delay == "Failed":
self.fail('Pex file did not behave as expected') self.fail('Pex file did not behave as expected')
# if pex data is bigger than original spice file then result is ok # if pex data is bigger than original spice file then result is ok
# However this may not always be true depending on the netlist provided # However this may not always be true depending on the netlist provided
# comment out for now # comment out for now
#debug.info(2,"pex_delay: {0}".format(pex_delay)) # debug.info(2,"pex_delay: {0}".format(pex_delay))
#debug.info(2,"sp_delay: {0}".format(sp_delay)) # debug.info(2,"sp_delay: {0}".format(sp_delay))
#assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\ # assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\
#.format(pex_delay,sp_delay) # .format(pex_delay,sp_delay)
globals.end_openram() globals.end_openram()
def simulate_delay(self, test_module, top_level_name): def simulate_delay(self, test_module, top_level_name):
from characterizer import charutils
from charutils import parse_spice_list from charutils import parse_spice_list
# setup simulation # setup simulation
sim_file = OPTS.openram_temp + "stim.sp" sim_file = OPTS.openram_temp + "stim.sp"
@ -86,47 +86,46 @@ class ngspice_pex_pinv_test(openram_test):
from characterizer import measurements, stimuli from characterizer import measurements, stimuli
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
sim_file = open(sim_file, "w") sim_file = open(sim_file, "w")
simulation = stimuli(sim_file,corner) simulation = stimuli(sim_file, corner)
# library files # library files
simulation.write_include(cir_file) simulation.write_include(cir_file)
# supply voltages # supply voltages
simulation.gen_constant(sig_name ="vdd", simulation.gen_constant(sig_name="vdd",
v_val = tech.spice["nom_supply_voltage"]) v_val=tech.spice["nom_supply_voltage"])
# The scn4m_subm and ngspice combination will have a gnd source error: # The scn4m_subm and ngspice combination will have a gnd source error:
# "Fatal error: instance vgnd is a shorted VSRC" # "Fatal error: instance vgnd is a shorted VSRC"
# However, remove gnd power for all techa pass for this test # However, remove gnd power for all techa pass for this test
# simulation.gen_constant(sig_name = "gnd", # simulation.gen_constant(sig_name = "gnd",
# v_val = "0v") # v_val = "0v")
run_time = tech.spice["feasible_period"] * 4 run_time = tech.spice["feasible_period"] * 4
# input voltage # input voltage
clk_period = tech.spice["feasible_period"] clk_period = tech.spice["feasible_period"]
simulation.gen_pwl(sig_name ="input", simulation.gen_pwl(sig_name="input",
clk_times = [clk_period,clk_period], clk_times=[clk_period, clk_period],
data_values = [1,0], data_values=[1, 0],
period = clk_period, period=clk_period,
slew = 0.001*tech.spice["feasible_period"], slew=0.001 * tech.spice["feasible_period"],
setup = 0) setup=0)
# instantiation of simulated pinv # instantiation of simulated pinv
simulation.inst_model(pins = ["input", "output", "vdd", "gnd"], simulation.inst_model(pins=["input", "output", "vdd", "gnd"],
model_name = top_module_name) model_name=top_module_name)
# delay measurement # delay measurement
delay_measure = measurements.delay_measure(measure_name = "pinv_delay", delay_measure = measurements.delay_measure(measure_name="pinv_delay",
trig_name = "input", trig_name="input",
targ_name = "output", targ_name="output",
trig_dir_str = "FALL", trig_dir_str="FALL",
targ_dir_str = "RISE", targ_dir_str="RISE",
has_port = False) has_port=False)
trig_td = trag_td = 0.01 * run_time trig_td = trag_td = 0.01 * run_time
rest_info = trig_td,trag_td,tech.spice["nom_supply_voltage"] rest_info = trig_td, trag_td, tech.spice["nom_supply_voltage"]
delay_measure.write_measure(simulation, rest_info) delay_measure.write_measure(simulation, rest_info)
simulation.write_control(end_time = run_time) simulation.write_control(end_time=run_time)
sim_file.close() sim_file.close()
return simulation return simulation

View File

@ -1,319 +0,0 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys,os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
@unittest.skip("SKIPPING 26_pex_test")
class sram_func_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
OPTS.use_pex = True
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
import characterizer
reload(characterizer)
from characterizer import setup_hold
if not OPTS.spice_exe:
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
self.func_test(bank_num=1)
self.func_test(bank_num=2)
self.func_test(bank_num=4)
globals.end_openram()
def func_test(self, bank_num):
import sram
import tech
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = sram.sram(word_size=OPTS.word_size,
num_words=OPTS.num_words,
num_banks=OPTS.num_banks,
name="test_sram1")
tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds"
s.sp_write(tempspice)
s.gds_write(tempgds)
self.assertFalse(verify.run_drc(s.name, tempgds))
self.assertFalse(verify.run_lvs(s.name, tempgds, tempspice))
self.assertFalse(verify.run_pex(s.name, tempgds,
tempspice, output=OPTS.openram_temp + "temp_pex.sp"))
import sp_file
stimulus_file = OPTS.openram_temp + "stimulus.sp"
a_stimulus = sp_file.sp_file(stimulus_file)
self.write_stimulus(a_stimulus)
simulator_file = OPTS.openram_temp + "simulator.sp"
a_simulator = sp_file.sp_file(simulator_file)
self.write_simulator(a_simulator)
result_file = OPTS.openram_temp + "result"
import os
if OPTS.spice_name == "hspice":
cmd = "hspice -mt 2 -i {0} > {1} ".format(
simulator_file, result_file)
else:
cmd = "ngspice -b -i {0} > {1} ".format(
simulator_file, result_file)
os.system(cmd)
import re
sp_result = open(result_file, "r")
contents = sp_result.read()
key = "vr1"
val = re.search(
r"{0}(\s*)=(\s*)(\d*(.).*)(\s*)(from)".format(key), contents)
val = val.group(3)
value1 = float(self.convert_voltage_unit(val))
key = "vr2"
val = re.search(
r"{0}(\s*)=(\s*)(\d*(.).*)(\s*)(from)".format(key), contents)
val = val.group(3)
value2 = float(self.convert_voltage_unit(val))
self.assertTrue(round(value1) > 0.5 * tech.spice["supply_voltage"])
self.assertTrue(round(value2) < 0.5 * tech.spice["supply_voltage"])
def convert_voltage_unit(self, string):
newstring = ""
for letter in string:
if letter == "m":
letter = "10e-3"
elif letter == "u":
letter = "10e-6"
else:
letter = letter
newstring = str(newstring) + str(letter)
return newstring
def convert_time_unit(self, string):
newstring = ""
for letter in string:
if letter == "f":
letter = "10e-15"
elif letter == "p":
letter = "10e-12"
elif letter == "n":
letter = "10e-9"
elif letter == "u":
letter = "10e-6"
elif letter == "m":
letter = "10e-3"
else:
letter = letter
newstring = str(newstring) + str(letter)
return newstring
def write_simulator(self, sim_file):
sim_file.write("\n")
import tech
time_step = tech.spice["clock_period"]
for model in tech.spice["fet_models"]:
sim_file.write(".inc " + str(model) + "\n")
sim_file.write(".inc stimulus.sp\n")
sim_file.write(".inc temp_pex.sp\n")
sim_file.write(".options post runlvl=6\n")
sim_file.write("\n")
sim_file.write(
"Xsource DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb WEb_inv OEb clk vdd vss source\n")
sim_file.write(
"Xsram DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb OEb clk vdd vss test_sram1\n")
sim_file.write("\n")
sim_file.write(".MEASURE TRAN vr1 AVG V(DATA[0]) FROM ={0}ns TO ={1}ns\n".format(
4.5 * tech.spice["clock_period"], 5 * tech.spice["clock_period"]))
sim_file.write(".MEASURE TRAN vr2 AVG V(DATA[0]) FROM ={0}ns TO ={1}ns\n".format(
9.5 * tech.spice["clock_period"], 10 * tech.spice["clock_period"]))
sim_file.write("\n")
if OPTS.spice_name in ["hspice","xa"]:
sim_file.write(".probe v(x*.*)\n")
sim_file.write(".tran 0.1ns {0}ns\n".format(
10 * tech.spice["clock_period"]))
sim_file.write(".end\n")
else:
sim_file.write(
".meas tran DELAY1.0 TRIG v(clk) VAL=0.5 RISE=6 TARG v(DATA[0]) VAL=0.5 TD=0.5n RISE=1\n")
sim_file.write(".tran 0.1ns {0}ns\n".format(
10 * tech.spice["clock_period"]))
sim_file.write(".control\n")
sim_file.write("run\n")
#sim_file.write("plot CSb WEb OEb \n")
#sim_file.write("plot clk DATA0 \n")
sim_file.write("quit\n")
sim_file.write(".endc\n")
sim_file.write(".end\n")
sim_file.file.close()
def write_stimulus(self, sti_file):
import tech
import sp_file
sti_file.write(
".subckt source DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb WEb_inv OEb clk vdd vss\n")
time_step = tech.spice["clock_period"]
clk = sp_file.PWL(name="clk", port=["clk", "0"])
for i in range(0, 11):
clk.write_pulse(i * time_step, time_step, "UP")
clk.write_to_sp(sti_file)
WEB_inv = sp_file.PWL(name="WEb_inv", port=["WEb_inv", "0"])
WEB = sp_file.PWL(name="WEB", port=["WEb", "0"])
OEb = sp_file.PWL(name="OEb", port=["OEb", "0"])
CSb = sp_file.PWL(name="CSb", port=["CSb", "0"])
# write
CSb.write_pulse(0.75 * time_step, time_step, "DN")
WEB.write_pulse(0.75 * time_step, time_step, "DN")
WEB_inv.write_pulse(0.75 * time_step, time_step, "UP")
CSb.write_pulse(1.75 * time_step, time_step, "DN")
WEB.write_pulse(1.75 * time_step, time_step, "DN")
WEB_inv.write_pulse(1.75 * time_step, time_step, "UP")
# read
OEb.write_pulse(3.75 * time_step, time_step, "DN")
CSb.write_pulse(3.75 * time_step, time_step, "DN")
# write
CSb.write_pulse(5.75 * time_step, time_step, "DN")
WEB.write_pulse(5.75 * time_step, time_step, "DN")
WEB_inv.write_pulse(5.75 * time_step, time_step, "UP")
CSb.write_pulse(6.75 * time_step, time_step, "DN")
WEB.write_pulse(6.75 * time_step, time_step, "DN")
WEB_inv.write_pulse(6.75 * time_step, time_step, "UP")
# read
OEb.write_pulse(8.75 * time_step, time_step, "DN")
CSb.write_pulse(8.75 * time_step, time_step, "DN")
CSb.write_to_sp(sti_file)
WEB.write_to_sp(sti_file)
WEB_inv.write_to_sp(sti_file)
OEb.write_to_sp(sti_file)
sti_file.write("VA[0] A[0] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
sti_file.write("VA[1] A[1] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
sti_file.write("VA[2] A[2] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
sti_file.write("VA[3] A[3] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
sti_file.write(
"xA[0]_buff A[0] ADDR[0]_inv ADDR[0] vdd vss test_buf\n")
sti_file.write(
"xA[1]_buff A[1] ADDR[1]_inv ADDR[1] vdd vss test_buf\n")
sti_file.write(
"xA[2]_buff A[2] ADDR[2]_inv ADDR[2] vdd vss test_buf\n")
sti_file.write(
"xA[3]_buff A[3] ADDR[3]_inv ADDR[3] vdd vss test_buf\n")
VD_0 = sp_file.PWL(name="VD[0]", port=["D[0]", "0"])
VD_0.write_pulse(0, 5 * time_step, "S1")
VD_0.write_pulse(5 * time_step, 5 * time_step, "S0")
VD_0.write_to_sp(sti_file)
sti_file.write(
"xD[0]_buff D[0] DATA[0]_inv DATA[0]s vdd vss test_buf\n")
sti_file.write(
"xD[0]_gate DATA[0]s WEb WEb_inv DATA[0] vdd vss tran_gate\n")
sti_file.write("mp[0]_gate_vdd vdd write_v DATA[0] vdd " + str(tech.spice["pmos"]) +
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write("mn[0]_gate_vss vss write_g DATA[0] vss " + str(tech.spice["nmos"]) +
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
Vwrite_v = sp_file.PWL(name="write_v", port=["write_vs", "0"])
Vwrite_v.write_pulse(0, 0.5 * time_step, "S1")
Vwrite_v.write_pulse(7.5 * time_step, time_step, "DN")
Vwrite_v.write_to_sp(sti_file)
sti_file.write(
"xwrite_v write_vs write_v_inv write_v vdd vss test_buf\n")
Vwrite_g = sp_file.PWL(name="write_g", port=["write_gs", "0"])
Vwrite_g.write_pulse(0, 0.5 * time_step, "S0")
Vwrite_g.write_pulse(3 * time_step, time_step, "UP")
Vwrite_g.write_to_sp(sti_file)
sti_file.write(
"xwrite_g write_gs write_g_inv write_g vdd vss test_buf\n")
sti_file.write("Vdd vdd 0 DC " +
str(tech.spice["supply_voltage"]) + "\n")
sti_file.write("Vvss vss 0 DC 0\n")
sti_file.write(".ENDS source\n")
sti_file.write("\n")
sti_file.write(".SUBCKT tran_gate in gate gate_inv out vdd vss\n")
sti_file.write("mp0 in gate out vdd " + str(tech.spice["pmos"]) +
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write("mn0 in gate_inv out vss " + str(tech.spice["nmos"]) +
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write(".ENDS tran_gate\n")
sti_file.write("\n")
sti_file.write(".SUBCKT test_buf in out_inv out_buf vdd vss\n")
sti_file.write("mpinv1 out_inv in vdd vdd " + str(tech.spice["pmos"]) +
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write("mninv1 out_inv in vss vss " + str(tech.spice["nmos"]) +
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write("mpinv2 out_buf out_inv vdd vdd " + str(tech.spice["pmos"]) +
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write("mninv2 out_buf out_inv vss vss " + str(tech.spice["nmos"]) +
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write(".ENDS test_buf\n")
sti_file.write("\n")
sti_file.file.close()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,58 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class sram_pex_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
OPTS.analytical_delay = False
OPTS.use_pex = True
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
import characterizer
reload(characterizer)
from characterizer import functional
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.words_per_row=2
c.recompute_sizes()
debug.info(1, "Functional test for sram with "
"{} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = self.run_pex(s)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
(fail, error) = f.run()
self.assertTrue(fail, error)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -8,13 +8,14 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
@unittest.skip("SKIPPING 50_riscv_func_test") @unittest.skip("SKIPPING 50_riscv_func_test")
class riscv_func_test(openram_test): class riscv_func_test(openram_test):
@ -24,6 +25,7 @@ class riscv_func_test(openram_test):
OPTS.analytical_delay = False OPTS.analytical_delay = False
OPTS.netlist_only = True OPTS.netlist_only = True
OPTS.trim_netlist = False OPTS.trim_netlist = False
OPTS.local_array_size = 16
OPTS.num_rw_ports = 1 OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0 OPTS.num_w_ports = 0
OPTS.num_r_ports = 1 OPTS.num_r_ports = 1
@ -33,7 +35,7 @@ class riscv_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=32, c = sram_config(word_size=32,
write_size=8, write_size=8,
@ -53,7 +55,7 @@ class riscv_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) f = functional(s.s, tempspice, corner)
(fail, error) = f.run() (fail, error) = f.run()
self.assertTrue(fail,error) self.assertTrue(fail, error)
globals.end_openram() globals.end_openram()

View File

@ -8,14 +8,15 @@
# #
import unittest import unittest
from testutils import * from testutils import *
import sys,os import sys, os
sys.path.append(os.getenv("OPENRAM_HOME")) sys.path.append(os.getenv("OPENRAM_HOME"))
import globals import globals
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
@unittest.skip("SKIPPING 50_riscv_phys_test")
#@unittest.skip("SKIPPING 50_riscv_phys_test")
class riscv_phys_test(openram_test): class riscv_phys_test(openram_test):
def runTest(self): def runTest(self):
@ -26,9 +27,10 @@ class riscv_phys_test(openram_test):
OPTS.num_rw_ports = 1 OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1 OPTS.num_r_ports = 1
OPTS.num_w_ports = 0 OPTS.num_w_ports = 0
OPTS.local_array_size = 16
globals.setup_bitcell() globals.setup_bitcell()
OPTS.route_supplies=False OPTS.route_supplies = False
OPTS.perimeter_pins=False OPTS.perimeter_pins = False
c = sram_config(word_size=32, c = sram_config(word_size=32,
write_size=8, write_size=8,

View File

@ -83,6 +83,8 @@ class openram_test(unittest.TestCase):
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name) tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name) tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name)
a.gds_write(tempgds)
import verify import verify
result=verify.run_pex(a.name, tempgds, tempspice, output=output, final_verification=False) result=verify.run_pex(a.name, tempgds, tempspice, output=output, final_verification=False)
if result != 0: if result != 0:

View File

@ -101,7 +101,7 @@ def write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name):
# FIXME: Remove when vdd/gnd connected # FIXME: Remove when vdd/gnd connected
#'lvsAbortOnSupplyError' : 0 #'lvsAbortOnSupplyError' : 0
if not final_verification: if not final_verification or not OPTS.route_supplies:
lvs_runset['cmnVConnectReport']=1 lvs_runset['cmnVConnectReport']=1
lvs_runset['cmnVConnectNamesState']='SOME' lvs_runset['cmnVConnectNamesState']='SOME'
lvs_runset['cmnVConnectNames']='vdd gnd' lvs_runset['cmnVConnectNames']='vdd gnd'

View File

@ -100,7 +100,7 @@ def write_magic_script(cell_name, extract=False, final_verification=False):
pre = "#" pre = "#"
else: else:
pre = "" pre = ""
if final_verification: if final_verification and OPTS.route_supplies:
f.write(pre + "extract unique all\n".format(cell_name)) f.write(pre + "extract unique all\n".format(cell_name))
# Hack to work around unit scales in SkyWater # Hack to work around unit scales in SkyWater
if OPTS.tech_name=="sky130": if OPTS.tech_name=="sky130":
@ -295,11 +295,8 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
global num_pex_runs global num_pex_runs
num_pex_runs += 1 num_pex_runs += 1
#debug.warning("PEX using magic not implemented.")
#return 1
os.chdir(OPTS.openram_temp) os.chdir(OPTS.openram_temp)
from tech import drc
if output == None: if output == None:
output = name + ".pex.netlist" output = name + ".pex.netlist"
@ -312,17 +309,11 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
# pex_fix did run the pex using a script while dev orignial method # pex_fix did run the pex using a script while dev orignial method
# use batch mode. # use batch mode.
# the dev old code using batch mode does not run and is split into functions # the dev old code using batch mode does not run and is split into functions
#pex_runset = write_batch_pex_rule(gds_name,name,sp_name,output) pex_runset = write_script_pex_rule(gds_name, name, output)
pex_runset = write_script_pex_rule(gds_name,name,output)
errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name) errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name)
outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, name) outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, name)
# bash mode command from dev branch
#batch_cmd = "{0} -gui -pex {1}pex_runset -batch 2> {2} 1> {3}".format(OPTS.pex_exe,
# OPTS.openram_temp,
# errfile,
# outfile)
script_cmd = "{0} 2> {1} 1> {2}".format(pex_runset, script_cmd = "{0} 2> {1} 1> {2}".format(pex_runset,
errfile, errfile,
outfile) outfile)
@ -334,8 +325,8 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
pex_nelist = open(output, 'r') pex_nelist = open(output, 'r')
s = pex_nelist.read() s = pex_nelist.read()
pex_nelist.close() pex_nelist.close()
s = s.replace('pfet','p') s = s.replace('pfet', 'p')
s = s.replace('nfet','n') s = s.replace('nfet', 'n')
f = open(output, 'w') f = open(output, 'w')
f.write(s) f.write(s)
f.close() f.close()
@ -345,12 +336,13 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
results = f.readlines() results = f.readlines()
f.close() f.close()
out_errors = find_error(results) out_errors = find_error(results)
debug.check(os.path.isfile(output),"Couldn't find PEX extracted output.") debug.check(os.path.isfile(output), "Couldn't find PEX extracted output.")
correct_port(name,output,sp_name) correct_port(name, output, sp_name)
return out_errors return out_errors
def write_batch_pex_rule(gds_name,name,sp_name,output):
def write_batch_pex_rule(gds_name, name, sp_name, output):
""" """
The dev branch old batch mode runset The dev branch old batch mode runset
2. magic can perform extraction with the following: 2. magic can perform extraction with the following:
@ -394,7 +386,8 @@ def write_batch_pex_rule(gds_name,name,sp_name,output):
f.close() f.close()
return file return file
def write_script_pex_rule(gds_name,cell_name,output):
def write_script_pex_rule(gds_name, cell_name, output):
global OPTS global OPTS
run_file = OPTS.openram_temp + "run_pex.sh" run_file = OPTS.openram_temp + "run_pex.sh"
f = open(run_file, "w") f = open(run_file, "w")
@ -412,23 +405,29 @@ def write_script_pex_rule(gds_name,cell_name,output):
pre = "#" pre = "#"
else: else:
pre = "" pre = ""
f.write(pre+"extract\n".format(cell_name)) f.write(pre + "extract\n")
f.write(pre+"ext2spice hierarchy off\n") f.write(pre + "ext2sim labels on\n")
f.write(pre+"ext2spice format ngspice\n") f.write(pre + "ext2sim\n")
f.write(pre+"ext2spice renumber off\n") f.write(pre + "extresist simplify off\n")
f.write(pre+"ext2spice scale off\n") f.write(pre + "extresist all\n")
f.write(pre+"ext2spice blackbox on\n") f.write(pre + "ext2spice hierarchy off\n")
f.write(pre+"ext2spice subcircuit top on\n") f.write(pre + "ext2spice format ngspice\n")
f.write(pre+"ext2spice global off\n") f.write(pre + "ext2spice renumber off\n")
f.write(pre+"ext2spice {}\n".format(cell_name)) f.write(pre + "ext2spice scale off\n")
f.write(pre + "ext2spice blackbox on\n")
f.write(pre + "ext2spice subcircuit top on\n")
f.write(pre + "ext2spice global off\n")
f.write(pre + "ext2spice extresist on\n")
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")
f.write("mv {0}.spice {1}\n".format(cell_name,output)) f.write("mv {0}.spice {1}\n".format(cell_name, output))
f.close() f.close()
os.system("chmod u+x {}".format(run_file)) os.system("chmod u+x {}".format(run_file))
return run_file return run_file
def find_error(results): def find_error(results):
# Errors begin with "ERROR:" # Errors begin with "ERROR:"
test = re.compile("ERROR:") test = re.compile("ERROR:")
@ -438,6 +437,7 @@ def find_error(results):
out_errors = len(stdouterrors) out_errors = len(stdouterrors)
return out_errors return out_errors
def correct_port(name, output_file_name, ref_file_name): 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()
@ -457,8 +457,8 @@ def correct_port(name, output_file_name, ref_file_name):
for bank in range(OPTS.num_banks): for bank in range(OPTS.num_banks):
row = int(OPTS.num_words / OPTS.words_per_row) - 1 row = int(OPTS.num_words / OPTS.words_per_row) - 1
col = int(OPTS.word_size * 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_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) 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 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): 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 += "bl{0}_{1} ".format(bank, col)
@ -484,13 +484,18 @@ def correct_port(name, output_file_name, ref_file_name):
# 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+'\n') output_file.write(circuit_title + '\n')
output_file.write(part2) output_file.write(part2)
output_file.close() output_file.close()
def print_drc_stats(): def print_drc_stats():
debug.info(1,"DRC runs: {0}".format(num_drc_runs)) debug.info(1, "DRC runs: {0}".format(num_drc_runs))
def print_lvs_stats(): def print_lvs_stats():
debug.info(1,"LVS runs: {0}".format(num_lvs_runs)) debug.info(1, "LVS runs: {0}".format(num_lvs_runs))
def print_pex_stats(): def print_pex_stats():
debug.info(1,"PEX runs: {0}".format(num_pex_runs)) debug.info(1, "PEX runs: {0}".format(num_pex_runs))

View File

@ -6,6 +6,9 @@ equate class {-circuit1 pfet} {-circuit2 p}
flatten class {-circuit1 dummy_cell_6t} flatten class {-circuit1 dummy_cell_6t}
flatten class {-circuit1 dummy_cell_1rw_1r} flatten class {-circuit1 dummy_cell_1rw_1r}
flatten class {-circuit1 dummy_cell_1w_1r} flatten class {-circuit1 dummy_cell_1w_1r}
flatten class {-circuit1 dummy_pbitcell}
flatten class {-circuit1 dummy_pbitcell_0}
flatten class {-circuit1 dummy_pbitcell_1}
flatten class {-circuit1 pbitcell} flatten class {-circuit1 pbitcell}
flatten class {-circuit1 pbitcell_0} flatten class {-circuit1 pbitcell_0}
flatten class {-circuit1 pbitcell_1} flatten class {-circuit1 pbitcell_1}