mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into analytical_cleanup
This commit is contained in:
commit
2efc0a3983
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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))]
|
||||
|
||||
|
||||
|
|
@ -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))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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={}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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={}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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 """
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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()
|
||||
|
|
@ -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())
|
||||
|
|
@ -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
|
||||
|
|
@ -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__":
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -16,5 +16,5 @@ temperatures = [25]
|
|||
inline_lvsdrc = True
|
||||
route_supplies = True
|
||||
check_lvsdrc = True
|
||||
|
||||
analytical_delay = False
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,6 @@ process_corners = ["TT"]
|
|||
supply_voltages = [1.0]
|
||||
temperatures = [25]
|
||||
|
||||
|
||||
analytical_delay = False
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ temperatures = [25]
|
|||
route_supplies = True
|
||||
check_lvsdrc = True
|
||||
inline_lvsdrc = True
|
||||
analytical_delay = False
|
||||
|
||||
drc_name = "magic"
|
||||
lvs_name = "netgen"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ temperatures = [25]
|
|||
route_supplies = True
|
||||
check_lvsdrc = True
|
||||
inline_lvsdrc = True
|
||||
analytical_delay = False
|
||||
|
||||
drc_name = "magic"
|
||||
lvs_name = "netgen"
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue