Remove temp files

This commit is contained in:
mrg 2020-10-08 10:35:27 -07:00
commit 43d2058b3c
128 changed files with 4273 additions and 2802 deletions

View File

@ -45,8 +45,7 @@ class _bitcell:
cell_s8_6t = _cell({'bl' : 'bl0',
'br' : 'bl1',
'wl0': 'wl0',
'wl1': 'wl1'})
'wl': 'wl'})
cell_6t = _cell({'bl' : 'bl',
'br' : 'br',
@ -181,4 +180,4 @@ class cell_properties():
if ports == "{}R_{}W_{}RW".format(OPTS.num_r_ports, OPTS.num_w_ports, OPTS.num_rw_ports):
use_custom_arrangement = True
break
return use_custom_arrangement
return use_custom_arrangement

View File

@ -26,6 +26,12 @@ class design(hierarchy_design):
self.setup_layer_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):
"""
These are some layer constants used

View File

@ -1,13 +1,7 @@
import os, copy
import copy
from collections import defaultdict
import gdsMill
import tech
import math
import globals
import debug
from vector import vector
from pin_layout import pin_layout
class timing_graph():
"""
@ -33,16 +27,16 @@ class timing_graph():
"""Add node to graph with no edges"""
node = node.lower()
if not node in self.graph:
if node not in self.graph:
self.graph[node] = set()
def remove_edges(self, node):
"""Helper function to remove edges, useful for removing vdd/gnd"""
node = node.lower()
self.graph[node] = set()
self.graph[node] = set()
def get_all_paths(self, src_node, dest_node, remove_rail_nodes=True, reduce_paths=True):
def get_all_paths(self, src_node, dest_node, remove_rail_nodes=True, reduce_paths=True):
"""Traverse all paths from source to destination"""
src_node = src_node.lower()
@ -59,44 +53,44 @@ class timing_graph():
visited = set()
# Create an array to store paths
path = []
path = []
self.all_paths = []
# Call the recursive helper function to print all paths
self.get_all_paths_util(src_node, dest_node, visited, path)
self.get_all_paths_util(src_node, dest_node, visited, path)
debug.info(2, "Paths found={}".format(len(self.all_paths)))
if reduce_paths:
self.reduce_paths()
return self.all_paths
return self.all_paths
def reduce_paths(self):
""" Remove any path that is a subset of another path """
self.all_paths = [p1 for p1 in self.all_paths if not any(set(p1)<=set(p2) for p2 in self.all_paths if p1 is not p2)]
def get_all_paths_util(self, cur_node, dest_node, visited, path):
def get_all_paths_util(self, cur_node, dest_node, visited, path):
"""Recursive function to find all paths in a Depth First Search manner"""
# Mark the current node as visited and store in path
visited.add(cur_node)
path.append(cur_node)
path.append(cur_node)
# If current vertex is same as destination, then print
# current path[]
if cur_node == dest_node:
if cur_node == dest_node:
self.all_paths.append(copy.deepcopy(path))
else:
else:
# If current vertex is not destination
# Recur for all the vertices adjacent to this vertex
for node in self.graph[cur_node]:
if node not in visited:
self.get_all_paths_util(node, dest_node, visited, path)
for node in self.graph[cur_node]:
if node not in visited:
self.get_all_paths_util(node, dest_node, visited, path)
# Remove current vertex from path[] and mark it as unvisited
path.pop()
visited.remove(cur_node)
path.pop()
visited.remove(cur_node)
def get_timing(self, path, corner, slew, load):
"""Returns the analytical delays in the input path"""
@ -106,20 +100,20 @@ class timing_graph():
delays = []
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
cout = 0
for node in self.graph[path[i+1]]:
output_edge_mod = self.edge_mods[(path[i+1], node)]
for node in self.graph[path[i + 1]]:
output_edge_mod = self.edge_mods[(path[i + 1], node)]
cout+=output_edge_mod.get_cin()
# If at the last output, include the final output load
if i == len(path)-2:
cout+=load
if i == len(path) - 2:
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
return delays
@ -127,4 +121,15 @@ class timing_graph():
def __str__(self):
""" 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
import tech
class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
"""
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)
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
self.graph_inst_exclude = set()
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
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)
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
if len(port_nets) != len(self.pins):
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
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):
"""Converts connection names to their spice hierarchy equivalent"""
"""
Converts connection names to their spice hierarchy equivalent
"""
converted_conns = []
for conn in subinst_ports:
if conn in port_dict:
@ -244,8 +197,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
return converted_conns
def add_graph_edges(self, graph, port_nets):
"""For every input, adds an edge to every output.
Only intended to be used for gates and other simple modules."""
"""
For every input, adds an edge to every output.
Only intended to be used for gates and other simple modules.
"""
# The final pin names will depend on the spice hierarchy, so
# they are passed as an input.
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}

View File

@ -37,6 +37,7 @@ class layout():
self.height = None
self.bounding_box = None
self.insts = [] # Holds module/cell layout instances
self.inst_names = set() # Set of names to check for duplicates
self.objs = [] # Holds all other objects (labels, geometries, etc)
self.pin_map = {} # Holds name->pin_layout map for all pins
self.visited = [] # List of modules we have already visited
@ -63,6 +64,16 @@ class layout():
self.translate_all(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):
"""
Gets the base offset and y orientation of stacked rows of gates
@ -201,6 +212,12 @@ class layout():
def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
""" 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
debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.name, name))
self.inst_names.add(name)
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
debug.info(3, "adding instance {}".format(self.insts[-1]))
# This is commented out for runtime reasons
@ -1019,7 +1036,7 @@ class layout():
"""
import channel_route
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([])
def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None):
@ -1028,7 +1045,7 @@ class layout():
"""
import channel_route
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([])
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):
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.
#
import debug
import math
class verilog:
"""
@ -53,7 +54,7 @@ class verilog:
self.vf.write("\n );\n\n")
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 DATA_WIDTH = {0} ;\n".format(self.word_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))
if self.write_size:
remainder_bits = self.word_size % self.write_size
for mask in range(0,self.num_wmasks):
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(" mem[addr{0}_reg][{1}:{2}] = din{0}_reg[{1}:{2}];\n".format(port,upper,lower))
self.vf.write(" end\n")

File diff suppressed because it is too large Load Diff

View File

@ -8,13 +8,11 @@
import collections
import debug
import random
import math
from .stimuli import *
from .charutils import *
from globals import OPTS
from .simulation import simulation
# from .delay import delay
import graph_util
from sram_factory import factory
class functional(simulation):
@ -23,7 +21,7 @@ class functional(simulation):
for successful SRAM operation.
"""
def __init__(self, sram, spfile, corner):
def __init__(self, sram, spfile, corner, cycles=15):
super().__init__(sram, spfile, corner)
# Seed the characterizer with a constant seed for unit tests
@ -31,32 +29,37 @@ class functional(simulation):
random.seed(12345)
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:
self.num_wmasks = 0
if not self.num_spare_cols:
self.num_spare_cols = 0
self.probe_address, self.probe_data = '0' * self.addr_size, 0
self.set_corner(corner)
self.set_spice_constants()
self.set_stimulus_variables()
# For the debug signal names
self.wordline_row = 0
self.bitline_column = 0
self.create_signal_names()
self.add_graph_exclusions()
self.create_graph()
self.set_internal_spice_names()
self.q_name, self.qbar_name = self.get_bit_name()
debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name))
# Number of checks can be changed
self.num_cycles = 15
self.num_cycles = cycles
# This is to have ordered keys for random selection
self.stored_words = collections.OrderedDict()
self.read_check = []
self.read_results = []
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
# Generate a random sequence of reads and writes
self.create_random_memory_sequence()
@ -226,17 +229,25 @@ class functional(simulation):
sp_read_value = ""
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))
if value > self.v_high:
sp_read_value = "1" + sp_read_value
elif value < self.v_low:
sp_read_value = "0" + sp_read_value
else:
error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port,
bit,
value,
eo_period,
self.v_low,
self.v_high)
try:
value = float(value)
if value > self.v_high:
sp_read_value = "1" + sp_read_value
elif value < self.v_low:
sp_read_value = "0" + sp_read_value
else:
error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port,
bit,
value,
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)
self.read_results.append([sp_read_value, dout_port, eo_period, check])
@ -245,11 +256,12 @@ class functional(simulation):
def check_stim_results(self):
for i in range(len(self.read_check)):
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],
self.read_results[i][0],
self.read_check[i][0],
int((self.read_results[i][2]-self.period)/self.period),
self.read_results[i][2])
str = "FAILED: {0} value {1} does not match written value {2} read during cycle {3} at time {4}n"
error = str.format(self.read_results[i][1],
self.read_results[i][0],
self.read_check[i][0],
int((self.read_results[i][2] - self.period) / self.period),
self.read_results[i][2])
return(0, error)
return(1, "SUCCESS")
@ -311,7 +323,7 @@ class functional(simulation):
else:
expected_value = self.word_size + self.num_spare_cols
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))
return new_value
@ -344,8 +356,8 @@ class functional(simulation):
# Write important signals to stim file
self.sf.write("\n\n* Important signals for debug\n")
self.sf.write("* bl: {}\n".format(self.bl_name))
self.sf.write("* br: {}\n".format(self.br_name))
self.sf.write("* bl: {}\n".format(self.bl_name.format(port)))
self.sf.write("* br: {}\n".format(self.br_name.format(port)))
self.sf.write("* s_en: {}\n".format(self.sen_name))
self.sf.write("* q: {}\n".format(self.q_name))
self.sf.write("* qbar: {}\n".format(self.qbar_name))
@ -419,50 +431,8 @@ class functional(simulation):
self.stim.write_control(self.cycle_times[-1] + self.period)
self.sf.close()
# FIXME: refactor to share with delay.py
def add_graph_exclusions(self):
"""Exclude portions of SRAM from timing graph which are not relevant"""
# other initializations can only be done during analysis when a bit has been selected
# for testing.
self.sram.bank.graph_exclude_precharge()
self.sram.graph_exclude_addr_dff()
self.sram.graph_exclude_data_dff()
self.sram.graph_exclude_ctrl_dffs()
self.sram.bank.bitcell_array.graph_exclude_replica_col_bits()
# FIXME: refactor to share with delay.py
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))
#FIXME: Similar function to delay.py, refactor this
def get_bit_name(self):
""" Get a bit cell name """
(cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0)
@ -473,63 +443,5 @@ class functional(simulation):
qbar_name = cell_name + '.' + str(storage_names[1])
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)
# All rights reserved.
#
import sys,re,shutil
from design import design
import debug
import math
import tech
from .stimuli import *
from .trim_spice import *
from .charutils import *
import utils
from globals import OPTS
from sram_factory import factory
import graph_util
class simulation():
@ -38,11 +35,11 @@ class simulation():
self.write_ports = self.sram.write_ports
self.words_per_row = self.sram.words_per_row
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:
self.num_wmasks = 0
def set_corner(self,corner):
def set_corner(self, corner):
""" Set the corner values """
self.corner = corner
(self.process, self.vdd_voltage, self.temperature) = corner
@ -50,27 +47,25 @@ class simulation():
def set_spice_constants(self):
""" sets feasible timing parameters """
self.period = tech.spice["feasible_period"]
self.slew = tech.spice["rise_time"]*2
self.load = tech.spice["dff_in_cap"]*4
self.slew = tech.spice["rise_time"] * 2
self.load = tech.spice["dff_in_cap"] * 4
self.v_high = self.vdd_voltage - tech.spice["nom_threshold"]
self.v_low = tech.spice["nom_threshold"]
self.v_low = tech.spice["nom_threshold"]
self.gnd_voltage = 0
def create_signal_names(self):
self.addr_name = "a"
self.din_name = "din"
self.dout_name = "dout"
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),
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),
abits=self.addr_size,
dbits=self.word_size + self.num_spare_cols)
debug.check(len(self.sram.pins) == len(self.pins),
"Number of pins generated for characterization \
do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins,
self.pins))
#This is TODO once multiport control has been finalized.
#self.control_name = "CSB"
self.pins))
def set_stimulus_variables(self):
# Clock signals
@ -78,20 +73,20 @@ class simulation():
self.t_current = 0
# control signals: only one cs_b for entire multiported sram, one we_b for each write port
self.csb_values = {port:[] for port in self.all_ports}
self.web_values = {port:[] for port in self.readwrite_ports}
self.csb_values = {port: [] for port in self.all_ports}
self.web_values = {port: [] for port in self.readwrite_ports}
# Raw values added as a bit vector
self.addr_value = {port:[] for port in self.all_ports}
self.data_value = {port:[] for port in self.write_ports}
self.wmask_value = {port:[] for port in self.write_ports}
self.spare_wen_value = {port:[] for port in self.write_ports}
self.addr_value = {port: [] for port in self.all_ports}
self.data_value = {port: [] for port in self.write_ports}
self.wmask_value = {port: [] for port in self.write_ports}
self.spare_wen_value = {port: [] for port in self.write_ports}
# Three dimensional list to handle each addr and data bits for each port over the number of checks
self.addr_values = {port:[[] for bit in range(self.addr_size)] for port in self.all_ports}
self.data_values = {port:[[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports}
self.wmask_values = {port:[[] for bit in range(self.num_wmasks)] for port in self.write_ports}
self.spare_wen_values = {port:[[] for bit in range(self.num_spare_cols)] for port in self.write_ports}
self.addr_values = {port: [[] for bit in range(self.addr_size)] for port in self.all_ports}
self.data_values = {port: [[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports}
self.wmask_values = {port: [[] for bit in range(self.num_wmasks)] for port in self.write_ports}
self.spare_wen_values = {port: [[] for bit in range(self.num_spare_cols)] for port in self.write_ports}
# For generating comments in SPICE stimulus
self.cycle_comments = []
@ -99,7 +94,7 @@ class simulation():
def add_control_one_port(self, port, op):
"""Appends control signals for operation to a given port"""
#Determine values to write to port
# Determine values to write to port
web_val = 1
csb_val = 1
if op == "read":
@ -108,7 +103,7 @@ class simulation():
csb_val = 0
web_val = 0
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
self.csb_values[port].append(csb_val)
@ -128,7 +123,7 @@ class simulation():
elif c=="1":
self.data_values[port][bit].append(1)
else:
debug.error("Non-binary data string",1)
debug.error("Non-binary data string", 1)
bit -= 1
def add_address(self, address, port):
@ -141,12 +136,11 @@ class simulation():
if c=="0":
self.addr_values[port][bit].append(0)
elif c=="1":
self.addr_values[port][bit].append(1)
self.addr_values[port][bit].append(1)
else:
debug.error("Non-binary address string",1)
debug.error("Non-binary address string", 1)
bit -= 1
def add_wmask(self, wmask, port):
""" Add the array of address values """
debug.check(len(wmask) == self.num_wmasks, "Invalid wmask size.")
@ -190,9 +184,9 @@ class simulation():
self.t_current += self.period
self.add_control_one_port(port, "write")
self.add_data(data,port)
self.add_address(address,port)
self.add_wmask(wmask,port)
self.add_data(data, port)
self.add_address(address, port)
self.add_wmask(wmask, port)
self.add_spare_wen("1" * self.num_spare_cols, port)
#Add noops to all other ports.
@ -220,11 +214,11 @@ class simulation():
try:
self.add_data(self.data_value[port][-1], port)
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:
self.add_wmask(self.wmask_value[port][-1], port)
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)
#Add noops to all other ports.
@ -275,12 +269,12 @@ class simulation():
try:
self.add_data(self.data_value[port][-1], port)
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:
self.add_wmask(self.wmask_value[port][-1], port)
except:
self.add_wmask("0"*self.num_wmasks, port)
self.add_spare_wen("0" * self.num_spare_cols, port)
self.add_wmask("0" * self.num_wmasks, port)
self.add_spare_wen("0" * self.num_spare_cols, port)
def add_noop_one_port(self, port):
""" Add the control values for a noop to a single port. Does not increment the period. """
@ -289,7 +283,7 @@ class simulation():
try:
self.add_address(self.addr_value[port][-1], port)
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
# the same value as previous cycle
@ -297,11 +291,11 @@ class simulation():
try:
self.add_data(self.data_value[port][-1], port)
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:
self.add_wmask(self.wmask_value[port][-1], port)
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)
def add_noop_clock_one_port(self, port):
@ -320,23 +314,23 @@ class simulation():
if unselected_port != port:
self.add_noop_one_port(unselected_port)
def append_cycle_comment(self, port, comment):
"""Add comment to list to be printed in stimulus file"""
#Clean up time before appending. Make spacing dynamic as well.
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),
port,
time,
time_spacing,
comment))
comment))
def gen_cycle_comment(self, op, word, addr, wmask, port, t_current):
if op == "noop":
comment = "\tIdle during cycle {0} ({1}ns - {2}ns)".format(int(t_current/self.period),
t_current,
t_current+self.period)
str = "\tIdle during cycle {0} ({1}ns - {2}ns)"
comment = str.format(int(t_current / self.period),
t_current,
t_current + self.period)
elif op == "write":
comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
addr,
@ -345,40 +339,41 @@ class simulation():
t_current,
t_current+self.period)
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,
addr,
wmask,
port,
int(t_current / self.period),
t_current,
t_current + self.period)
str = "\tWriting (partial) {0} to address {1} with mask bit {2} (from port {3}) during cycle {4} ({5}ns - {6}ns)"
comment = str.format(word,
addr,
wmask,
port,
int(t_current / self.period),
t_current,
t_current + self.period)
else:
comment = "\tReading {0} from address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
addr,
port,
int(t_current/self.period),
t_current,
t_current+self.period)
str = "\tReading {0} from address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)"
comment = str.format(word,
addr,
port,
int(t_current / self.period),
t_current,
t_current + self.period)
return comment
def gen_pin_names(self, port_signal_names, port_info, abits, dbits):
"""Creates the pins names of the SRAM based on the no. of ports."""
#This may seem redundant as the pin names are already defined in the sram. However, it is difficult
#to extract the functionality from the names, so they are recreated. As the order is static, changing
#the order of the pin names will cause issues here.
# This may seem redundant as the pin names are already defined in the sram. However, it is difficult
# to extract the functionality from the names, so they are recreated. As the order is static, changing
# the order of the pin names will cause issues here.
pin_names = []
(addr_name, din_name, dout_name) = port_signal_names
(total_ports, write_index, read_index) = port_info
for write_input in write_index:
for i in range(dbits):
pin_names.append("{0}{1}_{2}".format(din_name,write_input, i))
pin_names.append("{0}{1}_{2}".format(din_name, write_input, i))
for port in range(total_ports):
for i in range(abits):
pin_names.append("{0}{1}_{2}".format(addr_name,port,i))
pin_names.append("{0}{1}_{2}".format(addr_name, port, i))
#Control signals not finalized.
for port in range(total_ports):
@ -393,18 +388,159 @@ class simulation():
if self.write_size:
for port in write_index:
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:
for port in write_index:
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 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("gnd"))
return pin_names
def add_graph_exclusions(self):
"""
Exclude portions of SRAM from timing graph which are not relevant
"""
# other initializations can only be done during analysis when a bit has been selected
# for testing.
self.sram.bank.graph_exclude_precharge()
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 subprocess
import os
import sys
import numpy as np
from globals import OPTS
@ -40,32 +39,26 @@ class stimuli():
debug.info(2, "Not using spice library")
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):
""" 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))
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_sram_pex(self, pins, model_name):
def inst_pex_model(self, pins, model_name):
self.sf.write("X{0} ".format(model_name))
for pin in pins:
self.sf.write("{0} ".format(pin))
for bank in range(OPTS.num_banks):
row = int(OPTS.num_words / OPTS.words_per_row) - 1
col = int(OPTS.word_size * OPTS.words_per_row) - 1
self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col))
self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col))
col = int(OPTS.word_size * OPTS.words_per_row) - 1
self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank, row, col))
self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank, row, col))
# can't add all bitcells to top level due to ngspice max port count of 1005
# for row in range(int(OPTS.num_words / OPTS.words_per_row)):
# for col in range(int(OPTS.word_size * OPTS.words_per_row)):
@ -76,7 +69,6 @@ class stimuli():
for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports):
self.sf.write("bl{0}_{1} ".format(port, col))
self.sf.write("br{0}_{1} ".format(port, col))
self.sf.write("s_en{0} ".format(bank))
self.sf.write("{0}\n".format(model_name))
@ -94,14 +86,13 @@ class stimuli():
self.tx_length))
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
purposes). Size is pair for PMOS, NMOS width multiple.
"""
self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name,
self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name,
self.gnd_name,
buffer_name))
self.sf.write("mpinv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(self.vdd_name,
@ -122,8 +113,6 @@ class stimuli():
self.tx_length))
self.sf.write(".ENDS test_{0}\n\n".format(buffer_name))
def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
"""
Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
@ -140,7 +129,6 @@ class stimuli():
0.5*period-0.5*t_rise-0.5*t_fall,
period))
def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup):
"""
Generate a PWL stimulus given a signal name and data values at each period.
@ -149,18 +137,22 @@ class stimuli():
to the initial value.
"""
# 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
times = np.array(clk_times) - setup*period
times = np.array(clk_times) - setup * period
values = np.array(data_values) * self.voltage
half_slew = 0.5 * slew
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]))
for i in range(1,len(times)):
self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew,
values[i-1],
times[i]+half_slew,
for i in range(1, len(times)):
self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i] - half_slew,
values[i - 1],
times[i] + half_slew,
values[i]))
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))
def get_inverse_voltage(self, value):
if value > 0.5*self.voltage:
if value > 0.5 * self.voltage:
return 0
elif value <= 0.5*self.voltage:
elif value <= 0.5 * self.voltage:
return self.voltage
else:
debug.error("Invalid value to get an inverse of: {0}".format(value))
@ -184,7 +176,6 @@ class stimuli():
else:
debug.error("Invalid value to get an inverse of: {0}".format(value))
def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
""" Creates the .meas statement for the measurement of delay """
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
@ -230,7 +221,7 @@ class stimuli():
def gen_meas_value(self, meas_name, dout, t_intital, t_final):
measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_intital, t_final)
self.sf.write(measure_string)
def write_control(self, end_time, runlvl=4):
""" Write the control cards to run and end the simulation """
@ -243,10 +234,10 @@ class stimuli():
reltol = 0.005 # 0.5%
else:
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
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))
if OPTS.spice_name == "ngspice":
# ngspice sometimes has convergence problems if not using gear method
@ -260,7 +251,7 @@ class stimuli():
# create plots for all signals
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
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")
else:
self.sf.write(".plot V(*)\n")
@ -271,7 +262,6 @@ class stimuli():
# end the stimulus file
self.sf.write(".end\n\n")
def write_include(self, circuit):
"""Writes include statements, inputs are lists of model files"""
@ -291,13 +281,12 @@ class stimuli():
else:
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item))
def write_supply(self):
""" Writes supply voltage statements """
gnd_node_name = "0"
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("*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)
import datetime
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":
# 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_powernet_level 7 -node vdd\n")
xa_cfg.close()
cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt 2".format(OPTS.spice_exe,
temp_stim,
OPTS.openram_temp)
cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt {3}".format(OPTS.spice_exe,
temp_stim,
OPTS.openram_temp,
OPTS.num_threads)
valid_retcode=0
elif OPTS.spice_name == "hspice":
# TODO: Should make multithreading parameter a configuration option
cmd = "{0} -mt 2 -i {1} -o {2}timing".format(OPTS.spice_exe,
temp_stim,
OPTS.openram_temp)
cmd = "{0} -mt {1} -i {2} -o {3}timing".format(OPTS.spice_exe,
OPTS.num_threads,
temp_stim,
OPTS.openram_temp)
valid_retcode=0
else:
# 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
# -r {2}timing.raw
ng_cfg = open("{}.spiceinit".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,
temp_stim,
OPTS.openram_temp)
# for some reason, ngspice-25 returns 1 when it only has acceptable warnings
valid_retcode=1
spice_stdout = open("{0}spice_stdout.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)
else:
end_time = datetime.datetime.now()
delta_time = round((end_time-start_time).total_seconds(),1)
debug.info(2,"*** Spice: {} seconds".format(delta_time))
delta_time = round((end_time - start_time).total_seconds(), 1)
debug.info(2, "*** Spice: {} seconds".format(delta_time))

View File

@ -122,7 +122,7 @@ class and4_dec(design.design):
width=pin.width(),
height=pin.height())
for pin_name in ["A", "B", "C"]:
for pin_name in ["A", "B", "C", "D"]:
pin = self.nand_inst.get_pin(pin_name)
self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer,

View File

@ -55,12 +55,6 @@ class dff(design.design):
transition_prob = 0.5
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):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)

View File

@ -27,11 +27,11 @@ class s8_bitcell(bitcell_base.bitcell_base):
pin_names = ["bl0", "bl1", "wl0", "wl1", "vpwr", "vgnd"]
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]
pin_names = [props.bitcell.cell_s8_6t.pin.bl,
props.bitcell.cell_s8_6t.pin.br,
props.bitcell.cell_s8_6t.pin.wl,
"vpwr",
"vgnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
@ -61,15 +61,12 @@ class s8_bitcell(bitcell_base.bitcell_base):
layer["mem"],
"s8sram_cell\x00")
#debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells")
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
if props.compare_ports(props.bitcell.split_wl):
row_pins = ["wl0", "wl1"]
else:
row_pins = [props.bitcell.s8_sp.pin.wl]
row_pins = [props.bitcell.cell_s8_6t.pin.wl]
return row_pins
def get_all_bitline_names(self):

View File

@ -0,0 +1,137 @@
# 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.all_wordline_names = []
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

@ -22,7 +22,7 @@ class s8_col_end(design.design):
if version == "colend":
self.name = "s8sram16x16_colend"
structure = "s8sram16x16_colend\x00"
structure = "s8sram16x16_colend"
elif version == "colend_p_cent":
self.name = "s8sram16x16_colend_p_cent"
structure = "s8sram16x16_colend_p_cent\x00"

View File

@ -27,7 +27,7 @@ class s8_corner(design.design):
elif location == "ll":
self.name = "s8sram16x16_cornera"
elif location == "lr":
self.name = "s8sram16x16_cornerb"
self.name = "s8sram16x16_cornera"
else:
debug.error("Invalid s8_corner location", -1)
design.design.__init__(self, name=self.name)

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_s8_6t.pin.bl,
props.bitcell.cell_s8_6t.pin.br,
props.bitcell.cell_s8_6t.pin.wl,
"vpwr",
"vgnd"]
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":
self.name = "s8sram16x16_wlstrap_p"
self.structure = "s8sram16x16_wlstrap_p_ce"
elif version == "wlstrapa":
self.name = "s8sram_wlstrapa"
self.structure = "s8sram_wlstrapa_ce"
else:
debug.error("Invalid version", -1)
design.design.__init__(self, name=self.name)

View File

@ -33,14 +33,26 @@ class replica_bitcell(design.design):
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"])
(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"])
else:
(width,height) = (0,0)
pin_map = []
def __init__(self, name=""):
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"
self.pin_map = utils.get_libcell_pins(self.pin_names, self.name, GDS["unit"])
design.design.__init__(self, "replica_cell_6t")
debug.info(2, "Create replica bitcell object")

View File

@ -0,0 +1,162 @@
# 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(["wl_0_{}".format(row-1), "vpwr"])
alternate_bitcell = 1
else:
row_layout.append(self.rowend2)
self.cell_inst[row]=self.add_inst(name=name,mod=self.rowend2)
self.connect_inst(["wl_0_{}".format(row-1), "vpwr"])
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([])
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.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 - 2):
for port in self.all_ports:
self.add_pin("wl_{}_{}".format(port, row), "OUTPUT")
self.add_pin("vpwr", "POWER")
self.add_pin("vgnd", "GROUND")
def add_layout_pins(self):
""" Add the layout pins """
if self.column_offset == 0:
row_list = self.cell.get_all_wl_names()
for row in range(1, self.rows-1):
if row > 0 and row < self.rows:
for cell_row in row_list:
wl_pin = self.cell_inst[row].get_pin(cell_row)
self.add_layout_pin(text=cell_row + "_0_{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.rows):
inst = self.cell_inst[row]
for pin_name in ["vpwr", "vgnd"]:
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,38 @@
# 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 = ["wl", "vpwr"]
type_list = ["OUTPUT", "POWER"]
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")
self.pin_map = utils.get_libcell_pins(pin_names, self.name, GDS["unit"])
self.add_pin("wl", "OUTPUT")
self.add_pin("vpwr", "POWER")

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 2048
write_size = 8
local_array_size = 32
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "freepdk45"
nominal_corners_only = True
route_supplies = False
check_lvsdrc = False
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 4096
write_size = 8
local_array_size = 32
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
route_supplies = False
check_lvsdrc = False
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -0,0 +1,24 @@
word_size = 32
num_words = 256
write_size = 8
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
route_supplies = True
check_lvsdrc = True
perimeter_pins = True
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -0,0 +1,24 @@
word_size = 32
num_words = 512
write_size = 8
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
route_supplies = True
check_lvsdrc = True
perimeter_pins = True
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 8192
write_size = 8
local_array_size = 32
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
route_supplies = False
check_lvsdrc = False
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -0,0 +1,24 @@
word_size = 32
num_words = 1024
write_size = 8
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
route_supplies = True
check_lvsdrc = True
perimeter_pins = True
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 2048
write_size = 8
local_array_size = 32
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
route_supplies = False
check_lvsdrc = False
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 256
write_size = 8
local_array_size = 16
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "sky130"
nominal_corners_only = True
route_supplies = True
check_lvsdrc = True
perimeter_pins = True
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 512
write_size = 8
local_array_size = 16
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "sky130"
nominal_corners_only = True
route_supplies = True
check_lvsdrc = True
perimeter_pins = True
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 1024
write_size = 8
local_array_size = 16
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "sky130"
nominal_corners_only = True
route_supplies = True
check_lvsdrc = True
perimeter_pins = True
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -55,6 +55,11 @@ def parse_args():
action="store_false",
help="Disable all LVS/DRC checks",
dest="check_lvsdrc"),
optparse.make_option("-j", "--threads",
action="store",
type="int",
help="Specify the number of threads (default: 2)",
dest="num_threads"),
optparse.make_option("-v",
"--verbose",
action="count",

View File

@ -8,7 +8,7 @@
import debug
import design
from sram_factory import factory
from math import log, ceil
from math import log, ceil, floor
from tech import drc, layer
from vector import vector
from globals import OPTS
@ -27,7 +27,7 @@ class bank(design.design):
self.sram_config = sram_config
sram_config.set_local_config(self)
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:
self.num_wmasks = 0
@ -186,16 +186,26 @@ class bank(design.design):
self.bitcell_array_right = self.bitcell_array.width
# These are the offsets of the main array (excluding dummy and replica rows/cols)
self.main_bitcell_array_top = self.bitcell_array.bitcell_array_inst.uy()
self.main_bitcell_array_top = self.bitcell_array.get_main_array_top()
# 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
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()
if len(self.all_ports)==2:
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):
"""
Compute the instance offsets for port0 on the left/bottom of the bank.
@ -209,14 +219,14 @@ class bank(design.design):
# LOWER RIGHT QUADRANT
# 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
# 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.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
# 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
# by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs
# 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:
x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = 1.25 * self.dff.height + self.column_decoder.height
@ -253,11 +263,11 @@ class bank(design.design):
# UPPER LEFT QUADRANT
# 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
# 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.main_bitcell_array_bottom)
@ -268,7 +278,7 @@ class bank(design.design):
# 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
# 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:
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
@ -355,56 +365,66 @@ class bank(design.design):
def add_modules(self):
""" 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.bit_offsets = self.get_column_offsets()
for port in self.all_ports:
temp_pre = factory.create(module_type="port_data",
sram_config=self.sram_config,
port=port)
port=port,
bit_offsets=self.bit_offsets)
self.port_data.append(temp_pre)
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):
self.bank_select = factory.create(module_type="bank_select")
self.add_mod(self.bank_select)
def create_bitcell_array(self):
""" Creating Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array",
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
mod=self.bitcell_array)
# Arrays are always:
# word lines (bottom to top)
# bit lines (left to right)
# vdd
# gnd
temp = self.bitcell_array.get_inouts()
temp = self.bitcell_array.get_all_bitline_names()
wordline_names = self.bitcell_array.get_all_wordline_names()
# 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("rbl_wl0")
temp.extend(self.bitcell_array.get_wordline_names())
if len(self.all_ports) > 1:
temp.append("rbl_wl1")
temp.append("vdd")
temp.append("gnd")
@ -464,13 +484,15 @@ class bank(design.design):
self.port_address_inst = [None] * len(self.all_ports)
for port in self.all_ports:
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
mod=self.port_address)
mod=self.port_address[port])
temp = []
for bit in range(self.row_addr_size):
temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size))
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"])
self.connect_inst(temp)
@ -511,6 +533,9 @@ class bank(design.design):
elif self.col_addr_size == 3:
self.column_decoder = factory.create(module_type="hierarchical_predecode3x8",
height=self.dff.height)
elif self.col_addr_size == 4:
self.column_decoder = factory.create(module_type="hierarchical_predecode4x16",
height=self.dff.height)
else:
# No error checking before?
debug.error("Invalid column decoder?", -1)
@ -658,7 +683,7 @@ class bank(design.design):
# 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)
# 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",
offset=control_bus_offset,
names=self.control_signals[0],
@ -670,7 +695,7 @@ class bank(design.design):
# Port 1
if len(self.all_ports)==2:
# 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,
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
@ -690,7 +715,6 @@ class bank(design.design):
inst1 = self.bitcell_array_inst
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]
inst2_bl_name = []
inst2_br_name = []
for col in range(self.num_cols):
@ -709,8 +733,7 @@ class bank(design.design):
# 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_names, ["rbl_bl", "rbl_br"]):
for (array_name, data_name) in zip(["rbl_bl_{0}_{0}".format(port), "rbl_br_{0}_{0}".format(port)], ["rbl_bl", "rbl_br"]):
self.connect_bitline(inst1, inst2, array_name, data_name)
def route_port_data_out(self, port):
@ -825,33 +848,51 @@ class bank(design.design):
self.route_port_address_in(port)
if port % 2:
self.route_port_address_right(port)
self.route_port_address_out(port, "right")
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 """
driver_names = ["wl_{}".format(x) for x in range(self.num_rows)]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port)):
driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] + ["rbl_wl"]
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.
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_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)
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1)
self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
self.add_via_stack_center(from_layer=driver_wl_pin.layer,
to_layer=bitcell_wl_pin.layer,
offset=bitcell_wl_pos,
directions=("H", "H"))
if side == "left":
bitcell_wl_pos = bitcell_wl_pin.lc()
port_address_pos = self.port_address_inst[port].rx()
bitcell_array_pos = self.bitcell_array_inst.lx()
else:
bitcell_wl_pos = bitcell_wl_pin.rc()
port_address_pos = self.port_address_inst[port].lx()
bitcell_array_pos = self.bitcell_array_inst.rx()
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0)
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
if driver_wl_pin.layer != bitcell_wl_pin.layer:
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):
""" Connecting Wordline driver output to Bitcell WL connection """
driver_names = ["wl_{}".format(x) for x in range(self.num_rows)]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port)):
driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] + ["rbl_wl"]
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.
driver_wl_pin = self.port_address_inst[port].get_pin(driver_name)
driver_wl_pos = driver_wl_pin.lc()
@ -859,11 +900,12 @@ class bank(design.design):
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)
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,
to_layer=bitcell_wl_pin.layer,
offset=bitcell_wl_pos,
directions=("H", "H"))
offset=mid2)
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
def route_column_address_lines(self, port):
""" Connecting the select lines of column mux to the address bus """
@ -901,7 +943,7 @@ class bank(design.design):
offset = self.column_decoder_inst[port].lr() + vector(pitch, 0)
decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names]
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names]
@ -961,16 +1003,15 @@ class bank(design.design):
def route_unused_wordlines(self):
""" Connect the unused RBL and dummy wordlines to gnd """
gnd_wl_names = []
return
# Connect unused RBL WL to gnd
# All RBL WL 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
rbl_wl_names = set()
for port in self.all_ports:
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:
pin = self.bitcell_array_inst.get_pin(wl_name)
@ -1000,10 +1041,6 @@ class bank(design.design):
connection.append((self.prefix + "p_en_bar{}".format(port),
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:
connection.append((self.prefix + "w_en{}".format(port),
self.port_data_inst[port].get_pin("w_en")))
@ -1024,66 +1061,48 @@ class bank(design.design):
self.add_via_stack_center(from_layer=pin.layer,
to_layer="m2",
offset=control_pos)
# clk to wordline_driver
control_signal = self.prefix + "wl_en{}".format(port)
if port % 2:
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:
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_pos = vector(control_x_offset, mid_pos.y)
self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos])
self.add_via_center(layers=self.m1_stack,
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):
"""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:
if self.port_data[port]:
self.port_data[port].graph_exclude_precharge()
def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell."""
"""
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,
row,
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)
# All rights reserved.
#
import debug
from bitcell_base_array import bitcell_base_array
from s8_corner import s8_corner
from tech import drc, spice
@ -20,11 +21,17 @@ class bitcell_array(bitcell_base_array):
"""
def __init__(self, rows, cols, column_offset=0, 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()
if not OPTS.netlist_only:
self.create_layout()
# We don't offset this because we need to align
# the replica bitcell in the control logic
# self.offset_all_coordinates()
@ -58,9 +65,6 @@ class bitcell_array(bitcell_base_array):
self.add_mod(factory.create(module_type="s8_internal", version = "wlstrap"))
self.add_mod(factory.create(module_type="s8_internal", version = "wlstrap_p"))
def create_instances(self):
""" Create the module instances used in this design """
@ -115,15 +119,10 @@ class bitcell_array(bitcell_base_array):
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c
return bl_wire
def get_wordline_cin(self):
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
# A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
bitcell_wl_cin = self.cell.get_wl_cin()
total_cin = bitcell_wl_cin * self.column_size
return total_cin
def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target"""
def graph_exclude_bits(self, targ_row=None, targ_col=None):
"""
Excludes bits in column from being added to graph except target
"""
# Function is not robust with column mux configurations
for row in range(self.row_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):
super().__init__(name)
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.row_size = rows
@ -34,14 +33,19 @@ class bitcell_base_array(design.design):
self.strap = factory.create(module_type="s8_internal", version="wlstrap")
self.strap2 = factory.create(module_type="s8_internal", version="wlstrap_p")
self.create_all_bitline_names()
self.create_all_wordline_names()
self.wordline_names = [[] for port in self.all_ports]
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=""):
return [prefix + x for x in self.all_bitline_names]
def create_all_bitline_names(self):
self.bitline_names = [[] for port in self.all_ports]
for col in range(self.column_size):
for port in self.all_ports:
self.bitline_names[port].extend(["bl_{0}_{1}".format(port, col),
@ -49,47 +53,32 @@ class bitcell_base_array(design.design):
# Make a flat list too
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
def get_all_wordline_names(self, prefix=""):
return [prefix + x for x in self.all_wordline_names]
# def get_all_wordline_names(self, prefix=""):
# return [prefix + x for x in self.all_wordline_names]
def create_all_wordline_names(self):
self.wordline_names = [[] for port in self.all_ports]
for row in range(self.row_size):
def create_all_wordline_names(self, remove_wordline = 0):
for row in range(self.row_size - remove_wordline):
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]
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):
for bl_name in self.get_bitline_names():
self.add_pin(bl_name, "INOUT")
for wl_name in self.get_wordline_names():
self.add_pin(wl_name, "INPUT")
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
for bl_name in self.get_bitline_names():
self.add_pin(bl_name, "INOUT")
for wl_name in self.get_wordline_names():
self.add_pin(wl_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
else:
for bl_name in self.get_bitline_names():
self.add_pin(bl_name, "INOUT")
for wl_name in self.get_wordline_names():
self.add_pin(wl_name, "INPUT")
self.add_pin("vpwr", "POWER")
self.add_pin("vgnd", "GROUND")
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 """
@ -102,34 +91,89 @@ class bitcell_base_array(design.design):
return bitcell_pins
def get_rbl_wordline_names(self, port=None):
"""
Return the WL for the given RBL port.
"""
if port == None:
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):
""" Add the layout pins """
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
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_layout_pin(text="bl_{0}_{1}".format(port, col),
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_layout_pin(text="br_{0}_{1}".format(port, col),
layer=br_pin.layer,
offset=br_pin.ll().scale(1, 0),
width=br_pin.width(),
height=self.height)
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_layout_pin(text="bl_{0}_{1}".format(port, col),
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_layout_pin(text="br_{0}_{1}".format(port, col),
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()
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())
wl_names = self.cell.get_all_wl_names()
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())
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
# Copy a vdd/gnd layout pin from every cell
for row in range(self.row_size):
@ -138,37 +182,8 @@ class bitcell_base_array(design.design):
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
else:
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_layout_pin(text="bl0_{0}_{1}".format(port, col),
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_layout_pin(text="bl1_{0}_{1}".format(port, col),
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()
for row in range(self.row_size):
for port in self.all_ports:
wl0_pin = self.cell_inst[row, 0].get_pin(wl_names[port])
self.add_layout_pin(text="wl0_{0}_{1}".format(port, row),
layer=wl0_pin.layer,
offset=wl0_pin.ll().scale(0, 1),
width=self.width,
height=wl0_pin.height())
wl1_pin = self.cell_inst[row, 0].get_pin(wl_names[port])
self.add_layout_pin(text="wl1_{0}_{1}".format(port, row),
layer=wl1_pin.layer,
offset=wl1_pin.ll().scale(0, 1),
width=self.width,
height=wl1_pin.height())
# Copy a vdd/gnd layout pin from every cell
for row in range(self.row_size):
for col in range(self.column_size):
@ -224,3 +239,10 @@ class bitcell_base_array(design.design):
else:
from tech import custom_cell_placement
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

@ -11,22 +11,23 @@ from tech import layer, preferred_directions
from vector import vector
from sram_factory import factory
from globals import OPTS
import logical_effort
from tech import cell_properties
class single_level_column_mux_array(design.design):
class column_mux_array(design.design):
"""
Dynamically generated column mux array.
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)
debug.info(1, "Creating {0}".format(self.name))
self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br))
self.columns = columns
self.word_size = word_size
self.offsets = offsets
self.words_per_row = int(self.columns / self.word_size)
self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br
@ -88,7 +89,7 @@ class single_level_column_mux_array(design.design):
self.add_pin("gnd")
def add_modules(self):
self.mux = factory.create(module_type="single_level_column_mux",
self.mux = factory.create(module_type="column_mux",
bitcell_bl=self.bitcell_bl,
bitcell_br=self.bitcell_br)
self.add_mod(self.mux)
@ -116,10 +117,12 @@ class single_level_column_mux_array(design.design):
"gnd"])
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 col_num in range(self.columns):
xoffset = col_num * self.mux.width
for col_num, xoffset in enumerate(self.offsets[0:self.columns]):
if cell_properties.bitcell.mirror.y and (col_num + self.column_offset) % 2:
mirror = "MY"
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),
layer=self.sel_layer,
offset=offset,
width=self.mux.width * self.columns)
width=self.mux_inst[-1].rx())
def add_vertical_poly_rail(self):
""" Connect the poly to the address rails """
@ -230,10 +233,3 @@ class single_level_column_mux_array(design.design):
to_layer=self.sel_layer,
offset=br_out_offset_begin,
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

@ -156,96 +156,12 @@ class control_logic(design.design):
self.nand2 = factory.create(module_type="pnand2",
height=dff_height)
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,
"Must use odd number of delay chain stages for inverting delay chain.")
self.delay_chain=factory.create(module_type="delay_chain",
fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ])
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):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
@ -333,17 +249,6 @@ class control_logic(design.design):
delay_per_stage = fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
return delay_stages
def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall):
"""
Produces a list of fanouts which determine the size of the delay chain.
List length is the number of stages.
Assumes the first stage is falling.
"""
stage_list = []
for i in range(total_stages):
if i % 2 == 0:
stage_list.append()
def setup_signal_busses(self):
""" Setup bus names, determine the size of the busses etc """
@ -869,137 +774,6 @@ class control_logic(design.design):
offset=pin.ll(),
height=pin.height(),
width=pin.width())
def get_delays_to_wl(self):
"""Get the delay (in delay units) of the clk to a wordline in the bitcell array"""
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
self.wl_stage_efforts = self.get_wordline_stage_efforts()
clk_to_wl_rise, clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts)
total_delay = clk_to_wl_rise + clk_to_wl_fall
debug.info(1,
"Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise,
clk_to_wl_fall,
total_delay))
return clk_to_wl_rise, clk_to_wl_fall
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):
"""Exclude dffs from graph as they do not represent critical path"""

View File

@ -210,25 +210,3 @@ class delay_chain(design.design):
layer="m2",
start=mid_point,
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,
to_layer="m3",
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,
to_layer="m2",
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
self.add_via_center(layers=self.m2_stack,
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())
self.add_via_center(layers=self.m1_stack,
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

@ -188,10 +188,4 @@ class dff_inv_array(design.design):
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=self.m2_stack,
offset=vector(clk_pin.cx(),clk_ypos))
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
offset=vector(clk_pin.cx(),clk_ypos))

View File

@ -5,6 +5,8 @@
#
from bitcell_base_array import bitcell_base_array
from sram_factory import factory
from tech import GDS,layer,drc,parameter,cell_properties
from tech import cell_properties as props
from globals import OPTS
@ -16,6 +18,10 @@ class dummy_array(bitcell_base_array):
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
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()
if not OPTS.netlist_only:
self.create_layout()
@ -38,28 +44,96 @@ class dummy_array(bitcell_base_array):
def add_modules(self):
""" Add the modules used in this design """
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
if not props.compare_ports(props.bitcell_array.use_custom_cell_arrangement):
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
self.cell = factory.create(module_type="bitcell")
else:
self.dummy_cell = factory.create(module_type="s8_bitcell", version = "opt1")
self.dummy_cell2 = factory.create(module_type="s8_bitcell", version = "opt1a")
self.add_mod(factory.create(module_type="s8_internal", version = "wlstrap"))
self.add_mod(factory.create(module_type="s8_internal", version = "wlstrap_p"))
self.cell = factory.create(module_type="s8_bitcell", version = "opt1")
self.add_mod(self.dummy_cell2)
self.add_mod(self.dummy_cell)
self.cell = factory.create(module_type="bitcell")
def create_instances(self):
""" Create the module instances used in this design """
self.cell_inst = {}
if not props.compare_ports(props.bitcell_array.use_custom_cell_arrangement):
for col in range(self.column_size):
for row in range(self.row_size):
name = "bit_r{0}_c{1}".format(row, col)
self.cell_inst[row, col]=self.add_inst(name=name,
mod=self.dummy_cell)
self.connect_inst(self.get_bitcell_pins(row, col))
else:
from tech import custom_cell_arrangement
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.split_wl):
for row in range(self.row_size):
name = "bit_r{0}_c{1}".format(row, col)
self.cell_inst[row, col]=self.add_inst(name=name,
mod=self.dummy_cell)
self.connect_inst(self.get_bitcell_pins(row, col))
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):
# FIXME: This appears to be old code from previous characterization. Needs to be updated.
wl_wire = self.gen_wl_wire()
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)
# All rights reserved.
#
import design
import bitcell_base_array
from globals import OPTS
from sram_factory import factory
from vector import vector
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.
Rows is an integer number for all local arrays.
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
super().__init__(name=name)
self.cols = cols
self.rows = rows
self.all_ports = ports
super().__init__(name=name, rows=rows, cols=sum(cols), column_offset=0)
self.column_sizes = cols
self.col_offsets = [0] + list(cumsum(cols)[:-1])
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.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()
if not OPTS.netlist_only:
@ -48,6 +42,8 @@ class global_bitcell_array(design.design):
self.place()
self.route()
self.add_layout_pins()
self.add_boundary()
@ -58,21 +54,46 @@ class global_bitcell_array(design.design):
""" Add the modules used in this design """
self.local_mods = []
for i, cols in enumerate(self.cols):
# Always add the left RBLs to the first subarray and the right RBLs to the last subarray
# Special case of a single local array
# 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:
la = factory.create(module_type="local_bitcell_array", rows=self.rows, cols=cols, rbl=self.rbl, add_rbl=[self.left_rbl, 0])
elif i == len(self.cols) - 1:
la = factory.create(module_type="local_bitcell_array", rows=self.rows, cols=cols, rbl=self.rbl, add_rbl=[0, self.right_rbl])
la = factory.create(module_type="local_bitcell_array",
rows=self.row_size,
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:
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.local_mods.append(la)
def add_pins(self):
return
self.add_bitline_pins()
self.add_wordline_pins()
@ -80,60 +101,96 @@ class global_bitcell_array(design.design):
self.add_pin("gnd", "GROUND")
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:
self.add_pin_list(self.replica_bitline_names[port], "INOUT")
self.add_pin_list(self.bitline_names, "INOUT")
self.rbl_bitline_names[0].append("rbl_bl_{}_0".format(port))
for port in self.all_ports:
self.rbl_bitline_names[0].append("rbl_br_{}_0".format(port))
for col in range(self.column_size):
for port in self.all_ports:
self.bitline_names[port].append("bl_{0}_{1}".format(port, col))
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):
# All wordline names for 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]
self.rbl_wordline_names = [[] for x in self.all_ports]
self.wordline_names = [[] for x in self.all_ports]
# Regular array wordline names
self.bitcell_array_wordline_names = self.bitcell_array.get_all_wordline_names()
self.wordline_names = []
# Left port WLs
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)
for bit in self.all_ports:
for port in self.all_ports:
self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit))
self.all_rbl_wordline_names = [x for sl in zip(*self.rbl_wordline_names) for x in sl]
# Regular WLs
self.wordline_names.extend(self.bitcell_array_wordline_names)
# Right port WLs
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
for row in range(self.row_size):
for port in self.all_ports:
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
self.add_pin_list(self.wordline_names, "INPUT")
self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl]
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):
""" Create the module instances used in this design """
self.local_insts = []
for i, mod in enumerate(self.local_mods):
name = "la_{0}".format(i)
for col, mod in zip(self.col_offsets, self.local_mods):
name = "la_{0}".format(col)
self.local_insts.append(self.add_inst(name=name,
mod=mod))
self.connect_inst(mod.pins)
mod=mod))
temp = []
if col == 0:
temp.extend(self.get_rbl_bitline_names(0))
port_inouts = [x for x in mod.get_inouts() if x.startswith("bl") or x.startswith("br")]
for pin_name in port_inouts:
# Offset of the last underscore that defines the bit number
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):
offset = vector(0, 0)
@ -144,5 +201,136 @@ class global_bitcell_array(design.design):
self.height = self.local_mods[0].height
self.width = self.local_insts[-1].rx()
def add_layout_pins(self):
def route(self):
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

@ -24,13 +24,14 @@ class hierarchical_decoder(design.design):
self.pre2x4_inst = []
self.pre3x8_inst = []
self.pre4x16_inst = []
b = factory.create(module_type="bitcell")
self.cell_height = b.height
self.num_outputs = num_outputs
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
(self.no_of_pre2x4, self.no_of_pre3x8, self.no_of_pre4x16)=self.determine_predecodes(self.num_inputs)
self.create_netlist()
if not OPTS.netlist_only:
@ -55,9 +56,9 @@ class hierarchical_decoder(design.design):
self.route_decoder_bus()
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.DRC_LVS()
@ -86,25 +87,37 @@ class hierarchical_decoder(design.design):
height=self.cell_height)
self.add_mod(self.pre3_8)
self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16",
height=self.cell_height)
self.add_mod(self.pre4_16)
def determine_predecodes(self, num_inputs):
""" Determines the number of 2:4 pre-decoder and 3:8 pre-decoder
needed based on the number of inputs """
"""
Determines the number of 2:4, 3:8 and 4:16 pre-decoders
needed based on the number of inputs
"""
if (num_inputs == 2):
return (1, 0)
return (1, 0, 0)
elif (num_inputs == 3):
return(0, 1)
return(0, 1, 0)
elif (num_inputs == 4):
return(2, 0)
return(2, 0, 0)
elif (num_inputs == 5):
return(1, 1)
return(1, 1, 0)
elif (num_inputs == 6):
return(3, 0)
return(3, 0, 0)
elif (num_inputs == 7):
return(2, 1)
return(2, 1, 0)
elif (num_inputs == 8):
return(1, 2)
return(1, 2, 0)
elif (num_inputs == 9):
return(0, 3)
return(0, 3, 0)
elif (num_inputs == 10):
return(0, 2, 1)
elif (num_inputs == 11):
return(0, 1, 2)
elif (num_inputs == 12):
return(0, 0, 3)
else:
debug.error("Invalid number of inputs for hierarchical decoder", -1)
@ -131,12 +144,19 @@ class hierarchical_decoder(design.design):
index = index + 1
self.predec_groups.append(lines)
for i in range(self.no_of_pre4x16):
lines = []
for j in range(16):
lines.append(index)
index = index + 1
self.predec_groups.append(lines)
def setup_layout_constants(self):
""" Calculate the overall dimensions of the hierarchical decoder """
# If we have 4 or fewer rows, the predecoder is the decoder itself
if self.num_inputs>=4:
self.total_number_of_predecoder_outputs = 4 * self.no_of_pre2x4 + 8 * self.no_of_pre3x8
self.total_number_of_predecoder_outputs = 4 * self.no_of_pre2x4 + 8 * self.no_of_pre3x8 + 16 * self.no_of_pre4x16
else:
self.total_number_of_predecoder_outputs = 0
debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),
@ -144,17 +164,20 @@ class hierarchical_decoder(design.design):
# Calculates height and width of pre-decoder,
# FIXME: Update with 4x16
if self.no_of_pre3x8 > 0 and self.no_of_pre2x4 > 0:
self.predecoder_width = max(self.pre3_8.width, self.pre2_4.width)
elif self.no_of_pre3x8 > 0:
self.predecoder_width = self.pre3_8.width
else:
self.predecoder_width = self.pre2_4.width
self.predecoder_width = 0
if self.no_of_pre2x4 > 0:
self.predecoder_width = max(self.predecoder_width, self.pre2_4.width)
if self.no_of_pre3x8 > 0:
self.predecoder_width = max(self.predecoder_width, self.pre3_8.width)
if self.no_of_pre4x16 > 0:
self.predecoder_width = max(self.predecoder_width, self.pre4_16.width)
# How much space between each predecoder
self.predecoder_spacing = 2 * self.and2.height
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 \
+ (self.no_of_pre2x4 + self.no_of_pre3x8 - 1) * self.predecoder_spacing
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 \
+ self.pre3_8.height * self.no_of_pre3x8 \
+ self.pre4_16.height * self.no_of_pre4x16 \
+ (self.no_of_pre2x4 + self.no_of_pre3x8 + self.no_of_pre4x16 - 1) * self.predecoder_spacing
# Inputs to cells are on input layer
# Outputs from cells are on output layer
@ -192,6 +215,8 @@ class hierarchical_decoder(design.design):
min_x = min(min_x, self.pre2x4_inst[0].lx())
if self.no_of_pre3x8 > 0:
min_x = min(min_x, self.pre3x8_inst[0].lx())
if self.no_of_pre4x16 > 0:
min_x = min(min_x, self.pre4x16_inst[0].lx())
input_offset=vector(min_x - self.input_routing_width, 0)
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
@ -232,6 +257,20 @@ class hierarchical_decoder(design.design):
self.route_input_bus(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre4x16):
for i in range(4):
index = pre_num * 4 + i + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2
input_pos = self.input_bus["addr_{}".format(index)].center()
in_name = "in_{}".format(i)
decoder_pin = self.pre4x16_inst[pre_num].get_pin(in_name)
decoder_offset = decoder_pin.center()
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_bus(decoder_offset, input_offset)
def route_input_bus(self, input_offset, output_offset):
"""
Route a vertical M2 coordinate to another
@ -267,6 +306,9 @@ class hierarchical_decoder(design.design):
for i in range(self.no_of_pre3x8):
self.create_pre3x8(i)
for i in range(self.no_of_pre4x16):
self.create_pre4x16(i)
def create_pre2x4(self, num):
""" Add a 2x4 predecoder to the left of the origin """
@ -305,6 +347,24 @@ class hierarchical_decoder(design.design):
mod=self.pre3_8))
self.connect_inst(pins)
def create_pre4x16(self, num):
""" Add 4x16 predecoder to the left of the origin and above any 3x8 decoders """
# If we had 2x4 predecodes, those are used as the lower
# decode output bits
in_index_offset = num * 4 + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2
out_index_offset = num * 16 + self.no_of_pre3x8 * 8 + self.no_of_pre2x4 * 4
pins = []
for input_index in range(4):
pins.append("addr_{0}".format(input_index + in_index_offset))
for output_index in range(16):
pins.append("out_{0}".format(output_index + out_index_offset))
pins.extend(["vdd", "gnd"])
self.pre4x16_inst.append(self.add_inst(name="pre4x16_{0}".format(num),
mod=self.pre4_16))
self.connect_inst(pins)
def place_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """
@ -314,11 +374,16 @@ class hierarchical_decoder(design.design):
for i in range(self.no_of_pre3x8):
self.place_pre3x8(i)
for i in range(self.no_of_pre4x16):
self.place_pre4x16(i)
self.predecode_height = 0
if self.no_of_pre2x4 > 0:
self.predecode_height = self.pre2x4_inst[-1].uy()
if self.no_of_pre3x8 > 0:
self.predecode_height = self.pre3x8_inst[-1].uy()
if self.no_of_pre4x16 > 0:
self.predecode_height = self.pre4x16_inst[-1].uy()
def place_pre2x4(self, num):
""" Place 2x4 predecoder to the left of the origin """
@ -333,6 +398,14 @@ class hierarchical_decoder(design.design):
offset = vector(-self.pre3_8.width, height)
self.pre3x8_inst[num].place(offset)
def place_pre4x16(self, num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) \
+ self.no_of_pre3x8 * (self.pre3_8.height + self.predecoder_spacing) \
+ num * (self.pre4_16.height + self.predecoder_spacing)
offset = vector(-self.pre4_16.width, height)
self.pre4x16_inst[num].place(offset)
def create_row_decoder(self):
""" Create the row-decoder by placing AND2/AND3 and Inverters
and add the primary decoder output pins. """
@ -468,7 +541,17 @@ class hierarchical_decoder(design.design):
x_offset = self.pre3x8_inst[pre_num].rx() + self.output_layer_pitch
y_offset = self.pre3x8_inst[pre_num].by() + i * self.cell_height
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
# FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre4x16):
for i in range(16):
predecode_name = "predecode_{}".format(pre_num * 16 + i + self.no_of_pre3x8 * 8 + self.no_of_pre2x4 * 4)
out_name = "out_{}".format(i)
pin = self.pre4x16_inst[pre_num].get_pin(out_name)
x_offset = self.pre4x16_inst[pre_num].rx() + self.output_layer_pitch
y_offset = self.pre4x16_inst[pre_num].by() + i * self.cell_height
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
def route_bus_to_decoder(self):
"""
Use the self.predec_groups to determine the connections to the decoder AND gates.
@ -559,7 +642,7 @@ class hierarchical_decoder(design.design):
start_layer=supply_pin.layer)
# Copy the pins from the predecoders
for pre in self.pre2x4_inst + self.pre3x8_inst:
for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst:
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(pre, pin_name)
@ -609,11 +692,3 @@ class hierarchical_decoder(design.design):
to_layer=self.output_layer,
offset=rail_pos,
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

@ -45,7 +45,7 @@ class hierarchical_predecode(design.design):
def add_modules(self):
""" Add the INV and AND gate modules """
debug.check(self.number_of_inputs < 4,
debug.check(self.number_of_inputs <= 4,
"Invalid number of predecode inputs: {}".format(self.number_of_inputs))
if self.column_decoder:
@ -204,6 +204,7 @@ class hierarchical_predecode(design.design):
pin = top_and_gate.get_pin("D")
else:
debug.error("Too many inputs for predecoder.", -1)
y_offset = pin.cy()
in_pin = "in_{}".format(num)
a_pin = "A_{}".format(num)
@ -288,10 +289,14 @@ class hierarchical_predecode(design.design):
if self.number_of_inputs == 2:
gate_lst = ["A", "B"]
else:
elif self.number_of_inputs == 3:
gate_lst = ["A", "B", "C"]
elif self.number_of_inputs == 4:
gate_lst = ["A", "B", "C", "D"]
else:
debug.error("Invalid number of nand inputs for decode", -1)
# this will connect pins A,B or A,B,C
# this will connect pins A,B or A,B,C or A,B,C,D
for rail_pin, gate_pin in zip(index_lst, gate_lst):
pin = self.and_inst[k].get_pin(gate_pin)
pin_pos = pin.center()

View File

@ -32,14 +32,14 @@ class hierarchical_predecode4x16(hierarchical_predecode):
["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"],
["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"],
["inbar_0", "inbar_1", "inbar_2", "in_3", "out_0", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "in_3", "out_1", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "in_3", "out_2", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "in_3", "out_3", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "in_3", "out_4", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "in_3", "out_5", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "in_3", "out_6", "vdd", "gnd"],
["in_0", "in_1", "in_2", "in_3", "out_7", "vdd", "gnd"] ]
["inbar_0", "inbar_1", "inbar_2", "in_3", "out_8", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "in_3", "out_9", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "in_3", "out_10", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "in_3", "out_11", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "in_3", "out_12", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "in_3", "out_13", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "in_3", "out_14", "vdd", "gnd"],
["in_0", "in_1", "in_2", "in_3", "out_15", "vdd", "gnd"] ]
self.create_and_array(connections)

View File

@ -5,30 +5,34 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import bitcell_base_array
from globals import OPTS
from sram_factory import factory
from vector import vector
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.
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.
"""
def __init__(self, rows, cols, rbl, add_rbl=None, name=""):
super().__init__(name=name)
debug.info(2, "create local array of size {} rows x {} cols words".format(rows, cols))
def __init__(self, rows, cols, rbl, left_rbl=[], right_rbl=[], name=""):
super().__init__(name=name, rows=rows, cols=cols, column_offset=0)
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.cols = cols
self.rbl = rbl
if add_rbl == None:
self.add_rbl = rbl
else:
self.add_rbl = add_rbl
self.left_rbl = left_rbl
self.right_rbl = right_rbl
debug.check(len(self.all_ports) < 3, "Local bitcell array only supports dual port or less.")
self.create_netlist()
@ -38,7 +42,7 @@ class local_bitcell_array(design.design):
# We don't offset this because we need to align
# the replica bitcell in the control logic
# self.offset_all_coordinates()
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
@ -66,53 +70,51 @@ class local_bitcell_array(design.design):
cols=self.cols,
rows=self.rows,
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.wl_array = factory.create(module_type="wordline_buffer_array",
rows=self.rows + 1,
cols=self.cols)
self.add_mod(self.wl_array)
def add_pins(self):
# Inputs to the wordline driver (by port)
self.wordline_names = []
# Outputs from the wordline driver (by port)
self.driver_wordline_outputs = []
# Inputs to the bitcell array (by port)
self.array_wordline_inputs = []
for port in self.all_ports:
wordline_inputs = []
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.wordline_names = self.bitcell_array.wordline_names
self.all_wordline_names = self.bitcell_array.all_wordline_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.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:
# bit lines (left to right)
# word lines (bottom to top)
# vdd
# 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:
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("gnd", "GROUND")
@ -120,15 +122,41 @@ class local_bitcell_array(design.design):
""" Create the module instances used in this design """
self.wl_insts = []
self.driver_wordline_outputs = []
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))
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",
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):
""" 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:
self.wl_insts[1].place(vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing,
2 * self.cell.height),
mirror="MY")
2 * self.cell.height + self.wl_array.height),
mirror="XY")
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):
for x in self.get_inouts():
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]
for pin_name in ["vdd", "gnd"]:
for inst in supply_insts:
@ -188,8 +192,44 @@ class local_bitcell_array(design.design):
def route(self):
# Route the global wordlines
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]
out_pin = self.wl_insts[port].get_pin(driver_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()
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)..
"""
def __init__(self, cols, rows, name=""):
def __init__(self, cols, rows, port, name=""):
self.num_cols = cols
self.num_rows = rows
self.port = port
self.addr_size = ceil(log(self.num_rows, 2))
if name == "":
@ -39,6 +40,7 @@ class port_address(design.design):
self.add_modules()
self.create_row_decoder()
self.create_wordline_driver()
self.create_rbl_driver()
def create_layout(self):
if "li" in layer:
@ -59,6 +61,8 @@ class port_address(design.design):
for bit in range(self.num_rows):
self.add_pin("wl_{0}".format(bit), "OUTPUT")
self.add_pin("rbl_wl", "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
@ -71,10 +75,16 @@ class port_address(design.design):
def route_supplies(self):
""" 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, "gnd")
for rbl_vdd_pin in self.rbl_driver_inst.get_pins("vdd"):
if OPTS.tech_name == "sky130":
self.add_power_pin("vdd", rbl_vdd_pin.center())
else:
self.add_power_pin("vdd", rbl_vdd_pin.lc())
def route_pins(self):
for row in range(self.addr_size):
decoder_name = "addr_{}".format(row)
@ -82,16 +92,16 @@ class port_address(design.design):
for row in range(self.num_rows):
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):
for row in range(self.num_rows):
# 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_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()
self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos, var_offset=0.3)
@ -102,6 +112,26 @@ class port_address(design.design):
self.add_via_stack_center(from_layer=driver_in_pin.layer,
to_layer=self.route_layer,
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):
@ -109,10 +139,33 @@ class port_address(design.design):
num_outputs=self.num_rows)
self.add_mod(self.row_decoder)
self.wordline_driver = factory.create(module_type="wordline_driver_array",
rows=self.num_rows,
cols=self.num_cols)
self.add_mod(self.wordline_driver)
self.wordline_driver_array = factory.create(module_type="wordline_driver_array",
rows=self.num_rows,
cols=self.num_cols)
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):
""" Create the hierarchical row decoder """
@ -128,11 +181,24 @@ class port_address(design.design):
temp.extend(["vdd", "gnd"])
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):
""" Create the Wordline Driver """
self.wordline_driver_inst = self.add_inst(name="wordline_driver",
mod=self.wordline_driver)
self.wordline_driver_array_inst = self.add_inst(name="wordline_driver",
mod=self.wordline_driver_array)
temp = []
for row in range(self.num_rows):
@ -150,11 +216,23 @@ class port_address(design.design):
"""
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)
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
self.predecoder_height = self.row_decoder.predecoder_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
import debug
import design
import math
from sram_factory import factory
from collections import namedtuple
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.
"""
def __init__(self, sram_config, port, name=""):
def __init__(self, sram_config, port, bit_offsets=None, name=""):
sram_config.set_local_config(self)
self.port = port
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:
self.num_wmasks = 0
if self.num_spare_cols is None:
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 == "":
name = "port_data_{0}".format(self.port)
super().__init__(name)
@ -179,8 +188,19 @@ class port_data(design.design):
# Precharge will be shifted left if needed
# Column offset is set to port so extra column can be on left or right
# 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",
columns=self.num_cols + self.num_spare_cols + 1,
offsets=precharge_bit_offsets,
bitcell_bl=self.bl_names[self.port],
bitcell_br=self.br_names[self.port],
column_offset=self.port - 1)
@ -190,6 +210,7 @@ class port_data(design.design):
# RBLs don't get a sense amp
self.sense_amp_array = factory.create(module_type="sense_amp_array",
word_size=self.word_size,
offsets=self.bit_offsets,
words_per_row=self.words_per_row,
num_spare_cols=self.num_spare_cols)
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",
columns=self.num_cols,
word_size=self.word_size,
offsets=self.bit_offsets,
bitcell_bl=self.bl_names[self.port],
bitcell_br=self.br_names[self.port])
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",
columns=self.num_cols,
word_size=self.word_size,
offsets=self.bit_offsets,
write_size=self.write_size,
num_spare_cols=self.num_spare_cols)
self.add_mod(self.write_driver_array)
@ -219,6 +242,7 @@ class port_data(design.design):
# RBLs don't get a write mask
self.write_mask_and_array = factory.create(module_type="write_mask_and_array",
columns=self.num_cols,
offsets=self.bit_offsets,
word_size=self.word_size,
write_size=self.write_size)
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_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]
self.width = x_offset
self.width = 0
self.height = 0
for i, p in enumerate(vertical_port_order):
if p == None:
continue
self.height += (p.height + self.m2_gap)
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
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.column_mux_offset = vertical_port_offsets[1]
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):
""" Place the instances. """

View File

@ -10,7 +10,7 @@ import debug
from vector import vector
from sram_factory import factory
from globals import OPTS
from tech import layer
from tech import cell_properties
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.
"""
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)
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.columns = columns
self.offsets = offsets
self.size = size
self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br
@ -62,10 +63,11 @@ class precharge_array(design.design):
self.create_insts()
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.place_insts()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
@ -112,26 +114,20 @@ class precharge_array(design.design):
def place_insts(self):
""" Places precharge array by horizontally tiling the precharge cell"""
from tech import cell_properties
xoffset = 0
for i in range(self.columns):
tempx = xoffset
# Default to single spaced columns
if not self.offsets:
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:
mirror = "MY"
tempx = tempx + self.pc_cell.width
tempx = xoffset + self.pc_cell.width
else:
mirror = ""
tempx = xoffset
offset = vector(tempx, 0)
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,39 +21,48 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
Requires a regular bitcell array, replica bitcell, and dummy
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)
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("rbl: {0} left_rbl: {1} right_rbl: {2}".format(rbl, left_rbl, right_rbl))
self.column_size = cols
self.row_size = rows
# This is how many RBLs are in all the arrays
self.rbl = rbl
self.left_rbl = rbl[0]
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]
if rbl:
self.rbl = rbl
else:
self.add_left_rbl = add_rbl[0]
self.add_right_rbl = add_rbl[1]
for a, b in zip(add_rbl, rbl):
debug.check(a <= b,
"Invalid number of RBLs for port configuration.")
debug.check(sum(rbl) <= len(self.all_ports),
self.rbl=[1, 1 if len(self.all_ports)>1 else 0]
# This specifies which RBL to put on the left or right
# by port number
# This could be an empty list
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(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.")
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
# Two dummy rows plus replica even if we don't add the column
self.extra_rows = 2 + sum(rbl)
# Two dummy cols plus replica if we add the column
self.extra_cols = 2 + self.add_left_rbl + self.add_right_rbl
else:
self.extra_rows = 0
self.extra_cols = 2 + self.add_left_rbl + self.add_right_rbl
# Two dummy rows plus replica even if we don't add the column
self.extra_rows = 2 + sum(self.rbl)
# Two dummy cols plus replica if we add the column
self.extra_cols = 2 + len(self.left_rbl) + len(self.right_rbl)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -93,53 +102,60 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
"""
# 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,
rows=self.row_size)
self.add_mod(self.bitcell_array)
# Replica bitlines
self.replica_columns = {}
for bit in range(self.add_left_rbl + self.add_right_rbl):
# Creating left_rbl
if bit < self.add_left_rbl:
for port in self.all_ports:
if port in self.left_rbl:
# We will always have self.rbl[0] rows of replica wordlines below
# the array.
# These go from the top (where the bitcell array starts ) down
replica_bit = self.left_rbl - bit
# Creating right_rbl
else:
replica_bit = self.rbl[0] - port
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.left_rbl + self.row_size + 1 + bit
replica_bit = self.rbl[0] + self.row_size + port
else:
continue
# 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",
rows=self.row_size,
rbl=self.rbl,
column_offset=column_offset,
replica_bit=replica_bit)
self.add_mod(self.replica_columns[bit])
# If there are bitcell end caps, replace the dummy cells on the edge of the bitcell array with end caps.
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:
end_caps_enabled = cell_properties.bitcell.end_caps
except AttributeError:
end_caps_enabled = False
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
# Dummy row
self.dummy_row = factory.create(module_type="dummy_array",
# Dummy row
self.dummy_row = factory.create(module_type="dummy_array",
cols=self.column_size,
rows=1,
# dummy column + left replica column
column_offset=1 + self.add_left_rbl,
column_offset=1 + len(self.left_rbl),
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):
# Dummy Row or Col Cap, depending on bitcell array properties
col_cap_module_type = ("col_cap_array" if end_caps_enabled else "dummy_array")
self.col_cap = factory.create(module_type=col_cap_module_type,
cols=self.column_size,
rows=1,
# dummy column + left replica column(s)
column_offset=1 + self.add_left_rbl,
# dummy column + left replica column
column_offset=1 + len(self.left_rbl),
mirror=0)
self.add_mod(self.col_cap)
@ -150,7 +166,7 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
cols=1,
column_offset=0,
rows=self.row_size + self.extra_rows,
mirror=(self.left_rbl + 1) % 2)
mirror=(self.rbl[0] + 1) % 2)
self.add_mod(self.row_cap_left)
self.row_cap_right = factory.create(module_type=row_cap_module_type,
@ -159,11 +175,30 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
# + left replica column(s)
# + bitcell columns
# + 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,
mirror=(self.left_rbl + 1) %2)
mirror=(self.rbl[0] + 1) %2)
self.add_mod(self.row_cap_right)
else:
# Dummy Row or Col Cap, depending on bitcell array properties
col_cap_module_type = ("s8_col_cap_array" if end_caps_enabled else "dummy_array")
self.col_cap_top = factory.create(module_type=col_cap_module_type,
cols=self.column_size,
rows=1,
# dummy column + left replica column(s)
column_offset=1 + len(self.left_rbl),
mirror=0,
location="top")
self.add_mod(self.col_cap_top)
self.col_cap_bottom = factory.create(module_type=col_cap_module_type,
cols=self.column_size,
rows=1,
# dummy column + left replica column(s)
column_offset=1 + len(self.left_rbl),
mirror=0,
location="bottom")
self.add_mod(self.col_cap_bottom)
# Dummy Col or Row Cap, depending on bitcell array properties
row_cap_module_type = ("s8_row_cap_array" if end_caps_enabled else "dummy_array")
@ -180,7 +215,7 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
# + left replica column(s)
# + bitcell columns
# + 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,
mirror=0)
self.add_mod(self.row_cap_right)
@ -206,226 +241,313 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
self.add_pin("gnd", "GROUND")
def add_bitline_pins(self):
# Regular bitline names by port
self.bitline_names = []
# Replica bitlines by port
self.rbl_bitline_names = []
# Dummy bitlines by left/right
self.dummy_col_bitline_names = []
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)
# The bit is which port the RBL is for
for bit in self.rbls:
for port in self.all_ports:
self.rbl_bitline_names[bit].append("rbl_bl_{0}_{1}".format(port, bit))
for port in self.all_ports:
self.rbl_bitline_names[bit].append("rbl_br_{0}_{1}".format(port, bit))
# Make a flat list too
self.all_rbl_bitline_names = [x for sl in self.rbl_bitline_names for x in sl]
for port in self.all_ports:
bitline_names = self.bitcell_array.get_bitline_names(port)
self.bitline_names.append(bitline_names)
self.bitline_names = self.bitcell_array.bitline_names
# Make a flat list too
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 range(self.add_left_rbl):
self.add_pin_list(self.rbl_bitline_names[port], "INOUT")
self.add_pin_list(self.all_bitline_names, "INOUT")
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.dummy_col_bitline_names[1], "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")
def add_wordline_pins(self):
# Regular wordlines by port
self.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):
wordline_names=["rbl_wl_{0}_{1}".format(x, port) for x in self.all_ports]
self.rbl_wordline_names.append(wordline_names)
self.all_rbl_wordline_names = [x for sl in self.rbl_wordline_names for x in sl]
# Wordlines to ground
self.gnd_wordline_names = []
for port in self.all_ports:
wordline_names = self.bitcell_array.get_wordline_names(port)
self.wordline_names.append(wordline_names)
self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl]
for bit in self.all_ports:
#if not cell_properties.compare_ports(cell_properties.bitcell.split_wl):
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):
self.replica_array_wordline_names.extend(self.dummy_row_wordline_names[0])
for p in range(self.left_rbl):
self.replica_array_wordline_names.extend(self.rbl_wordline_names[p])
self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap.get_wordline_names()))
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 p in range(self.left_rbl, self.left_rbl + self.right_rbl):
self.replica_array_wordline_names.extend(self.rbl_wordline_names[p])
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]])
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[1])
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
self.add_pin_list(self.dummy_row_wordline_names[0], "INPUT")
for port in range(self.left_rbl):
self.add_pin_list(self.rbl_wordline_names[port], "INPUT")
self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap.get_wordline_names()))
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.left_rbl, self.left_rbl + self.right_rbl):
self.add_pin_list(self.rbl_wordline_names[port], "INPUT")
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
self.add_pin_list(self.dummy_row_wordline_names[1], "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):
""" Create the module instances used in this design """
supplies = ["vdd", "gnd"]
# Used for names/dimensions only
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
self.cell = factory.create(module_type="bitcell")
self.supplies = ["vdd", "gnd"]
# Used for names/dimensions only
self.cell = factory.create(module_type="bitcell")
# Main array
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
mod=self.bitcell_array)
self.connect_inst(self.all_bitline_names + self.all_wordline_names + self.supplies)
# Replica columns
self.replica_col_insts = []
for port in self.all_ports:
if port in self.rbls:
self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port),
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)
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!
for port in self.all_ports:
self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port),
mod=self.dummy_row))
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
self.dummy_row_insts = []
self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot",
mod=self.col_cap))
self.connect_inst(["gnd"] * len(self.col_cap.get_wordline_names()) + self.supplies)
self.dummy_row_insts.append(self.add_inst(name="dummy_row_top",
mod=self.col_cap))
self.connect_inst(["gnd"] * len(self.col_cap.get_wordline_names()) + self.supplies)
# Left/right Dummy columns
self.dummy_col_insts = []
self.dummy_col_insts.append(self.add_inst(name="dummy_col_left",
mod=self.row_cap_left))
self.connect_inst(self.replica_array_wordline_names + self.supplies)
self.dummy_col_insts.append(self.add_inst(name="dummy_col_right",
mod=self.row_cap_right))
self.connect_inst(self.replica_array_wordline_names + self.supplies)
else:
self.cell = factory.create(module_type="s8_bitcell", version = "opt1")
# Main array
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
mod=self.bitcell_array)
self.connect_inst(self.all_bitline_names + self.all_wordline_names + supplies)
# Replica columns
self.replica_col_insts = []
for port in range(self.add_left_rbl + self.add_right_rbl):
self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port),
mod=self.replica_columns[port]))
self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + supplies)
# Dummy rows under the bitcell array (connected with with the replica cell wl)
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!
for port in range(self.left_rbl + self.right_rbl):
self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port),
mod=self.dummy_row))
self.connect_inst(self.all_bitline_names + self.rbl_wordline_names[port] + supplies)
# Top/bottom dummy rows or col caps
self.dummy_row_insts = []
self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot",
mod=self.col_cap))
self.connect_inst(self.all_bitline_names
+ self.dummy_row_wordline_names[0]
+ supplies)
self.dummy_row_insts.append(self.add_inst(name="dummy_row_top",
mod=self.col_cap))
self.connect_inst(self.all_bitline_names
+ self.dummy_row_wordline_names[1]
+ supplies)
# Left/right Dummy columns
self.dummy_col_insts = []
self.dummy_col_insts.append(self.add_inst(name="dummy_col_left",
mod=self.row_cap_left))
self.connect_inst(self.dummy_col_bitline_names[0] + self.replica_array_wordline_names + supplies)
self.dummy_col_insts.append(self.add_inst(name="dummy_col_right",
mod=self.row_cap_right))
self.connect_inst(self.dummy_col_bitline_names[1] + self.replica_array_wordline_names + supplies)
from tech import custom_replica_bitcell_array_arrangement
custom_replica_bitcell_array_arrangement(self)
def create_layout(self):
self.height = (self.row_size + self.extra_rows) * self.dummy_row.height
self.width = (self.column_size + self.extra_cols) * self.cell.width
# 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):
self.height = (self.row_size + self.extra_rows) * self.dummy_row.height
self.width = (self.column_size + self.extra_cols) * self.cell.width + 2 * self.unused_pitch
else:
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
# This is a bitcell x bitcell offset to scale
self.bitcell_offset = vector(self.cell.width, self.cell.height)
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
self.strap_offset = vector(0, 0)
self.col_end_offset = vector(self.cell.width, self.cell.height)
self.row_end_offset = vector(self.cell.width, self.cell.height)
else:
self.strap_offset = vector(self.replica_col_insts[0].mod.strap1.width, self.replica_col_insts[0].mod.strap1.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)
# Everything is computed with the main array at (0, 0) to start
self.bitcell_array_inst.place(offset=[0, 0])
# Everything is computed with the main array at (self.unused_pitch, 0) to start
self.bitcell_array_inst.place(offset=self.unused_offset)
self.add_replica_columns()
self.add_end_caps()
# 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
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.route_unused_wordlines()
self.add_boundary()
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):
""" Add replica columns on left and right of array """
end_caps_enabled = cell_properties.bitcell.end_caps
# Grow from left to right, toward the array
for bit in range(self.add_left_rbl):
offset = self.bitcell_offset.scale(-self.add_left_rbl + bit, -self.left_rbl - 1)
for bit, port in enumerate(self.left_rbl):
if not end_caps_enabled:
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:
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)
# Grow to the right of the bitcell array, array outward
for bit in range(self.add_right_rbl):
offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.left_rbl - 1)
self.replica_col_insts[self.add_left_rbl + bit].place(offset)
for bit, port in enumerate(self.right_rbl):
if not end_caps_enabled:
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:
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[len(self.left_rbl) + bit].place(offset)
# Replica dummy rows
# Add the dummy rows even if we aren't adding the replica column to this bitcell array
# These grow up, toward the array
for bit in range(self.left_rbl):
self.dummy_row_replica_insts[bit].place(offset=self.bitcell_offset.scale(0, -self.left_rbl + bit + (-self.left_rbl + bit) % 2),
mirror="MX" if (-self.left_rbl + bit) % 2 else "R0")
for bit in range(self.rbl[0]):
dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) + self.unused_offset
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
for bit in range(self.right_rbl):
self.dummy_row_replica_insts[self.left_rbl + bit].place(offset=self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(),
mirror="MX" if bit % 2 else "R0")
for bit in range(self.rbl[1]):
dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul()
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):
""" Add dummy cells or end caps around the array """
end_caps_enabled = cell_properties.bitcell.end_caps
# FIXME: These depend on the array size itself
# Far top dummy row (first row above array is NOT flipped)
flip_dummy = self.right_rbl % 2
dummy_row_offset = self.bitcell_offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul()
flip_dummy = self.rbl[1] % 2
if not end_caps_enabled:
dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul()
else:
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,
mirror="MX" if flip_dummy else "R0")
# FIXME: These depend on the array size itself
# Far bottom dummy row (first row below array IS flipped)
flip_dummy = (self.left_rbl + 1) % 2
dummy_row_offset = self.bitcell_offset.scale(0, -self.left_rbl - 1 + flip_dummy)
flip_dummy = (self.rbl[0] + 1) % 2
if not end_caps_enabled:
dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset
else:
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,
mirror="MX" if flip_dummy else "R0")
# Far left dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
dummy_col_offset = self.bitcell_offset.scale(-self.add_left_rbl - 1, -self.left_rbl - 1)
if not end_caps_enabled:
dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -len(self.left_rbl) - 1)
else:
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)
# Far right dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
dummy_col_offset = self.bitcell_offset.scale(self.add_right_rbl, -self.left_rbl - 1) + self.bitcell_array_inst.lr()
if not end_caps_enabled:
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:
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)
def add_layout_pins(self):
""" Add the layout pins """
#All wordlines
#Main array wl and bl/br
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
# All wordlines
# Main array wl and bl/br
for pin_name in self.all_wordline_names:
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
self.add_layout_pin(text=pin_name,
for pin_name in self.all_wordline_names:
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
self.add_layout_pin(text=pin_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
# 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 (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)
self.add_layout_pin(text=wl_name,
layer=pin.layer,
offset=pin.ll().scale(0, 1),
width=self.width,
height=pin.height())
else:
for pin_name in self.all_wordline_names:
pin_list = self.dummy_col_insts[0].get_pins(pin_name)
for pin in pin_list:
self.add_layout_pin(text=pin_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
# 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 (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)
self.add_layout_pin(text=wl_name,
layer=pin.layer,
offset=pin.ll().scale(0, 1),
width=self.width,
height=pin.height())
for pin_name in self.all_bitline_names:
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
@ -435,126 +557,34 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
width=pin.width(),
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
# 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 (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()):
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 bitlines
for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts):
for (bl_name, pin_name) in zip(names, self.replica_columns[0].all_bitline_names):
pin = inst.get_pin(pin_name)
self.add_layout_pin(text=bl_name,
layer=pin.layer,
offset=pin.ll().scale(1, 0),
width=pin.width(),
height=self.height)
if len(self.rbls) > 0:
for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts):
pin_names = self.replica_columns[self.rbls[0]].all_bitline_names
for (bl_name, pin_name) in zip(names, pin_names):
pin = inst.get_pin(pin_name)
self.add_layout_pin(text=bl_name,
layer=pin.layer,
offset=pin.ll().scale(1, 0),
width=pin.width(),
height=self.height)
# vdd/gnd are only connected in the perimeter cells
# replica column should only have a vdd/gnd in the dummy cell on top/bottom
supply_insts = self.dummy_col_insts + self.dummy_row_insts
for pin_name in ["vdd", "gnd"]:
for pin_name in self.supplies:
for inst in supply_insts:
pin_list = inst.get_pins(pin_name)
for pin in pin_list:
self.add_power_pin(name=pin_name,
loc=pin.center(),
directions=("V", "V"),
start_layer=pin.layer)
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):
"""Power of Bitcell array and bitline in nW."""
# Dynamic Power from Bitline
@ -572,6 +602,37 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
cell_power.leakage * self.column_size * self.row_size)
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):
if OPTS.netlist_only:
height = 0
@ -582,23 +643,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
return bl_wire
def get_wordline_cin(self):
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
# A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
bitcell_wl_cin = self.cell.get_wl_cin()
total_cin = bitcell_wl_cin * self.column_size
return total_cin
def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target"""
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 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()
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)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bitcell_array.init_graph_params()

View File

@ -4,14 +4,14 @@
# All rights reserved.
#
import debug
import design
from bitcell_base_array import bitcell_base_array
from tech import cell_properties
from sram_factory import factory
from vector import vector
from globals import OPTS
class replica_column(design.design):
class replica_column(bitcell_base_array):
"""
Generate a replica bitline column for the replica array.
Rows is the total number of rows i the main array.
@ -21,14 +21,17 @@ class replica_column(design.design):
"""
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.left_rbl = rbl[0]
self.right_rbl = rbl[1]
self.replica_bit = replica_bit
# left, right, regular rows plus top/bottom dummy cells
self.total_size = self.left_rbl + rows + self.right_rbl + 2
if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement):
self.total_size = self.left_rbl + rows + self.right_rbl + 2
else:
self.total_size = self.left_rbl + rows + self.right_rbl + 2
self.column_offset = column_offset
debug.check(replica_bit != 0 and replica_bit != rows,
@ -57,37 +60,16 @@ class replica_column(design.design):
self.DRC_LVS()
def add_pins(self):
self.bitline_names = [[] for port in self.all_ports]
col = 0
for port in self.all_ports:
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")
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")
self.create_all_bitline_names()
#remove 2 wordlines to account for top/bot
if not cell_properties.bitcell.end_caps:
self.create_all_wordline_names()
else:
self.wordline_names = [[] for port in self.all_ports]
for row in range(self.rows):
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.create_all_wordline_names(2)
self.add_pin_list(self.all_bitline_names, "OUTPUT")
self.add_pin_list(self.all_wordline_names, "INPUT")
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("gnd", "GROUND")
@ -276,13 +258,13 @@ class replica_column(design.design):
width=self.width,
height=wl_pin.height())
# # Supplies are only connected in the ends
# for (index, inst) in self.cell_inst.items():
# for pin_name in ["vdd", "gnd"]:
# if inst in [self.cell_inst[0], self.cell_inst[self.total_size - 1]]:
# self.copy_power_pins(inst, pin_name)
# else:
# self.copy_layout_pin(inst, pin_name)
# Supplies are only connected in the ends
for (index, inst) in self.cell_inst.items():
for pin_name in ["vpwr", "vgnd"]:
if inst in [self.cell_inst[0], self.cell_inst[self.total_size - 1]]:
self.copy_power_pins(inst, pin_name)
else:
self.copy_layout_pin(inst, pin_name)
def get_bitline_names(self, port=None):
if port == None:
@ -291,8 +273,10 @@ class replica_column(design.design):
return self.bitline_names[port]
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 = []
for port in self.all_ports:
bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))])
@ -303,18 +287,24 @@ class replica_column(design.design):
return bitcell_pins
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 = []
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.append("vdd")
bitcell_pins.append("gnd")
if len(self.edge_cell.get_pins("vdd")) > 0:
bitcell_pins.append("vdd")
if len(self.edge_cell.get_pins("gnd")) > 0:
bitcell_pins.append("gnd")
return bitcell_pins
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():
if row != self.replica_bit:

View File

@ -82,13 +82,6 @@ class sense_amp(design.design):
# Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
return total_power
def get_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):
"""Returns name used for enable net"""

View File

@ -6,12 +6,11 @@
# All rights reserved.
#
import design
from tech import drc
from vector import vector
from sram_factory import factory
import debug
from globals import OPTS
import logical_effort
from tech import cell_properties
class sense_amp_array(design.design):
@ -20,7 +19,7 @@ class sense_amp_array(design.design):
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)
debug.info(1, "Creating {0}".format(self.name))
@ -29,6 +28,8 @@ class sense_amp_array(design.design):
self.word_size = word_size
self.words_per_row = words_per_row
self.num_cols = word_size * words_per_row
self.offsets = offsets
if not num_spare_cols:
self.num_spare_cols = 0
else:
@ -68,16 +69,15 @@ class sense_amp_array(design.design):
self.create_sense_amp_array()
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.height = self.amp.height
self.width = self.local_insts[-1].rx()
self.add_layout_pins()
self.route_rails()
self.add_boundary()
self.DRC_LVS()
@ -111,29 +111,32 @@ class sense_amp_array(design.design):
self.en_name, "vdd", "gnd"])
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
if not self.offsets:
self.offsets = []
for i in range(self.num_cols + self.num_spare_cols):
self.offsets.append(i * self.bitcell.width)
for i in range(0, self.row_size, self.words_per_row):
index = int(i / self.words_per_row)
xoffset = 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"
xoffset = xoffset + self.amp.width
xoffset = xoffset + self.amp_spacing
else:
mirror = ""
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)
for i in range(0, self.num_spare_cols):
for i, xoffset in enumerate(self.offsets[self.num_cols:]):
index = self.word_size + i
xoffset = ((self.word_size * self.words_per_row) + i) * self.bitcell.width
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"
xoffset = xoffset + self.amp.width
xoffset = xoffset + self.amp_width
else:
mirror = ""
@ -190,18 +193,3 @@ class sense_amp_array(design.design):
self.add_via_stack_center(from_layer=en_pin.layer,
to_layer=self.en_layer,
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

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

View File

@ -44,7 +44,7 @@ class wordline_driver_array(design.design):
self.place_drivers()
self.route_layout()
self.route_vdd_gnd()
self.offset_all_coordinates()
self.offset_x_coordinates()
self.add_boundary()
self.DRC_LVS()
@ -60,8 +60,10 @@ class wordline_driver_array(design.design):
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.wl_driver = factory.create(module_type="wordline_driver",
size=self.cols)
cols=self.cols)
self.add_mod(self.wl_driver)
def route_vdd_gnd(self):
@ -159,24 +161,3 @@ class wordline_driver_array(design.design):
layer=self.route_layer,
start=wl_offset,
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 debug
import math
from tech import drc
from sram_factory import factory
from vector import vector
from globals import OPTS
from tech import cell_properties
class write_driver_array(design.design):
@ -19,7 +21,7 @@ class write_driver_array(design.design):
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)
debug.info(1, "Creating {0}".format(self.name))
@ -29,6 +31,7 @@ class write_driver_array(design.design):
self.columns = columns
self.word_size = word_size
self.write_size = write_size
self.offsets = offsets
self.column_offset = column_offset
self.words_per_row = int(columns / word_size)
if not num_spare_cols:
@ -37,7 +40,7 @@ class write_driver_array(design.design):
self.num_spare_cols = num_spare_cols
if self.write_size:
self.num_wmasks = int(self.word_size / self.write_size)
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
self.create_netlist()
if not OPTS.netlist_only:
@ -66,17 +69,9 @@ class write_driver_array(design.design):
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.width = self.driver_insts[-1].rx()
self.height = self.driver.height
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
@ -107,14 +102,14 @@ class write_driver_array(design.design):
self.bitcell = factory.create(module_type="bitcell")
def create_write_array(self):
self.driver_insts = {}
self.driver_insts = []
w = 0
windex=0
for i in range(0, self.columns, self.words_per_row):
name = "write_driver{}".format(i)
index = int(i / self.words_per_row)
self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver)
self.driver_insts.append(self.add_inst(name=name,
mod=self.driver))
if self.write_size:
self.connect_inst([self.data_name + "_{0}".format(index),
@ -146,8 +141,8 @@ class write_driver_array(design.design):
else:
offset = 1
name = "write_driver{}".format(self.columns + i)
self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver)
self.driver_insts.append(self.add_inst(name=name,
mod=self.driver))
self.connect_inst([self.data_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"])
def place_write_array(self):
from tech import cell_properties
if self.bitcell.width > self.driver.width:
self.driver_spacing = self.bitcell.width
else:
self.driver_spacing = self.driver.width
for i in range(0, self.columns, self.words_per_row):
index = int(i / self.words_per_row)
xoffset = i * self.driver_spacing
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
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"
xoffset = xoffset + self.driver.width
else:
mirror = ""
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)
for i in range(self.num_spare_cols):
for i, xoffset in enumerate(self.offsets[self.columns:]):
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"
xoffset = xoffset + self.driver.width
else:
@ -243,12 +239,14 @@ class write_driver_array(design.design):
elif self.num_spare_cols and not self.write_size:
# shorten enable rail to accomodate those for spare write drivers
inst = self.driver_insts[0]
en_pin = inst.get_pin(inst.mod.en_name)
left_inst = self.driver_insts[0]
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),
layer="m1",
offset=en_pin.ll(),
width=self.width_regular_cols - self.words_per_row * en_pin.width())
offset=left_en_pin.ll(),
width=right_en_pin.rx() - left_en_pin.lx())
# individual enables for every spare write driver
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)
self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1),
layer="m1",
offset=en_pin.lr() + vector(-drc("minwidth_m1"),0))
offset=en_pin.lr() + vector(-drc("minwidth_m1"), 0))
else:
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),
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 debug
import math
from sram_factory import factory
from vector import vector
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.
"""
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)
debug.info(1, "Creating {0}".format(self.name))
self.add_comment("columns: {0}".format(columns))
@ -28,9 +29,10 @@ class write_mask_and_array(design.design):
self.columns = columns
self.word_size = word_size
self.write_size = write_size
self.offsets = offsets
self.column_offset = column_offset
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()
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,
"Write mask AND is wider than the corresponding write drivers {0} vs {1}.".format(self.and2.width,
self.wmask_en_len))
self.width = self.bitcell.width * self.columns
if not self.offsets:
self.offsets = []
for i in range(self.columns):
self.offsets.append(i * self.driver_spacing)
self.width = self.offsets[-1] + self.driver_spacing
self.height = self.and2.height
write_bits = self.columns / 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)
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_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()])
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

@ -9,6 +9,7 @@ import optparse
import getpass
import os
class options(optparse.Values):
"""
Class for holding all of the OpenRAM options. All
@ -30,6 +31,9 @@ class options(optparse.Values):
num_r_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_size = None
@ -93,6 +97,8 @@ class options(optparse.Values):
trim_netlist = False
# Run with extracted parasitics
use_pex = False
# Output config with all options
output_extended_config = False
###################
@ -114,6 +120,9 @@ class options(optparse.Values):
# For sky130, we need magic for filtering.
magic_exe = None
# Number of threads to use
num_threads = 2
# Should we print out the banner at startup
print_banner = True
@ -137,7 +146,8 @@ class options(optparse.Values):
bank_select = "bank_select"
bitcell_array = "bitcell_array"
bitcell = "bitcell"
column_mux_array = "single_level_column_mux_array"
buf_dec = "pbuf"
column_mux_array = "column_mux_array"
control_logic = "control_logic"
decoder = "hierarchical_decoder"
delay_chain = "delay_chain"
@ -146,7 +156,7 @@ class options(optparse.Values):
inv_dec = "pinv"
nand2_dec = "pnand2"
nand3_dec = "pnand3"
nand4_dec = "pnand4" # Not available right now
nand4_dec = "pnand4"
precharge_array = "precharge_array"
ptx = "ptx"
replica_bitline = "replica_bitline"

