Merge branch 'dev' into analytical_cleanup

This commit is contained in:
Hunter Nichols 2019-08-06 14:51:30 -07:00
commit 2efc0a3983
105 changed files with 2331 additions and 2229 deletions

View File

@ -0,0 +1,43 @@
# 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.
#
class delay_data():
"""
This is the delay class to represent the delay information
Time is 50% of the signal to 50% of reference signal delay.
Slew is the 10% of the signal to 90% of signal
"""
def __init__(self, delay=0.0, slew=0.0):
""" init function support two init method"""
# will take single input as a coordinate
self.delay = delay
self.slew = slew
def __str__(self):
""" override print function output """
return "Delay Data: Delay "+str(self.delay)+", Slew "+str(self.slew)+""
def __add__(self, other):
"""
Override - function (left), for delay_data: a+b != b+a
"""
assert isinstance(other,delay_data)
return delay_data(other.delay + self.delay,
other.slew)
def __radd__(self, other):
"""
Override - function (right), for delay_data: a+b != b+a
"""
assert isinstance(other,delay_data)
return delay_data(other.delay + self.delay,
self.slew)

View File

@ -10,8 +10,9 @@ from vector import vector
from pin_layout import pin_layout
class timing_graph():
"""Implements a directed graph
Nodes are currently just Strings.
"""
Implements a directed graph
Nodes are currently just Strings.
"""
def __init__(self):
@ -20,30 +21,34 @@ class timing_graph():
def add_edge(self, src_node, dest_node):
"""Adds edge to graph. Nodes added as well if they do not exist."""
src_node = src_node.lower()
dest_node = dest_node.lower()
self.graph[src_node].add(dest_node)
def add_node(self, node):
"""Add node to graph with no edges"""
node = node.lower()
if not node in self.graph:
self.graph[node] = set()
def remove_edges(self, node):
"""Helper function to remove edges, useful for removing vdd/gnd"""
node = node.lower()
self.graph[node] = set()
def get_all_paths(self, src_node, dest_node, rmv_rail_nodes=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()
dest_node = dest_node.lower()
#Remove vdd and gnd by default
#Will require edits if separate supplies are implemented.
if rmv_rail_nodes:
#Names are also assumed.
# Remove vdd and gnd by default
# Will require edits if separate supplies are implemented.
if remove_rail_nodes:
# Names are also assumed.
self.remove_edges('vdd')
self.remove_edges('gnd')
@ -57,11 +62,20 @@ class timing_graph():
# Call the recursive helper function to print all paths
self.get_all_paths_util(src_node, dest_node, visited, path)
debug.info(2, "Paths found={}".format(len(self.all_paths)))
if reduce_paths:
self.reduce_paths()
return self.all_paths
def reduce_paths(self):
""" Remove any path that is a subset of another path """
self.all_paths = [p1 for p1 in self.all_paths if not any(set(p1)<=set(p2) for p2 in self.all_paths if p1 is not p2)]
def get_all_paths_util(self, cur_node, dest_node, visited, path):
"""Recursive function to find all paths in a Depth First Search manner"""
# Mark the current node as visited and store in path
visited.add(cur_node)
path.append(cur_node)
@ -72,7 +86,7 @@ class timing_graph():
self.all_paths.append(copy.deepcopy(path))
else:
# If current vertex is not destination
#Recur for all the vertices adjacent to this vertex
# 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)
@ -83,4 +97,5 @@ class timing_graph():
def __str__(self):
""" override print function output """
return "Nodes: {}\nEdges:{} ".format(list(self.graph), self.graph)
return "Nodes: {}\nEdges:{} ".format(list(self.graph), self.graph)

View File

@ -10,6 +10,9 @@ import re
import os
import math
import tech
from delay_data import *
from wire_spice_model import *
from power_data import *
class spice():
@ -25,6 +28,7 @@ class spice():
def __init__(self, name):
self.name = name
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
# Holds subckts/mods for this module
self.mods = []
# Holds the pins for this module
@ -49,27 +53,32 @@ class spice():
def add_comment(self, comment):
""" Add a comment to the spice file """
try:
self.commments
except:
self.comments = []
else:
self.comments.append(comment)
self.comments.append(comment)
def add_pin(self, name, pin_type="INOUT"):
""" Adds a pin to the pins list. Default type is INOUT signal. """
self.pins.append(name)
self.pin_type[name]=pin_type
debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(name,pin_type))
def add_pin_list(self, pin_list, pin_type_list="INOUT"):
def add_pin_list(self, pin_list, pin_type="INOUT"):
""" Adds a pin_list to the pins list """
# The type list can be a single type for all pins
# or a list that is the same length as the pin list.
if type(pin_type_list)==str:
if type(pin_type)==str:
for pin in pin_list:
self.add_pin(pin,pin_type_list)
elif len(pin_type_list)==len(pin_list):
for (pin,ptype) in zip(pin_list, pin_type_list):
debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(pin,pin_type))
self.add_pin(pin,pin_type)
elif len(pin_type)==len(pin_list):
for (pin,ptype) in zip(pin_list, pin_type):
debug.check(ptype in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(pin,ptype))
self.add_pin(pin,ptype)
else:
debug.error("Mismatch in type and pin list lengths.", -1)
@ -87,7 +96,9 @@ class spice():
def get_pin_type(self, name):
""" Returns the type of the signal pin. """
return self.pin_type[name]
pin_type = self.pin_type[name]
debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(name,pin_type))
return pin_type
def get_pin_dir(self, name):
""" Returns the direction of the pin. (Supply/ground are INOUT). """
@ -238,9 +249,12 @@ class spice():
sp.write("\n.SUBCKT {0} {1}\n".format(self.name,
" ".join(self.pins)))
for pin in self.pins:
sp.write("* {1:6}: {0} \n".format(pin,self.pin_type[pin]))
for line in self.comments:
sp.write("* {}\n".format(line))
# every instance must have a set of connections, even if it is empty.
if len(self.insts)!=len(self.conns):
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
@ -383,102 +397,3 @@ class spice():
def return_power(self, dynamic=0.0, leakage=0.0):
return power_data(dynamic, leakage)
class delay_data:
"""
This is the delay class to represent the delay information
Time is 50% of the signal to 50% of reference signal delay.
Slew is the 10% of the signal to 90% of signal
"""
def __init__(self, delay=0.0, slew=0.0):
""" init function support two init method"""
# will take single input as a coordinate
self.delay = delay
self.slew = slew
def __str__(self):
""" override print function output """
return "Delay Data: Delay "+str(self.delay)+", Slew "+str(self.slew)+""
def __add__(self, other):
"""
Override - function (left), for delay_data: a+b != b+a
"""
assert isinstance(other,delay_data)
return delay_data(other.delay + self.delay,
other.slew)
def __radd__(self, other):
"""
Override - function (right), for delay_data: a+b != b+a
"""
assert isinstance(other,delay_data)
return delay_data(other.delay + self.delay,
self.slew)
class power_data:
"""
This is the power class to represent the power information
Dynamic and leakage power are stored as a single object with this class.
"""
def __init__(self, dynamic=0.0, leakage=0.0):
""" init function support two init method"""
# will take single input as a coordinate
self.dynamic = dynamic
self.leakage = leakage
def __str__(self):
""" override print function output """
return "Power Data: Dynamic "+str(self.dynamic)+", Leakage "+str(self.leakage)+" in nW"
def __add__(self, other):
"""
Override - function (left), for power_data: a+b != b+a
"""
assert isinstance(other,power_data)
return power_data(other.dynamic + self.dynamic,
other.leakage + self.leakage)
def __radd__(self, other):
"""
Override - function (left), for power_data: a+b != b+a
"""
assert isinstance(other,power_data)
return power_data(other.dynamic + self.dynamic,
other.leakage + self.leakage)
class wire_spice_model:
"""
This is the spice class to represent a wire
"""
def __init__(self, lump_num, wire_length, wire_width):
self.lump_num = lump_num # the number of segment the wire delay has
self.wire_c = self.cal_wire_c(wire_length, wire_width) # c in each segment
self.wire_r = self.cal_wire_r(wire_length, wire_width) # r in each segment
def cal_wire_c(self, wire_length, wire_width):
from tech import spice
total_c = spice["wire_unit_c"] * wire_length * wire_width
wire_c = total_c / self.lump_num
return wire_c
def cal_wire_r(self, wire_length, wire_width):
from tech import spice
total_r = spice["wire_unit_r"] * wire_length / wire_width
wire_r = total_r / self.lump_num
return wire_r
def return_input_cap(self):
return 0.5 * self.wire_c * self.lump_num
def return_delay_over_wire(self, slew, swing = 0.5):
# delay will be sum of arithmetic sequence start from
# rc to self.lump_num*rc with step of rc
swing_factor = abs(math.log(1-swing)) # time constant based on swing
sum_factor = (1+self.lump_num) * self.lump_num * 0.5 # sum of the arithmetic sequence
delay = sum_factor * swing_factor * self.wire_r * self.wire_c
slew = delay * 2 + slew
result= delay_data(delay, slew)
return result

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.
#
class power_data():
"""
This is the power class to represent the power information
Dynamic and leakage power are stored as a single object with this class.
"""
def __init__(self, dynamic=0.0, leakage=0.0):
""" init function support two init method"""
# will take single input as a coordinate
self.dynamic = dynamic
self.leakage = leakage
def __str__(self):
""" override print function output """
return "Power Data: Dynamic "+str(self.dynamic)+", Leakage "+str(self.leakage)+" in nW"
def __add__(self, other):
"""
Override - function (left), for power_data: a+b != b+a
"""
assert isinstance(other,power_data)
return power_data(other.dynamic + self.dynamic,
other.leakage + self.leakage)
def __radd__(self, other):
"""
Override - function (left), for power_data: a+b != b+a
"""
assert isinstance(other,power_data)
return power_data(other.dynamic + self.dynamic,
other.leakage + self.leakage)

View File

@ -0,0 +1,42 @@
# 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.
#
class wire_spice_model():
"""
This is the spice class to represent a wire
"""
def __init__(self, lump_num, wire_length, wire_width):
self.lump_num = lump_num # the number of segment the wire delay has
self.wire_c = self.cal_wire_c(wire_length, wire_width) # c in each segment
self.wire_r = self.cal_wire_r(wire_length, wire_width) # r in each segment
def cal_wire_c(self, wire_length, wire_width):
from tech import spice
total_c = spice["wire_unit_c"] * wire_length * wire_width
wire_c = total_c / self.lump_num
return wire_c
def cal_wire_r(self, wire_length, wire_width):
from tech import spice
total_r = spice["wire_unit_r"] * wire_length / wire_width
wire_r = total_r / self.lump_num
return wire_r
def return_input_cap(self):
return 0.5 * self.wire_c * self.lump_num
def return_delay_over_wire(self, slew, swing = 0.5):
# delay will be sum of arithmetic sequence start from
# rc to self.lump_num*rc with step of rc
swing_factor = abs(math.log(1-swing)) # time constant based on swing
sum_factor = (1+self.lump_num) * self.lump_num * 0.5 # sum of the arithmetic sequence
delay = sum_factor * swing_factor * self.wire_r * self.wire_c
slew = delay * 2 + slew
result= delay_data(delay, slew)
return result

View File

@ -42,34 +42,41 @@ class bitcell(design.design):
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
return logical_effort.logical_effort('bitline', size, cin, load, parasitic_delay, False)
def list_all_wl_names(self):
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = ["wl"]
return row_pins
def list_all_bitline_names(self):
def get_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """
column_pins = ["bl", "br"]
return column_pins
def list_all_bl_names(self):
def get_all_bl_names(self):
""" Creates a list of all bl pins names """
column_pins = ["bl"]
return column_pins
def list_all_br_names(self):
def get_all_br_names(self):
""" Creates a list of all br pins names """
column_pins = ["br"]
return column_pins
def get_bl_name(self):
def get_bl_name(self, port=0):
"""Get bl name"""
debug.check(port==0,"One port for bitcell only.")
return "bl"
def get_br_name(self):
def get_br_name(self, port=0):
"""Get bl name"""
debug.check(port==0,"One port for bitcell only.")
return "br"
def get_wl_name(self, port=0):
"""Get wl name"""
debug.check(port==0,"One port for bitcell only.")
return "wl"
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
@ -96,4 +103,4 @@ class bitcell(design.design):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
self.add_graph_edges(graph, port_nets)

View File

@ -43,7 +43,7 @@ class bitcell_1rw_1r(design.design):
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
def list_bitcell_pins(self, col, row):
def get_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
bitcell_pins = ["bl0_{0}".format(col),
"br0_{0}".format(col),
@ -55,53 +55,60 @@ class bitcell_1rw_1r(design.design):
"gnd"]
return bitcell_pins
def list_all_wl_names(self):
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = ["wl0", "wl1"]
return row_pins
def list_all_bitline_names(self):
def get_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """
column_pins = ["bl0", "br0", "bl1", "br1"]
return column_pins
def list_all_bl_names(self):
def get_all_bl_names(self):
""" Creates a list of all bl pins names """
column_pins = ["bl0", "bl1"]
return column_pins
def list_all_br_names(self):
def get_all_br_names(self):
""" Creates a list of all br pins names """
column_pins = ["br0", "br1"]
return column_pins
def list_read_bl_names(self):
def get_read_bl_names(self):
""" Creates a list of bl pin names associated with read ports """
column_pins = ["bl0", "bl1"]
return column_pins
def list_read_br_names(self):
def get_read_br_names(self):
""" Creates a list of br pin names associated with read ports """
column_pins = ["br0", "br1"]
return column_pins
def list_write_bl_names(self):
def get_write_bl_names(self):
""" Creates a list of bl pin names associated with write ports """
column_pins = ["bl0"]
return column_pins
def list_write_br_names(self):
def get_write_br_names(self):
""" Creates a list of br pin names asscociated with write ports"""
column_pins = ["br0"]
return column_pins
def get_bl_name(self, port=0):
"""Get bl name by port"""
debug.check(port<2,"Two ports for bitcell_1rw_1r only.")
return "bl{}".format(port)
def get_br_name(self, port=0):
"""Get bl name by port"""
debug.check(port<2,"Two ports for bitcell_1rw_1r only.")
return "br{}".format(port)
def get_wl_name(self, port=0):
"""Get wl name by port"""
debug.check(port<2,"Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port)
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""

View File

@ -43,7 +43,7 @@ class bitcell_1w_1r(design.design):
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
def list_bitcell_pins(self, col, row):
def get_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
bitcell_pins = ["bl0_{0}".format(col),
"br0_{0}".format(col),
@ -55,42 +55,42 @@ class bitcell_1w_1r(design.design):
"gnd"]
return bitcell_pins
def list_all_wl_names(self):
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = ["wl0", "wl1"]
return row_pins
def list_all_bitline_names(self):
def get_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """
column_pins = ["bl0", "br0", "bl1", "br1"]
return column_pins
def list_all_bl_names(self):
def get_all_bl_names(self):
""" Creates a list of all bl pins names """
column_pins = ["bl0", "bl1"]
return column_pins
def list_all_br_names(self):
def get_all_br_names(self):
""" Creates a list of all br pins names """
column_pins = ["br0", "br1"]
return column_pins
def list_read_bl_names(self):
def get_read_bl_names(self):
""" Creates a list of bl pin names associated with read ports """
column_pins = ["bl0", "bl1"]
return column_pins
def list_read_br_names(self):
def get_read_br_names(self):
""" Creates a list of br pin names associated with read ports """
column_pins = ["br0", "br1"]
return column_pins
def list_write_bl_names(self):
def get_write_bl_names(self):
""" Creates a list of bl pin names associated with write ports """
column_pins = ["bl0"]
return column_pins
def list_write_br_names(self):
def get_write_br_names(self):
""" Creates a list of br pin names asscociated with write ports"""
column_pins = ["br0"]
return column_pins
@ -103,6 +103,11 @@ class bitcell_1w_1r(design.design):
"""Get bl name by port"""
return "br{}".format(port)
def get_wl_name(self, port=0):
"""Get wl name by port"""
debug.check(port<2,"Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port)
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
@ -134,6 +139,6 @@ class bitcell_1w_1r(design.design):
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
#Edges hardcoded here. Essentially wl->bl/br for both ports.
# Port 0 edges
graph.add_edge(pin_dict["wl0"], pin_dict["bl0"])
graph.add_edge(pin_dict["wl0"], pin_dict["br0"])
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"])
graph.add_edge(pin_dict["wl1"], pin_dict["br1"])
# Port 1 is a write port, so its timing is not considered here.

View File

@ -0,0 +1,45 @@
# 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
class dummy_bitcell_1rw_1r(design.design):
"""
A single bit cell which is forced to store a 0.
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. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("dummy_cell_1rw_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1rw_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "dummy_cell_1rw_1r")
debug.info(2, "Create dummy bitcell 1rw+1r object")
self.width = dummy_bitcell_1rw_1r.width
self.height = dummy_bitcell_1rw_1r.height
self.pin_map = dummy_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width.
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets):
"""Dummy bitcells are cannot form a path and be part of the timing graph"""
return

View File

@ -0,0 +1,45 @@
# 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
class dummy_bitcell_1w_1r(design.design):
"""
A single bit cell which is forced to store a 0.
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. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("dummy_cell_1w_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1w_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "dummy_cell_1w_1r")
debug.info(2, "Create dummy bitcell 1w+1r object")
self.width = dummy_bitcell_1w_1r.width
self.height = dummy_bitcell_1w_1r.height
self.pin_map = dummy_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width.
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets):
"""Dummy bitcells are cannot form a path and be part of the timing graph"""
return

View File

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

View File

@ -20,21 +20,30 @@ class pbitcell(design.design):
with a variable number of read/write, write, and read ports
"""
def __init__(self, name, replica_bitcell=False):
def __init__(self, name, replica_bitcell=False, dummy_bitcell=False):
self.num_rw_ports = OPTS.num_rw_ports
self.num_w_ports = OPTS.num_w_ports
self.num_r_ports = OPTS.num_r_ports
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
self.replica_bitcell = replica_bitcell
self.dummy_bitcell = dummy_bitcell
design.design.__init__(self, name)
debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
self.num_w_ports,
self.num_r_ports))
info_string = "{0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
self.num_w_ports,
self.num_r_ports)
debug.info(2, "create a multi-port bitcell with {}".format(info_string))
self.add_comment(info_string)
if self.dummy_bitcell:
self.add_comment("dummy bitcell")
if self.replica_bitcell:
self.add_comment("replica bitcell")
self.create_netlist()
# We must always create the bitcell layout because some transistor sizes in the other netlists depend on it
# We must always create the bitcell layout
# because some transistor sizes in the other netlists depend on it
self.create_layout()
self.add_boundary()
@ -376,14 +385,20 @@ class pbitcell(design.design):
# iterate over the number of read/write ports
for k in range(0,self.num_rw_ports):
bl_name = self.rw_bl_names[k]
br_name = self.rw_br_names[k]
if self.dummy_bitcell:
bl_name += "_noconn"
br_name += "_noconn"
# add read/write transistors
self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k),
mod=self.readwrite_nmos)
self.connect_inst([self.rw_bl_names[k], self.rw_wl_names[k], self.Q, "gnd"])
self.connect_inst([bl_name, self.rw_wl_names[k], self.Q, "gnd"])
self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k),
mod=self.readwrite_nmos)
self.connect_inst([self.Q_bar, self.rw_wl_names[k], self.rw_br_names[k], "gnd"])
self.connect_inst([self.Q_bar, self.rw_wl_names[k], br_name, "gnd"])
def place_readwrite_ports(self):
""" Places read/write ports in the bit cell """
@ -450,14 +465,20 @@ class pbitcell(design.design):
# iterate over the number of write ports
for k in range(0,self.num_w_ports):
bl_name = self.w_bl_names[k]
br_name = self.w_br_names[k]
if self.dummy_bitcell:
bl_name += "_noconn"
br_name += "_noconn"
# add write transistors
self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k),
mod=self.write_nmos)
self.connect_inst([self.w_bl_names[k], self.w_wl_names[k], self.Q, "gnd"])
self.connect_inst([bl_name, self.w_wl_names[k], self.Q, "gnd"])
self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k),
mod=self.write_nmos)
self.connect_inst([self.Q_bar, self.w_wl_names[k], self.w_br_names[k], "gnd"])
self.connect_inst([self.Q_bar, self.w_wl_names[k], br_name, "gnd"])
def place_write_ports(self):
""" Places write ports in the bit cell """
@ -532,6 +553,12 @@ class pbitcell(design.design):
# iterate over the number of read ports
for k in range(0,self.num_r_ports):
bl_name = self.r_bl_names[k]
br_name = self.r_br_names[k]
if self.dummy_bitcell:
bl_name += "_noconn"
br_name += "_noconn"
# add read-access transistors
self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k),
mod=self.read_nmos)
@ -544,11 +571,11 @@ class pbitcell(design.design):
# add read transistors
self.read_nmos_left[k] = self.add_inst(name="read_nmos_left{}".format(k),
mod=self.read_nmos)
self.connect_inst([self.r_bl_names[k], self.r_wl_names[k], "RA_to_R_left{}".format(k), "gnd"])
self.connect_inst([bl_name, self.r_wl_names[k], "RA_to_R_left{}".format(k), "gnd"])
self.read_nmos_right[k] = self.add_inst(name="read_nmos_right{}".format(k),
mod=self.read_nmos)
self.connect_inst(["RA_to_R_right{}".format(k), self.r_wl_names[k], self.r_br_names[k], "gnd"])
self.connect_inst(["RA_to_R_right{}".format(k), self.r_wl_names[k], br_name, "gnd"])
def place_read_ports(self):
""" Places the read ports in the bit cell """
@ -686,8 +713,10 @@ class pbitcell(design.design):
port_contact_offest = left_port_transistors[k].get_pin("S").center()
bl_offset = vector(bl_positions[k].x, port_contact_offest.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=port_contact_offest)
# Leave bitline disconnected if a dummy cell
if not self.dummy_bitcell:
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=port_contact_offest)
self.add_path("metal2", [port_contact_offest, bl_offset], width=contact.m1m2.height)
@ -695,8 +724,10 @@ class pbitcell(design.design):
port_contact_offest = right_port_transistors[k].get_pin("D").center()
br_offset = vector(br_positions[k].x, port_contact_offest.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=port_contact_offest)
# Leave bitline disconnected if a dummy cell
if not self.dummy_bitcell:
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=port_contact_offest)
self.add_path("metal2", [port_contact_offest, br_offset], width=contact.m1m2.height)
@ -846,7 +877,7 @@ class pbitcell(design.design):
implant_type="n",
well_type="n")
def list_bitcell_pins(self, col, row):
def get_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
bitcell_pins = []
for port in range(self.total_ports):
@ -858,12 +889,12 @@ class pbitcell(design.design):
bitcell_pins.append("gnd")
return bitcell_pins
def list_all_wl_names(self):
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
wordline_names = self.rw_wl_names + self.w_wl_names + self.r_wl_names
return wordline_names
def list_all_bitline_names(self):
def get_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """
bitline_pins = []
for port in range(self.total_ports):
@ -871,15 +902,13 @@ class pbitcell(design.design):
bitline_pins.append("br{0}".format(port))
return bitline_pins
def list_all_bl_names(self):
def get_all_bl_names(self):
""" Creates a list of all bl pins names """
bl_pins = self.rw_bl_names + self.w_bl_names + self.r_bl_names
return bl_pins
return self.rw_bl_names + self.w_bl_names + self.r_bl_names
def list_all_br_names(self):
def get_all_br_names(self):
""" Creates a list of all br pins names """
br_pins = self.rw_br_names + self.w_br_names + self.r_br_names
return br_pins
return self.rw_br_names + self.w_br_names + self.r_br_names
def route_rbc_short(self):
""" route the short from Q_bar to gnd necessary for the replica bitcell """
@ -897,7 +926,13 @@ class pbitcell(design.design):
def get_br_name(self, port=0):
"""Get bl name by port"""
return "br{}".format(port)
return "br{}".format(port)
def get_wl_name(self, port=0):
"""Get wl name by port"""
debug.check(port<2,"Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port)
def analytical_delay(self, corner, slew, load=0, swing = 0.5):
parasitic_delay = 1
@ -927,11 +962,16 @@ class pbitcell(design.design):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph for pbitcell. Only readwrite and read ports."""
if self.dummy_bitcell:
return
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
# Edges added wl->bl, wl->br for every port except write ports
rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names)
r_pin_names = zip(self.rw_wl_names, self.rw_bl_names, self.rw_br_names)
for pin_zip in zip(rw_pin_names, r_pin_names):
for pin_zip in [rw_pin_names, r_pin_names]:
for wl,bl,br in pin_zip:
graph.add_edge(pin_dict[wl],pin_dict[bl])
graph.add_edge(pin_dict[wl],pin_dict[br])

View File

@ -43,9 +43,10 @@ class replica_bitcell_1w_1r(design.design):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function."""
debug.info(1,'Adding edges for {}'.format(inst_name))
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
#Edges hardcoded here. Essentially wl->bl/br for both ports.
# Port 0 edges
graph.add_edge(pin_dict["wl0"], pin_dict["bl0"])
graph.add_edge(pin_dict["wl0"], pin_dict["br0"])
# Port 1 is a write port, so its timing is not considered here.
#Edges hardcoded here. Essentially wl->bl/br for the read port.
# Port 1 edges
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"])
graph.add_edge(pin_dict["wl1"], pin_dict["br1"])
# Port 0 is a write port, so its timing is not considered here.

View File

@ -13,7 +13,6 @@ from .lib import *
from .delay import *
from .setup_hold import *
from .functional import *
from .worst_case import *
from .simulation import *
from .measurements import *
from .model_check import *

View File

@ -0,0 +1,14 @@
# 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.
#
from enum import Enum
class bit_polarity(Enum):
NONINVERTING = 0
INVERTING = 1

View File

@ -8,17 +8,7 @@
import re
import debug
from globals import OPTS
from enum import Enum
class sram_op(Enum):
READ_ZERO = 0
READ_ONE = 1
WRITE_ZERO = 2
WRITE_ONE = 3
class bit_polarity(Enum):
NONINVERTING = 0
INVERTING = 1
def relative_compare(value1,value2,error_tolerance=0.001):
""" This is used to compare relative values for convergence. """
@ -102,4 +92,4 @@ def check_dict_values_is_float(dict):
for key, value in dict.items():
if type(value)!=float:
return False
return True
return True

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@
# All rights reserved.
#
import sys,re,shutil
import collections
from design import design
import debug
import math
@ -15,9 +16,10 @@ from .stimuli import *
from .charutils import *
import utils
from globals import OPTS
from .simulation import simulation
from .delay import delay
import graph_util
from sram_factory import factory
class functional(simulation):
"""
@ -39,17 +41,24 @@ class functional(simulation):
self.set_corner(corner)
self.set_spice_constants()
#self.set_feasible_period(sram, spfile, corner)
self.set_stimulus_variables()
# For the debug signal names
self.create_signal_names()
self.add_graph_exclusions()
self.create_graph()
self.set_internal_spice_names()
self.initialize_wmask()
# Number of checks can be changed
self.num_cycles = 2
self.stored_words = {}
self.num_cycles = 15
# This is to have ordered keys for random selection
self.stored_words = collections.OrderedDict()
self.write_check = []
self.read_check = []
def initialize_wmask(self):
self.wmask = ""
if self.write_size is not None:
@ -132,7 +141,6 @@ class functional(simulation):
elif op == "write":
addr = self.gen_addr()
word = self.gen_data()
# print("write",self.t_current,addr,word)
# two ports cannot write to the same address
if addr in w_addrs:
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port)
@ -154,7 +162,6 @@ class functional(simulation):
lower = bit * self.write_size
upper = lower + self.write_size - 1
new_word = new_word[:lower] + old_word[lower:upper+1] + new_word[upper + 1:]
# print("partial_w",self.t_current,addr,wmask,word, "partial_w_word:", new_word)
# two ports cannot write to the same address
if addr in w_addrs:
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port)
@ -165,7 +172,6 @@ class functional(simulation):
w_addrs.append(addr)
else:
(addr,word) = random.choice(list(self.stored_words.items()))
# print("read",self.t_current,addr,word)
# cannot read from an address that is currently being written to
if addr in w_addrs:
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port)
@ -241,23 +247,14 @@ class functional(simulation):
def gen_data(self):
""" Generates a random word to write. """
rand = random.randint(0,(2**self.word_size)-1)
data_bits = self.convert_to_bin(rand,False)
random_value = random.randint(0,(2**self.word_size)-1)
data_bits = self.convert_to_bin(random_value,False)
return data_bits
def gen_data_all_bits(self):
""" Generates a random word, either all 0's or all 1's, to write. """
rand = random.randint(0,1)
bits = []
for bit in range(self.word_size):
bits.append(rand)
data_bits = ''.join(map(str,bits))
return data_bits
def gen_addr(self):
""" Generates a random address value to write to. """
rand = random.randint(0,(2**self.addr_size)-1)
addr_bits = self.convert_to_bin(rand,True)
random_value = random.randint(0,(2**self.addr_size)-1)
addr_bits = self.convert_to_bin(random_value,True)
return addr_bits
def get_data(self):
@ -273,6 +270,7 @@ class functional(simulation):
if(is_addr):
expected_value = self.addr_size
else:
expected_value = self.word_size
for i in range (expected_value - len(new_value)):
new_value = "0" + new_value
@ -288,9 +286,7 @@ class functional(simulation):
self.stim = stimuli(self.sf,self.corner)
#Write include statements
self.sram_sp_file = "{}sram.sp".format(OPTS.openram_temp)
shutil.copy(self.sp_file, self.sram_sp_file)
self.stim.write_include(self.sram_sp_file)
self.stim.write_include(self.sp_file)
#Write Vdd/Gnd statements
self.sf.write("\n* Global Power Supplies\n")
@ -307,9 +303,17 @@ class functional(simulation):
for bit in range(self.word_size):
sig_name="{0}{1}_{2} ".format(self.dout_name, port, bit)
self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(port, bit, sig_name, self.load))
# 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("* s_en: {}\n".format(self.sen_name))
self.sf.write("* q: {}\n".format(self.q_name))
self.sf.write("* qbar: {}\n".format(self.qbar_name))
# Write debug comments to stim file
self.sf.write("\n\n * Sequence of operations\n")
self.sf.write("\n\n* Sequence of operations\n")
for comment in self.fn_cycle_comments:
self.sf.write("*{}\n".format(comment))
@ -369,4 +373,117 @@ 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(tech.spice["clk"], port),
'{}{}_{}'.format(self.dout_name, port, 0).lower())
self.sen_name = self.get_sen_name(self.graph.all_paths)
debug.info(2,"s_en name = {}".format(self.sen_name))
self.bl_name,self.br_name = self.get_bl_name(self.graph.all_paths, port)
debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name))
self.q_name,self.qbar_name = self.get_bit_name()
debug.info(2,"q name={}\nqbar name={}".format(self.q_name,self.qbar_name))
def get_bit_name(self):
""" Get a bit cell name """
(cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0)
storage_names = cell_inst.mod.get_storage_net_names()
debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes"
"supported for characterization. Storage nets={}").format(storage_names))
q_name = cell_name+'.'+str(storage_names[0])
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)
bl_found = False
# Only a single path should contain a single s_en name. Anything else is an error.
bl_names = []
exclude_set = self.get_bl_name_search_exclusions()
for int_net in [cell_bl, cell_br]:
bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set))
return bl_names[0], bl_names[1]
def get_bl_name_search_exclusions(self):
"""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

@ -25,9 +25,6 @@ class simulation():
self.word_size = self.sram.word_size
self.addr_size = self.sram.addr_size
self.write_size = self.sram.write_size
self.num_cols = self.sram.num_cols
self.num_rows = self.sram.num_rows
self.num_banks = self.sram.num_banks
self.sp_file = spfile
self.all_ports = self.sram.all_ports
@ -262,19 +259,21 @@ class simulation():
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)
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)
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):

View File

@ -0,0 +1,15 @@
# 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.
#
from enum import Enum
class sram_op(Enum):
READ_ZERO = 0
READ_ONE = 1
WRITE_ZERO = 2
WRITE_ONE = 3

View File

@ -1,84 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys,re,shutil
import debug
import tech
import math
from .stimuli import *
from .trim_spice import *
from .charutils import *
import utils
from globals import OPTS
from .delay import delay
class worst_case(delay):
"""Functions to test for the worst case delay in a target SRAM
The current worst case determines a feasible period for the SRAM then tests
several bits and record the delay and differences between the bits.
"""
def __init__(self, sram, spfile, corner):
delay.__init__(self,sram,spfile,corner)
def analyze(self,probe_address, probe_data, slews, loads):
"""
Main function to test the delays of different bits.
"""
debug.check(OPTS.num_rw_ports < 2 and OPTS.num_w_ports < 1 and OPTS.num_r_ports < 1 ,
"Bit testing does not currently support multiport.")
#Dict to hold all characterization values
char_sram_data = {}
self.set_probe(probe_address, probe_data)
#self.prepare_netlist()
self.load=max(loads)
self.slew=max(slews)
# 1) Find a feasible period and it's corresponding delays using the trimmed array.
feasible_delays = self.find_feasible_period()
# 2) Find the delays of several bits
test_bits = self.get_test_bits()
bit_delays = self.simulate_for_bit_delays(test_bits)
for i in range(len(test_bits)):
debug.info(1, "Bit tested: addr {0[0]} data_pos {0[1]}\n Values {1}".format(test_bits[i], bit_delays[i]))
def simulate_for_bit_delays(self, test_bits):
"""Simulates the delay of the sram of over several bits."""
bit_delays = [{} for i in range(len(test_bits))]
#Assumes a bitcell with only 1 rw port. (6t, port 0)
port = 0
self.targ_read_ports = [self.read_ports[port]]
self.targ_write_ports = [self.write_ports[port]]
for i in range(len(test_bits)):
(bit_addr, bit_data) = test_bits[i]
self.set_probe(bit_addr, bit_data)
debug.info(1,"Delay bit test: period {}, addr {}, data_pos {}".format(self.period, bit_addr, bit_data))
(success, results)=self.run_delay_simulation()
debug.check(success, "Bit Test Failed: period {}, addr {}, data_pos {}".format(self.period, bit_addr, bit_data))
bit_delays[i] = results[port]
return bit_delays
def get_test_bits(self):
"""Statically determines address and bit values to test"""
#First and last address, first middle, and last bit. Last bit is repeated twice with different data position.
bit_addrs = ["0"*self.addr_size, "0"+"1"*(self.addr_size-1), "1"*self.addr_size, "1"*self.addr_size]
data_positions = [0, (self.word_size-1)//2, 0, self.word_size-1]
#Return them in a tuple form
return [(bit_addrs[i], data_positions[i]) for i in range(len(bit_addrs))]

View File

@ -169,11 +169,12 @@ def setup_bitcell():
# If we have non-1rw ports,
# and the user didn't over-ride the bitcell manually,
# figure out the right bitcell to use
if (OPTS.bitcell=="bitcell" and OPTS.replica_bitcell=="replica_bitcell"):
if (OPTS.bitcell=="bitcell"):
if (OPTS.num_rw_ports==1 and OPTS.num_w_ports==0 and OPTS.num_r_ports==0):
OPTS.bitcell = "bitcell"
OPTS.replica_bitcell = "replica_bitcell"
OPTS.dummy_bitcell = "dummy_bitcell"
else:
ports = ""
if OPTS.num_rw_ports>0:
@ -185,21 +186,26 @@ def setup_bitcell():
OPTS.bitcell = "bitcell_"+ports
OPTS.replica_bitcell = "replica_bitcell_"+ports
# See if a custom bitcell exists
from importlib import find_loader
try:
__import__(OPTS.bitcell)
__import__(OPTS.replica_bitcell)
except ImportError:
# Use the pbitcell if we couldn't find a custom bitcell
# or its custom replica bitcell
# Use the pbitcell (and give a warning if not in unit test mode)
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
if not OPTS.is_unit_test:
debug.warning("Using the parameterized bitcell which may have suboptimal density.")
OPTS.dummy_bitcell = "dummy_bitcell_"+ports
else:
OPTS.replica_bitcell = "replica_" + OPTS.bitcell
OPTS.replica_bitcell = "dummy_" + OPTS.bitcell
# See if bitcell exists
from importlib import find_loader
try:
__import__(OPTS.bitcell)
__import__(OPTS.replica_bitcell)
__import__(OPTS.dummy_bitcell)
except ImportError:
# Use the pbitcell if we couldn't find a custom bitcell
# or its custom replica bitcell
# Use the pbitcell (and give a warning if not in unit test mode)
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.replica_bitcell = "dummy_pbitcell"
if not OPTS.is_unit_test:
debug.warning("Using the parameterized bitcell which may have suboptimal density.")
debug.info(1,"Using bitcell: {}".format(OPTS.bitcell))

View File

@ -40,6 +40,7 @@ class bank(design.design):
design.design.__init__(self, name)
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words))
# The local control signals are gated when we have bank select logic,
# so this prefix will be added to all of the input signals to create
# the internal gated signals.
@ -57,8 +58,8 @@ class bank(design.design):
def create_netlist(self):
self.compute_sizes()
self.add_pins()
self.add_modules()
self.add_pins() # Must create the replica bitcell array first
self.create_instances()
@ -80,10 +81,14 @@ class bank(design.design):
""" Adding pins for Bank module"""
for port in self.read_ports:
for bit in range(self.word_size):
self.add_pin("dout{0}_{1}".format(port,bit),"OUT")
self.add_pin("dout{0}_{1}".format(port,bit),"OUTPUT")
for port in self.read_ports:
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]),"OUTPUT")
for port in self.read_ports:
self.add_pin(self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port]),"INPUT")
for port in self.write_ports:
for bit in range(self.word_size):
self.add_pin("din{0}_{1}".format(port,bit),"IN")
self.add_pin("din{0}_{1}".format(port,bit),"INPUT")
for port in self.all_ports:
for bit in range(self.addr_size):
self.add_pin("addr{0}_{1}".format(port,bit),"INPUT")
@ -95,7 +100,7 @@ class bank(design.design):
self.add_pin("bank_sel{}".format(port),"INPUT")
for port in self.read_ports:
self.add_pin("s_en{0}".format(port), "INPUT")
for port in self.read_ports:
for port in self.all_ports:
self.add_pin("p_en_bar{0}".format(port), "INPUT")
for port in self.write_ports:
self.add_pin("w_en{0}".format(port), "INPUT")
@ -113,6 +118,7 @@ class bank(design.design):
for port in self.all_ports:
self.route_bitlines(port)
self.route_rbl(port)
self.route_port_address(port)
self.route_column_address_lines(port)
self.route_control_lines(port)
@ -121,6 +127,20 @@ class bank(design.design):
self.route_supplies()
def route_rbl(self,port):
""" Route the rbl_bl and rbl_wl """
if self.port_data[port].has_rbl():
bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
self.add_layout_pin(text="rbl_bl{0}".format(port),
layer=bl_pin.layer,
offset=bl_pin.ll(),
height=bl_pin.height(),
width=bl_pin.width())
def route_bitlines(self, port):
""" Route the bitlines depending on the port type rw, w, or r. """
@ -154,11 +174,17 @@ class bank(design.design):
# The center point for these cells are the upper-right corner of
# the bitcell array.
# The decoder/driver logic is placed on the right and mirrored on Y-axis.
# The write/sense/precharge/mux is placed on the top and mirrored on the X-axis.
# The port address decoder/driver logic is placed on the right and mirrored on Y-axis.
# The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis.
self.bitcell_array_top = self.bitcell_array.height
self.bitcell_array_right = self.bitcell_array.width + self.m1_width + self.m2_gap
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()
# Just past the dummy column
self.main_bitcell_array_left = self.bitcell_array.bitcell_array_inst.lx()
# Just past the dummy row and replica row
self.main_bitcell_array_bottom = self.bitcell_array.bitcell_array_inst.by()
self.compute_instance_port0_offsets()
if len(self.all_ports)==2:
@ -167,7 +193,7 @@ class bank(design.design):
def compute_instance_port0_offsets(self):
"""
Compute the instance offsets for port0.
Compute the instance offsets for port0 on the left/bottom of the bank.
"""
port = 0
@ -178,25 +204,27 @@ class bank(design.design):
# LOWER RIGHT QUADRANT
# Below the bitcell array
self.port_data_offsets[port] = vector(0,0)
if self.port_data[port].has_rbl():
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width,0)
else:
self.port_data_offsets[port] = vector(self.main_bitcell_array_left,0)
# UPPER LEFT QUADRANT
# To the left of the bitcell array
# The wordline driver is placed to the right of the main decoder width.
x_offset = self.m2_gap + self.port_address.width
self.port_address_offsets[port] = vector(-x_offset,0)
self.port_address_offsets[port] = vector(-x_offset,self.main_bitcell_array_bottom)
# LOWER LEFT QUADRANT
# Place the col decoder left aligned with wordline driver plus halfway under row decoder
# Place the col decoder left aligned with row decoder (x_offset doesn't change)
# Below the bitcell array with well spacing
# Place the col decoder left aligned with wordline driver
# This is also placed so that it's supply rails do not align with the SRAM-level
# control logic to allow control signals to easily pass over in M3
# by placing 1/2 a cell pitch down
x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width
if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = self.m2_gap + self.column_decoder.height
y_offset = 0.5*self.dff.height + self.column_decoder.height
else:
y_offset = 0
y_offset += 2*drc("well_to_well")
self.column_decoder_offsets[port] = vector(-x_offset,-y_offset)
# Bank select gets placed below the column decoder (x_offset doesn't change)
@ -210,7 +238,7 @@ class bank(design.design):
def compute_instance_port1_offsets(self):
"""
Compute the instance offsets for port1 on the top of the bank.
Compute the instance offsets for port1 on the right/top of the bank.
"""
port=1
@ -220,24 +248,22 @@ class bank(design.design):
# UPPER LEFT QUADRANT
# Above the bitcell array
self.port_data_offsets[port] = vector(0,self.bitcell_array_top)
self.port_data_offsets[port] = vector(self.main_bitcell_array_left, self.bitcell_array_top)
# LOWER RIGHT QUADRANT
# To the left of the bitcell array
# The wordline driver is placed to the right of the main decoder width.
x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap
self.port_address_offsets[port] = vector(x_offset,0)
self.port_address_offsets[port] = vector(x_offset,self.main_bitcell_array_bottom)
# UPPER RIGHT QUADRANT
# Place the col decoder right aligned with wordline driver plus halfway under row decoder
# Place the col decoder right aligned with wordline driver
# Above the bitcell array with a well spacing
x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width
if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = self.bitcell_array_top + self.m2_gap + self.column_decoder.height
y_offset = self.bitcell_array_top + 0.5*self.dff.height + self.column_decoder.height
else:
y_offset = self.bitcell_array_top
y_offset += 2*drc("well_to_well")
self.column_decoder_offsets[port] = vector(x_offset,y_offset)
# Bank select gets placed above the column decoder (x_offset doesn't change)
@ -253,16 +279,12 @@ class bank(design.design):
self.compute_instance_offsets()
# UPPER RIGHT QUADRANT
self.place_bitcell_array(self.bitcell_array_offset)
# LOWER RIGHT QUADRANT
self.place_port_data(self.port_data_offsets)
# UPPER LEFT QUADRANT
self.place_port_address(self.port_address_offsets)
# LOWER LEFT QUADRANT
self.place_column_decoder(self.column_decoder_offsets)
self.place_bank_select(self.bank_select_offsets)
@ -280,22 +302,17 @@ class bank(design.design):
debug.check(self.num_rows*self.num_cols==self.word_size*self.num_words,"Invalid bank sizes.")
debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,"Invalid address break down.")
# Width for the vdd/gnd rails
self.supply_rail_width = 4*self.m2_width
# FIXME: This spacing should be width dependent...
self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space
# The order of the control signals on the control bus:
self.input_control_signals = []
port_num = 0
for port in range(OPTS.num_rw_ports):
self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)])
self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num), "rbl_wl{}".format(port_num)])
port_num += 1
for port in range(OPTS.num_w_ports):
self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num)])
self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num), "p_en_bar{}".format(port_num)])
port_num += 1
for port in range(OPTS.num_r_ports):
self.input_control_signals.append(["wl_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)])
self.input_control_signals.append(["wl_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num), "rbl_wl{}".format(port_num)])
port_num += 1
# Number of control lines in the bus for each port
@ -329,15 +346,10 @@ class bank(design.design):
# create arrays of bitline and bitline_bar names for read, write, or all ports
self.bitcell = factory.create(module_type="bitcell")
self.bl_names = self.bitcell.list_all_bl_names()
self.br_names = self.bitcell.list_all_br_names()
self.wl_names = self.bitcell.list_all_wl_names()
self.bitline_names = self.bitcell.list_all_bitline_names()
self.bitcell_array = factory.create(module_type="bitcell_array",
cols=self.num_cols,
rows=self.num_rows)
self.add_mod(self.bitcell_array)
self.bl_names = self.bitcell.get_all_bl_names()
self.br_names = self.bitcell.get_all_br_names()
self.wl_names = self.bitcell.get_all_wl_names()
self.bitline_names = self.bitcell.get_all_bitline_names()
self.port_data = []
for port in self.all_ports:
@ -352,7 +364,41 @@ class bank(design.design):
cols=self.num_cols,
rows=self.num_rows)
self.add_mod(self.port_address)
# The number of replica lines depends on the port configuration
rbl_counts = [self.read_ports.count(p) for p in self.all_ports]
self.num_rbl = sum(rbl_counts)
# The replica array indices always start at 0, so this will map them to
# the correct SRAM port
# (e.g. if port 0 is w, then port 1 will use RBL 0 in replica bitcell array
# because write ports don't use an RBL)
self.port_rbl_map = {}
index = 0
for (i,num) in enumerate(rbl_counts):
if num>0:
self.port_rbl_map[i]=index
index += 1
if len(rbl_counts)<2:
rbl_counts.append(0)
# Which bitcell port should be used in the RBL
# For now (since only 2 ports), if port 0 is not a read port, skip it in the RBLs
bitcell_ports=list(range(len(self.read_ports)))
if 0 not in self.read_ports:
bitcell_ports = [x+1 for x in bitcell_ports]
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
cols=self.num_cols,
rows=self.num_rows,
left_rbl=rbl_counts[0],
right_rbl=rbl_counts[1],
bitcell_ports=bitcell_ports)
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)
@ -361,17 +407,24 @@ class bank(design.design):
def create_bitcell_array(self):
""" Creating Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array",
mod=self.bitcell_array)
temp = []
for col in range(self.num_cols):
for bitline in self.bitline_names:
temp.append(bitline+"_{0}".format(col))
temp.append("{0}_{1}".format(bitline,col))
for rbl in range(self.num_rbl):
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(rbl)
temp.append(rbl_bl_name)
rbl_br_name=self.bitcell_array.get_rbl_br_name(rbl)
temp.append(rbl_br_name)
for row in range(self.num_rows):
for wordline in self.wl_names:
temp.append(wordline+"_{0}".format(row))
temp.append("{0}_{1}".format(wordline,row))
for rbl in range(self.num_rbl):
rbl_wl_name=self.bitcell_array.get_rbl_wl_name(rbl)
temp.append(rbl_wl_name)
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
@ -391,6 +444,11 @@ class bank(design.design):
mod=self.port_data[port])
temp = []
if self.port_data[port].has_rbl():
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port])
temp.append(rbl_bl_name)
temp.append(rbl_br_name)
for col in range(self.num_cols):
temp.append("{0}_{1}".format(self.bl_names[port],col))
temp.append("{0}_{1}".format(self.br_names[port],col))
@ -405,8 +463,7 @@ class bank(design.design):
temp.extend(sel_names)
if port in self.read_ports:
temp.append("s_en{0}".format(port))
if port in self.read_ports:
temp.append("p_en_bar{0}".format(port))
temp.append("p_en_bar{0}".format(port))
if port in self.write_ports:
temp.append("w_en{0}".format(port))
for bit in range(self.num_wmasks):
@ -440,7 +497,7 @@ class bank(design.design):
temp.append("addr{0}_{1}".format(port,bit+self.col_addr_size))
temp.append("wl_en{0}".format(port))
for row in range(self.num_rows):
temp.append(self.wl_names[port]+"_{0}".format(row))
temp.append("{0}_{1}".format(self.wl_names[port],row))
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
@ -457,7 +514,7 @@ class bank(design.design):
# The address flop and decoder are aligned in the x coord.
for port in self.all_ports:
if port%2 == 1:
if port%2:
mirror = "MY"
else:
mirror = "R0"
@ -469,16 +526,16 @@ class bank(design.design):
Create a 2:4 or 3:8 column address decoder.
"""
dff = factory.create(module_type="dff")
self.dff = factory.create(module_type="dff")
if self.col_addr_size == 0:
return
elif self.col_addr_size == 1:
self.column_decoder = factory.create(module_type="pinvbuf", height=dff.height)
self.column_decoder = factory.create(module_type="pinvbuf", height=self.dff.height)
elif self.col_addr_size == 2:
self.column_decoder = factory.create(module_type="hierarchical_predecode2x4", height=dff.height)
self.column_decoder = factory.create(module_type="hierarchical_predecode2x4", height=self.dff.height)
elif self.col_addr_size == 3:
self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=dff.height)
self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=self.dff.height)
else:
# No error checking before?
debug.error("Invalid column decoder?",-1)
@ -560,8 +617,8 @@ class bank(design.design):
bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"]
elif self.port_id[port] == "w":
bank_sel_signals = ["clk_buf", "w_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en"]
bank_sel_signals = ["clk_buf", "w_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_p_en_bar"]
else:
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
@ -615,9 +672,9 @@ class bank(design.design):
# Port 0
# The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_width, self.min_y_offset)
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_pitch, self.min_y_offset)
# The control bus is routed up to two pitches below the bitcell array
control_bus_length = -2*self.m1_pitch - self.min_y_offset
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2*self.m1_pitch
self.bus_xoffset[0] = self.create_bus(layer="metal2",
pitch=self.m2_pitch,
offset=control_bus_offset,
@ -629,14 +686,14 @@ 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.bitcell_array_top - 2*self.m1_pitch
control_bus_offset = vector(self.bitcell_array_right,
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2*self.m1_pitch
control_bus_offset = vector(self.bitcell_array_right + self.m2_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
self.bus_xoffset[1] = self.create_bus(layer="metal2",
pitch=self.m2_pitch,
offset=control_bus_offset,
names=self.control_signals[1],
names=list(reversed(self.control_signals[1])),
length=control_bus_length,
vertical=True,
make_pins=(self.num_banks==1))
@ -645,12 +702,22 @@ class bank(design.design):
def route_port_data_to_bitcell_array(self, port):
""" Routing of BL and BR between port data and bitcell array """
# Connect the regular bitlines
inst2 = self.port_data_inst[port]
inst1 = self.bitcell_array_inst
inst1_bl_name = self.bl_names[port]+"_{}"
inst1_br_name = self.br_names[port]+"_{}"
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
# Connect the replica bitlines
if self.port_data[port].has_rbl():
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port])
self.connect_bitline(inst1, inst2, rbl_bl_name, "rbl_bl")
self.connect_bitline(inst1, inst2, rbl_br_name, "rbl_br")
def route_port_data_out(self, port):
@ -710,12 +777,9 @@ class bank(design.design):
route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset)
def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
def connect_bitline(self, inst1, inst2, inst1_name, inst2_name):
"""
Connect the bl and br of two modules.
Connect two pins of two modules.
This assumes that they have sufficient space to create a jog
in the middle between the two modules (if needed).
"""
@ -723,23 +787,34 @@ class bank(design.design):
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name)
(top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name)
(bottom_inst, bottom_name) = (inst1, inst1_name)
(top_inst, top_name) = (inst2, inst2_name)
else:
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name)
(top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name)
(bottom_inst, bottom_name) = (inst2, inst2_name)
(top_inst, top_name) = (inst1, inst1_name)
bottom_pin = bottom_inst.get_pin(bottom_name)
top_pin = top_inst.get_pin(top_name)
debug.check(bottom_pin.layer == top_pin.layer, "Pin layers do not match.")
bottom_loc = bottom_pin.uc()
top_loc = top_pin.bc()
yoffset = 0.5*(top_loc.y+bottom_loc.y)
self.add_path(top_pin.layer,[bottom_loc, vector(bottom_loc.x,yoffset),
vector(top_loc.x,yoffset), top_loc])
def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
"""
Connect the bl and br of two modules.
"""
for col in range(num_bits):
bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col)).uc()
bottom_br = bottom_inst.get_pin(bottom_br_name.format(col)).uc()
top_bl = top_inst.get_pin(top_bl_name.format(col)).bc()
top_br = top_inst.get_pin(top_br_name.format(col)).bc()
yoffset = 0.5*(top_bl.y+bottom_bl.y)
self.add_path("metal2",[bottom_bl, vector(bottom_bl.x,yoffset),
vector(top_bl.x,yoffset), top_bl])
self.add_path("metal2",[bottom_br, vector(bottom_br.x,yoffset),
vector(top_br.x,yoffset), top_br])
self.connect_bitline(inst1, inst2, inst1_bl_name.format(col), inst2_bl_name.format(col))
self.connect_bitline(inst1, inst2, inst1_br_name.format(col), inst2_br_name.format(col))
def route_port_address(self, port):
@ -868,21 +943,26 @@ class bank(design.design):
read_inst = 0
connection = []
connection.append((self.prefix+"p_en_bar{}".format(port), self.port_data_inst[port].get_pin("p_en_bar").lc()))
if port in self.read_ports:
connection.append((self.prefix+"p_en_bar{}".format(port), self.port_data_inst[port].get_pin("p_en_bar").lc()))
rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port])
connection.append((self.prefix+"rbl_wl{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name).lc()))
if port in self.write_ports:
connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").lc()))
if port in self.read_ports:
connection.append((self.prefix+"s_en{}".format(port), self.port_data_inst[port].get_pin("s_en").lc()))
for (control_signal, pin_pos) in connection:
control_mid_pos = self.bus_xoffset[port][control_signal]
control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y)
self.add_path("metal1", [control_pos, pin_pos])
self.add_wire(("metal1","via1","metal2"), [control_mid_pos, control_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_pos)
# clk to wordline_driver
control_signal = self.prefix+"wl_en{}".format(port)
if port%2:

View File

@ -73,32 +73,32 @@ class bitcell_array(design.design):
self.DRC_LVS()
def add_pins(self):
row_list = self.cell.list_all_wl_names()
column_list = self.cell.list_all_bitline_names()
row_list = self.cell.get_all_wl_names()
column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for cell_column in column_list:
self.add_pin(cell_column+"_{0}".format(col))
self.add_pin(cell_column+"_{0}".format(col), "INOUT")
for row in range(self.row_size):
for cell_row in row_list:
self.add_pin(cell_row+"_{0}".format(row))
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin(cell_row+"_{0}".format(row), "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
""" Add the modules used in this design """
self.cell = factory.create(module_type="bitcell")
self.add_mod(self.cell)
def list_bitcell_pins(self, col, row):
def get_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array """
bitcell_pins = []
pin_names = self.cell.list_all_bitline_names()
pin_names = self.cell.get_all_bitline_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(col))
pin_names = self.cell.list_all_wl_names()
pin_names = self.cell.get_all_wl_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(row))
bitcell_pins.append("vdd")
@ -115,13 +115,13 @@ class bitcell_array(design.design):
name = "bit_r{0}_c{1}".format(row, col)
self.cell_inst[row,col]=self.add_inst(name=name,
mod=self.cell)
self.connect_inst(self.list_bitcell_pins(col, row))
self.connect_inst(self.get_bitcell_pins(col, row))
def add_layout_pins(self):
""" Add the layout pins """
row_list = self.cell.list_all_wl_names()
column_list = self.cell.list_all_bitline_names()
row_list = self.cell.get_all_wl_names()
column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for cell_column in column_list:
@ -215,4 +215,4 @@ class bitcell_array(design.design):
def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell."""
return inst_name+'.x'+self.cell_inst[row,col].name, self.cell_inst[row,col]
return inst_name+'.x'+self.cell_inst[row,col].name, self.cell_inst[row,col]

View File

@ -39,7 +39,7 @@ class control_logic(design.design):
self.num_cols = word_size*words_per_row
self.num_words = num_rows*words_per_row
self.enable_delay_chain_resizing = True
self.enable_delay_chain_resizing = False
self.inv_parasitic_delay = logical_effort.logical_effort.pinv
#Determines how much larger the sen delay should be. Accounts for possible error in model.
@ -73,10 +73,8 @@ class control_logic(design.design):
def add_pins(self):
""" Add the pins to the control logic module. """
for pin in self.input_list + ["clk"]:
self.add_pin(pin,"INPUT")
for pin in self.output_list:
self.add_pin(pin,"OUTPUT")
self.add_pin_list(self.input_list + ["clk"] + self.rbl_list, "INPUT")
self.add_pin_list(self.output_list,"OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
@ -96,6 +94,12 @@ class control_logic(design.design):
size=4,
height=dff_height)
self.add_mod(self.and2)
self.rbl_driver = factory.create(module_type="pbuf",
size=self.num_cols,
height=dff_height)
self.add_mod(self.rbl_driver)
# clk_buf drives a flop for every address and control bit
# plus about 5 fanouts for the control logic
@ -115,16 +119,16 @@ class control_logic(design.design):
self.add_mod(self.wl_en_driver)
# w_en drives every write driver
self.w_en_driver = factory.create(module_type="pdriver",
fanout=self.word_size+8,
height=dff_height)
self.add_mod(self.w_en_driver)
self.wen_and2 = factory.create(module_type="pand2",
size=self.word_size+8,
height=dff_height)
self.add_mod(self.wen_and2)
# s_en drives every sense amp
self.s_en_driver = factory.create(module_type="pdriver",
fanout=self.word_size,
height=dff_height)
self.add_mod(self.s_en_driver)
self.sen_and2 = factory.create(module_type="pand2",
size=self.word_size,
height=dff_height)
self.add_mod(self.sen_and2)
# used to generate inverted signals with low fanout
self.inv = factory.create(module_type="pinv",
@ -132,53 +136,58 @@ class control_logic(design.design):
height=dff_height)
self.add_mod(self.inv)
# p_en_bar drives every column in the bicell array
# p_en_bar drives every column in the bitcell array
self.p_en_bar_driver = factory.create(module_type="pdriver",
neg_polarity=True,
fanout=self.num_cols,
height=dff_height)
self.add_mod(self.p_en_bar_driver)
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()
# 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)
# 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)
# #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
self.add_mod(self.replica_bitline)
# 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 """
@ -312,8 +321,13 @@ class control_logic(design.design):
# List of input control signals
if self.port_type == "rw":
self.input_list = ["csb", "web"]
self.rbl_list = ["rbl_bl"]
elif self.port_type == "r":
self.input_list = ["csb"]
self.rbl_list = ["rbl_bl"]
else:
self.input_list = ["csb"]
self.rbl_list = []
if self.port_type == "rw":
self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"]
@ -332,11 +346,12 @@ class control_logic(design.design):
# Outputs to the bank
if self.port_type == "rw":
self.output_list = ["s_en", "w_en", "p_en_bar"]
self.output_list = ["rbl_wl", "s_en", "w_en"]
elif self.port_type == "r":
self.output_list = ["s_en", "p_en_bar"]
self.output_list = ["rbl_wl", "s_en"]
else:
self.output_list = ["w_en"]
self.output_list.append("p_en_bar")
self.output_list.append("wl_en")
self.output_list.append("clk_buf")
@ -360,12 +375,12 @@ class control_logic(design.design):
self.create_wlen_row()
if (self.port_type == "rw") or (self.port_type == "w"):
self.create_wen_row()
if self.port_type == "rw":
self.create_rbl_in_row()
if (self.port_type == "rw") or (self.port_type == "r"):
self.create_pen_row()
if (self.port_type == "rw") or (self.port_type == "r"):
self.create_rbl_row()
self.create_sen_row()
self.create_rbl()
self.create_delay()
self.create_pen_row()
def place_instances(self):
@ -392,20 +407,20 @@ class control_logic(design.design):
row += 1
if (self.port_type == "rw") or (self.port_type == "w"):
self.place_wen_row(row)
height = self.w_en_inst.uy()
control_center_y = self.w_en_inst.uy()
height = self.w_en_gate_inst.uy()
control_center_y = self.w_en_gate_inst.uy()
row += 1
if self.port_type == "rw":
self.place_rbl_in_row(row)
if (self.port_type == "rw") or (self.port_type == "r"):
self.place_rbl_row(row)
row += 1
self.place_pen_row(row)
row += 1
if (self.port_type == "rw") or (self.port_type == "r"):
self.place_pen_row(row)
row += 1
self.place_sen_row(row)
row += 1
self.place_rbl(row)
height = self.rbl_inst.uy()
control_center_y = self.rbl_inst.by()
self.place_delay(row)
height = self.delay_inst.uy()
control_center_y = self.delay_inst.by()
# This offset is used for placement of the control logic in the SRAM level.
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y)
@ -415,7 +430,7 @@ class control_logic(design.design):
# Max of modules or logic rows
self.width = max([inst.rx() for inst in self.row_end_inst])
if (self.port_type == "rw") or (self.port_type == "r"):
self.width = max(self.rbl_inst.rx() , self.width)
self.width = max(self.delay_inst.rx() , self.width)
self.width += self.m2_pitch
def route_all(self):
@ -426,33 +441,29 @@ class control_logic(design.design):
if (self.port_type == "rw") or (self.port_type == "w"):
self.route_wen()
if (self.port_type == "rw") or (self.port_type == "r"):
self.route_rbl_in()
self.route_pen()
self.route_rbl()
self.route_sen()
self.route_pen()
self.route_clk_buf()
self.route_gated_clk_bar()
self.route_gated_clk_buf()
self.route_supply()
def create_rbl(self):
def create_delay(self):
""" Create the replica bitline """
if self.port_type == "r":
input_name = "gated_clk_bar"
else:
input_name = "rbl_in"
self.rbl_inst=self.add_inst(name="replica_bitline",
mod=self.replica_bitline)
self.connect_inst([input_name, "pre_s_en", "vdd", "gnd"])
self.delay_inst=self.add_inst(name="delay_chain",
mod=self.delay_chain)
self.connect_inst(["rbl_bl", "pre_s_en", "vdd", "gnd"])
def place_rbl(self,row):
def place_delay(self,row):
""" Place the replica bitline """
y_off = row * self.and2.height + 2*self.m1_pitch
# Add the RBL above the rows
# Add to the right of the control rows and routing channel
offset = vector(0, y_off)
self.rbl_inst.place(offset)
offset = vector(self.delay_chain.width, y_off)
self.delay_inst.place(offset, mirror="MY")
def create_clk_buf_row(self):
@ -588,44 +599,31 @@ class control_logic(design.design):
self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.rail_offsets)
self.connect_output(self.wl_en_inst, "Z", "wl_en")
def create_rbl_in_row(self):
# input: gated_clk_bar, we_bar, output: rbl_in
self.rbl_in_inst=self.add_inst(name="and2_rbl_in",
mod=self.and2)
self.connect_inst(["gated_clk_bar", "we_bar", "rbl_in", "vdd", "gnd"])
def create_rbl_row(self):
def place_rbl_in_row(self,row):
self.rbl_inst=self.add_inst(name="rbl_driver",
mod=self.rbl_driver)
# input: gated_clk_bar, output: rbl_wl
self.connect_inst(["gated_clk_bar", "rbl_wl", "vdd", "gnd"])
def place_rbl_row(self,row):
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.rbl_in_inst.place(offset, mirror)
self.rbl_inst.place(offset, mirror)
self.row_end_inst.append(self.rbl_in_inst)
self.row_end_inst.append(self.rbl_inst)
def route_rbl_in(self):
def route_rbl(self):
""" Connect the logic for the rbl_in generation """
if self.port_type == "rw":
input_name = "we_bar"
# Connect the NAND gate inputs to the bus
rbl_in_map = zip(["A", "B"], ["gated_clk_bar", "we_bar"])
self.connect_vertical_bus(rbl_in_map, self.rbl_in_inst, self.rail_offsets)
# Connect the output of the precharge enable to the RBL input
if self.port_type == "rw":
out_pos = self.rbl_in_inst.get_pin("Z").center()
else:
out_pos = vector(self.rail_offsets["gated_clk_bar"].x, self.rbl_inst.by()-3*self.m2_pitch)
in_pos = self.rbl_inst.get_pin("en").center()
mid1 = vector(in_pos.x,out_pos.y)
self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, in_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=out_pos)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=out_pos)
rbl_in_map = zip(["A"], ["gated_clk_bar"])
self.connect_vertical_bus(rbl_in_map, self.rbl_inst, self.rail_offsets)
self.connect_output(self.rbl_inst, "Z", "rbl_wl")
# Input from RBL goes to the delay line for futher delay
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
def create_pen_row(self):
input_name = "gated_clk_buf"
@ -639,7 +637,6 @@ class control_logic(design.design):
def place_pen_row(self,row):
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off,y_off)
self.p_en_bar_inst.place(offset, mirror)
@ -647,17 +644,21 @@ class control_logic(design.design):
def route_pen(self):
in_map = zip(["A"], ["gated_clk_buf"])
self.connect_vertical_bus(in_map, self.p_en_bar_inst, self.rail_offsets)
self.connect_vertical_bus(in_map, self.p_en_bar_inst, self.rail_offsets)
self.connect_output(self.p_en_bar_inst, "Z", "p_en_bar")
def create_sen_row(self):
""" Create the sense enable buffer. """
# BUFFER FOR S_EN
# input: pre_s_en, output: s_en
self.s_en_inst=self.add_inst(name="buf_s_en",
mod=self.s_en_driver)
self.connect_inst(["pre_s_en", "s_en", "vdd", "gnd"])
if self.port_type=="rw":
input_name = "we_bar"
else:
input_name = "cs_bar"
# GATE FOR S_EN
self.s_en_gate_inst = self.add_inst(name="buf_s_en_and",
mod=self.sen_and2)
self.connect_inst(["pre_s_en", input_name, "s_en", "vdd", "gnd"])
def place_sen_row(self,row):
"""
@ -668,19 +669,27 @@ class control_logic(design.design):
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.s_en_inst.place(offset, mirror)
self.s_en_gate_inst.place(offset, mirror)
self.row_end_inst.append(self.s_en_inst)
self.row_end_inst.append(self.s_en_gate_inst)
def route_sen(self):
if self.port_type=="rw":
input_name = "we_bar"
else:
input_name = "cs_bar"
sen_map = zip(["B"], [input_name])
self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.rail_offsets)
out_pos = self.rbl_inst.get_pin("out").bc()
in_pos = self.s_en_inst.get_pin("A").lc()
out_pos = self.delay_inst.get_pin("out").bc()
in_pos = self.s_en_gate_inst.get_pin("A").lc()
mid1 = vector(out_pos.x,in_pos.y)
self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos])
self.connect_output(self.s_en_inst, "Z", "s_en")
self.connect_output(self.s_en_gate_inst, "Z", "s_en")
def create_wen_row(self):
@ -690,34 +699,33 @@ class control_logic(design.design):
else:
# No we for write-only reports, so use cs
input_name = "cs"
# BUFFER FOR W_EN
self.w_en_inst = self.add_inst(name="buf_w_en_buf",
mod=self.w_en_driver)
self.connect_inst([input_name, "w_en", "vdd", "gnd"])
# GATE THE W_EN
self.w_en_gate_inst = self.add_inst(name="w_en_and",
mod=self.wen_and2)
self.connect_inst([input_name, "gated_clk_bar", "w_en", "vdd", "gnd"])
def place_wen_row(self,row):
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.w_en_inst.place(offset, mirror)
self.w_en_gate_inst.place(offset, mirror)
self.row_end_inst.append(self.w_en_inst)
self.row_end_inst.append(self.w_en_gate_inst)
def route_wen(self):
if self.port_type == "rw":
input_name = "we"
else:
# No we for write-only reports, so use cs
input_name = "cs"
wen_map = zip(["A"], [input_name])
self.connect_vertical_bus(wen_map, self.w_en_inst, self.rail_offsets)
wen_map = zip(["A", "B"], [input_name, "gated_clk_bar"])
self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.rail_offsets)
self.connect_output(self.w_en_inst, "Z", "w_en")
self.connect_output(self.w_en_gate_inst, "Z", "w_en")
def create_dffs(self):
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
@ -794,8 +802,8 @@ class control_logic(design.design):
self.add_path("metal1", [row_loc, pin_loc])
if (self.port_type == "rw") or (self.port_type == "r"):
self.copy_layout_pin(self.rbl_inst,"gnd")
self.copy_layout_pin(self.rbl_inst,"vdd")
self.copy_layout_pin(self.delay_inst,"gnd")
self.copy_layout_pin(self.delay_inst,"vdd")
self.copy_layout_pin(self.ctrl_dff_inst,"gnd")
self.copy_layout_pin(self.ctrl_dff_inst,"vdd")
@ -821,7 +829,7 @@ class control_logic(design.design):
# height=pin.height(),
# width=pin.width())
pin=self.rbl_inst.get_pin("out")
pin=self.delay_inst.get_pin("out")
self.add_label_pin(text="out",
layer=pin.layer,
offset=pin.ll(),
@ -882,7 +890,7 @@ class control_logic(design.design):
last_stage_rise = stage_effort_list[-1].is_rise
#Replica bitline stage, rbl_in -(rbl)-> pre_s_en
stage2_cout = self.s_en_driver.get_cin()
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
@ -895,6 +903,7 @@ class control_logic(design.design):
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)
@ -903,6 +912,7 @@ class control_logic(design.design):
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()
@ -930,8 +940,10 @@ class control_logic(design.design):
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."""
"""
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()
@ -943,6 +955,7 @@ class control_logic(design.design):
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':
@ -951,4 +964,7 @@ class control_logic(design.design):
def graph_exclude_dffs(self):
"""Exclude dffs from graph as they do not represent critical path"""
self.graph_inst_exclude.add(self.ctrl_dff_inst)
if self.port_type=="rw" or self.port_type=="w":
self.graph_inst_exclude.add(self.w_en_gate_inst)

View File

@ -57,10 +57,10 @@ class delay_chain(design.design):
def add_pins(self):
""" Add the pins of the delay chain"""
self.add_pin("in")
self.add_pin("out")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin("in", "INPUT")
self.add_pin("out", "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.inv = factory.create(module_type="pinv", route_output=False)

View File

@ -54,13 +54,13 @@ class dff_array(design.design):
def add_pins(self):
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_din_name(row,col))
self.add_pin(self.get_din_name(row,col), "INPUT")
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_dout_name(row,col))
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin(self.get_dout_name(row,col), "OUTPUT")
self.add_pin("clk", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_dff_array(self):
self.dff_insts={}

View File

@ -75,12 +75,12 @@ class dff_buf(design.design):
def add_pins(self):
self.add_pin("D")
self.add_pin("Q")
self.add_pin("Qb")
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin("D", "INPUT")
self.add_pin("Q", "OUTPUT")
self.add_pin("Qb", "OUTPUT")
self.add_pin("clk", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_instances(self):
self.dff_inst=self.add_inst(name="dff_buf_dff",

View File

@ -55,14 +55,14 @@ class dff_buf_array(design.design):
def add_pins(self):
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_din_name(row,col))
self.add_pin(self.get_din_name(row,col), "INPUT")
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_dout_name(row,col))
self.add_pin(self.get_dout_bar_name(row,col))
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin(self.get_dout_name(row,col), "OUTPUT")
self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT")
self.add_pin("clk", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.dff = factory.create(module_type="dff_buf",

View File

@ -59,14 +59,14 @@ class dff_inv_array(design.design):
def add_pins(self):
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_din_name(row,col))
self.add_pin(self.get_din_name(row,col), "INPUT")
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_dout_name(row,col))
self.add_pin(self.get_dout_bar_name(row,col))
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin(self.get_dout_name(row,col), "OUTPUT")
self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT")
self.add_pin("clk", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_dff_array(self):
self.dff_insts={}

View File

@ -15,13 +15,14 @@ class dummy_array(design.design):
"""
Generate a dummy row/column for the replica array.
"""
def __init__(self, cols, rows, name):
def __init__(self, cols, rows, mirror=0, name=""):
design.design.__init__(self, 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
self.mirror = mirror
self.create_netlist()
if not OPTS.netlist_only:
@ -46,7 +47,7 @@ class dummy_array(design.design):
for row in range(self.row_size):
name = "dummy_r{0}_c{1}".format(row, col)
if row % 2:
if (row+self.mirror) % 2:
tempy = yoffset + self.dummy_cell.height
dir_key = "MX"
else:
@ -65,16 +66,16 @@ class dummy_array(design.design):
self.DRC_LVS()
def add_pins(self):
row_list = self.cell.list_all_wl_names()
column_list = self.cell.list_all_bitline_names()
row_list = self.cell.get_all_wl_names()
column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for cell_column in column_list:
self.add_pin(cell_column+"_{0}".format(col))
self.add_pin(cell_column+"_{0}".format(col), "INOUT")
for row in range(self.row_size):
for cell_row in row_list:
self.add_pin(cell_row+"_{0}".format(row))
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin(cell_row+"_{0}".format(row), "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
""" Add the modules used in this design """
@ -83,16 +84,16 @@ class dummy_array(design.design):
self.cell = factory.create(module_type="bitcell")
def list_bitcell_pins(self, col, row):
def get_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array """
bitcell_pins = []
pin_names = self.cell.list_all_bitline_names()
pin_names = self.cell.get_all_bitline_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(col))
pin_names = self.cell.list_all_wl_names()
pin_names = self.cell.get_all_wl_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(row))
bitcell_pins.append("vdd")
@ -109,13 +110,13 @@ class dummy_array(design.design):
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.list_bitcell_pins(col, row))
self.connect_inst(self.get_bitcell_pins(col, row))
def add_layout_pins(self):
""" Add the layout pins """
row_list = self.cell.list_all_wl_names()
column_list = self.cell.list_all_bitline_names()
row_list = self.cell.get_all_wl_names()
column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for cell_column in column_list:

View File

@ -231,12 +231,12 @@ class hierarchical_decoder(design.design):
""" Add the module pins """
for i in range(self.num_inputs):
self.add_pin("addr_{0}".format(i))
self.add_pin("addr_{0}".format(i), "INPUT")
for j in range(self.rows):
self.add_pin("decode_{0}".format(j))
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin("decode_{0}".format(j), "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_pre_decoder(self):

View File

@ -26,11 +26,11 @@ class hierarchical_predecode(design.design):
def add_pins(self):
for k in range(self.number_of_inputs):
self.add_pin("in_{0}".format(k))
self.add_pin("in_{0}".format(k), "INPUT")
for i in range(self.number_of_outputs):
self.add_pin("out_{0}".format(i))
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin("out_{0}".format(i), "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
""" Add the INV and NAND gate modules """

View File

@ -83,8 +83,7 @@ class port_address(design.design):
driver_name = "wl_{}".format(row)
self.copy_layout_pin(self.wordline_driver_inst, driver_name)
# FIXME: Is this still inverted!?
self.copy_layout_pin(self.wordline_driver_inst, "en_bar", "wl_en")
self.copy_layout_pin(self.wordline_driver_inst, "en", "wl_en")
def route_internal(self):
for row in range(self.num_rows):

View File

@ -15,6 +15,7 @@ from globals import OPTS
class port_data(design.design):
"""
Create the data port (column mux, sense amps, write driver, etc.) for the given port number.
Port 0 always has the RBL on the left while port 1 is on the right.
"""
def __init__(self, sram_config, port, name=""):
@ -80,9 +81,13 @@ class port_data(design.design):
def add_pins(self):
""" Adding pins for port address module"""
if self.has_rbl():
self.add_pin("rbl_bl","INOUT")
self.add_pin("rbl_br","INOUT")
for bit in range(self.num_cols):
self.add_pin(self.bl_names[self.port]+"_{0}".format(bit),"INOUT")
self.add_pin(self.br_names[self.port]+"_{0}".format(bit),"INOUT")
self.add_pin("{0}_{1}".format(self.bl_names[self.port], bit),"INOUT")
self.add_pin("{0}_{1}".format(self.br_names[self.port], bit),"INOUT")
if self.port in self.read_ports:
for bit in range(self.word_size):
self.add_pin("dout_{}".format(bit),"OUTPUT")
@ -95,8 +100,7 @@ class port_data(design.design):
self.add_pin(pin_name,"INPUT")
if self.port in self.read_ports:
self.add_pin("s_en", "INPUT")
if self.port in self.read_ports:
self.add_pin("p_en_bar", "INPUT")
self.add_pin("p_en_bar", "INPUT")
if self.port in self.write_ports:
self.add_pin("w_en", "INPUT")
for bit in range(self.num_wmasks):
@ -132,12 +136,14 @@ class port_data(design.design):
self.route_sense_amp_to_column_mux_or_precharge_array(self.port)
self.route_column_mux_to_precharge_array(self.port)
else:
# write_driver -> (column_mux) -> bitcell_array
# write_driver -> (column_mux ->) precharge -> bitcell_array
self.route_write_driver_in(self.port)
self.route_write_driver_to_column_mux_or_bitcell_array(self.port)
self.route_write_driver_to_column_mux_or_precharge_array(self.port)
self.route_column_mux_to_precharge_array(self.port)
def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
for inst in self.insts:
self.copy_power_pins(inst,"vdd")
self.copy_power_pins(inst,"gnd")
@ -145,8 +151,10 @@ class port_data(design.design):
def add_modules(self):
if self.port in self.read_ports:
# Extra column +1 is for RBL
# Precharge will be shifted left if needed
self.precharge_array = factory.create(module_type="precharge_array",
columns=self.num_cols,
columns=self.num_cols + 1,
bitcell_bl=self.bl_names[self.port],
bitcell_br=self.br_names[self.port])
self.add_mod(self.precharge_array)
@ -156,8 +164,17 @@ class port_data(design.design):
words_per_row=self.words_per_row)
self.add_mod(self.sense_amp_array)
else:
self.precharge_array = None
# Precharge is needed when we have a column mux or for byte writes
# to prevent corruption of half-selected cells, so just always add it
# This is a little power inefficient for write ports without a column mux,
# but it is simpler.
self.precharge_array = factory.create(module_type="precharge_array",
columns=self.num_cols,
bitcell_bl=self.bl_names[self.port],
bitcell_br=self.br_names[self.port])
self.add_mod(self.precharge_array)
self.sense_amp_array = None
if self.col_addr_size > 0:
self.column_mux_array = factory.create(module_type="column_mux_array",
@ -206,9 +223,9 @@ class port_data(design.design):
# create arrays of bitline and bitline_bar names for read, write, or all ports
self.bitcell = factory.create(module_type="bitcell")
self.bl_names = self.bitcell.list_all_bl_names()
self.br_names = self.bitcell.list_all_br_names()
self.wl_names = self.bitcell.list_all_wl_names()
self.bl_names = self.bitcell.get_all_bl_names()
self.br_names = self.bitcell.get_all_br_names()
self.wl_names = self.bitcell.get_all_wl_names()
def create_precharge_array(self):
""" Creating Precharge """
@ -218,21 +235,32 @@ class port_data(design.design):
self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port),
mod=self.precharge_array)
temp = []
# Use left BLs for RBL
if self.has_rbl() and self.port==0:
temp.append("rbl_bl")
temp.append("rbl_br")
for bit in range(self.num_cols):
temp.append(self.bl_names[self.port]+"_{0}".format(bit))
temp.append(self.br_names[self.port]+"_{0}".format(bit))
# Use right BLs for RBL
if self.has_rbl() and self.port==1:
temp.append("rbl_bl")
temp.append("rbl_br")
temp.extend(["p_en_bar", "vdd"])
self.connect_inst(temp)
def place_precharge_array(self, offset):
""" Placing Precharge """
self.precharge_array_inst.place(offset=offset, mirror="MX")
def create_column_mux_array(self):
""" Creating Column Mux when words_per_row > 1 . """
self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port),
mod=self.column_mux_array)
@ -248,7 +276,6 @@ class port_data(design.design):
temp.append("gnd")
self.connect_inst(temp)
def place_column_mux_array(self, offset):
""" Placing Column Mux when words_per_row > 1 . """
@ -272,7 +299,7 @@ class port_data(design.design):
else:
temp.append(self.bl_names[self.port]+"_out_{0}".format(bit))
temp.append(self.br_names[self.port]+"_out_{0}".format(bit))
temp.extend(["s_en", "vdd", "gnd"])
self.connect_inst(temp)
@ -292,7 +319,7 @@ class port_data(design.design):
temp.append("din_{}".format(bit))
for bit in range(self.word_size):
if (self.words_per_row == 1):
if (self.words_per_row == 1):
temp.append(self.bl_names[self.port]+"_{0}".format(bit))
temp.append(self.br_names[self.port]+"_{0}".format(bit))
else:
@ -305,6 +332,7 @@ class port_data(design.design):
else:
temp.append("w_en")
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
@ -339,21 +367,32 @@ class port_data(design.design):
vertical_port_order.append(self.sense_amp_array_inst)
vertical_port_order.append(self.write_driver_array_inst)
# Add one column for the the RBL
if self.has_rbl() and self.port==0:
x_offset = self.bitcell.width
else:
x_offset = 0
vertical_port_offsets = 4*[None]
self.width = 0
self.width = x_offset
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(0,self.height)
vertical_port_offsets[i]=vector(x_offset,self.height)
# Reversed order
self.write_driver_offset = vertical_port_offsets[3]
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)
@ -399,30 +438,39 @@ class port_data(design.design):
inst1 = self.column_mux_array_inst
inst2 = self.precharge_array_inst
self.connect_bitlines(inst1, inst2, self.num_cols)
if self.has_rbl() and self.port==0:
self.connect_bitlines(inst1, inst2, self.num_cols, inst2_start_bit=1)
else:
self.connect_bitlines(inst1, inst2, self.num_cols)
def route_sense_amp_to_column_mux_or_precharge_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """
inst2 = self.sense_amp_array_inst
if self.col_addr_size>0:
# Sense amp is connected to the col mux
inst1 = self.column_mux_array_inst
inst1_bl_name = "bl_out_{}"
inst1_br_name = "br_out_{}"
start_bit = 0
else:
# Sense amp is directly connected to the precharge array
inst1 = self.precharge_array_inst
inst1_bl_name = "bl_{}"
inst1_br_name = "br_{}"
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
if self.has_rbl() and self.port==0:
start_bit=1
else:
start_bit=0
def route_write_driver_to_column_mux_or_bitcell_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit)
def route_write_driver_to_column_mux_or_precharge_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """
inst2 = self.write_driver_array_inst
if self.col_addr_size>0:
@ -430,12 +478,19 @@ class port_data(design.design):
inst1 = self.column_mux_array_inst
inst1_bl_name = "bl_out_{}"
inst1_br_name = "br_out_{}"
start_bit = 0
else:
# Write driver is directly connected to the bitcell array
return
# Sense amp is directly connected to the precharge array
inst1 = self.precharge_array_inst
inst1_bl_name = "bl_{}"
inst1_br_name = "br_{}"
if self.has_rbl() and self.port==0:
start_bit=1
else:
start_bit=0
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit)
def route_write_driver_to_sense_amp(self, port):
""" Routing of BL and BR between write driver and sense amp """
@ -451,16 +506,25 @@ class port_data(design.design):
def route_bitline_pins(self):
""" Add the bitline pins for the given port """
# Connect one bitline to the RBL and offset the indices for the other BLs
if self.has_rbl() and self.port==0:
self.copy_layout_pin(self.precharge_array_inst, "bl_0", "rbl_bl")
self.copy_layout_pin(self.precharge_array_inst, "br_0", "rbl_br")
bit_offset=1
elif self.has_rbl() and self.port==1:
self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(self.num_cols), "rbl_bl")
self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(self.num_cols), "rbl_br")
bit_offset=0
else:
bit_offset=0
for bit in range(self.num_cols):
if self.port in self.read_ports:
self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit))
self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit))
elif self.column_mux_array_inst:
self.copy_layout_pin(self.column_mux_array_inst, "bl_{}".format(bit))
self.copy_layout_pin(self.column_mux_array_inst, "br_{}".format(bit))
if self.precharge_array_inst:
self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit+bit_offset), "bl_{}".format(bit))
self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit+bit_offset), "br_{}".format(bit))
else:
self.copy_layout_pin(self.write_driver_array_inst, "bl_{}".format(bit))
self.copy_layout_pin(self.write_driver_array_inst, "br_{}".format(bit))
debug.error("Didn't find precharge arra.")
def route_control_pins(self):
""" Add the control pins: s_en, p_en_bar, w_en """
@ -477,8 +541,8 @@ class port_data(design.design):
def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0,
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0):
"""
Route the bl and br of two modules using the channel router.
"""
@ -486,26 +550,27 @@ class port_data(design.design):
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name)
(top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name)
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
else:
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name)
(top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name)
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
# Channel route each mux separately since we don't minimize the number
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
offset = bottom_inst.ul() + vector(0,self.m1_pitch)
for bit in range(num_bits):
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))]
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit+bottom_start_bit)), bottom_inst.get_pin(bottom_br_name.format(bit+bottom_start_bit))]
top_names = [top_inst.get_pin(top_bl_name.format(bit+top_start_bit)), top_inst.get_pin(top_br_name.format(bit+top_start_bit))]
route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset)
def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0,
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0):
"""
Connect the bl and br of two modules.
This assumes that they have sufficient space to create a jog
@ -515,17 +580,17 @@ class port_data(design.design):
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name)
(top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name)
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
else:
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name)
(top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name)
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
for col in range(num_bits):
bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col)).uc()
bottom_br = bottom_inst.get_pin(bottom_br_name.format(col)).uc()
top_bl = top_inst.get_pin(top_bl_name.format(col)).bc()
top_br = top_inst.get_pin(top_br_name.format(col)).bc()
bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col+bottom_start_bit)).uc()
bottom_br = bottom_inst.get_pin(bottom_br_name.format(col+bottom_start_bit)).uc()
top_bl = top_inst.get_pin(top_bl_name.format(col+top_start_bit)).bc()
top_br = top_inst.get_pin(top_br_name.format(col+top_start_bit)).bc()
yoffset = 0.5*(top_bl.y+bottom_bl.y)
self.add_path("metal2",[bottom_bl, vector(bottom_bl.x,yoffset),
@ -537,3 +602,6 @@ class port_data(design.design):
"""Precharge adds a loop between bitlines, can be excluded to reduce complexity"""
if self.precharge_array_inst:
self.graph_inst_exclude.add(self.precharge_array_inst)
def has_rbl(self):
return self.port in self.read_ports

View File

@ -35,10 +35,11 @@ class precharge_array(design.design):
def add_pins(self):
"""Adds pins for spice file"""
for i in range(self.columns):
self.add_pin("bl_{0}".format(i))
self.add_pin("br_{0}".format(i))
self.add_pin("en_bar")
self.add_pin("vdd")
# These are outputs from the precharge only
self.add_pin("bl_{0}".format(i), "OUTPUT")
self.add_pin("br_{0}".format(i), "OUTPUT")
self.add_pin("en_bar", "INPUT")
self.add_pin("vdd", "POWER")
def create_netlist(self):
self.add_modules()
@ -115,4 +116,4 @@ class precharge_array(design.design):
#Assume single port
precharge_en_cin = self.pc_cell.get_en_cin()
return precharge_en_cin*self.columns

View File

@ -17,18 +17,30 @@ import dummy_array
class replica_bitcell_array(design.design):
"""
Creates a bitcell arrow of cols x rows and then adds the replica and dummy columns
and rows for one or two read ports. Replica columns are on the left and right, respectively.
Creates a bitcell arrow of cols x rows and then adds the replica
and dummy columns and rows. Replica columns are on the left and
right, respectively and connected to the given bitcell ports.
Dummy are the outside columns/rows with WL and BL tied to gnd.
Requires a regular bitcell array, replica bitcell, and dummy bitcell (Bl/BR disconnected).
Requires a regular bitcell array, replica bitcell, and dummy
bitcell (Bl/BR disconnected).
"""
def __init__(self, cols, rows, name):
def __init__(self, cols, rows, left_rbl, right_rbl, bitcell_ports, name):
design.design.__init__(self, 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
self.left_rbl = left_rbl
self.right_rbl = right_rbl
self.bitcell_ports = bitcell_ports
debug.check(left_rbl+right_rbl==len(self.read_ports),"Invalid number of RBLs for port configuration.")
debug.check(left_rbl+right_rbl==len(self.bitcell_ports),"Bitcell ports must match total RBLs.")
# Two dummy rows/cols plus replica for each port
self.extra_rows = 2 + left_rbl + right_rbl
self.extra_cols = 2 + left_rbl + right_rbl
self.create_netlist()
if not OPTS.netlist_only:
@ -78,127 +90,201 @@ class replica_bitcell_array(design.design):
rows=self.row_size)
self.add_mod(self.bitcell_array)
# Replica bitline
self.replica_column = factory.create(module_type="replica_column",
rows=self.row_size + 4)
self.add_mod(self.replica_column)
# Replica bitlines
self.replica_columns = {}
for bit in range(self.left_rbl+self.right_rbl):
if bit<self.left_rbl:
replica_bit = bit+1
else:
replica_bit = bit+self.row_size+1
self.replica_columns[bit] = factory.create(module_type="replica_column",
rows=self.row_size,
left_rbl=self.left_rbl,
right_rbl=self.right_rbl,
replica_bit=replica_bit)
self.add_mod(self.replica_columns[bit])
# Dummy row
self.dummy_row = factory.create(module_type="dummy_array",
cols=self.column_size,
rows=1,
cols=self.column_size)
mirror=0)
self.add_mod(self.dummy_row)
# Dummy col
# Dummy col (mirror starting at first if odd replica+dummy rows)
self.dummy_col = factory.create(module_type="dummy_array",
cols=1,
rows=self.row_size + 4)
rows=self.row_size + self.extra_rows,
mirror=(self.left_rbl+1)%2)
self.add_mod(self.dummy_col)
def add_pins(self):
self.wl_names = [x for x in self.bitcell_array.pins if x.startswith("w")]
self.bl_names = [x for x in self.bitcell_array.pins if x.startswith("b")]
self.bitcell_array_wl_names = [x for x in self.bitcell_array.pins if x.startswith("w")]
self.bitcell_array_bl_names = [x for x in self.bitcell_array.pins if x.startswith("b")]
# top/bottom rows (in the middle)
self.replica_wl_names = ["replica_"+x for x in self.cell.pins if x.startswith("w")]
self.dummy_wl_names = ["dummy_"+x for x in self.cell.pins if x.startswith("w")]
self.dummy_bl_names = ["dummy_"+x for x in self.cell.pins if x.startswith("b")]
self.dummy_row_bl_names = self.bl_names
# These are the non-indexed names
self.dummy_cell_wl_names = ["dummy_"+x for x in self.cell.get_all_wl_names()]
self.dummy_cell_bl_names = ["dummy_"+x for x in self.cell.get_all_bitline_names()]
self.dummy_row_bl_names = self.bitcell_array_bl_names
# dummy row and replica on each side of the bitcell rows
self.replica_col_wl_names = [x+"_0" for x in self.dummy_wl_names] \
+ ["replica_"+x+"_0" for x in self.cell.list_all_wl_names()] \
+ self.wl_names \
+ ["replica_"+x+"_1" for x in self.cell.list_all_wl_names()] \
+ [x+"_1" for x in self.dummy_wl_names]
self.replica_bl_names = ["replica_"+x for x in self.cell.pins if x.startswith("b")]
# A dictionary because some ports may have nothing
self.rbl_bl_names = {}
self.rbl_br_names = {}
self.rbl_wl_names = {}
# left/right rows
# Create the full WL names include dummy, replica, and regular bit cells
self.replica_col_wl_names = []
self.replica_col_wl_names.extend(["{0}_bot".format(x) for x in self.dummy_cell_wl_names])
# Left port WLs (one dummy for each port when we allow >1 port)
for port in range(self.left_rbl):
# Make names for all RBLs
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.all_ports))]
# Keep track of the pin that is the RBL
self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]]
self.replica_col_wl_names.extend(wl_names)
# Regular WLs
self.replica_col_wl_names.extend(self.bitcell_array_wl_names)
# Right port WLs (one dummy for each port when we allow >1 port)
for port in range(self.left_rbl,self.left_rbl+self.right_rbl):
# Make names for all RBLs
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.all_ports))]
# Keep track of the pin that is the RBL
self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]]
self.replica_col_wl_names.extend(wl_names)
self.replica_col_wl_names.extend(["{0}_top".format(x) for x in self.dummy_cell_wl_names])
# Left/right dummy columns are connected identically to the replica column
self.dummy_col_wl_names = self.replica_col_wl_names
self.add_pin_list(self.bl_names)
self.add_pin_list([x+"_0" for x in self.replica_bl_names])
self.add_pin_list([x+"_1" for x in self.replica_bl_names])
self.add_pin_list([x for x in self.replica_col_wl_names if not x.startswith("dummy")])
self.add_pin("vdd")
self.add_pin("gnd")
# Per port bitline names
self.replica_bl_names = {}
self.replica_wl_names = {}
# Array of all port bitline names
for port in range(self.left_rbl+self.right_rbl):
left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x),port) for x in range(len(self.all_ports))]
right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x),port) for x in range(len(self.all_ports))]
# Keep track of the left pins that are the RBL
self.rbl_bl_names[port]=left_names[self.bitcell_ports[port]]
self.rbl_br_names[port]=right_names[self.bitcell_ports[port]]
# Interleave the left and right lists
bl_names = [x for t in zip(left_names, right_names) for x in t]
self.replica_bl_names[port] = bl_names
wl_names = ["rbl_{0}_{1}".format(x,port) for x in self.cell.get_all_wl_names()]
#wl_names[port] = "rbl_wl{}".format(port)
self.replica_wl_names[port] = wl_names
# External pins
self.add_pin_list(self.bitcell_array_bl_names, "INOUT")
# Need to sort by port order since dictionary values may not be in order
bl_names = [self.rbl_bl_names[x] for x in sorted(self.rbl_bl_names.keys())]
br_names = [self.rbl_br_names[x] for x in sorted(self.rbl_br_names.keys())]
for (bl_name,br_name) in zip(bl_names,br_names):
self.add_pin(bl_name,"OUTPUT")
self.add_pin(br_name,"OUTPUT")
self.add_pin_list(self.bitcell_array_wl_names, "INPUT")
# Need to sort by port order since dictionary values may not be in order
wl_names = [self.rbl_wl_names[x] for x in sorted(self.rbl_wl_names.keys())]
for pin_name in wl_names:
self.add_pin(pin_name,"INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_instances(self):
""" Create the module instances used in this design """
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.bitcell_array.pins)
self.connect_inst(self.bitcell_array_bl_names + self.bitcell_array_wl_names + supplies)
# Replica columns (two even if one port for now)
self.replica_col_left_inst=self.add_inst(name="replica_col_left",
mod=self.replica_column)
self.connect_inst([x+"_0" for x in self.replica_bl_names] + self.replica_col_wl_names + supplies)
self.replica_col_right_inst=self.add_inst(name="replica_col_right",
mod=self.replica_column)
self.connect_inst([x+"_1" for x in self.replica_bl_names] + self.replica_col_wl_names[::-1] + supplies)
# Replica columns
self.replica_col_inst = {}
for port in range(self.left_rbl+self.right_rbl):
self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port),
mod=self.replica_columns[port])
self.connect_inst(self.replica_bl_names[port] + self.replica_col_wl_names + supplies)
# Replica rows with replica bitcell
self.dummy_row_bottop_inst=self.add_inst(name="dummy_row_bottop",
mod=self.dummy_row)
self.connect_inst(self.dummy_row_bl_names + [x+"_0" for x in self.replica_wl_names] + supplies)
self.dummy_row_topbot_inst=self.add_inst(name="dummy_row_topbot",
mod=self.dummy_row)
self.connect_inst(self.dummy_row_bl_names + [x+"_1" for x in self.replica_wl_names] + supplies)
# Dummy rows under the bitcell array (connected with with the replica cell wl)
self.dummy_row_replica_inst = {}
for port in range(self.left_rbl+self.right_rbl):
self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port),
mod=self.dummy_row)
self.connect_inst(self.dummy_row_bl_names + self.replica_wl_names[port] + supplies)
# Dummy rows without replica bitcell
self.dummy_row_botbot_inst=self.add_inst(name="dummy_row_botbot",
# Top/bottom dummy rows
self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot",
mod=self.dummy_row)
self.connect_inst(self.dummy_row_bl_names + [x+"_0" for x in self.dummy_wl_names] + supplies)
self.dummy_row_toptop_inst=self.add_inst(name="dummy_row_toptop",
self.connect_inst(self.dummy_row_bl_names + [x+"_bot" for x in self.dummy_cell_wl_names] + supplies)
self.dummy_row_top_inst=self.add_inst(name="dummy_row_top",
mod=self.dummy_row)
self.connect_inst(self.dummy_row_bl_names + [x+"_1" for x in self.dummy_wl_names] + supplies)
self.connect_inst(self.dummy_row_bl_names + [x+"_top" for x in self.dummy_cell_wl_names] + supplies)
# Dummy columns
# Left/right Dummy columns
self.dummy_col_left_inst=self.add_inst(name="dummy_col_left",
mod=self.dummy_col)
self.connect_inst([x+"_0" for x in self.dummy_bl_names] + self.dummy_col_wl_names + supplies)
self.connect_inst([x+"_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies)
self.dummy_col_right_inst=self.add_inst(name="dummy_col_right",
mod=self.dummy_col)
self.connect_inst([x+"_1" for x in self.dummy_bl_names] + self.dummy_col_wl_names + supplies)
self.connect_inst([x+"_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies)
def create_layout(self):
self.height = (self.row_size+4)*self.dummy_row.height
self.width = (self.column_size+4)*self.replica_column.width
self.height = (self.row_size+self.extra_rows)*self.dummy_row.height
self.width = (self.column_size+self.extra_cols)*self.cell.width
# This is a bitcell x bitcell offset to scale
offset = vector(self.replica_column.width, self.dummy_row.height)
offset = vector(self.cell.width, self.cell.height)
self.bitcell_array_inst.place(offset=[0,0])
self.replica_col_left_inst.place(offset=offset.scale(-1,-2))
self.replica_col_right_inst.place(offset=offset.scale(0,2)+self.bitcell_array_inst.ur(),
mirror="MX")
self.dummy_row_toptop_inst.place(offset=offset.scale(0,2)+self.bitcell_array_inst.ul(),
mirror="MX")
self.dummy_row_topbot_inst.place(offset=offset.scale(0,0)+self.bitcell_array_inst.ul())
self.dummy_row_bottop_inst.place(offset=offset.scale(0,0),
mirror="MX")
self.dummy_row_botbot_inst.place(offset=offset.scale(0,-2))
self.dummy_col_left_inst.place(offset=offset.scale(-2,-2))
self.dummy_col_right_inst.place(offset=offset.scale(1,-2)+self.bitcell_array_inst.lr())
# To the left of the bitcell array
for bit in range(self.left_rbl):
self.replica_col_inst[bit].place(offset=offset.scale(-bit-1,-self.left_rbl-1))
# To the right of the bitcell array
for bit in range(self.right_rbl):
self.replica_col_inst[self.left_rbl+bit].place(offset=offset.scale(bit,-self.left_rbl-1)+self.bitcell_array_inst.lr())
self.translate_all(offset.scale(-2,-2))
# Far top dummy row (first row above array is NOT flipped)
flip_dummy = self.right_rbl%2
self.dummy_row_top_inst.place(offset=offset.scale(0,self.right_rbl+flip_dummy)+self.bitcell_array_inst.ul(),
mirror="MX" if flip_dummy else "R0")
# Far bottom dummy row (first row below array IS flipped)
flip_dummy = (self.left_rbl+1)%2
self.dummy_row_bot_inst.place(offset=offset.scale(0,-self.left_rbl-1+flip_dummy),
mirror="MX" if flip_dummy else "R0")
# Far left dummy col
self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl-1,-self.left_rbl-1))
# Far right dummy col
self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl,-self.left_rbl-1)+self.bitcell_array_inst.lr())
# Replica dummy rows
for bit in range(self.left_rbl):
self.dummy_row_replica_inst[bit].place(offset=offset.scale(0,-bit-bit%2),
mirror="R0" if bit%2 else "MX")
for bit in range(self.right_rbl):
self.dummy_row_replica_inst[self.left_rbl+bit].place(offset=offset.scale(0,bit+bit%2)+self.bitcell_array_inst.ul(),
mirror="MX" if bit%2 else "R0")
self.translate_all(offset.scale(-1-self.left_rbl,-1-self.left_rbl))
self.add_layout_pins()
@ -216,90 +302,77 @@ class replica_bitcell_array(design.design):
if pin_name.startswith("wl"):
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer,
offset=pin.center(),
width=self.width,
height=pin.height())
self.add_layout_pin(text=pin_name,
layer=pin.layer,
offset=pin.ll().scale(0,1),
width=self.width,
height=pin.height())
elif pin_name.startswith("bl") or pin_name.startswith("br"):
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer,
offset=pin.center(),
width=pin.width(),
height=self.height)
self.add_layout_pin(text=pin_name,
layer=pin.layer,
offset=pin.ll().scale(1,0),
width=pin.width(),
height=self.height)
for index,(side1,side2) in enumerate([("bottop","left"),("topbot","right")]):
inst = getattr(self, "dummy_row_{}_inst".format(side1))
pin_names = inst.mod.get_pin_names()
for pin_name in pin_names:
if pin_name.startswith("wl"):
pin_list = inst.get_pins(pin_name)
for pin in pin_list:
name = "replica_{0}_{1}".format(pin_name,index)
self.add_layout_pin_rect_center(text=name,
layer=pin.layer,
offset=pin.center(),
width=self.width,
height=pin.height())
# Replica wordlines
for port in range(self.left_rbl+self.right_rbl):
inst = self.replica_col_inst[port]
for (pin_name,wl_name) in zip(self.cell.get_all_wl_names(),self.replica_wl_names[port]):
# +1 for dummy row
pin_bit = port+1
# +row_size if above the array
if port>=self.left_rbl:
pin_bit += self.row_size
pin_name += "_{}".format(pin_bit)
pin = inst.get_pin(pin_name)
if wl_name in self.rbl_wl_names.values():
self.add_layout_pin(text=wl_name,
layer=pin.layer,
offset=pin.ll().scale(0,1),
width=self.width,
height=pin.height())
# Replica columns
for index,side in enumerate(["left","right"]):
inst = getattr(self, "replica_col_{}_inst".format(side))
pin_names = inst.mod.get_pin_names()
for pin_name in pin_names:
if pin_name.startswith("bl") or pin_name.startswith("br"):
pin_list = inst.get_pins(pin_name)
for pin in pin_list:
name = "replica_{0}_{1}".format(pin_name,index)
self.add_layout_pin(text=name,
layer=pin.layer,
offset=pin.ll().scale(1,0),
width=pin.width(),
height=self.height)
# Replica bitlines
for port in range(self.left_rbl+self.right_rbl):
inst = self.replica_col_inst[port]
for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(),self.replica_bl_names[port]):
pin = inst.get_pin(pin_name)
if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names:
name = bl_name
else:
name = "rbl_{0}_{1}".format(pin_name,port)
self.add_layout_pin(text=name,
layer=pin.layer,
offset=pin.ll().scale(1,0),
width=pin.width(),
height=self.height)
for pin_name in ["vdd","gnd"]:
for inst in [self.bitcell_array_inst,
self.replica_col_left_inst, self.replica_col_right_inst,
self.dummy_col_left_inst, self.dummy_col_right_inst,
self.dummy_row_toptop_inst, self.dummy_row_topbot_inst,
self.dummy_row_bottop_inst, self.dummy_row_botbot_inst]:
for inst in self.insts:
pin_list = inst.get_pins(pin_name)
for pin in pin_list:
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
# Non-pins
for side in ["botbot", "toptop"]:
inst = getattr(self, "dummy_row_{}_inst".format(side))
pin_names = inst.mod.get_pin_names()
for pin_name in pin_names:
if pin_name.startswith("wl"):
pin_list = inst.get_pins(pin_name)
for pin in pin_list:
self.add_rect_center(layer=pin.layer,
offset=pin.center(),
width=self.width,
height=pin.height())
for side in ["left", "right"]:
inst = getattr(self, "dummy_col_{}_inst".format(side))
pin_names = inst.mod.get_pin_names()
for pin_name in pin_names:
if pin_name.startswith("b"):
pin_list = inst.get_pins(pin_name)
for pin in pin_list:
self.add_rect_center(layer=pin.layer,
offset=pin.center(),
width=pin.width(),
height=self.height)
def get_rbl_wl_name(self, port):
""" Return the WL for the given RBL port """
return self.rbl_wl_names[port]
def get_rbl_bl_name(self, port):
""" Return the BL for the given RBL port """
return self.rbl_bl_names[port]
def get_rbl_br_name(self, port):
""" Return the BR for the given RBL port """
return self.rbl_br_names[port]
def analytical_delay(self, corner, slew, load):
"""Returns relative delay of the bitline in the bitcell array"""
@ -307,7 +380,7 @@ class replica_bitcell_array(design.design):
#The load being driven/drained is mostly the bitline but could include the sense amp or the column mux.
#The load from the bitlines is due to the drain capacitances from all the other bitlines and wire parasitics.
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
wire_unit_load = .05 * drain_load #Wires add 5% to this.
wire_unit_load = 0.05 * drain_load #Wires add 5% to this.
bitline_load = (drain_load+wire_unit_load)*self.row_size
return [self.cell.analytical_delay(corner, slew, load+bitline_load)]
@ -318,7 +391,7 @@ class replica_bitcell_array(design.design):
# Dynamic Power from Bitline
bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap()
bl_swing = parameter["rbl_height_percentage"]
bl_swing = OPTS.rbl_delay_percentage
freq = spice["default_event_rate"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
@ -346,3 +419,17 @@ class replica_bitcell_array(design.design):
bitcell_wl_cin = self.cell.get_wl_cin()
total_cin = bitcell_wl_cin * self.column_size
return total_cin
def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target"""
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def graph_exclude_replica_col_bits(self):
"""Exclude all replica/dummy cells in the replica columns except the replica bit."""
for port in range(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."""
return self.bitcell_array.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col)

View File

@ -1,621 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
import design
from tech import drc
import contact
from sram_factory import factory
from vector import vector
from globals import OPTS
class replica_bitline(design.design):
"""
Generate a module that simulates the delay of control logic
and bit line charging. Stages is the depth of the delay
line and rows is the height of the replica bit loads.
"""
def __init__(self, name, delay_fanout_list, bitcell_loads):
design.design.__init__(self, name)
self.bitcell_loads = bitcell_loads
self.delay_fanout_list = delay_fanout_list
if len(delay_fanout_list) == 0 or len(delay_fanout_list)%2 == 1:
debug.error('Delay chain must contain an even amount of stages to maintain polarity.',1)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.calculate_module_offsets()
self.place_instances()
self.route()
self.add_layout_pins()
self.offset_all_coordinates()
#self.add_lvs_correspondence_points()
# Extra pitch on top and right
self.width = self.replica_column_inst.rx() - self.delay_chain_inst.lx() + self.m2_pitch
self.height = max(self.replica_column_inst.uy(), self.delay_chain_inst.uy()) + self.m3_pitch
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
for pin in ["en", "out", "vdd", "gnd"]:
self.add_pin(pin)
def calculate_module_offsets(self):
""" Calculate all the module offsets """
# These aren't for instantiating, but we use them to get the dimensions
self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height)
# Quadrant 1: Replica bitline and such are not rotated, but they must be placed far enough
# away from the delay chain/inverter with space for three M2 tracks
self.bitcell_offset = vector(0,self.replica_bitcell.height)
self.rbl_offset = self.bitcell_offset
# Gap between the delay chain and RBL
gap_width = 2*self.m2_pitch
# Quadrant 4: with some space below it and tracks on the right for vdd/gnd
self.delay_chain_offset = vector(-self.delay_chain.width-gap_width,self.replica_bitcell.height)
# Will be flipped vertically below the delay chain
# Align it with the inverters in the delay chain to simplify supply connections
self.rbl_inv_offset = self.delay_chain_offset + vector(2*self.inv.width, 0)
# Placed next to the replica bitcell
self.access_tx_offset = vector(-gap_width-self.access_tx.width-self.inv.width, 0.5*self.inv.height)
def add_modules(self):
""" Add the modules for later usage """
self.replica_bitcell = factory.create(module_type="replica_bitcell")
self.add_mod(self.replica_bitcell)
# This is the replica bitline load column that is the height of our array
self.rbl = factory.create(module_type="bitcell_array",
cols=1,
rows=self.bitcell_loads)
self.add_mod(self.rbl)
# FIXME: The FO and depth of this should be tuned
self.delay_chain = factory.create(module_type="delay_chain",
fanout_list=self.delay_fanout_list)
self.add_mod(self.delay_chain)
self.inv = factory.create(module_type="pinv")
self.add_mod(self.inv)
self.access_tx = factory.create(module_type="ptx",
tx_type="pmos")
self.add_mod(self.access_tx)
def create_instances(self):
""" Create all of the module instances in the logical netlist """
# This is the threshold detect inverter on the output of the RBL
self.rbl_inv_inst=self.add_inst(name="rbl_inv",
mod=self.inv)
self.connect_inst(["bl0_0", "out", "vdd", "gnd"])
self.tx_inst=self.add_inst(name="rbl_access_tx",
mod=self.access_tx)
# D, G, S, B
self.connect_inst(["vdd", "delayed_en", "bl0_0", "vdd"])
# add the well and poly contact
self.delay_chain_inst=self.add_inst(name="delay_chain",
mod=self.delay_chain)
self.connect_inst(["en", "delayed_en", "vdd", "gnd"])
self.replica_cell_inst=self.add_inst(name="bitcell",
mod=self.replica_bitcell)
temp = []
for port in self.all_ports:
temp.append("bl{}_0".format(port))
temp.append("br{}_0".format(port))
for port in self.all_ports:
temp.append("delayed_en")
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
self.replica_column_inst=self.add_inst(name="load",
mod=self.rbl)
temp = []
for port in self.all_ports:
temp.append("bl{}_0".format(port))
temp.append("br{}_0".format(port))
for wl in range(self.bitcell_loads):
for port in self.all_ports:
temp.append("gnd")
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
self.wl_list = self.rbl.cell.list_all_wl_names()
self.bl_list = self.rbl.cell.list_all_bl_names()
def place_instances(self):
""" Add all of the module instances in the logical netlist """
# This is the threshold detect inverter on the output of the RBL
self.rbl_inv_inst.place(offset=self.rbl_inv_offset,
rotate=180)
self.tx_inst.place(self.access_tx_offset)
self.delay_chain_inst.place(self.delay_chain_offset)
self.replica_cell_inst.place(offset=self.bitcell_offset,
mirror="MX")
self.replica_column_inst.place(self.rbl_offset)
def route(self):
""" Connect all the signals together """
self.route_supplies()
self.route_wl()
self.route_access_tx()
def route_wl(self):
""" Connect the RBL word lines to gnd """
# Connect the WL and gnd pins directly to the center and right gnd rails
for row in range(self.bitcell_loads):
wl = self.wl_list[0]+"_{}".format(row)
pin = self.replica_column_inst.get_pin(wl)
# Route the connection to the right so that it doesn't interfere with the cells
# Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions
pin_right = pin.rc()
pin_extension = pin_right + vector(self.m3_pitch,0)
if pin.layer != "metal1":
continue
pin_width_ydir = pin.uy()-pin.by()
#Width is set to pin y width to avoid DRC issues with m1 gaps
self.add_path("metal1", [pin_right, pin_extension], pin_width_ydir)
self.add_power_pin("gnd", pin_extension)
# for multiport, need to short wordlines to each other so they all connect to gnd.
wl_last = self.wl_list[-1]+"_{}".format(row)
pin_last = self.replica_column_inst.get_pin(wl_last)
self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0))
def short_wordlines(self, wl_pin_a, wl_pin_b, pin_side, is_replica_cell, cell_row=0, offset_x_vec=None):
"""Connects the word lines together for a single bitcell. Also requires which side of the bitcell to short the pins."""
#Assumes input pins are wordlines. Also assumes the word lines are horizontal in metal1. Also assumes pins have same x coord.
#This is my (Hunter) first time editing layout in openram so this function is likely not optimal.
if len(self.all_ports) > 1:
#1. Create vertical metal for all the bitlines to connect to
#m1 needs to be extended in the y directions, direction needs to be determined as every other cell is flipped
correct_y = vector(0, 0.5*drc("minwidth_metal1"))
#x spacing depends on the side being drawn. Unknown to me (Hunter) why the size of the space differs by the side.
#I assume this is related to how a wire is draw, but I have not investigated the issue.
if pin_side == "right":
correct_x = vector(0.5*drc("minwidth_metal1"), 0)
if offset_x_vec != None:
correct_x = offset_x_vec
else:
correct_x = vector(1.5*drc("minwidth_metal1"), 0)
if wl_pin_a.uy() > wl_pin_b.uy():
self.add_path("metal1", [wl_pin_a.rc()+correct_x+correct_y, wl_pin_b.rc()+correct_x-correct_y])
else:
self.add_path("metal1", [wl_pin_a.rc()+correct_x-correct_y, wl_pin_b.rc()+correct_x+correct_y])
elif pin_side == "left":
if offset_x_vec != None:
correct_x = offset_x_vec
else:
correct_x = vector(1.5*drc("minwidth_metal1"), 0)
if wl_pin_a.uy() > wl_pin_b.uy():
self.add_path("metal1", [wl_pin_a.lc()-correct_x+correct_y, wl_pin_b.lc()-correct_x-correct_y])
else:
self.add_path("metal1", [wl_pin_a.lc()-correct_x-correct_y, wl_pin_b.lc()-correct_x+correct_y])
else:
debug.error("Could not connect wordlines on specified input side={}".format(pin_side),1)
#2. Connect word lines horizontally. Only replica cell needs. Bitline loads currently already do this.
for port in self.all_ports:
if is_replica_cell:
wl = self.wl_list[port]
pin = self.replica_cell_inst.get_pin(wl)
else:
wl = self.wl_list[port]+"_{}".format(cell_row)
pin = self.replica_column_inst.get_pin(wl)
if pin_side == "left":
self.add_path("metal1", [pin.lc()-correct_x, pin.lc()])
elif pin_side == "right":
self.add_path("metal1", [pin.rc()+correct_x, pin.rc()])
def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
# These are the instances that every bank has
top_instances = [self.replica_column_inst,
self.delay_chain_inst]
for inst in top_instances:
self.copy_layout_pin(inst, "vdd")
self.copy_layout_pin(inst, "gnd")
# Route the inverter supply pin from M1
# Only vdd is needed because gnd shares a rail with the delay chain
pin = self.rbl_inv_inst.get_pin("vdd")
self.add_power_pin("vdd", pin.lc())
for pin in self.replica_cell_inst.get_pins("vdd"):
self.add_power_pin(name="vdd", loc=pin.center(), vertical=True, start_layer=pin.layer)
for pin in self.replica_cell_inst.get_pins("gnd"):
self.add_power_pin("gnd", pin.center(), vertical=True, start_layer=pin.layer)
def route_access_tx(self):
# GATE ROUTE
# 1. Add the poly contact and nwell enclosure
# Determines the y-coordinate of where to place the gate input poly pin
# (middle in between the pmos and nmos)
poly_pin = self.tx_inst.get_pin("G")
poly_offset = poly_pin.uc()
# This centers the contact above the poly by one pitch
contact_offset = poly_offset + vector(0,self.m2_pitch)
self.add_via_center(layers=("poly", "contact", "metal1"),
offset=contact_offset)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=contact_offset)
self.add_segment_center(layer="poly",
start=poly_offset,
end=contact_offset)
nwell_offset = self.rbl_inv_offset + vector(-self.inv.height,self.inv.width)
# self.add_rect(layer="nwell",
# offset=nwell_offset,
# width=0.5*self.inv.height,
# height=self.delay_chain_offset.y-nwell_offset.y)
# 2. Route delay chain output to access tx gate
delay_en_offset = self.delay_chain_inst.get_pin("out").bc()
self.add_path("metal2", [delay_en_offset,contact_offset])
# 3. Route the contact of previous route to the bitcell WL
# route bend of previous net to bitcell WL
wl_offset = self.replica_cell_inst.get_pin(self.wl_list[0]).lc()
wl_mid1 = wl_offset - vector(1.5*drc("minwidth_metal1"), 0)
wl_mid2 = vector(wl_mid1.x, contact_offset.y)
#xmid_point= 0.5*(wl_offset.x+contact_offset.x)
#wl_mid1 = vector(xmid_point,contact_offset.y)
#wl_mid2 = vector(xmid_point,wl_offset.y)
self.add_path("metal1", [wl_offset, wl_mid1, wl_mid2, contact_offset])
# 4. Short wodlines if multiport
wl = self.wl_list[0]
wl_last = self.wl_list[-1]
pin = self.replica_cell_inst.get_pin(wl)
pin_last = self.replica_cell_inst.get_pin(wl_last)
x_offset = self.short_wordlines(pin, pin_last, "left", True)
#correct = vector(0.5*drc("minwidth_metal1"), 0)
#self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct])
# DRAIN ROUTE
# Route the drain to the vdd rail
drain_offset = self.tx_inst.get_pin("D").center()
self.add_power_pin("vdd", drain_offset, vertical=True)
# SOURCE ROUTE
# Route the drain to the RBL inverter input
source_offset = self.tx_inst.get_pin("S").center()
inv_A_offset = self.rbl_inv_inst.get_pin("A").center()
self.add_path("metal1",[source_offset, inv_A_offset])
# Route the connection of the source route to the RBL bitline (left)
# Via will go halfway down from the bitcell
bl_offset = self.replica_cell_inst.get_pin(self.bl_list[0]).bc()
# Route down a pitch so we can use M2 routing
bl_down_offset = bl_offset - vector(0, self.m2_pitch)
self.add_path("metal2",[source_offset, bl_down_offset, bl_offset])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=source_offset)
# BODY ROUTE
# Connect it to the inverter well
nwell_offset = self.rbl_inv_inst.lr()
ur_offset = self.tx_inst.ur()
self.add_rect(layer="nwell",
offset=nwell_offset,
width=ur_offset.x-nwell_offset.x,
height=ur_offset.y-nwell_offset.y)
def route_vdd(self):
""" Route all signals connected to vdd """
self.copy_layout_pin(self.delay_chain_inst,"vdd")
self.copy_layout_pin(self.replica_cell_inst,"vdd")
# Connect the WL and vdd pins directly to the center and right vdd rails
# Connect RBL vdd pins to center and right rails
rbl_vdd_pins = self.replica_column_inst.get_pins("vdd")
for pin in rbl_vdd_pins:
if pin.layer != "metal1":
continue
start = vector(self.center_vdd_pin.cx(),pin.cy())
end = vector(self.right_vdd_pin.cx(),pin.cy())
self.add_layout_pin_segment_center(text="vdd",
layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end)
# Add via for the inverter
pin = self.rbl_inv_inst.get_pin("vdd")
start = vector(self.left_vdd_pin.cx(),pin.cy())
end = vector(self.center_vdd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end)
# Add via for the RBC
pin = self.replica_cell_inst.get_pin("vdd")
start = pin.lc()
end = vector(self.right_vdd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end)
# Create the RBL rails too
rbl_pins = self.replica_column_inst.get_pins("vdd")
for pin in rbl_pins:
if pin.layer != "metal1":
continue
# If above the delay line, route the full width
left = vector(self.left_vdd_pin.cx(),pin.cy())
center = vector(self.center_vdd_pin.cx(),pin.cy())
if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch:
start = left
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left)
else:
start = center
end = vector(self.right_vdd_pin.cx()+0.5*self.m1_width,pin.cy())
self.add_layout_pin_segment_center(text="vdd",
layer="metal1",
start=start,
end=end)
def route_gnd(self):
""" Route all signals connected to gnd """
# Route the gnd lines from left to right
# Add via for the delay chain
left_gnd_start = self.delay_chain_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0)
left_gnd_end = vector(left_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch)
self.left_gnd_pin=self.add_segment_center(layer="metal2",
start=left_gnd_start,
end=left_gnd_end)
# Gnd line to the left of the replica bitline
center_gnd_start = self.replica_cell_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0)
center_gnd_end = vector(center_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch)
self.center_gnd_pin=self.add_segment_center(layer="metal2",
start=center_gnd_start,
end=center_gnd_end)
# Gnd line to the right of the replica bitline
right_gnd_start = self.replica_cell_inst.lr().scale(1,0) + vector(self.m2_pitch,0)
right_gnd_end = vector(right_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch)
self.right_gnd_pin=self.add_segment_center(layer="metal2",
start=right_gnd_start,
end=right_gnd_end)
# Connect the WL and gnd pins directly to the center and right gnd rails
for row in range(self.bitcell_loads):
wl = self.wl_list[0]+"_{}".format(row)
pin = self.replica_column_inst.get_pin(wl)
if pin.layer != "metal1":
continue
# If above the delay line, route the full width
left = vector(self.left_gnd_pin.cx(),pin.cy())
center = vector(self.center_gnd_pin.cx(),pin.cy())
if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch:
start = left
else:
start = center
end = vector(self.right_gnd_pin.cx(),pin.cy())
self.add_layout_pin_segment_center(text="gnd",
layer="metal1",
start=start,
end=end)
if start == left:
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=center)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end)
# rbl_gnd_pins = self.replica_column_inst.get_pins("gnd")
# # Add L shapes to each vertical gnd rail
# for pin in rbl_gnd_pins:
# if pin.layer != "metal1":
# continue
# # If above the delay line, route the full width
# left = vector(self.left_gnd_pin.cx(),pin.cy())
# center = vector(self.center_gnd_pin.cx(),pin.cy())
# if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch:
# start = left
# else:
# start = center
# end = vector(self.right_gnd_pin.cx(),pin.cy())
# self.add_segment_center(layer="metal1",
# start=start,
# end=end)
# if start == left:
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=left)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=center)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=end)
# Connect the gnd pins of the delay chain to the left rails
dc_gnd_pins = self.delay_chain_inst.get_pins("gnd")
for pin in dc_gnd_pins:
if pin.layer != "metal1":
continue
start = vector(self.left_gnd_pin.cx(),pin.cy())
# Note, we don't connect to the center rails because of
# via conflicts with the RBL
#end = vector(self.center_gnd_pin.cx(),pin.cy())
end = pin.rc()
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=end)
# Add via for the inverter
# pin = self.rbl_inv_inst.get_pin("gnd")
# start = vector(self.left_gnd_pin.cx(),pin.cy())
# end = vector(self.center_gnd_pin.cx(),pin.cy())
# self.add_segment_center(layer="metal1",
# start=start,
# end=end)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=start)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=end)
# Create RBL rails too
rbl_pins = self.replica_column_inst.get_pins("gnd")
for pin in rbl_pins:
if pin.layer != "metal2":
continue
start = vector(pin.cx(),self.right_gnd_pin.by())
end = vector(pin.cx(),self.right_gnd_pin.uy())
self.add_layout_pin_segment_center(text="gnd",
layer="metal2",
start=start,
end=end)
def add_layout_pins(self):
""" Route the input and output signal """
en_offset = self.delay_chain_inst.get_pin("in").bc()
self.add_layout_pin_segment_center(text="en",
layer="metal2",
start=en_offset,
end=en_offset.scale(1,0))
out_offset = self.rbl_inv_inst.get_pin("Z").center()
self.add_layout_pin_segment_center(text="out",
layer="metal2",
start=out_offset,
end=out_offset.scale(1,0))
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=out_offset)
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
pin = self.rbl_inv_inst.get_pin("A")
self.add_label_pin(text="bl[0]",
layer=pin.layer,
offset=pin.ll(),
height=pin.height(),
width=pin.width())
pin = self.delay_chain_inst.get_pin("out")
self.add_label_pin(text="delayed_en",
layer=pin.layer,
offset=pin.ll(),
height=pin.height(),
width=pin.width())
def get_en_cin(self):
"""Get the enable input relative capacitance"""
#The enable is only connected to the delay, get the cin from that module
en_cin = self.delay_chain.get_cin()
return en_cin
def determine_sen_stage_efforts(self, ext_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 = []
#Stage 1 is the delay chain
stage1_cout = self.get_delayed_en_cin()
stage1 = self.delay_chain.determine_delayed_en_stage_efforts(stage1_cout, inp_is_rise)
stage_effort_list += stage1
#There is a disconnect between the delay chain and inverter. The rise/fall of the input to the inverter
#Will be the negation of the previous stage.
last_stage_is_rise = not stage_effort_list[-1].is_rise
#The delay chain triggers the enable on the replica bitline (rbl). This is used to track the bitline delay whereas this
#model is intended to track every but that. Therefore, the next stage is the inverter after the rbl.
stage2 = self.inv.get_stage_effort(ext_cout, last_stage_is_rise)
stage_effort_list.append(stage2)
return stage_effort_list
def get_delayed_en_cin(self):
"""Get the fanout capacitance (relative) of the delayed enable from the delay chain."""
access_tx_cin = self.access_tx.get_cin()
rbc_cin = self.replica_bitcell.get_wl_cin()
return access_tx_cin + rbc_cin

View File

@ -14,12 +14,25 @@ from globals import OPTS
class replica_column(design.design):
"""
Generate a replica bitline column for the replica array.
Rows is the total number of rows i the main array.
Left_rbl and right_rbl are the number of left and right replica bitlines.
Replica bit specifies which replica column this is (to determine where to put the
replica cell.
"""
def __init__(self, name, rows):
def __init__(self, name, rows, left_rbl, right_rbl, replica_bit):
design.design.__init__(self, name)
self.row_size = rows
self.rows = rows
self.left_rbl = left_rbl
self.right_rbl = right_rbl
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
debug.check(replica_bit!=0 and replica_bit!=rows,"Replica bit cannot be the dummy row.")
debug.check(replica_bit<=left_rbl or replica_bit>=self.total_size-right_rbl-1,
"Replica bit cannot be in the regular array.")
self.create_netlist()
if not OPTS.netlist_only:
@ -31,26 +44,26 @@ class replica_column(design.design):
self.create_instances()
def create_layout(self):
self.place_instances()
self.add_layout_pins()
self.height = self.row_size*self.cell.height
self.height = self.total_size*self.cell.height
self.width = self.cell.width
self.place_instances()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
column_list = self.cell.list_all_bitline_names()
for cell_column in column_list:
self.add_pin("{0}_{1}".format(cell_column,0))
row_list = self.cell.list_all_wl_names()
for row in range(self.row_size):
for cell_row in row_list:
self.add_pin("{0}_{1}".format(cell_row,row))
for bl_name in self.cell.get_all_bitline_names():
# In the replica column, these are only outputs!
self.add_pin("{0}_{1}".format(bl_name,0), "OUTPUT")
for row in range(self.total_size):
for wl_name in self.cell.get_all_wl_names():
self.add_pin("{0}_{1}".format(wl_name,row), "INPUT")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.replica_cell = factory.create(module_type="replica_bitcell")
@ -62,76 +75,77 @@ class replica_column(design.design):
def create_instances(self):
self.cell_inst = {}
for row in range(self.row_size):
for row in range(self.total_size):
name="rbc_{0}".format(row)
if row>0 and row<self.row_size-2:
# 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.left_rbl and row<self.total_size-self.right_rbl-1):
self.cell_inst[row]=self.add_inst(name=name,
mod=self.replica_cell)
elif row==self.replica_bit:
self.cell_inst[row]=self.add_inst(name=name,
mod=self.replica_cell)
else:
self.cell_inst[row]=self.add_inst(name=name,
mod=self.dummy_cell)
self.connect_inst(self.list_bitcell_pins(0, row))
self.connect_inst(self.get_bitcell_pins(0, row))
def place_instances(self):
yoffset = 0
for row in range(self.row_size):
# Flip the mirrors if we have an odd number of replica+dummy rows at the bottom
# so that we will start with mirroring rather than not mirroring
rbl_offset = (self.left_rbl+1)%2
for row in range(self.total_size):
name = "bit_r{0}_{1}".format(row,"rbl")
# This is opposite of a bitcell array since we will be 1 row off
if not row % 2:
tempy = yoffset
dir_key = ""
else:
tempy = yoffset + self.cell.height
offset = vector(0,self.cell.height*(row+(row+rbl_offset)%2))
if (row+rbl_offset)%2:
dir_key = "MX"
else:
dir_key = "R0"
self.cell_inst[row].place(offset=[0.0, tempy],
mirror=dir_key)
yoffset += self.cell.height
self.cell_inst[row].place(offset=offset,
mirror=dir_key)
def add_layout_pins(self):
""" Add the layout pins """
row_list = self.cell.list_all_wl_names()
column_list = self.cell.list_all_bitline_names()
col = "0"
for cell_column in column_list:
bl_pin = self.cell_inst[0].get_pin(cell_column)
self.add_layout_pin(text=cell_column+"_{0}".format(col),
for bl_name in self.cell.get_all_bitline_names():
bl_pin = self.cell_inst[0].get_pin(bl_name)
self.add_layout_pin(text=bl_name,
layer="metal2",
offset=bl_pin.ll(),
width=bl_pin.width(),
height=self.height)
for row in range(self.row_size):
for cell_row in row_list:
wl_pin = self.cell_inst[row].get_pin(cell_row)
self.add_layout_pin(text=cell_row+"_{0}".format(row),
for row in range(self.total_size):
for wl_name in self.cell.get_all_wl_names():
wl_pin = self.cell_inst[row].get_pin(wl_name)
self.add_layout_pin(text="{0}_{1}".format(wl_name,row),
layer="metal1",
offset=wl_pin.ll(),
offset=wl_pin.ll().scale(0,1),
width=self.width,
height=wl_pin.height())
# For every second row and column, add a via for gnd and vdd
for row in range(self.row_size):
for row in range(self.total_size):
inst = self.cell_inst[row]
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
def list_bitcell_pins(self, col, row):
def get_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array """
bitcell_pins = []
pin_names = self.cell.list_all_bitline_names()
pin_names = self.cell.get_all_bitline_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(col))
pin_names = self.cell.list_all_wl_names()
pin_names = self.cell.get_all_wl_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(row))
bitcell_pins.append("vdd")
@ -139,3 +153,9 @@ class replica_column(design.design):
return bitcell_pins
def exclude_all_but_replica(self):
"""Excludes all bits except the replica cell (self.replica_bit)."""
for row, cell in self.cell_inst.items():
if row != self.replica_bit:
self.graph_inst_exclude.add(cell)

View File

@ -55,12 +55,12 @@ class sense_amp_array(design.design):
def add_pins(self):
for i in range(0,self.word_size):
self.add_pin("data_{0}".format(i))
self.add_pin("bl_{0}".format(i))
self.add_pin("br_{0}".format(i))
self.add_pin("en")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin("data_{0}".format(i), "OUTPUT")
self.add_pin("bl_{0}".format(i), "INPUT")
self.add_pin("br_{0}".format(i), "INPUT")
self.add_pin("en", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.amp = factory.create(module_type="sense_amp")

View File

@ -50,13 +50,13 @@ class wordline_driver(design.design):
def add_pins(self):
# inputs to wordline_driver.
for i in range(self.rows):
self.add_pin("in_{0}".format(i))
self.add_pin("in_{0}".format(i), "INPUT")
# Outputs from wordline_driver.
for i in range(self.rows):
self.add_pin("wl_{0}".format(i))
self.add_pin("en_bar")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin("wl_{0}".format(i), "OUTPUT")
self.add_pin("en", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
@ -109,7 +109,7 @@ class wordline_driver(design.design):
# add nand 2
self.nand_inst.append(self.add_inst(name=name_nand,
mod=self.nand2))
self.connect_inst(["en_bar",
self.connect_inst(["en",
"in_{0}".format(row),
"wl_bar_{0}".format(row),
"vdd", "gnd"])
@ -151,7 +151,7 @@ class wordline_driver(design.design):
""" Route all of the signals """
# Wordline enable connection
en_pin=self.add_layout_pin(text="en_bar",
en_pin=self.add_layout_pin(text="en",
layer="metal2",
offset=[self.m1_width + 2*self.m1_space,0],
width=self.m2_width,
@ -162,7 +162,7 @@ class wordline_driver(design.design):
nand_inst = self.nand_inst[row]
inv2_inst = self.inv2_inst[row]
# en_bar connection
# en connection
a_pin = nand_inst.get_pin("A")
a_pos = a_pin.lc()
clk_offset = vector(en_pin.bc().x,a_pos.y)

View File

@ -59,17 +59,17 @@ class write_driver_array(design.design):
def add_pins(self):
for i in range(self.word_size):
self.add_pin("data_{0}".format(i))
self.add_pin("data_{0}".format(i), "INPUT")
for i in range(self.word_size):
self.add_pin("bl_{0}".format(i))
self.add_pin("br_{0}".format(i))
self.add_pin("bl_{0}".format(i), "OUTPUT")
self.add_pin("br_{0}".format(i), "OUTPUT")
if self.write_size != None:
for i in range(self.num_wmasks):
self.add_pin("en_{}".format(i))
self.add_pin("en_{}".format(i), "INPUT")
else:
self.add_pin("en")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin("en", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.driver = factory.create(module_type="write_driver")

View File

@ -47,12 +47,12 @@ class options(optparse.Values):
###################
# Optimization options
###################
rbl_delay_percentage = .5 #Approximate percentage of delay compared to bitlines
rbl_delay_percentage = 0.5 #Approximate percentage of delay compared to bitlines
# Allow manual adjustment of the delay chain over automatic
use_tech_delay_chain_size = False
delay_chain_stages = 4
delay_chain_fanout_per_stage = 3
delay_chain_stages = 9
delay_chain_fanout_per_stage = 4
@ -84,7 +84,7 @@ class options(optparse.Values):
# This determines whether LVS and DRC is checked for every submodule.
inline_lvsdrc = False
# Remove noncritical memory cells for characterization speed-up
trim_netlist = True
trim_netlist = False
# Run with extracted parasitics
use_pex = False
@ -131,6 +131,7 @@ class options(optparse.Values):
delay_chain = "delay_chain"
dff_array = "dff_array"
dff = "dff"
dummy_bitcell = "dummy_bitcell"
precharge_array = "precharge_array"
ptx = "ptx"
replica_bitcell = "replica_bitcell"

View File

@ -47,11 +47,11 @@ class pand2(pgate.pgate):
self.DRC_LVS()
def add_pins(self):
self.add_pin("A")
self.add_pin("B")
self.add_pin("Z")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin("A", "INPUT")
self.add_pin("B", "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="pand2_nand",

View File

@ -42,10 +42,10 @@ class pbuf(pgate.pgate):
self.add_layout_pins()
def add_pins(self):
self.add_pin("A")
self.add_pin("Z")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin("A", "INPUT")
self.add_pin("Z", "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4

View File

@ -53,15 +53,15 @@ class pdriver(pgate.pgate):
elif not self.neg_polarity and (self.num_stages%2):
self.num_stages += 1
self.size_list = []
# compute sizes backwards from the fanout
fanout_prev = self.fanout
for x in range(self.num_stages):
fanout_prev = max(round(fanout_prev/self.stage_effort),1)
self.size_list.append(fanout_prev)
self.size_list = []
# compute sizes backwards from the fanout
fanout_prev = self.fanout
for x in range(self.num_stages):
fanout_prev = max(round(fanout_prev/self.stage_effort),1)
self.size_list.append(fanout_prev)
# reverse the sizes to be from input to output
self.size_list.reverse()
# reverse the sizes to be from input to output
self.size_list.reverse()
def create_netlist(self):
@ -81,10 +81,10 @@ class pdriver(pgate.pgate):
def add_pins(self):
self.add_pin("A")
self.add_pin("Z")
self.add_pin("vdd")
self.add_pin("gnd")
self.add_pin("A", "INPUT")
self.add_pin("Z", "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.inv_list = []
@ -178,7 +178,7 @@ class pdriver(pgate.pgate):
return self.inv_list[0].input_load()
def analytical_delay(self, corner, slew, load=0.0):
"""Calculate the analytical delay of INV1 -> ... -> INVn"""
""" Calculate the analytical delay of INV1 -> ... -> INVn """
cout_list = []
for prev_inv,inv in zip(self.inv_list, self.inv_list[1:]):
@ -198,9 +198,12 @@ class pdriver(pgate.pgate):
return delay
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"""
""" 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())
@ -217,5 +220,5 @@ class pdriver(pgate.pgate):
return stage_effort_list
def get_cin(self):
"""Returns the relative capacitance of the input"""
""" Returns the relative capacitance of the input """
return self.inv_list[0].get_cin()

View File

@ -60,7 +60,7 @@ class pinv(pgate.pgate):
def add_pins(self):
""" Adds pins for spice netlist """
pin_list = ["A", "Z", "vdd", "gnd"]
dir_list = ['INPUT', 'OUTPUT', 'POWER', 'GROUND']
dir_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list)
@ -300,4 +300,4 @@ class pinv(pgate.pgate):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
self.add_graph_edges(graph, port_nets)

View File

@ -61,7 +61,7 @@ class pnand2(pgate.pgate):
def add_pins(self):
""" Adds pins for spice netlist """
pin_list = ["A", "B", "Z", "vdd", "gnd"]
dir_list = ['INPUT', 'INPUT', 'OUTPUT', 'POWER', 'GROUND']
dir_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list)
@ -281,4 +281,4 @@ class pnand2(pgate.pgate):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
self.add_graph_edges(graph, port_nets)

View File

@ -44,7 +44,7 @@ class pnand3(pgate.pgate):
def add_pins(self):
""" Adds pins for spice netlist """
pin_list = ["A", "B", "C", "Z", "vdd", "gnd"]
dir_list = ['INPUT', 'INPUT', 'INPUT', 'OUTPUT', 'POWER', 'GROUND']
dir_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list)
def create_netlist(self):
@ -283,4 +283,4 @@ class pnand3(pgate.pgate):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
self.add_graph_edges(graph, port_nets)

View File

@ -41,7 +41,7 @@ class pnor2(pgate.pgate):
def add_pins(self):
""" Adds pins for spice netlist """
pin_list = ["A", "B", "Z", "vdd", "gnd"]
dir_list = ['INPUT', 'INPUT', 'OUTPUT', 'INOUT', 'INOUT']
dir_list = ["INPUT", "INPUT", "OUTPUT", "INOUT", "INOUT"]
self.add_pin_list(pin_list, dir_list)
def create_netlist(self):
@ -242,4 +242,4 @@ class pnor2(pgate.pgate):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
self.add_graph_edges(graph, port_nets)

View File

@ -53,7 +53,7 @@ class precharge(design.design):
self.connect_to_bitlines()
def add_pins(self):
self.add_pin_list(["bl", "br", "en_bar", "vdd"])
self.add_pin_list(["bl", "br", "en_bar", "vdd"], ["OUTPUT", "OUTPUT", "INPUT", "POWER"])
def add_ptx(self):
"""

View File

@ -99,13 +99,13 @@ class sram_1bank(sram_base):
# This includes 2 M2 pitches for the row addr clock line.
control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch,
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - self.bank.m2_gap)
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - 2*self.bank.m2_gap )
self.control_logic_insts[port].place(control_pos[port])
# The row address bits are placed above the control logic aligned on the right.
x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width
# It is aove the control logic but below the top of the bitcell array
y_offset = max(self.control_logic_insts[port].uy(), self.bank.bank_array_ur.y - self.row_addr_dff_insts[port].height)
# It is above the control logic but below the top of the bitcell array
y_offset = max(self.control_logic_insts[port].uy(), self.bank_inst.uy() - self.row_addr_dff_insts[port].height)
row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port])
@ -166,16 +166,35 @@ class sram_1bank(sram_base):
# This includes 2 M2 pitches for the row addr clock line
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch,
self.bank.bank_array_ur.y + self.control_logic_insts[port].mod.control_logic_center.y + self.bank.m2_gap)
self.bank.bank_array_ur.y + self.control_logic_insts[port].height -
(self.control_logic_insts[port].height - self.control_logic_insts[port].mod.control_logic_center.y)
+ 2*self.bank.m2_gap)
#import pdb; pdb.set_trace()
self.control_logic_insts[port].place(control_pos[port], mirror="XY")
# The row address bits are placed above the control logic aligned on the left.
x_offset = control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width
# It is above the control logic but below the top of the bitcell array
y_offset = min(self.control_logic_insts[port].by(), self.bank.bank_array_ll.y - self.row_addr_dff_insts[port].height)
# It is below the control logic but below the bottom of the bitcell array
y_offset = min(self.control_logic_insts[port].by(), self.bank_inst.by() + self.row_addr_dff_insts[port].height)
row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY")
# Add the data flops above the bank to the left of the upper-right of bank array
# This relies on the upper-right of the array of the bank
# decoder in upper left, bank in upper right, sensing in lower right.
# These flops go below the sensing and leave a gap to channel route to the
# sense amps.
if port in self.write_ports:
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
self.bank.height + max_gap_size + self.dff.height)
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
# Add the write mask flops to the left of the din flops.
if self.write_size is not None:
if port in self.write_ports:
wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
self.bank.height + max_gap_size + self.data_dff_insts[port].height)
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
def add_layout_pins(self):
@ -271,7 +290,8 @@ class sram_1bank(sram_base):
def route_control_logic(self):
""" Route the outputs from the control logic module """
""" Route the control logic pins that are not inputs """
for port in self.all_ports:
for signal in self.control_logic_outputs[port]:
# The clock gets routed separately and is not a part of the bank
@ -279,10 +299,14 @@ class sram_1bank(sram_base):
continue
src_pin = self.control_logic_insts[port].get_pin(signal)
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
self.add_via_center(layers=("metal1","via1","metal2"),
offset=src_pin.rc())
self.connect_vbus_m2m3(src_pin, dest_pin)
for port in self.read_ports:
# Only input (besides pins) is the replica bitline
src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
self.connect_vbus_m2m3(src_pin, dest_pin)
def route_row_addr_dff(self):
""" Connect the output of the row flops to the bank pins """
@ -401,4 +425,4 @@ class sram_1bank(sram_base):
#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)
return self.bank_inst.mod.get_cell_name(inst_name+'.x'+self.bank_inst.name, row, col)

View File

@ -120,7 +120,7 @@ class sram_base(design, verilog, lef):
self.add_lvs_correspondence_points()
self.offset_all_coordinates()
#self.offset_all_coordinates()
highest_coord = self.find_highest_coords()
self.width = highest_coord[0]
@ -197,7 +197,7 @@ class sram_base(design, verilog, lef):
if self.port_id[port] == "r":
self.control_bus_names[port].extend([sen, pen])
elif self.port_id[port] == "w":
self.control_bus_names[port].extend([wen])
self.control_bus_names[port].extend([wen, pen])
else:
self.control_bus_names[port].extend([sen, wen, pen])
self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
@ -301,9 +301,6 @@ class sram_base(design, verilog, lef):
self.bank_count = 0
self.supply_rail_width = self.bank.supply_rail_width
self.supply_rail_pitch = self.bank.supply_rail_pitch
#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
@ -342,6 +339,10 @@ class sram_base(design, verilog, lef):
for port in self.read_ports:
for bit in range(self.word_size):
temp.append("DOUT{0}[{1}]".format(port,bit))
for port in self.read_ports:
temp.append("rbl_bl{0}".format(port))
for port in self.read_ports:
temp.append("rbl_wl{0}".format(port))
for port in self.write_ports:
for bit in range(self.word_size):
temp.append("BANK_DIN{0}[{1}]".format(port,bit))
@ -353,7 +354,7 @@ class sram_base(design, verilog, lef):
temp.append("bank_sel{0}[{1}]".format(port,bank_num))
for port in self.read_ports:
temp.append("s_en{0}".format(port))
for port in self.read_ports:
for port in self.all_ports:
temp.append("p_en_bar{0}".format(port))
for port in self.write_ports:
temp.append("w_en{0}".format(port))
@ -501,41 +502,46 @@ class sram_base(design, verilog, lef):
temp.append("web{}".format(port))
temp.append("clk{}".format(port))
# for port in self.all_ports:
# self.add_pin("csb{}".format(port), "INPUT")
# for port in self.readwrite_ports:
# self.add_pin("web{}".format(port), "INPUT")
# for port in self.all_ports:
# self.add_pin("clk{}".format(port), "INPUT")
if port in self.read_ports:
temp.append("rbl_bl{}".format(port))
# Ouputs
if port in self.read_ports:
temp.append("rbl_wl{}".format(port))
# Outputs
if port in self.read_ports:
temp.append("s_en{}".format(port))
if port in self.write_ports:
temp.append("w_en{}".format(port))
if port in self.read_ports:
temp.append("p_en_bar{}".format(port))
temp.append("p_en_bar{}".format(port))
temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"])
self.connect_inst(temp)
return insts
def connect_rail_from_left_m2m3(self, src_pin, dest_pin):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = src_pin.rc()
def connect_vbus_m2m3(self, src_pin, dest_pin):
""" Helper routine to connect an instance to a vertical bus.
Routes horizontal then vertical L shape.
Dest pin is assumed to be on M2.
Src pin can be on M1/M2/M3."""
if src_pin.cx()<dest_pin.cx():
in_pos = src_pin.rc()
else:
in_pos = src_pin.lc()
out_pos = dest_pin.center()
# move horizontal first
self.add_wire(("metal3","via2","metal2"),[in_pos, vector(out_pos.x,in_pos.y),out_pos])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=src_pin.rc())
def connect_rail_from_left_m2m1(self, src_pin, dest_pin):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = src_pin.rc()
out_pos = vector(dest_pin.cx(), in_pos.y)
self.add_wire(("metal2","via1","metal1"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)])
if src_pin.layer=="metal1":
self.add_via_center(layers=("metal1","via1","metal2"),
offset=in_pos)
if src_pin.layer in ["metal1","metal2"]:
self.add_via_center(layers=("metal2","via2","metal3"),
offset=in_pos)
def sp_write(self, sp_name):
# Write the entire spice of the object to the file

View File

@ -61,7 +61,6 @@ class sram_config:
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)
debug.info(1,"Words per row: {}".format(self.words_per_row))
self.recompute_sizes()
def recompute_sizes(self):
@ -71,6 +70,8 @@ class sram_config:
SRAM for testing.
"""
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
@ -78,12 +79,16 @@ class sram_config:
# Fix the number of columns and rows
self.num_cols = int(self.words_per_row*self.word_size)
self.num_rows = int(self.num_words_per_bank/self.words_per_row)
debug.info(1,"Rows: {} Cols: {}".format(self.num_rows,self.num_cols))
# Compute the address and bank sizes
self.row_addr_size = int(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)
+ " 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):

View File

@ -15,25 +15,31 @@ from globals import OPTS
from sram_factory import factory
import debug
class replica_bitline_test(openram_test):
class replica_pbitcell_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
import dummy_pbitcell
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
# check replica bitline in single port
stages=4
fanout=4
rows=13
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = factory.create(module_type="replica_bitline", delay_fanout_list=stages*[fanout], bitcell_loads=rows)
self.local_check(a)
stages=8
rows=100
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = factory.create(module_type="replica_bitline", delay_fanout_list=stages*[fanout], bitcell_loads=rows)
self.local_check(a)
factory.reset()
debug.info(2, "Checking dummy bitcell using pbitcell (small cell)")
tx = dummy_pbitcell.dummy_pbitcell(name="rpbc")
self.local_check(tx)
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(2, "Checking dummy bitcell using pbitcell (large cell)")
tx = dummy_pbitcell.dummy_pbitcell(name="rpbc")
self.local_check(tx)
globals.end_openram()
# run the test from the command line

View File

@ -20,13 +20,17 @@ import debug
class bitcell_1rw_1r_array_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
globals.init_openram("config_{0}".format(OPTS.tech_name))
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.dummy_bitcell="dummy_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
a = factory.create(module_type="bitcell_array", cols=4, rows=4)
self.local_check(a)

View File

@ -0,0 +1,51 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California
# 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 replica_bitcell_array_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
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, left_rbl=1, right_rbl=1, bitcell_ports=[0,1])
self.local_check(a)
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
factory.reset()
debug.info(2, "Testing 4x4 array for pbitcell")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_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

@ -0,0 +1,43 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California
# 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 replica_bitcell_array_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.dummy_bitcell="dummy_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0,1])
self.local_check(a)
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0,1])
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

@ -19,7 +19,7 @@ class replica_bitcell_array_test(openram_test):
globals.init_openram("config_{0}".format(OPTS.tech_name))
debug.info(2, "Testing 4x4 array for 6t_cell")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4)
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0])
self.local_check(a)
globals.end_openram()