View File

@ -10,14 +10,13 @@ import debug
from tech import drc, layer
from vector import vector
from sram_factory import factory
import logical_effort
from globals import OPTS
class single_level_column_mux(pgate.pgate):
class column_mux(pgate.pgate):
"""
This module implements the columnmux bitline cell used in the design.
Creates a single columnmux cell with the given integer size relative
Creates a single column mux cell with the given integer size relative
to minimum size. Default is 8x. Per Samira and Hodges-Jackson book:
Column-mux transistors driven by the decoder must be sized
for optimal speed
@ -241,18 +240,3 @@ class single_level_column_mux(pgate.pgate):
offset=vector(0, 0),
width=self.bitcell.width,
height=self.height)
def get_stage_effort(self, corner, slew, load):
"""
Returns relative delay that the column mux.
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

@ -146,21 +146,4 @@ class pand2(pgate.pgate):
offset=pin.center(),
width=pin.width(),
height=pin.height())
def get_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A or B -> Z path"""
stage_effort_list = []
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

@ -161,21 +161,4 @@ class pand3(pgate.pgate):
slew=nand_delay.slew,
load=load)
return nand_delay + inv_delay
def get_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A or B -> Z path"""
stage_effort_list = []
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()

165
compiler/pgates/pand4.py Normal file
View File

@ -0,0 +1,165 @@
# 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
from vector import vector
import pgate
from sram_factory import factory
class pand4(pgate.pgate):
"""
This is a simple buffer used for driving loads.
"""
def __init__(self, name, size=1, height=None, vertical=False, add_wells=True):
debug.info(1, "Creating pand4 {}".format(name))
self.add_comment("size: {}".format(size))
self.vertical = vertical
self.size = size
# Creates the netlist and layout
super().__init__(name, height, add_wells)
def create_netlist(self):
self.add_pins()
self.create_modules()
self.create_insts()
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
self.nand = factory.create(module_type="pnand4",
height=self.height,
add_wells=self.vertical)
# Add the well tap to the inverter because when stacked
# vertically it is sometimes narrower
self.inv = factory.create(module_type="pdriver",
size_list=[self.size],
height=self.height,
add_wells=self.add_wells)
self.add_mod(self.nand)
self.add_mod(self.inv)
def create_layout(self):
if self.vertical:
self.height = 2 * self.nand.height
self.width = max(self.nand.width, self.inv.width)
else:
self.width = self.nand.width + self.inv.width
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.route_supply_rails()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
self.add_pin("A", "INPUT")
self.add_pin("B", "INPUT")
self.add_pin("C", "INPUT")
self.add_pin("D", "INPUT")
self.add_pin("Z", "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_insts(self):
self.nand_inst = self.add_inst(name="pand4_nand",
mod=self.nand)
self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"])
self.inv_inst = self.add_inst(name="pand4_inv",
mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self):
# Add NAND to the right
self.nand_inst.place(offset=vector(0, 0))
if self.vertical:
# Add INV above
self.inv_inst.place(offset=vector(self.inv.width,
2 * self.nand.height),
mirror="XY")
else:
# Add INV to the right
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
def route_supply_rails(self):
""" Add vdd/gnd rails to the top, (middle), and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer=self.route_layer,
offset=vector(0.5 * self.width, 0),
width=self.width)
# Second gnd of the inverter gate
if self.vertical:
self.add_layout_pin_rect_center(text="gnd",
layer=self.route_layer,
offset=vector(0.5 * self.width, self.height),
width=self.width)
if self.vertical:
# Shared between two gates
y_offset = 0.5 * self.height
else:
y_offset = self.height
self.add_layout_pin_rect_center(text="vdd",
layer=self.route_layer,
offset=vector(0.5 * self.width, y_offset),
width=self.width)
def add_wires(self):
# nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z")
a2_pin = self.inv_inst.get_pin("A")
if self.vertical:
route_layer = "m2"
self.add_via_stack_center(offset=z1_pin.center(),
from_layer=z1_pin.layer,
to_layer=route_layer)
self.add_zjog(route_layer,
z1_pin.uc(),
a2_pin.bc(),
"V")
self.add_via_stack_center(offset=a2_pin.center(),
from_layer=a2_pin.layer,
to_layer=route_layer)
else:
route_layer = self.route_layer
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path(route_layer,
[z1_pin.center(), mid1_point, a2_pin.center()])
def add_layout_pins(self):
pin = self.inv_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=pin.layer,
offset=pin.center(),
width=pin.width(),
height=pin.height())
for pin_name in ["A", "B", "C", "D"]:
pin = self.nand_inst.get_pin(pin_name)
self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer,
offset=pin.center(),
width=pin.width(),
height=pin.height())
def analytical_delay(self, corner, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
nand_delay = self.nand.analytical_delay(corner,
slew=slew,
load=self.inv.input_load())
inv_delay = self.inv.analytical_delay(corner,
slew=nand_delay.slew,
load=load)
return nand_delay + inv_delay

View File

@ -96,21 +96,4 @@ class pbuf(pgate.pgate):
offset=a_pin.center(),
width=a_pin.width(),
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

@ -168,24 +168,4 @@ class pdriver(pgate.pgate):
def get_sizes(self):
""" Return the relative sizes of the buffers """
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",
layer=a_pin.layer,
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

@ -133,7 +133,6 @@ class pnand3(pgate.pgate):
# This is the extra space needed to ensure DRC rules
# to the active contacts
nmos = factory.create(module_type="ptx", tx_type="nmos")
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
def create_ptx(self):
"""

371
compiler/pgates/pnand4.py Normal file
View File

@ -0,0 +1,371 @@
# 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 pgate
import debug
from tech import drc, parameter, spice
from vector import vector
import logical_effort
from sram_factory import factory
from globals import OPTS
import contact
class pnand4(pgate.pgate):
"""
This module generates gds of a parametrically sized 4-input nand.
This model use ptx to generate a 4-input nand within a cetrain height.
"""
def __init__(self, name, size=1, height=None, add_wells=True):
""" Creates a cell for a simple 3 input nand """
debug.info(2,
"creating pnand4 structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
# We have trouble pitch matching a 3x sizes to the bitcell...
# If we relax this, we could size this better.
self.size = size
self.nmos_size = 2 * size
self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
# FIXME: Allow these to be sized
debug.check(size == 1,
"Size 1 pnand4 is only supported now.")
self.tx_mults = 1
if OPTS.tech_name == "sky130":
self.nmos_width = self.nearest_bin("nmos", self.nmos_width)
self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
# Creates the netlist and layout
super().__init__(name, height, add_wells)
def add_pins(self):
""" Adds pins for spice netlist """
pin_list = ["A", "B", "C", "D", "Z", "vdd", "gnd"]
dir_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list)
def create_netlist(self):
self.add_pins()
self.add_ptx()
self.create_ptx()
def create_layout(self):
""" Calls all functions related to the generation of the layout """
self.setup_layout_constants()
self.place_ptx()
if self.add_wells:
self.add_well_contacts()
self.route_inputs()
self.route_output()
self.determine_width()
self.route_supply_rails()
self.connect_rails()
self.extend_wells()
self.add_boundary()
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos_center = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact="active",
add_drain_contact="active")
self.add_mod(self.nmos_center)
self.nmos_right = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact="active",
add_drain_contact=self.route_layer)
self.add_mod(self.nmos_right)
self.nmos_left = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=self.route_layer,
add_drain_contact="active")
self.add_mod(self.nmos_left)
self.pmos_left = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_left)
self.pmos_center = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_center)
self.pmos_right = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_right)
def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """
# Compute the overlap of the source and drain pins
self.ptx_offset = self.pmos_left.get_pin("D").center() - self.pmos_left.get_pin("S").center()
# This is the extra space needed to ensure DRC rules
# to the active contacts
nmos = factory.create(module_type="ptx", tx_type="nmos")
def create_ptx(self):
"""
Create the PMOS and NMOS in the netlist.
"""
self.pmos1_inst = self.add_inst(name="pnand4_pmos1",
mod=self.pmos_left)
self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand4_pmos2",
mod=self.pmos_center)
self.connect_inst(["Z", "B", "vdd", "vdd"])
self.pmos3_inst = self.add_inst(name="pnand4_pmos3",
mod=self.pmos_center)
self.connect_inst(["Z", "C", "vdd", "vdd"])
self.pmos4_inst = self.add_inst(name="pnand4_pmos4",
mod=self.pmos_right)
self.connect_inst(["Z", "D", "vdd", "vdd"])
self.nmos1_inst = self.add_inst(name="pnand4_nmos1",
mod=self.nmos_left)
self.connect_inst(["Z", "D", "net1", "gnd"])
self.nmos2_inst = self.add_inst(name="pnand4_nmos2",
mod=self.nmos_center)
self.connect_inst(["net1", "C", "net2", "gnd"])
self.nmos3_inst = self.add_inst(name="pnand4_nmos3",
mod=self.nmos_center)
self.connect_inst(["net2", "B", "net3", "gnd"])
self.nmos4_inst = self.add_inst(name="pnand4_nmos4",
mod=self.nmos_right)
self.connect_inst(["net3", "A", "gnd", "gnd"])
def place_ptx(self):
"""
Place the PMOS and NMOS in the layout at the upper-most
and lowest position to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos_left.active_offset.x,
self.height - self.pmos_left.active_height - self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
pmos2_pos = pmos1_pos + self.ptx_offset
self.pmos2_inst.place(pmos2_pos)
pmos3_pos = pmos2_pos + self.ptx_offset
self.pmos3_inst.place(pmos3_pos)
self.pmos4_pos = pmos3_pos + self.ptx_offset
self.pmos4_inst.place(self.pmos4_pos)
nmos1_pos = vector(self.pmos_left.active_offset.x,
self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos)
nmos2_pos = nmos1_pos + self.ptx_offset
self.nmos2_inst.place(nmos2_pos)
nmos3_pos = nmos2_pos + self.ptx_offset
self.nmos3_inst.place(nmos3_pos)
self.nmos4_pos = nmos3_pos + self.ptx_offset
self.nmos4_inst.place(self.nmos4_pos)
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.pmos_right,
self.pmos4_pos + vector(self.m1_pitch, 0))
self.add_pwell_contact(self.nmos_right,
self.nmos4_pos + vector(self.m1_pitch, 0))
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd")
self.connect_pin_to_rail(self.pmos4_inst, "D", "vdd")
def route_inputs(self):
""" Route the A and B and C inputs """
# We can use this pitch because the contacts and overlap won't be adjacent
pmos_drain_bottom = self.pmos1_inst.get_pin("D").by()
self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space
bottom_pin = self.nmos1_inst.get_pin("D")
# active contact metal to poly contact metal spacing
active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * contact.poly_contact.second_layer_height
# active diffusion to poly contact spacing
# doesn't use nmos uy because that is calculated using offset + poly height
active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height
active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height
active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width
self.inputA_yoffset = max(active_contact_to_poly_contact,
active_to_poly_contact,
active_to_poly_contact2)
apin = self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A",
position="left")
self.inputB_yoffset = self.inputA_yoffset + self.m1_pitch
bpin = self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
self.inputB_yoffset,
"B",
position="center")
self.inputC_yoffset = self.inputB_yoffset + self.m1_pitch
cpin = self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
self.inputC_yoffset,
"C",
position="right")
self.inputD_yoffset = self.inputC_yoffset + self.m1_pitch
dpin = self.route_input_gate(self.pmos4_inst,
self.nmos4_inst,
self.inputD_yoffset,
"D",
position="right")
if OPTS.tech_name == "sky130":
self.add_enclosure([apin, bpin, cpin, dpin], "npc", drc("npc_enclose_poly"))
def route_output(self):
""" Route the Z output """
# PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain
pmos3_pin = self.pmos3_inst.get_pin("D")
# NMOS3 drain
nmos4_pin = self.nmos4_inst.get_pin("D")
out_offset = vector(nmos4_pin.cx() + self.route_layer_pitch,
self.output_yoffset)
# Go up to metal2 for ease on all output pins
# self.add_via_center(layers=self.m1_stack,
# offset=pmos1_pin.center(),
# directions=("V", "V"))
# self.add_via_center(layers=self.m1_stack,
# offset=pmos3_pin.center(),
# directions=("V", "V"))
# self.add_via_center(layers=self.m1_stack,
# offset=nmos3_pin.center(),
# directions=("V", "V"))
# # Route in the A input track (top track)
# mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
# self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
# This extends the output to the edge of the cell
# self.add_via_center(layers=self.m1_stack,
# offset=mid_offset)
top_left_pin_offset = pmos1_pin.center()
top_right_pin_offset = pmos3_pin.center()
bottom_pin_offset = nmos4_pin.center()
# PMOS1 to output
self.add_path(self.route_layer, [top_left_pin_offset,
vector(top_left_pin_offset.x, out_offset.y),
out_offset])
# PMOS4 to output
self.add_path(self.route_layer, [top_right_pin_offset,
vector(top_right_pin_offset.x, out_offset.y),
out_offset])
# NMOS4 to output
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
self.add_path(self.route_layer,
[bottom_pin_offset, mid2_offset],
width=nmos4_pin.height())
mid3_offset = vector(out_offset.x, nmos4_pin.by())
self.add_path(self.route_layer, [mid3_offset, out_offset])
self.add_layout_pin_rect_center(text="Z",
layer=self.route_layer,
offset=out_offset)
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_frequency"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["nand4_leakage"]
total_power = self.return_power(power_dyn, power_leak)
return total_power
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1094
return transition_prob * (c_load + c_para)
def input_load(self):
"""Return the relative input capacitance of a single input"""
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""
Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall.
Input inverted by this stage.
"""
parasitic_delay = 3
return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
not inp_is_rise)
def build_graph(self, graph, inst_name, port_nets):
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -300,11 +300,4 @@ class precharge(design.design):
self.add_path(self.bitline_layer,
[left_pos, right_pos],
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

@ -18,9 +18,9 @@ class wordline_driver(design.design):
This is an AND (or NAND) with configurable drive strength to drive the wordlines.
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))
self.add_comment("size: {}".format(size))
self.add_comment("cols: {}".format(cols))
super().__init__(name)
if height is None:
@ -28,7 +28,7 @@ class wordline_driver(design.design):
self.height = b.height
else:
self.height = height
self.size = size
self.cols = cols
self.create_netlist()
if not OPTS.netlist_only:
@ -42,10 +42,25 @@ class wordline_driver(design.design):
def create_modules(self):
self.nand = factory.create(module_type="nand2_dec",
height=self.height)
self.driver = factory.create(module_type="inv_dec",
size=self.size,
height=self.nand.height)
try:
local_array_size = OPTS.local_array_size
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.driver)

View File

@ -63,17 +63,22 @@ class sram():
def verilog_write(self, 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):
""" Save all the output files while reporting time to do it as well. """
if not OPTS.netlist_only:
# Create a LEF physical model
start_time = datetime.datetime.now()
lefname = OPTS.output_path + self.s.name + ".lef"
debug.print_raw("LEF: Writing to {0}".format(lefname))
self.lef_write(lefname)
print_time("LEF", datetime.datetime.now(), start_time)
# Write the layout
start_time = datetime.datetime.now()
gdsname = OPTS.output_path + self.s.name + ".gds"
@ -81,6 +86,13 @@ class sram():
self.gds_write(gdsname)
print_time("GDS", datetime.datetime.now(), start_time)
# Create a LEF physical model
start_time = datetime.datetime.now()
lefname = OPTS.output_path + self.s.name + ".lef"
debug.print_raw("LEF: Writing to {0}".format(lefname))
self.lef_write(lefname)
print_time("LEF", datetime.datetime.now(), start_time)
# Save the spice file
start_time = datetime.datetime.now()
spname = OPTS.output_path + self.s.name + ".sp"
@ -137,3 +149,11 @@ class sram():
debug.print_raw("Verilog: Writing to {0}".format(vname))
self.verilog_write(vname)
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,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.add_inst(cr.name, cr)
self.connect_inst([])
else:
self.col_addr_bus_size[port] = cr.height
@ -470,7 +470,7 @@ class sram_1bank(sram_base):
layer_stack=layer_stack,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.add_inst(cr.name, cr)
self.connect_inst([])
else:
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,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.add_inst(cr.name, cr)
self.connect_inst([])
else:
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])
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 signal in self.control_logic_outputs[port]:
@ -567,7 +569,9 @@ class sram_1bank(sram_base):
offset=dest_pin.center())
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 bit in range(self.row_addr_size):
flop_name = "dout_{}".format(bit)
@ -600,7 +604,9 @@ class sram_1bank(sram_base):
offset=pin.center())
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.
for inst in self.data_dff_insts:
self.graph_inst_exclude.add(inst)
@ -612,7 +618,9 @@ class sram_1bank(sram_base):
self.graph_inst_exclude.add(inst)
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
for inst in self.row_addr_dff_insts:
self.graph_inst_exclude.add(inst)
@ -622,31 +630,21 @@ class sram_1bank(sram_base):
self.graph_inst_exclude.add(inst)
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
for inst in self.control_logic_insts:
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):
"""Gets the spice name of the target bitcell."""
"""
Gets the spice name of the target bitcell.
"""
# Sanity check in case it was forgotten
if inst_name.find('x') != 0:
inst_name = 'x'+inst_name
return self.bank_inst.mod.get_cell_name(inst_name+'.x'+self.bank_inst.name, row, col)
if inst_name.find("x") != 0:
inst_name = "x" + inst_name
return self.bank_inst.mod.get_cell_name(inst_name + ".x" + self.bank_inst.name, row, col)
def get_bank_num(self, inst_name, row, col):
return 0;
return 0

View File

@ -7,7 +7,7 @@
#
import datetime
import debug
from math import log
from math import log, ceil
from importlib import reload
from vector import vector
from globals import OPTS, print_time
@ -15,9 +15,6 @@ from design import design
from verilog import verilog
from lef import lef
from sram_factory import factory
from tech import drc
import numpy as np
import logical_effort
class sram_base(design, verilog, lef):
@ -36,16 +33,13 @@ class sram_base(design, verilog, lef):
self.bank_insts = []
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:
self.num_wmasks = 0
if not self.num_spare_cols:
self.num_spare_cols = 0
# For logical effort delay calculations.
self.all_mods_except_control_done = False
def add_pins(self):
""" 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):
self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_global_pex_labels(self):
"""
Add pex labels at the sram level for spice analysis
"""
# add pex labels for bitcells
for bank_num in range(len(self.bank_insts)):
bank = self.bank_insts[bank_num]
pex_data = bank.reverse_transformation_bitcell(bank.mod.bitcell.name)
pex_data = bank.reverse_transformation_bitcell(self.bitcell.name)
bank_offset = pex_data[0] # offset bank relative to sram
Q_offset = pex_data[1] # offset of storage relative to bank
@ -112,46 +108,61 @@ class sram_base(design, verilog, lef):
br = []
storage_layer_name = "m1"
bitline_layer_name = "m2"
bitline_layer_name = self.bitcell.get_pin("bl").layer
for cell in range(len(bank_offset)):
Q = [bank_offset[cell][0] + Q_offset[cell][0], bank_offset[cell][1] + Q_offset[cell][1]]
Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], bank_offset[cell][1] + Q_bar_offset[cell][1]]
Q = [bank_offset[cell][0] + Q_offset[cell][0],
bank_offset[cell][1] + Q_offset[cell][1]]
Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0],
bank_offset[cell][1] + Q_bar_offset[cell][1]]
OPTS.words_per_row = self.words_per_row
self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))) , storage_layer_name, Q)
self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))), storage_layer_name, Q_bar)
row = int(cell % (OPTS.num_words / self.words_per_row))
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)):
col = bl_meta[cell][0][2]
for bitline in range(len(bl_offsets[cell])):
bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0], float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]]
bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0],
float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]]
bl.append([bitline_location, bl_meta[cell][bitline][3], col])
for cell in range(len(br_offsets)):
col = br_meta[cell][0][2]
for bitline in range(len(br_offsets[cell])):
bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0], float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]]
br.append([bitline_location, br_meta[cell][bitline][3], col])
bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0],
float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]]
br.append([bitline_location, br_meta[cell][bitline][3], col])
for i in range(len(bl)):
self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]), bitline_layer_name, bl[i][0])
self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]),
bitline_layer_name, bl[i][0])
for i in range(len(br)):
self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]), bitline_layer_name, br[i][0])
self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]),
bitline_layer_name, br[i][0])
# add pex labels for control logic
for i in range (len(self.control_logic_insts)):
for i in range(len(self.control_logic_insts)):
instance = self.control_logic_insts[i]
control_logic_offset = instance.offset
for output in instance.mod.output_list:
pin = instance.mod.get_pin(output)
pin.transform([0,0], instance.mirror, instance.rotate)
offset = [control_logic_offset[0] + pin.center()[0], control_logic_offset[1] + pin.center()[1]]
self.add_layout_pin_rect_center("{0}{1}".format(pin.name,i), storage_layer_name, offset)
pin.transform([0, 0], instance.mirror, instance.rotate)
offset = [control_logic_offset[0] + pin.center()[0],
control_logic_offset[1] + pin.center()[1]]
self.add_layout_pin_rect_center("{0}{1}".format(pin.name, i),
storage_layer_name,
offset)
def create_netlist(self):
""" Netlist creation """
@ -370,10 +381,6 @@ class sram_base(design, verilog, lef):
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))
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("**************************************************\n")
# This causes unit test mismatch
# sp.write("* Created: {0}\n".format(datetime.datetime.now()))
# sp.write("* User: {0}\n".format(getpass.getuser()))
# sp.write(".global {0} {1}\n".format(spice["vdd_name"],
@ -630,50 +638,15 @@ class sram_base(design, verilog, lef):
def lvs_write(self, sp_name):
self.sp_write(sp_name, lvs_netlist=True)
def get_wordline_stage_efforts(self, inp_is_rise=True):
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""
stage_effort_list = []
# Clk_buf originates from the control logic so only the bank is related to the wordline path
# No loading on the wordline other than in the bank.
external_wordline_cout = 0
stage_effort_list += self.bank.determine_wordline_stage_efforts(external_wordline_cout, inp_is_rise)
def graph_exclude_bits(self, targ_row, targ_col):
"""
Excludes bits in column from being added to graph except target
"""
self.bank.graph_exclude_bits(targ_row, targ_col)
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
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bank.clear_exclude_bits()