View File

@ -1,66 +0,0 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys,os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class replica_bitline_multiport_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
stages=4
fanout=4
rows=13
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
factory.reset()
debug.info(2, "Testing 1rw 1r RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = factory.create(module_type="replica_bitline", delay_fanout_list=stages*[fanout], bitcell_loads=rows)
self.local_check(a)
# check replica bitline in pbitcell multi-port
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing RBL pbitcell 1rw with {0} FO4 stages, {1} rows".format(stages,rows))
a = factory.create(module_type="replica_bitline", delay_fanout_list=stages*[fanout], bitcell_loads=rows)
self.local_check(a)
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 1
OPTS.num_r_ports = 1
factory.reset()
debug.info(2, "Testing RBL pbitcell 1rw 1w 1r with {0} FO4 stages, {1} rows".format(stages,rows))
a = factory.create(module_type="replica_bitline", delay_fanout_list=stages*[fanout], bitcell_loads=rows)
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

@ -19,9 +19,17 @@ class replica_column_test(openram_test):
globals.init_openram("config_{0}".format(OPTS.tech_name))
debug.info(2, "Testing replica column for 6t_cell")
a = factory.create(module_type="replica_column", rows=4)
a = factory.create(module_type="replica_column", rows=4, left_rbl=1, right_rbl=0, replica_bit=1)
self.local_check(a)
debug.info(2, "Testing replica column for 6t_cell")
a = factory.create(module_type="replica_column", rows=4, left_rbl=1, right_rbl=1, replica_bit=6)
self.local_check(a)
debug.info(2, "Testing replica column for 6t_cell")
a = factory.create(module_type="replica_column", rows=4, left_rbl=2, right_rbl=0, replica_bit=2)
self.local_check(a)
globals.end_openram()
# run the test from the command line

View File

@ -22,10 +22,17 @@ class control_logic_test(openram_test):
import control_logic
import tech
# check control logic for single port
debug.info(1, "Testing sample for control_logic")
debug.info(1, "Testing sample for control_logic_rw")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32)
self.local_check(a)
debug.info(1, "Testing sample for control_logic_r")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="r")
self.local_check(a)
debug.info(1, "Testing sample for control_logic_w")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="w")
self.local_check(a)
# run the test from the command line
if __name__ == "__main__":