View File

@ -6,15 +6,15 @@
# All rights reserved.
#
import debug
from math import log,sqrt,ceil
from importlib import reload
from math import log, sqrt, ceil
from globals import OPTS
from sram_factory import factory
class sram_config:
""" This is a structure that is used to hold the SRAM configuration options. """
def __init__(self, word_size, num_words, write_size = None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=0):
def __init__(self, word_size, num_words, write_size=None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=0):
self.word_size = word_size
self.num_words = num_words
self.write_size = write_size
@ -25,7 +25,7 @@ class sram_config:
# This will get over-written when we determine the organization
self.words_per_row = words_per_row
self.compute_sizes()
self.compute_sizes()
def set_local_config(self, module):
""" Copy all of the member variables to the given module for convenience """
@ -34,31 +34,31 @@ class sram_config:
# Copy all the variables to the local module
for member in members:
setattr(module,member,getattr(self,member))
setattr(module, member, getattr(self, member))
def compute_sizes(self):
""" Computes the organization of the memory using bitcell size by trying to make it square."""
bitcell = factory.create(module_type="bitcell")
debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.")
debug.check(self.num_banks in [1, 2, 4],
"Valid number of banks are 1 , 2 and 4.")
self.num_words_per_bank = self.num_words/self.num_banks
self.num_bits_per_bank = self.word_size*self.num_words_per_bank
self.num_words_per_bank = self.num_words / self.num_banks
self.num_bits_per_bank = self.word_size * self.num_words_per_bank
# If this was hard coded, don't dynamically compute it!
if not self.words_per_row:
# Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
self.bank_area = bitcell.width*bitcell.height*self.num_bits_per_bank
self.bank_area = bitcell.width * bitcell.height * self.num_bits_per_bank
self.bank_side_length = sqrt(self.bank_area)
# Estimate the words per row given the height of the bitcell and the square side length
self.tentative_num_cols = int(self.bank_side_length/bitcell.width)
self.tentative_num_cols = int(self.bank_side_length / bitcell.width)
self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size)
# Estimate the number of rows given the tentative words per row
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size)
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row * self.word_size)
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
self.recompute_sizes()
@ -70,57 +70,57 @@ class sram_config:
SRAM for testing.
"""
debug.info(1,"Recomputing with words per row: {}".format(self.words_per_row))
debug.info(1, "Recomputing with words per row: {}".format(self.words_per_row))
# If the banks changed
self.num_words_per_bank = self.num_words/self.num_banks
self.num_bits_per_bank = self.word_size*self.num_words_per_bank
self.num_words_per_bank = self.num_words / self.num_banks
self.num_bits_per_bank = self.word_size * self.num_words_per_bank
# Fix the number of columns and rows
self.num_cols = int(self.words_per_row*self.word_size)
self.num_rows_temp = int(self.num_words_per_bank/self.words_per_row)
self.num_cols = int(self.words_per_row * self.word_size)
self.num_rows_temp = int(self.num_words_per_bank / self.words_per_row)
self.num_rows = self.num_rows_temp + self.num_spare_rows
debug.info(1,"Rows: {} Cols: {}".format(self.num_rows_temp,self.num_cols))
debug.info(1, "Rows: {} Cols: {}".format(self.num_rows_temp, self.num_cols))
# Compute the address and bank sizes
self.row_addr_size = ceil(log(self.num_rows, 2))
self.col_addr_size = int(log(self.words_per_row, 2))
self.bank_addr_size = self.col_addr_size + self.row_addr_size
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
debug.info(1,"Row addr size: {}".format(self.row_addr_size)
debug.info(1, "Row addr size: {}".format(self.row_addr_size)
+ " Col addr size: {}".format(self.col_addr_size)
+ " Bank addr size: {}".format(self.bank_addr_size))
def estimate_words_per_row(self,tentative_num_cols, word_size):
def estimate_words_per_row(self, tentative_num_cols, word_size):
"""
This provides a heuristic rounded estimate for the number of words
per row.
"""
tentative_column_ways = tentative_num_cols / word_size
column_mux_sizes = [1, 2, 4, 8, 16]
# If we are double, we may want a larger column mux
if tentative_column_ways > 2 * column_mux_sizes[-1]:
debug.warning("Extremely large number of columns for 16-way maximum column mux.")
if tentative_num_cols < 1.5*word_size:
return 1
elif tentative_num_cols < 3*word_size:
return 2
elif tentative_num_cols < 6*word_size:
return 4
else:
if tentative_num_cols > 16*word_size:
debug.warning("Reaching column mux size limit. Consider increasing above 8-way.")
return 8
closest_way = min(column_mux_sizes, key=lambda x: abs(x - tentative_column_ways))
def amend_words_per_row(self,tentative_num_rows, words_per_row):
return closest_way
def amend_words_per_row(self, tentative_num_rows, words_per_row):
"""
This picks the number of words per row more accurately by limiting
it to a minimum and maximum.
"""
# Recompute the words per row given a hard max
if(not OPTS.is_unit_test and tentative_num_rows > 512):
debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048")
return int(words_per_row*tentative_num_rows/512)
debug.check(tentative_num_rows * words_per_row <= 4096,
"Number of words exceeds 2048")
return int(words_per_row * tentative_num_rows / 512)
# Recompute the words per row given a hard min
if(not OPTS.is_unit_test and tentative_num_rows < 16):
debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows))
return int(words_per_row*tentative_num_rows/16)
if (not OPTS.is_unit_test and tentative_num_rows < 16):
debug.check(tentative_num_rows * words_per_row >= 16,
"Minimum number of rows is 16, but given {0}".format(tentative_num_rows))
return int(words_per_row * tentative_num_rows / 16)
return words_per_row

View File

@ -8,7 +8,7 @@
#
import unittest
from testutils import *
import sys,os
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
@ -16,7 +16,7 @@ from sram_factory import factory
import debug
@unittest.skip("SKIPPING 04_and4_dec_test")
# @unittest.skip("SKIPPING 04_and4_dec_test")
class and4_dec_test(openram_test):
def runTest(self):

View File

@ -8,7 +8,7 @@
#
import unittest
from testutils import *
import sys,os
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
@ -16,7 +16,7 @@ from sram_factory import factory
import debug
class single_level_column_mux_1rw_1r_test(openram_test):
class column_mux_1rw_1r_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
@ -28,11 +28,11 @@ class single_level_column_mux_1rw_1r_test(openram_test):
globals.setup_bitcell()
debug.info(2, "Checking column mux port 0")
tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0")
tx = factory.create(module_type="column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(tx)
debug.info(2, "Checking column mux port 1")
tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl1", bitcell_br="br1")
tx = factory.create(module_type="column_mux", tx_size=8, bitcell_bl="bl1", bitcell_br="br1")
self.local_check(tx)
globals.end_openram()

View File

@ -8,16 +8,15 @@
#
import unittest
from testutils import *
import sys,os
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
#@unittest.skip("SKIPPING 04_driver_test")
class single_level_column_mux_pbitcell_test(openram_test):
class column_mux_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
@ -31,12 +30,12 @@ class single_level_column_mux_pbitcell_test(openram_test):
factory.reset()
debug.info(2, "Checking column mux for pbitcell (innermost connections)")
tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0")
tx = factory.create(module_type="column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(tx)
factory.reset()
debug.info(2, "Checking column mux for pbitcell (outermost connections)")
tx = factory.create(module_type="single_level_column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2")
tx = factory.create(module_type="column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2")
self.local_check(tx)
globals.end_openram()

View File

@ -8,7 +8,7 @@
#
import unittest
from testutils import *
import sys,os
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
@ -16,7 +16,7 @@ from sram_factory import factory
import debug
class single_level_column_mux_test(openram_test):
class column_mux_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
@ -24,7 +24,7 @@ class single_level_column_mux_test(openram_test):
# check single level column mux in single port
debug.info(2, "Checking column mux")
tx = factory.create(module_type="single_level_column_mux", tx_size=8)
tx = factory.create(module_type="column_mux", tx_size=8)
self.local_check(tx)
globals.end_openram()

View File

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

View File

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

39
compiler/tests/04_pand4_test.py Executable file
View File

@ -0,0 +1,39 @@
#!/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
import debug
class pand4_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
global verify
import verify
import pand4
debug.info(2, "Testing pand4 gate 4x")
a = pand4.pand4(name="pand4x4", size=4)
self.local_check(a)
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
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,37 @@
#!/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 pnand4_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
debug.info(2, "Checking 4-input nand gate")
tx = factory.create(module_type="pnand4", size=1)
self.local_check(tx)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -25,7 +25,7 @@ class wordline_driver_test(openram_test):
# check wordline driver for single port
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)
globals.end_openram()

View File

@ -8,33 +8,40 @@
#
import unittest
from testutils import *
import sys,os
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class hierarchical_decoder_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# Use the 2 port cell since it is usually bigger/easier
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
# Checks 2x4 and 2-input NAND decoder
#debug.info(1, "Testing 16 row sample for hierarchical_decoder")
#a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
#self.local_check(a)
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
self.local_check(a)
# Checks 2x4 and 2-input NAND decoder with non-power-of-two
#debug.info(1, "Testing 17 row sample for hierarchical_decoder")
#a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
#self.local_check(a)
debug.info(1, "Testing 17 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
self.local_check(a)
# Checks 2x4 with 3x8 and 2-input NAND decoder
#debug.info(1, "Testing 32 row sample for hierarchical_decoder")
#a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
#self.local_check(a)
debug.info(1, "Testing 32 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
self.local_check(a)
# Checks 3 x 2x4 and 3-input NAND decoder
debug.info(1, "Testing 64 row sample for hierarchical_decoder")
@ -42,18 +49,20 @@ class hierarchical_decoder_test(openram_test):
self.local_check(a)
# Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two
#debug.info(1, "Testing 132 row sample for hierarchical_decoder")
#a = factory.create(module_type="hierarchical_decoder", num_outputs=132)
#self.local_check(a)
# Checks 3 x 3x8 and 3-input NAND decoder
#debug.info(1, "Testing 512 row sample for hierarchical_decoder")
#a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
#self.local_check(a)
debug.info(1, "Testing 132 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=132)
self.local_check(a)
# Checks 3 x 3x8 and 3-input NAND decoder
debug.info(1, "Testing 512 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
self.local_check(a)
# Checks 3 x 4x16 and 4-input NAND decoder
debug.info(1, "Testing 4096 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=4096)
self.local_check(a)
globals.end_openram()
# run the test from the command line

View File

@ -8,14 +8,15 @@
#
import unittest
from testutils import *
import sys,os
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 hierarchical_predecode4x16_test")
# @unittest.skip("SKIPPING hierarchical_predecode4x16_test")
class hierarchical_predecode4x16_test(openram_test):
def runTest(self):

View File

@ -14,7 +14,8 @@ from globals import OPTS
from sram_factory import factory
import debug
class single_level_column_mux_test(openram_test):
class column_mux_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
@ -26,27 +27,27 @@ class single_level_column_mux_test(openram_test):
globals.setup_bitcell()
debug.info(1, "Testing sample for 2-way column_mux_array port 0")
a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=4, bitcell_bl="bl0", bitcell_br="br0")
a = factory.create(module_type="column_mux_array", columns=8, word_size=4, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 2-way column_mux_array port 1")
a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=4, bitcell_bl="bl1", bitcell_br="br1")
a = factory.create(module_type="column_mux_array", columns=8, word_size=4, bitcell_bl="bl1", bitcell_br="br1")
self.local_check(a)
debug.info(1, "Testing sample for 4-way column_mux_array port 0")
a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0")
a = factory.create(module_type="column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 4-way column_mux_array port 1")
a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl1", bitcell_br="br1")
a = factory.create(module_type="column_mux_array", columns=8, word_size=2, bitcell_bl="bl1", bitcell_br="br1")
self.local_check(a)
debug.info(1, "Testing sample for 8-way column_mux_array port 0")
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=2, bitcell_bl="bl0", bitcell_br="br0")
a = factory.create(module_type="column_mux_array", columns=16, word_size=2, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 8-way column_mux_array port 1")
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=2, bitcell_bl="bl1", bitcell_br="br1")
a = factory.create(module_type="column_mux_array", columns=16, word_size=2, bitcell_bl="bl1", bitcell_br="br1")
self.local_check(a)
globals.end_openram()

View File

@ -7,19 +7,19 @@
# All rights reserved.
#
from testutils import *
import sys,os
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class single_level_column_mux_pbitcell_test(openram_test):
class column_mux_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
import single_level_column_mux_array
# check single level column mux array in multi-port
OPTS.bitcell = "pbitcell"
@ -29,19 +29,19 @@ class single_level_column_mux_pbitcell_test(openram_test):
factory.reset()
debug.info(1, "Testing sample for 2-way column_mux_array in multi-port")
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0")
a = factory.create(module_type="column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 4-way column_mux_array in multi-port")
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0")
a = factory.create(module_type="column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)")
a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0")
a = factory.create(module_type="column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)
debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)")
a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2", column_offset=3)
a = factory.create(module_type="column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2", column_offset=3)
self.local_check(a)
globals.end_openram()

View File

@ -14,22 +14,23 @@ from globals import OPTS
from sram_factory import factory
import debug
class single_level_column_mux_test(openram_test):
class column_mux_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
debug.info(1, "Testing sample for 2-way column_mux_array")
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8)
a = factory.create(module_type="column_mux_array", columns=16, word_size=8)
self.local_check(a)
debug.info(1, "Testing sample for 4-way column_mux_array")
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4)
a = factory.create(module_type="column_mux_array", columns=16, word_size=4)
self.local_check(a)
debug.info(1, "Testing sample for 8-way column_mux_array")
a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4)
a = factory.create(module_type="column_mux_array", columns=32, word_size=4)
self.local_check(a)
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",
cols=4,
rows=4,
rbl=[1, 1],
add_rbl=[0, 0])
rbl=[1, 1])
self.local_check(a)
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,
rows=4,
rbl=[1, 1],
add_rbl=[1, 0])
left_rbl=[0])
self.local_check(a)
debug.info(2, "Testing 4x4 array left and right replica for cell_1rw_1r")
a = factory.create(module_type="replica_bitcell_array",
cols=4,
rows=4,
rbl=[1, 1])
rbl=[1, 1],
left_rbl=[0],
right_rbl=[1])
self.local_check(a)
@ -55,7 +56,7 @@ class replica_bitcell_array_1rw_1r_test(openram_test):
cols=4,
rows=4,
rbl=[1, 1],
add_rbl=[0, 1])
right_rbl=[1])
self.local_check(a)
globals.end_openram()

View File

@ -27,7 +27,7 @@ class replica_pbitcell_array_test(openram_test):
OPTS.num_w_ports = 0
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)
OPTS.bitcell = "pbitcell"
@ -39,7 +39,7 @@ class replica_pbitcell_array_test(openram_test):
factory.reset()
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)
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,20 +15,20 @@ from sram_factory import factory
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):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
debug.info(2, "Testing 2 x 4x4 global bitcell array for 6t_cell without replica")
a = factory.create(module_type="global_bitcell_array", cols=[4, 4], rows=4, ports=[0])
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])
# 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)
# 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()

View File

@ -28,20 +28,21 @@ class local_bitcell_array_1rw_1r_test(openram_test):
globals.setup_bitcell()
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)
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])
self.local_check(a)
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], right_rbl=[1])
self.local_check(a)
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])
self.local_check(a)
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], left_rbl=[0], right_rbl=[1])
self.local_check(a)
globals.end_openram()

View File

@ -23,11 +23,11 @@ class local_bitcell_array_test(openram_test):
globals.init_openram(config_file)
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)
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)
globals.end_openram()

View File

@ -27,11 +27,11 @@ class port_address_1rw_1r_test(openram_test):
globals.setup_bitcell()
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)
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)
globals.end_openram()

View File

@ -21,11 +21,11 @@ class port_address_test(openram_test):
globals.init_openram(config_file)
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)
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)
globals.end_openram()

View File

@ -8,13 +8,14 @@
#
import unittest
from testutils import *
import sys,os
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):

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