View File

@ -55,9 +55,9 @@ class port_data_test(openram_test):
self.local_check(a)
OPTS.bitcell = "bitcell_1w_1r"
OPTS.num_rw_ports = 1
OPTS.num_rw_ports = 0
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
OPTS.num_w_ports = 1
c.num_words=16
c.words_per_row=1

View File

@ -20,14 +20,16 @@ class psingle_bank_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from bank import bank
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
# testing layout of bank using pbitcell with 1 RW port (a 6T-cell equivalent)
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.dummy_bitcell="dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
c = sram_config(word_size=4,
num_words=16)
@ -35,8 +37,7 @@ class psingle_bank_test(openram_test):
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
name = "bank1_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
a = bank(c, name=name)
a = factory.create(module_type="bank", sram_config=c)
self.local_check(a)
c.num_words=32
@ -44,8 +45,7 @@ class psingle_bank_test(openram_test):
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
name = "bank2_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
a = bank(c, name=name)
a = factory.create(module_type="bank", sram_config=c)
self.local_check(a)
c.num_words=64
@ -53,8 +53,7 @@ class psingle_bank_test(openram_test):
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
name = "bank3_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
a = bank(c, name=name)
a = factory.create(module_type="bank", sram_config=c)
self.local_check(a)
c.word_size=2
@ -63,93 +62,9 @@ class psingle_bank_test(openram_test):
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
name = "bank4_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
a = bank(c, name=name)
a = factory.create(module_type="bank", sram_config=c)
self.local_check(a)
# testing bank using pbitcell in various port combinations
# layout for multiple ports does not work yet
"""
OPTS.netlist_only = True
c.num_words=16
c.words_per_row=1
OPTS.num_rw_ports = c.num_rw_ports = 2
OPTS.num_w_ports = c.num_w_ports = 2
OPTS.num_r_ports = c.num_r_ports = 2
debug.info(1, "No column mux")
name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports)
a = bank(c, name=name)
self.local_check(a)
OPTS.num_rw_ports = c.num_rw_ports = 0
OPTS.num_w_ports = c.num_w_ports = 2
OPTS.num_r_ports = c.num_r_ports = 2
debug.info(1, "No column mux")
name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports)
a = bank(c, name=name)
self.local_check(a)
OPTS.num_rw_ports = c.num_rw_ports = 2
OPTS.num_w_ports = c.num_w_ports = 0
OPTS.num_r_ports = c.num_r_ports = 2
debug.info(1, "No column mux")
name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports)
a = bank(c, name=name)
self.local_check(a)
OPTS.num_rw_ports = c.num_rw_ports = 2
OPTS.num_w_ports = c.num_w_ports = 2
OPTS.num_r_ports = c.num_r_ports = 0
debug.info(1, "No column mux")
name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports)
a = bank(c, name=name)
self.local_check(a)
OPTS.num_rw_ports = c.num_rw_ports = 2
OPTS.num_w_ports = c.num_w_ports = 0
OPTS.num_r_ports = c.num_r_ports = 0
debug.info(1, "No column mux")
name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports)
a = bank(c, name=name)
self.local_check(a)
# testing with various column muxes
OPTS.num_rw_ports = c.num_rw_ports = 2
OPTS.num_w_ports = c.num_w_ports = 2
OPTS.num_r_ports = c.num_r_ports = 2
c.num_words=32
c.words_per_row=2
debug.info(1, "Two way column mux")
name = "bank2_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports)
a = bank(c, name=name)
self.local_check(a)
c.num_words=64
c.words_per_row=4
debug.info(1, "Four way column mux")
name = "bank3_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports)
a = bank(c, name=name)
self.local_check(a)
# Eight way has a short circuit of one column mux select to gnd rail
c.word_size=2
c.num_words=128
c.words_per_row=8
debug.info(1, "Eight way column mux")
name = "bank4_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports)
a = bank(c, name=name)
self.local_check(a)
"""
globals.end_openram()
# run the test from the command line

View File

@ -19,13 +19,15 @@ class single_bank_1rw_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from sram_config import sram_config
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.dummy_bitcell="dummy_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=16)

View File

@ -19,13 +19,16 @@ class single_bank_1w_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from sram_config import sram_config
OPTS.bitcell = "bitcell_1w_1r"
OPTS.num_rw_ports = 1
OPTS.replica_bitcell = "replica_bitcell_1w_1r"
OPTS.dummy_bitcell="dummy_bitcell_1w_1r"
OPTS.num_rw_ports = 0
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
OPTS.num_w_ports = 1
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=16)

View File

@ -21,9 +21,10 @@ class psram_1bank_2mux_1rw_1w_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.dummy_bitcell="dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 1
OPTS.num_r_ports = 0

View File

@ -21,9 +21,10 @@ class psram_1bank_2mux_1w_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.dummy_bitcell="dummy_pbitcell"
OPTS.num_rw_ports = 0
OPTS.num_w_ports = 1
OPTS.num_r_ports = 1

View File

@ -23,6 +23,7 @@ class psram_1bank_2mux_test(openram_test):
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.dummy_bitcell="dummy_pbitcell"
# testing layout of sram using pbitcell with 1 RW port (a 6T-cell equivalent)
OPTS.num_rw_ports = 1

View File

@ -20,9 +20,10 @@ class psram_1bank_4mux_1rw_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.dummy_bitcell="dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1

View File

@ -23,6 +23,7 @@ class sram_1bank_2mux_1rw_1r_test(openram_test):
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.dummy_bitcell="dummy_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0

View File

@ -21,9 +21,10 @@ class psram_1bank_2mux_1w_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from sram_config import sram_config
OPTS.bitcell = "bitcell_1w_1r"
OPTS.replica_bitcell="replica_bitcell_1w_1r"
OPTS.dummy_bitcell="dummy_bitcell_1w_1r"
OPTS.num_rw_ports = 0
OPTS.num_w_ports = 1
OPTS.num_r_ports = 1

View File

@ -23,6 +23,7 @@ class sram_1bank_8mux_1rw_1r_test(openram_test):
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.dummy_bitcell="dummy_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0

View File

@ -23,6 +23,7 @@ class sram_1bank_nomux_1rw_1r_test(openram_test):
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.dummy_bitcell = "dummy_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0

View File

@ -61,28 +61,27 @@ class timing_sram_test(openram_test):
data.update(port_data[0])
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [0.2121267],
'delay_lh': [0.2121267],
'leakage_power': 0.0023761999999999998,
'min_period': 0.43,
'read0_power': [0.5139368],
'read1_power': [0.48940979999999995],
'slew_hl': [0.0516745],
'slew_lh': [0.0516745],
'write0_power': [0.46267169999999996],
'write1_power': [0.4670826]}
golden_data = {'delay_hl': [0.2181231],
'delay_lh': [0.2181231],
'leakage_power': 0.0025453999999999997,
'min_period': 0.781,
'read0_power': [0.34664159999999994],
'read1_power': [0.32656349999999995],
'slew_hl': [0.21136519999999998],
'slew_lh': [0.21136519999999998],
'write0_power': [0.37980179999999997],
'write1_power': [0.3532026]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [1.288],
'delay_lh': [1.288],
'leakage_power': 0.0273896,
'min_period': 2.578,
'read0_power': [16.9996],
'read1_power': [16.2616],
'slew_hl': [0.47891700000000004],
'slew_lh': [0.47891700000000004],
'write0_power': [16.0656],
'write1_power': [16.2616]}
golden_data = {'delay_hl': [1.4082],
'delay_lh': [1.4082],
'leakage_power': 0.0267388,
'min_period': 4.688,
'read0_power': [11.5255],
'read1_power': [10.9406],
'slew_hl': [1.2979],
'slew_lh': [1.2979],
'write0_power': [12.9458],
'write1_power': [11.7444]}
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results

View File

@ -15,8 +15,10 @@ from globals import OPTS
from sram_factory import factory
import debug
class model_delay_sram_test(openram_test):
@unittest.skip("SKIPPING 21_model_delay_test")
class model_delay_test(openram_test):
""" Compare the accuracy of the analytical model with a spice simulation. """
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
OPTS.analytical_delay = False
@ -61,9 +63,9 @@ class model_delay_sram_test(openram_test):
debug.info(1,"Spice Delays={}".format(spice_delays))
debug.info(1,"Model Delays={}".format(model_delays))
if OPTS.tech_name == "freepdk45":
error_tolerance = .25
error_tolerance = 0.25
elif OPTS.tech_name == "scn4m_subm":
error_tolerance = .25
error_tolerance = 0.25
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results

View File

@ -54,28 +54,27 @@ class timing_sram_test(openram_test):
data.update(port_data[0])
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [0.2108836],
'delay_lh': [0.2108836],
'leakage_power': 0.001564799,
'min_period': 0.508,
'read0_power': [0.43916689999999997],
'read1_power': [0.4198608],
'slew_hl': [0.0455126],
'slew_lh': [0.0455126],
'write0_power': [0.40681890000000004],
'write1_power': [0.4198608]}
golden_data = {'delay_hl': [0.22609590000000002],
'delay_lh': [0.22609590000000002],
'leakage_power': 0.003317743,
'min_period': 0.859,
'read0_power': [0.3271056],
'read1_power': [0.3064244],
'slew_hl': [0.2153979],
'slew_lh': [0.2153979],
'write0_power': [0.3532067],
'write1_power': [0.3381259]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [1.5747600000000002],
'delay_lh': [1.5747600000000002],
'leakage_power': 0.00195795,
'min_period': 3.281,
'read0_power': [14.92874],
'read1_power': [14.369810000000001],
'slew_hl': [0.49631959999999997],
'slew_lh': [0.49631959999999997],
'write0_power': [13.79953],
'write1_power': [14.369810000000001]}
golden_data = {'delay_hl': [1.709791],
'delay_lh': [1.709791],
'leakage_power': 0.06803324999999999,
'min_period': 7.812,
'read0_power': [7.9499070000000005],
'read1_power': [7.619662999999999],
'slew_hl': [1.390261],
'slew_lh': [1.390261],
'write0_power': [8.913003],
'write1_power': [8.166687000000001]}
else:
self.assertTrue(False) # other techs fail

View File

@ -15,18 +15,20 @@ from globals import OPTS
from sram_factory import factory
import debug
#@unittest.skip("SKIPPING 22_psram_1bank_2mux_1rw_1r_1w_func_test, third port reads are broken?")
class psram_1bank_2mux_1rw_1r_1w_func_test(openram_test):
class psram_1bank_2mux_func_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.trim_netlist = False
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.dummy_bitcell="dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
@ -35,28 +37,25 @@ class psram_1bank_2mux_1rw_1r_1w_func_test(openram_test):
reload(characterizer)
from characterizer import functional, delay
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=64,
c = sram_config(word_size=2,
num_words=32,
num_banks=1)
c.words_per_row=2
c.recompute_sizes()
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for {}rw,{}r,{}w psram with"
"{} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
d = delay(s.s, tempspice, corner)
feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew)
f.num_cycles = 10
(fail, error) = f.run()
self.assertTrue(fail,error)

View File

@ -23,10 +23,13 @@ class psram_1bank_4mux_func_test(openram_test):
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.trim_netlist = False
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.dummy_bitcell="dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
@ -35,28 +38,25 @@ class psram_1bank_4mux_func_test(openram_test):
reload(characterizer)
from characterizer import functional, delay
from sram_config import sram_config
c = sram_config(word_size=4,
c = sram_config(word_size=2,
num_words=256,
num_banks=1)
c.words_per_row=4
c.recompute_sizes()
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for {}rw,{}r,{}w psram with"
"{} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
d = delay(s.s, tempspice, corner)
feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew)
f.num_cycles = 10
(fail, error) = f.run()
self.assertTrue(fail,error)

View File

@ -23,10 +23,13 @@ class psram_1bank_8mux_func_test(openram_test):
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.trim_netlist = False
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.dummy_bitcell="dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
@ -40,23 +43,20 @@ class psram_1bank_8mux_func_test(openram_test):
num_banks=1)
c.words_per_row=8
c.recompute_sizes()
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for {}rw,{}r,{}w psram with"
"{} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
d = delay(s.s, tempspice, corner)
feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew)
f.num_cycles = 10
(fail, error) = f.run()
self.assertTrue(fail,error)

View File

@ -23,10 +23,13 @@ class psram_1bank_nomux_func_test(openram_test):
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.trim_netlist = False
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.dummy_bitcell="dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
@ -35,28 +38,25 @@ class psram_1bank_nomux_func_test(openram_test):
reload(characterizer)
from characterizer import functional, delay
from sram_config import sram_config
c = sram_config(word_size=4,
c = sram_config(word_size=2,
num_words=32,
num_banks=1)
c.words_per_row=1
c.recompute_sizes()
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for {}rw,{}r,{}w psram with"
"{} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
d = delay(s.s, tempspice, corner)
feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew)
f.num_cycles = 10
(fail, error) = f.run()
self.assertTrue(fail,error)

7
compiler/tests/22_psram_wmask_func_test.py Normal file → Executable file
View File

@ -27,6 +27,7 @@ class psram_wmask_func_test(openram_test):
OPTS.trim_netlist = False
OPTS.bitcell = "bitcell_1w_1r"
OPTS.replica_bitcell = "replica_bitcell_1w_1r"
OPTS.dummy_bitcell = "dummy_bitcell_1w_1r"
OPTS.num_rw_ports = 0
OPTS.num_w_ports = 1
@ -52,13 +53,11 @@ class psram_wmask_func_test(openram_test):
c.write_size,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f.num_cycles = 10
(fail, error) = f.run()
self.assertTrue(fail, error)
@ -70,4 +69,4 @@ if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())
unittest.main(testRunner=debugTestRunner())

View File

@ -31,25 +31,22 @@ class sram_1bank_2mux_func_test(openram_test):
from characterizer import functional, delay
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=64,
num_words=32,
num_banks=1)
c.words_per_row=2
c.recompute_sizes()
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for sram with "
"{} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
d = delay(s.s, tempspice, corner)
feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew)
f.num_cycles = 10
(fail, error) = f.run(feasible_period)
(fail, error) = f.run()
self.assertTrue(fail,error)
globals.end_openram()

View File

@ -31,24 +31,21 @@ class sram_1bank_4mux_func_test(openram_test):
from characterizer import functional, delay
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=256,
num_words=128,
num_banks=1)
c.words_per_row=4
c.recompute_sizes()
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for sram with "
"{} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
d = delay(s.s, tempspice, corner)
feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew)
f.num_cycles = 10
(fail, error) = f.run()
self.assertTrue(fail,error)

View File

@ -34,24 +34,21 @@ class sram_1bank_8mux_func_test(openram_test):
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=256,
num_words=128,
num_banks=1)
c.words_per_row=8
c.recompute_sizes()
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for sram with "
"{} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
d = delay(s.s, tempspice, corner)
feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew)
f.num_cycles = 10
(fail, error) = f.run()
self.assertTrue(fail,error)

View File

@ -30,22 +30,21 @@ class sram_1bank_nomux_func_test(openram_test):
from characterizer import functional
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=32,
num_words=16,
num_banks=1)
c.words_per_row=1
c.recompute_sizes()
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for sram with "
"{} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f.num_cycles = 10
(fail, error) = f.run()
self.assertTrue(fail,error)

View File

@ -25,6 +25,7 @@ class psram_1bank_nomux_func_test(openram_test):
OPTS.trim_netlist = False
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.dummy_bitcell="dummy_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1
@ -40,20 +41,17 @@ class psram_1bank_nomux_func_test(openram_test):
num_banks=1)
c.words_per_row=1
c.recompute_sizes()
debug.info(1, "Functional test for sram 1rw,1r with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for sram 1rw,1r with "
"{} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
d = delay(s.s, tempspice, corner)
feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew)
f.num_cycles = 10
(fail, error) = f.run()
self.assertTrue(fail,error)

View File

@ -36,19 +36,18 @@ class sram_wmask_func_test(openram_test):
num_banks=1)
c.words_per_row=1
c.recompute_sizes()
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} bit writes, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.write_size,
c.num_banks))
debug.info(1, "Functional test for sram with "
"{} bit words, {} words, {} words per row, {} bit writes, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.write_size,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f.num_cycles = 10
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -14,7 +14,8 @@ import globals
from globals import OPTS
import debug
class model_corners_lib_test(openram_test):
@unittest.skip("SKIPPING 23_lib_sram_model_corners_test")
class lib_model_corners_lib_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))

View File

@ -14,7 +14,8 @@ import globals
from globals import OPTS
import debug
class lib_test(openram_test):
@unittest.skip("SKIPPING 23_lib_sram_model_test")
class lib_sram_model_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))

View File

@ -14,7 +14,8 @@ import globals
from globals import OPTS
import debug
class lib_test(openram_test):
@unittest.skip("SKIPPING 23_lib_sram_prune_test")
class lib_sram_prune_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))

View File

@ -16,7 +16,7 @@ from sram_factory import factory
import debug
import getpass
class openram_test(openram_test):
class openram_back_end_test(openram_test):
def runTest(self):
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))

View File

@ -16,7 +16,8 @@ from sram_factory import factory
import debug
import getpass
class openram_test(openram_test):
@unittest.skip("SKIPPING 30_openram_front_end_test")
class openram_front_end_test(openram_test):
def runTest(self):
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))

View File

@ -16,5 +16,5 @@ temperatures = [25]
inline_lvsdrc = True
route_supplies = True
check_lvsdrc = True
analytical_delay = False

View File

@ -13,6 +13,6 @@ process_corners = ["TT"]
supply_voltages = [1.0]
temperatures = [25]
analytical_delay = False

View File

@ -16,6 +16,7 @@ temperatures = [25]
route_supplies = True
check_lvsdrc = True
inline_lvsdrc = True
analytical_delay = False
drc_name = "magic"
lvs_name = "netgen"

View File

@ -13,6 +13,8 @@ process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
analytical_delay = False
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -16,6 +16,7 @@ temperatures = [25]
route_supplies = True
check_lvsdrc = True
inline_lvsdrc = True
analytical_delay = False
drc_name = "magic"
lvs_name = "netgen"

View File

@ -60,6 +60,8 @@ class openram_test(unittest.TestCase):
#shutil.make_archive(zip_file, 'zip', OPTS.openram_temp)
self.fail("LVS mismatch: {}".format(a.name))
# For debug...
#import pdb; pdb.set_trace()
if OPTS.purge_temp:
self.cleanup()

View File

@ -345,11 +345,11 @@ spice["msflop_leakage"] = 1 # Leakage power of flop in nW
spice["flop_para_cap"] = 2 # Parasitic Output capacitance in fF
spice["default_event_rate"] = 100 # Default event activity of every gate. MHz
spice["flop_transition_prob"] = .5 # Transition probability of inverter.
spice["inv_transition_prob"] = .5 # Transition probability of inverter.
spice["nand2_transition_prob"] = .1875 # Transition probability of 2-input nand.
spice["nand3_transition_prob"] = .1094 # Transition probability of 3-input nand.
spice["nor2_transition_prob"] = .1875 # Transition probability of 2-input nor.
spice["flop_transition_prob"] = 0.5 # Transition probability of inverter.
spice["inv_transition_prob"] = 0.5 # Transition probability of inverter.
spice["nand2_transition_prob"] = 0.1875 # Transition probability of 2-input nand.
spice["nand3_transition_prob"] = 0.1094 # Transition probability of 3-input nand.
spice["nor2_transition_prob"] = 0.1875 # Transition probability of 2-input nor.
#Parameters related to sense amp enable timing and delay chain/RBL sizing
parameter['le_tau'] = 2.25 #In pico-seconds.
@ -357,10 +357,10 @@ parameter['cap_relative_per_ff'] = 7.5 #Units of Relative Capacitance/ Femt
parameter["dff_clk_cin"] = 30.6 #relative capacitance
parameter["6tcell_wl_cin"] = 3 #relative capacitance
parameter["min_inv_para_delay"] = 2.4 #Tau delay units
parameter["sa_en_pmos_size"] = .72 #micro-meters
parameter["sa_en_nmos_size"] = .27 #micro-meters
parameter["sa_inv_pmos_size"] = .54 #micro-meters
parameter["sa_inv_nmos_size"] = .27 #micro-meters
parameter["sa_en_pmos_size"] = 0.72 #micro-meters
parameter["sa_en_nmos_size"] = 0.27 #micro-meters
parameter["sa_inv_pmos_size"] = 0.54 #micro-meters
parameter["sa_inv_nmos_size"] = 0.27 #micro-meters
parameter['bitcell_drain_cap'] = 0.1 #In Femto-Farad, approximation of drain capacitance
###################################################

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