mirror of https://github.com/VLSIDA/OpenRAM.git
merge dev
This commit is contained in:
commit
854d51c721
|
|
@ -43,7 +43,7 @@ The OpenRAM compiler has very few dependencies:
|
|||
|
||||
If you want to perform DRC and LVS, you will need either:
|
||||
+ Calibre (for [FreePDK45])
|
||||
+ [Magic] 8.2.79 or higher (for [SCMOS])
|
||||
+ [Magic] 8.3.27 or higher (for [SCMOS])
|
||||
+ [Netgen] 1.5 (for [SCMOS])
|
||||
|
||||
You must set two environment variables:
|
||||
|
|
@ -81,6 +81,8 @@ We have included the most recent SCN4M_SUBM design rules from [Qflow].
|
|||
|
||||
## Docker Image
|
||||
|
||||
**WARNING! Some OpenRAM dependency tools installed in the Docker image are out-of-date.**
|
||||
|
||||
We have a pre-configured Ubuntu [Docker](https://www.docker.com/) image
|
||||
available that has all tools installed for the [SCMOS] process. It is
|
||||
available at [docker hub](https://hub.docker.com/r/vlsida/openram-ubuntu).
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ class channel_route(design.design):
|
|||
"""
|
||||
name = "cr_{0}".format(channel_route.unique_id)
|
||||
channel_route.unique_id += 1
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
self.netlist = netlist
|
||||
self.offset = offset
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
# This will ignore the name parameter since
|
||||
# we can guarantee a unique name here
|
||||
|
||||
hierarchy_design.hierarchy_design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(4, "create contact object {0}".format(name))
|
||||
|
||||
self.add_comment("layers: {0}".format(layer_stack))
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class design(hierarchy_design):
|
|||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
hierarchy_design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
self.setup_drc_constants()
|
||||
self.setup_layer_constants()
|
||||
|
|
@ -230,13 +230,14 @@ class design(hierarchy_design):
|
|||
self.write_ports = []
|
||||
# These are the write-only port indices.
|
||||
self.writeonly_ports = []
|
||||
# These are teh read/write and read-only port indice
|
||||
# These are the read/write and read-only port indices
|
||||
self.read_ports = []
|
||||
# These are the read-only port indices.
|
||||
self.readonly_ports = []
|
||||
# These are all the ports
|
||||
self.all_ports = list(range(total_ports))
|
||||
|
||||
|
||||
# The order is always fixed as RW, W, R
|
||||
port_number = 0
|
||||
for port in range(OPTS.num_rw_ports):
|
||||
self.readwrite_ports.append(port_number)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import debug
|
|||
from vector import vector
|
||||
import tech
|
||||
import math
|
||||
import copy
|
||||
import numpy as np
|
||||
from globals import OPTS
|
||||
from utils import round_to_grid
|
||||
|
||||
|
|
@ -160,7 +162,7 @@ class instance(geometry):
|
|||
"""
|
||||
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
|
||||
"""Initializes an instance to represent a module"""
|
||||
geometry.__init__(self)
|
||||
super().__init__()
|
||||
debug.check(mirror not in ["R90", "R180", "R270"],
|
||||
"Please use rotation and not mirroring during instantiation.")
|
||||
|
||||
|
|
@ -269,6 +271,134 @@ class instance(geometry):
|
|||
p.transform(self.offset, self.mirror, self.rotate)
|
||||
new_pins.append(p)
|
||||
return new_pins
|
||||
|
||||
def calculate_transform(self, node):
|
||||
#set up the rotation matrix
|
||||
angle = math.radians(float(node.rotate))
|
||||
mRotate = np.array([[math.cos(angle),-math.sin(angle),0.0],
|
||||
[math.sin(angle),math.cos(angle),0.0],
|
||||
[0.0,0.0,1.0]])
|
||||
|
||||
#set up translation matrix
|
||||
translateX = float(node.offset[0])
|
||||
translateY = float(node.offset[1])
|
||||
mTranslate = np.array([[1.0,0.0,translateX],
|
||||
[0.0,1.0,translateY],
|
||||
[0.0,0.0,1.0]])
|
||||
|
||||
#set up the scale matrix (handles mirror X)
|
||||
scaleX = 1.0
|
||||
if(node.mirror == 'MX'):
|
||||
scaleY = -1.0
|
||||
else:
|
||||
scaleY = 1.0
|
||||
mScale = np.array([[scaleX,0.0,0.0],
|
||||
[0.0,scaleY,0.0],
|
||||
[0.0,0.0,1.0]])
|
||||
|
||||
return (mRotate, mScale, mTranslate)
|
||||
|
||||
def apply_transform(self, mtransforms, uVector, vVector, origin):
|
||||
origin = np.dot(mtransforms[0], origin) #rotate
|
||||
uVector = np.dot(mtransforms[0], uVector) #rotate
|
||||
vVector = np.dot(mtransforms[0], vVector) #rotate
|
||||
origin = np.dot(mtransforms[1], origin) #scale
|
||||
uVector = np.dot(mtransforms[1], uVector) #scale
|
||||
vVector = np.dot(mtransforms[1], vVector) #scale
|
||||
origin = np.dot(mtransforms[2], origin)
|
||||
|
||||
return(uVector, vVector, origin)
|
||||
|
||||
def apply_path_transform(self, path):
|
||||
uVector = np.array([[1.0],[0.0],[0.0]])
|
||||
vVector = np.array([[0.0],[1.0],[0.0]])
|
||||
origin = np.array([[0.0],[0.0],[1.0]])
|
||||
|
||||
while(path):
|
||||
instance = path.pop(-1)
|
||||
mtransforms = self.calculate_transform(instance)
|
||||
(uVector, vVector, origin) = self.apply_transform(mtransforms, uVector, vVector, origin)
|
||||
|
||||
return (uVector, vVector, origin)
|
||||
|
||||
def reverse_transformation_bitcell(self, cell_name):
|
||||
path = [] # path currently follwed in bitcell search
|
||||
cell_paths = [] # saved paths to bitcells
|
||||
origin_offsets = [] # cell to bank offset
|
||||
Q_offsets = [] # Q to cell offet
|
||||
Q_bar_offsets = [] # Q_bar to cell offset
|
||||
bl_offsets = [] # bl to cell offset
|
||||
br_offsets = [] # br to cell offset
|
||||
bl_meta = [] # bl offset metadata (row,col,name)
|
||||
br_meta = [] #br offset metadata (row,col,name)
|
||||
|
||||
def walk_subtree(node):
|
||||
path.append(node)
|
||||
|
||||
if node.mod.name == cell_name:
|
||||
cell_paths.append(copy.copy(path))
|
||||
|
||||
inst_name = path[-1].name
|
||||
|
||||
# get the row and col names from the path
|
||||
row = int(path[-1].name.split('_')[-2][1:])
|
||||
col = int(path[-1].name.split('_')[-1][1:])
|
||||
|
||||
cell_bl_meta = []
|
||||
cell_br_meta = []
|
||||
|
||||
normalized_storage_nets = node.mod.get_normalized_storage_nets_offset()
|
||||
(normalized_bl_offsets, normalized_br_offsets, bl_names, br_names) = node.mod.get_normalized_bitline_offset()
|
||||
|
||||
for offset in range(len(normalized_bl_offsets)):
|
||||
for port in range(len(bl_names)):
|
||||
cell_bl_meta.append([bl_names[offset], row, col, port])
|
||||
|
||||
for offset in range(len(normalized_br_offsets)):
|
||||
for port in range(len(br_names)):
|
||||
cell_br_meta.append([br_names[offset], row, col, port])
|
||||
|
||||
Q_x = normalized_storage_nets[0][0]
|
||||
Q_y = normalized_storage_nets[0][1]
|
||||
|
||||
Q_bar_x = normalized_storage_nets[1][0]
|
||||
Q_bar_y = normalized_storage_nets[1][1]
|
||||
|
||||
if node.mirror == 'MX':
|
||||
Q_y = -1 * Q_y
|
||||
Q_bar_y = -1 * Q_bar_y
|
||||
|
||||
for pair in range(len(normalized_bl_offsets)):
|
||||
normalized_bl_offsets[pair] = (normalized_bl_offsets[pair][0],
|
||||
-1 * normalized_bl_offsets[pair][1])
|
||||
|
||||
for pair in range(len(normalized_br_offsets)):
|
||||
normalized_br_offsets[pair] = (normalized_br_offsets[pair][0],
|
||||
-1 * normalized_br_offsets[pair][1])
|
||||
|
||||
|
||||
Q_offsets.append([Q_x, Q_y])
|
||||
Q_bar_offsets.append([Q_bar_x, Q_bar_y])
|
||||
|
||||
|
||||
bl_offsets.append(normalized_bl_offsets)
|
||||
br_offsets.append(normalized_br_offsets)
|
||||
|
||||
bl_meta.append(cell_bl_meta)
|
||||
br_meta.append(cell_br_meta)
|
||||
|
||||
elif node.mod.insts is not []:
|
||||
for instance in node.mod.insts:
|
||||
walk_subtree(instance)
|
||||
path.pop(-1)
|
||||
|
||||
walk_subtree(self)
|
||||
for path in cell_paths:
|
||||
vector_spaces = self.apply_path_transform(path)
|
||||
origin = vector_spaces[2]
|
||||
origin_offsets.append([origin[0], origin[1]])
|
||||
|
||||
return(origin_offsets, Q_offsets, Q_bar_offsets, bl_offsets, br_offsets, bl_meta, br_meta)
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
|
|
@ -284,7 +414,7 @@ class path(geometry):
|
|||
|
||||
def __init__(self, lpp, coordinates, path_width):
|
||||
"""Initializes a path for the specified layer"""
|
||||
geometry.__init__(self)
|
||||
super().__init__()
|
||||
self.name = "path"
|
||||
self.layerNumber = lpp[0]
|
||||
self.layerPurpose = lpp[1]
|
||||
|
|
@ -322,7 +452,7 @@ class label(geometry):
|
|||
|
||||
def __init__(self, text, lpp, offset, zoom=-1):
|
||||
"""Initializes a text label for specified layer"""
|
||||
geometry.__init__(self)
|
||||
super().__init__()
|
||||
self.name = "label"
|
||||
self.text = text
|
||||
self.layerNumber = lpp[0]
|
||||
|
|
@ -366,7 +496,7 @@ class rectangle(geometry):
|
|||
|
||||
def __init__(self, lpp, offset, width, height):
|
||||
"""Initializes a rectangular shape for specified layer"""
|
||||
geometry.__init__(self)
|
||||
super().__init__()
|
||||
self.name = "rect"
|
||||
self.layerNumber = lpp[0]
|
||||
self.layerPurpose = lpp[1]
|
||||
|
|
|
|||
|
|
@ -1324,6 +1324,8 @@ class layout():
|
|||
pdf.drawLayout()
|
||||
pdf.writeToFile(pdf_name)
|
||||
|
||||
|
||||
|
||||
def print_attr(self):
|
||||
"""Prints a list of attributes for the current layout object"""
|
||||
debug.info(0,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class route(design):
|
|||
def __init__(self, obj, layer_stack, path, layer_widths=[None,1,None]):
|
||||
name = "route_{0}".format(route.unique_route_id)
|
||||
route.unique_route_id += 1
|
||||
design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(3, "create route obj {0}".format(name))
|
||||
|
||||
self.obj = obj
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class bitcell(bitcell_base.bitcell_base):
|
|||
props.bitcell.cell_6t.pin.vdd,
|
||||
props.bitcell.cell_6t.pin.gnd]
|
||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||
storage_nets = ['Q', 'Qbar']
|
||||
storage_nets = ['Q', 'Q_bar']
|
||||
|
||||
(width, height) = utils.get_libcell_size("cell_6t",
|
||||
GDS["unit"],
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@
|
|||
|
||||
import debug
|
||||
import design
|
||||
from globals import OPTS
|
||||
import logical_effort
|
||||
from tech import parameter, drc
|
||||
from tech import parameter, drc, layer
|
||||
|
||||
|
||||
class bitcell_base(design.design):
|
||||
|
|
@ -78,7 +79,85 @@ class bitcell_base(design.design):
|
|||
fmt_str = "Storage nodes={} not found in spice file."
|
||||
debug.info(1, fmt_str.format(self.storage_nets))
|
||||
return None
|
||||
|
||||
|
||||
def get_storage_net_offset(self):
|
||||
"""
|
||||
Gets the location of the storage net labels to add top level
|
||||
labels for pex simulation.
|
||||
"""
|
||||
# If we generated the bitcell, we already know where Q and Q_bar are
|
||||
if OPTS.bitcell is not "pbitcell":
|
||||
self.storage_net_offsets = []
|
||||
for i in range(len(self.get_storage_net_names())):
|
||||
for text in self.gds.getTexts(layer["m1"]):
|
||||
if self.storage_nets[i] == text.textString.rstrip('\x00'):
|
||||
self.storage_net_offsets.append(text.coordinates[0])
|
||||
|
||||
for i in range(len(self.storage_net_offsets)):
|
||||
self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]])
|
||||
|
||||
|
||||
return(self.storage_net_offsets)
|
||||
|
||||
def get_bitline_offset(self):
|
||||
|
||||
bl_names = self.get_all_bl_names()
|
||||
br_names = self.get_all_br_names()
|
||||
|
||||
found_bl = []
|
||||
found_br = []
|
||||
|
||||
self.bl_offsets = []
|
||||
self.br_offsets = []
|
||||
|
||||
for i in range(len(bl_names)):
|
||||
for text in self.gds.getTexts(layer["m2"]):
|
||||
if not bl_names[i] in found_bl:
|
||||
if bl_names[i] == text.textString.rstrip('\x00'):
|
||||
self.bl_offsets.append(text.coordinates[0])
|
||||
found_bl.append(bl_names[i])
|
||||
|
||||
continue
|
||||
|
||||
for i in range(len(br_names)):
|
||||
for text in self.gds.getTexts(layer["m2"]):
|
||||
if not br_names[i] in found_br:
|
||||
if br_names[i] == text.textString.rstrip('\x00'):
|
||||
self.br_offsets.append(text.coordinates[0])
|
||||
found_br.append(br_names[i])
|
||||
continue
|
||||
|
||||
for i in range(len(self.bl_offsets)):
|
||||
self.bl_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.bl_offsets[i]])
|
||||
|
||||
for i in range(len(self.br_offsets)):
|
||||
self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]])
|
||||
|
||||
return(self.bl_offsets, self.br_offsets, found_bl, found_br)
|
||||
|
||||
def get_normalized_storage_nets_offset(self):
|
||||
"""
|
||||
Convert storage net offset to be relative to the bottom left corner
|
||||
of the bitcell. This is useful for making sense of offsets outside
|
||||
of the bitcell.
|
||||
"""
|
||||
if OPTS.bitcell is not "pbitcell":
|
||||
normalized_storage_net_offset = self.get_storage_net_offset()
|
||||
|
||||
else:
|
||||
net_offset = self.get_storage_net_offset()
|
||||
Q_x = net_offset[0][0] - self.leftmost_xpos
|
||||
Q_y = net_offset[0][1] - self.botmost_ypos
|
||||
Q_bar_x = net_offset[1][0] - self.leftmost_xpos
|
||||
Q_bar_y = net_offset[1][1] - self.botmost_ypos
|
||||
|
||||
normalized_storage_net_offset = [[Q_x,Q_y],[Q_bar_x,Q_bar_y]]
|
||||
|
||||
return normalized_storage_net_offset
|
||||
|
||||
def get_normalized_bitline_offset(self):
|
||||
return self.get_bitline_offset()
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
By default, bitcells won't be part of the graph.
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
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
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
self.Q_bar = "Q_bar"
|
||||
self.Q = "Q"
|
||||
self.storage_nets = [self.Q, self.Q_bar]
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
""" Determine size of transistors and add ptx modules """
|
||||
# if there are any read/write ports,
|
||||
|
|
@ -352,6 +352,11 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
self.right_building_edge = right_inverter_xpos \
|
||||
+ self.inverter_nmos.active_width
|
||||
|
||||
def add_pex_labels(self, left_inverter_offset, right_inverter_offset):
|
||||
self.add_label("Q", "metal1", left_inverter_offset)
|
||||
self.add_label("Q_bar", "metal1", right_inverter_offset)
|
||||
self.storage_net_offsets = [left_inverter_offset, right_inverter_offset]
|
||||
|
||||
def route_storage(self):
|
||||
""" Routes inputs and outputs of inverters to cross couple them """
|
||||
# connect input (gate) of inverters
|
||||
|
|
@ -396,6 +401,16 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x,
|
||||
contact_offset_right.y)
|
||||
self.add_path("poly", [contact_offset_right, gate_offset_left])
|
||||
if OPTS.use_pex:
|
||||
# add labels to cross couple inverter for extracted simulation
|
||||
contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \
|
||||
+ 0.5 * contact.poly.height,
|
||||
self.cross_couple_upper_ypos)
|
||||
|
||||
contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \
|
||||
- 0.5*contact.poly.height,
|
||||
self.cross_couple_lower_ypos)
|
||||
self.add_pex_labels(contact_offset_left_output, contact_offset_right_output)
|
||||
|
||||
def route_rails(self):
|
||||
""" Adds gnd and vdd rails and connects them to the inverters """
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class delay(simulation):
|
|||
"""
|
||||
|
||||
def __init__(self, sram, spfile, corner):
|
||||
simulation.__init__(self, sram, spfile, corner)
|
||||
super().__init__(sram, spfile, corner)
|
||||
|
||||
self.targ_read_ports = []
|
||||
self.targ_write_ports = []
|
||||
|
|
@ -136,19 +136,18 @@ class delay(simulation):
|
|||
"""
|
||||
|
||||
self.bitline_volt_meas = []
|
||||
|
||||
self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO",
|
||||
self.bl_name))
|
||||
self.bl_name))
|
||||
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO
|
||||
self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ZERO",
|
||||
self.br_name))
|
||||
self.br_name))
|
||||
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO
|
||||
|
||||
self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ONE",
|
||||
self.bl_name))
|
||||
self.bl_name))
|
||||
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE
|
||||
self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ONE",
|
||||
self.br_name))
|
||||
self.br_name))
|
||||
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE
|
||||
return self.bitline_volt_meas
|
||||
|
||||
|
|
@ -181,13 +180,16 @@ class delay(simulation):
|
|||
self.dout_volt_meas.append(voltage_at_measure("v_{}".format(meas.name),
|
||||
meas.targ_name_no_port))
|
||||
self.dout_volt_meas[-1].meta_str = meas.meta_str
|
||||
|
||||
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9)
|
||||
|
||||
if not OPTS.use_pex:
|
||||
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9)
|
||||
else:
|
||||
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name, "FALL", "RISE", measure_scale=1e9)
|
||||
|
||||
self.sen_meas.meta_str = sram_op.READ_ZERO
|
||||
self.sen_meas.meta_add_delay = True
|
||||
self.dout_volt_meas.append(self.sen_meas)
|
||||
|
||||
return self.dout_volt_meas
|
||||
|
||||
return self.dout_volt_meas + [self.sen_meas]
|
||||
|
||||
def create_read_bit_measures(self):
|
||||
""" Adds bit measurements for read0 and read1 cycles """
|
||||
|
|
@ -229,8 +231,13 @@ class delay(simulation):
|
|||
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])
|
||||
if not OPTS.use_pex:
|
||||
q_name = cell_name+'.'+str(storage_names[0])
|
||||
qbar_name = cell_name+'.'+str(storage_names[1])
|
||||
else:
|
||||
bank_num = self.sram.get_bank_num(self.sram.name, bit_row, bit_col)
|
||||
q_name = "bitcell_Q_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col)
|
||||
qbar_name = "bitcell_Q_bar_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col)
|
||||
|
||||
# Bit measures, measurements times to be defined later. The measurement names must be unique
|
||||
# but they is enforced externally. {} added to names to differentiate between ports allow the
|
||||
|
|
@ -272,37 +279,50 @@ class delay(simulation):
|
|||
"""Sets important names for characterization such as Sense amp enable and internal bit nets."""
|
||||
|
||||
port = self.read_ports[0]
|
||||
self.graph.get_all_paths('{}{}'.format("clk", port),
|
||||
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
|
||||
|
||||
sen_with_port = self.get_sen_name(self.graph.all_paths)
|
||||
if sen_with_port.endswith(str(port)):
|
||||
self.sen_name = sen_with_port[:-len(str(port))]
|
||||
else:
|
||||
self.sen_name = sen_with_port
|
||||
debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.")
|
||||
if not OPTS.use_pex:
|
||||
self.graph.get_all_paths('{}{}'.format("clk", port),
|
||||
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
|
||||
|
||||
debug.info(2,"s_en name = {}".format(self.sen_name))
|
||||
|
||||
bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port)
|
||||
port_pos = -1-len(str(self.probe_data))-len(str(port))
|
||||
|
||||
if bl_name_port.endswith(str(port)+"_"+str(self.probe_data)):
|
||||
self.bl_name = bl_name_port[:port_pos] +"{}"+ bl_name_port[port_pos+len(str(port)):]
|
||||
elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
|
||||
self.bl_name = bl_name_port
|
||||
else:
|
||||
self.bl_name = bl_name_port
|
||||
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
|
||||
sen_with_port = self.get_sen_name(self.graph.all_paths)
|
||||
if sen_with_port.endswith(str(port)):
|
||||
self.sen_name = sen_with_port[:-len(str(port))]
|
||||
else:
|
||||
self.sen_name = sen_with_port
|
||||
debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.")
|
||||
|
||||
debug.info(2,"s_en name = {}".format(self.sen_name))
|
||||
|
||||
if br_name_port.endswith(str(port)+"_"+str(self.probe_data)):
|
||||
self.br_name = br_name_port[:port_pos] +"{}"+ br_name_port[port_pos+len(str(port)):]
|
||||
elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
|
||||
self.br_name = br_name_port
|
||||
bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port)
|
||||
port_pos = -1-len(str(self.probe_data))-len(str(port))
|
||||
|
||||
if bl_name_port.endswith(str(port)+"_"+str(self.probe_data)):
|
||||
self.bl_name = bl_name_port[:port_pos] +"{}"+ bl_name_port[port_pos+len(str(port)):]
|
||||
elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
|
||||
self.bl_name = bl_name_port
|
||||
else:
|
||||
self.bl_name = bl_name_port
|
||||
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
|
||||
|
||||
if br_name_port.endswith(str(port)+"_"+str(self.probe_data)):
|
||||
self.br_name = br_name_port[:port_pos] +"{}"+ br_name_port[port_pos+len(str(port)):]
|
||||
elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
|
||||
self.br_name = br_name_port
|
||||
else:
|
||||
self.br_name = br_name_port
|
||||
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
|
||||
debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name))
|
||||
else:
|
||||
self.br_name = br_name_port
|
||||
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
|
||||
debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name))
|
||||
self.graph.get_all_paths('{}{}'.format("clk", port),
|
||||
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
|
||||
|
||||
self.sen_name = self.get_sen_name(self.graph.all_paths)
|
||||
debug.info(2,"s_en name = {}".format(self.sen_name))
|
||||
|
||||
|
||||
self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size-1)
|
||||
self.br_name = "br{0}_{1}".format(port, OPTS.word_size-1)
|
||||
debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name))
|
||||
|
||||
|
||||
def get_sen_name(self, paths, assumed_port=None):
|
||||
"""
|
||||
|
|
@ -316,7 +336,8 @@ class delay(simulation):
|
|||
debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.")
|
||||
enable_name = sa_mods[0].get_enable_name()
|
||||
sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0])
|
||||
|
||||
if OPTS.use_pex:
|
||||
sen_name = sen_name.split('.')[-1]
|
||||
return sen_name
|
||||
|
||||
def get_bl_name(self, paths, port):
|
||||
|
|
@ -332,7 +353,9 @@ class delay(simulation):
|
|||
exclude_set = self.get_bl_name_search_exclusions()
|
||||
for int_net in [cell_bl, cell_br]:
|
||||
bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set))
|
||||
|
||||
if OPTS.use_pex:
|
||||
for i in range(len(bl_names)):
|
||||
bl_names[i] = bl_names[i].split('.')[-1]
|
||||
return bl_names[0], bl_names[1]
|
||||
|
||||
|
||||
|
|
@ -423,8 +446,13 @@ class delay(simulation):
|
|||
|
||||
# instantiate the sram
|
||||
self.sf.write("\n* Instantiation of the SRAM\n")
|
||||
self.stim.inst_model(pins=self.pins,
|
||||
model_name=self.sram.name)
|
||||
if not OPTS.use_pex:
|
||||
self.stim.inst_model(pins=self.pins,
|
||||
model_name=self.sram.name)
|
||||
else:
|
||||
self.stim.inst_sram_pex(pins=self.pins,
|
||||
model_name=self.sram.name)
|
||||
|
||||
self.sf.write("\n* SRAM output loads\n")
|
||||
for port in self.read_ports:
|
||||
for i in range(self.word_size):
|
||||
|
|
@ -1351,7 +1379,7 @@ class delay(simulation):
|
|||
Return the analytical model results for the SRAM.
|
||||
"""
|
||||
if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0:
|
||||
debug.warning("Analytical characterization results are not supported for multiport.")
|
||||
debug.warning("In analytical mode, all ports have the timing of the first read port.")
|
||||
|
||||
# Probe set to 0th bit, does not matter for analytical delay.
|
||||
self.set_probe('0'*self.addr_size, 0)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class functional(simulation):
|
|||
"""
|
||||
|
||||
def __init__(self, sram, spfile, corner):
|
||||
simulation.__init__(self, sram, spfile, corner)
|
||||
super().__init__(sram, spfile, corner)
|
||||
|
||||
# Seed the characterizer with a constant seed for unit tests
|
||||
if OPTS.is_unit_test:
|
||||
|
|
|
|||
|
|
@ -198,4 +198,4 @@ class voltage_at_measure(spice_measurement):
|
|||
meas_name = self.name
|
||||
targ_name = self.targ_name_no_port
|
||||
return (meas_name,targ_name,time_at)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class model_check(delay):
|
|||
"""
|
||||
|
||||
def __init__(self, sram, spfile, corner, custom_delaychain=False):
|
||||
delay.__init__(self,sram,spfile,corner)
|
||||
super().__init__(sram, spfile, corner)
|
||||
self.period = tech.spice["feasible_period"]
|
||||
self.create_data_names()
|
||||
self.custom_delaychain=custom_delaychain
|
||||
|
|
@ -446,4 +446,4 @@ class model_check(delay):
|
|||
return name_dict
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,30 @@ class stimuli():
|
|||
for pin in pins:
|
||||
self.sf.write("{0} ".format(pin))
|
||||
self.sf.write("{0}\n".format(model_name))
|
||||
|
||||
def inst_sram_pex(self, pins, model_name):
|
||||
self.sf.write("X{0} ".format(model_name))
|
||||
for pin in pins:
|
||||
self.sf.write("{0} ".format(pin))
|
||||
for bank in range(OPTS.num_banks):
|
||||
row = int(OPTS.num_words / OPTS.words_per_row) - 1
|
||||
col = int(OPTS.word_size * OPTS.words_per_row) - 1
|
||||
self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col))
|
||||
self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col))
|
||||
# can't add all bitcells to top level due to ngspice max port count of 1005
|
||||
# for row in range(int(OPTS.num_words / OPTS.words_per_row)):
|
||||
# for col in range(int(OPTS.word_size * OPTS.words_per_row)):
|
||||
# self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col))
|
||||
# self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col))
|
||||
for bank in range(OPTS.num_banks):
|
||||
for col in range(OPTS.word_size * OPTS.words_per_row):
|
||||
for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports):
|
||||
self.sf.write("bl{0}_{1} ".format(port, col))
|
||||
self.sf.write("br{0}_{1} ".format(port, col))
|
||||
|
||||
|
||||
self.sf.write("s_en{0} ".format(bank))
|
||||
self.sf.write("{0}\n".format(model_name))
|
||||
|
||||
def create_inverter(self, size=1, beta=2.5):
|
||||
""" Generates inverter for the top level signals (only for sim purposes) """
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
word_size = 2
|
||||
num_words = 16
|
||||
|
||||
num_rw_ports = 1
|
||||
num_rw_ports = 0
|
||||
num_r_ports = 1
|
||||
num_w_ports = 0
|
||||
num_w_ports = 1
|
||||
|
||||
tech_name = "scn4m_subm"
|
||||
nominal_corners_only = False
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import re
|
|||
import copy
|
||||
import importlib
|
||||
|
||||
VERSION = "1.1.5"
|
||||
VERSION = "1.1.6"
|
||||
NAME = "OpenRAM v{}".format(VERSION)
|
||||
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class bank(design.design):
|
|||
|
||||
if name == "":
|
||||
name = "bank_{0}_{1}".format(self.word_size, self.num_words)
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,
|
||||
self.num_words))
|
||||
|
||||
|
|
@ -74,7 +74,6 @@ class bank(design.design):
|
|||
self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1)
|
||||
self.bank_array_ur = self.bitcell_array_inst.ur()
|
||||
self.bank_array_ul = self.bitcell_array_inst.ul()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -83,7 +82,7 @@ class bank(design.design):
|
|||
for bit in range(self.word_size + self.num_spare_cols):
|
||||
self.add_pin("dout{0}_{1}".format(port, bit), "OUTPUT")
|
||||
for port in self.all_ports:
|
||||
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT")
|
||||
self.add_pin_list(self.bitcell_array.get_rbl_bitline_names(port), "OUTPUT")
|
||||
for port in self.write_ports:
|
||||
for bit in range(self.word_size + self.num_spare_cols):
|
||||
self.add_pin("din{0}_{1}".format(port, bit), "INPUT")
|
||||
|
|
@ -114,6 +113,8 @@ class bank(design.design):
|
|||
def route_layout(self):
|
||||
""" Create routing amoung the modules """
|
||||
self.route_central_bus()
|
||||
|
||||
self.route_unused_wordlines()
|
||||
|
||||
for port in self.all_ports:
|
||||
self.route_bitlines(port)
|
||||
|
|
@ -255,7 +256,7 @@ class bank(design.design):
|
|||
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
|
||||
# To the right of the bitcell array
|
||||
x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap
|
||||
self.port_address_offsets[port] = vector(x_offset,
|
||||
self.main_bitcell_array_bottom)
|
||||
|
|
@ -375,7 +376,6 @@ class bank(design.design):
|
|||
rows=self.num_rows)
|
||||
self.add_mod(self.port_address)
|
||||
|
||||
self.port_rbl_map = self.all_ports
|
||||
self.num_rbl = len(self.all_ports)
|
||||
|
||||
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
|
||||
|
|
@ -392,24 +392,28 @@ class bank(design.design):
|
|||
|
||||
def create_bitcell_array(self):
|
||||
""" Creating 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 + self.num_spare_cols):
|
||||
for bitline in self.bitline_names:
|
||||
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("{0}_{1}".format(wordline, row))
|
||||
rbl_names = self.bitcell_array.get_rbl_bitline_names()
|
||||
temp.extend(rbl_names)
|
||||
bitline_names = self.bitcell_array.get_bitline_names()
|
||||
temp.extend(bitline_names)
|
||||
# Replace RBL wordline with wl_en#
|
||||
wordline_names = self.bitcell_array.get_wordline_names()
|
||||
|
||||
rbl_wl_names = []
|
||||
for port in self.all_ports:
|
||||
temp.append("wl_en{0}".format(port))
|
||||
rbl_wl_names.append(self.bitcell_array.get_rbl_wordline_names(port))
|
||||
|
||||
# Rename the RBL WL to the enable name
|
||||
for port in self.all_ports:
|
||||
wordline_names = [x.replace(rbl_wl_names[port], "wl_en{0}".format(port)) for x in wordline_names]
|
||||
# Connect the other RBL WL to gnd
|
||||
wordline_names = ["gnd" if x.startswith("rbl_wl") else x for x in wordline_names]
|
||||
# Connect the dummy WL to gnd
|
||||
wordline_names = ["gnd" if x.startswith("dummy") else x for x in wordline_names]
|
||||
temp.extend(wordline_names)
|
||||
temp.append("vdd")
|
||||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
|
|
@ -420,17 +424,14 @@ class bank(design.design):
|
|||
|
||||
def create_port_data(self):
|
||||
""" Creating Port Data """
|
||||
|
||||
self.port_data_inst = [None] * len(self.all_ports)
|
||||
for port in self.all_ports:
|
||||
self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port),
|
||||
mod=self.port_data[port])
|
||||
|
||||
temp = []
|
||||
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)
|
||||
rbl_bl_names = self.bitcell_array.get_rbl_bitline_names(port)
|
||||
temp.extend(rbl_bl_names)
|
||||
for col in range(self.num_cols + self.num_spare_cols):
|
||||
temp.append("{0}_{1}".format(self.bl_names[port], col))
|
||||
temp.append("{0}_{1}".format(self.br_names[port], col))
|
||||
|
|
@ -453,7 +454,6 @@ class bank(design.design):
|
|||
for bit in range(self.num_spare_cols):
|
||||
temp.append("bank_spare_wen{0}_{1}".format(port, bit))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
|
||||
self.connect_inst(temp)
|
||||
|
||||
def place_port_data(self, offsets):
|
||||
|
|
@ -478,7 +478,7 @@ class bank(design.design):
|
|||
temp = []
|
||||
for bit in range(self.row_addr_size):
|
||||
temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size))
|
||||
temp.append("wl_en{0}".format(port))
|
||||
temp.append("wl_en{}".format(port))
|
||||
for row in range(self.num_rows):
|
||||
temp.append("{0}_{1}".format(self.wl_names[port], row))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
|
|
@ -666,7 +666,7 @@ 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.m3_pitch * self.num_control_lines[0] - self.m3_pitch, self.min_y_offset)
|
||||
control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - 2 * self.m3_pitch, self.min_y_offset)
|
||||
# The control bus is routed up to two pitches below the bitcell array
|
||||
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch
|
||||
self.bus_pins[0] = self.create_bus(layer="m2",
|
||||
|
|
@ -681,7 +681,7 @@ class bank(design.design):
|
|||
if len(self.all_ports)==2:
|
||||
# The other control bus is routed up to two pitches above the bitcell array
|
||||
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch
|
||||
control_bus_offset = vector(self.bitcell_array_right + self.m3_pitch,
|
||||
control_bus_offset = vector(self.bitcell_array_right + 2.5 * self.m3_pitch,
|
||||
self.max_y_offset - control_bus_length)
|
||||
# The bus for the right port is reversed so that the rbl_wl is closest to the array
|
||||
self.bus_pins[1] = self.create_bus(layer="m2",
|
||||
|
|
@ -714,14 +714,13 @@ class bank(design.design):
|
|||
|
||||
# connect spare bitlines
|
||||
for i in range(self.num_spare_cols):
|
||||
self.connect_bitline(inst1, inst2, inst1_bl_name.format(self.num_cols+i), "spare" + inst2_bl_name.format(i))
|
||||
self.connect_bitline(inst1, inst2, inst1_br_name.format(self.num_cols+i), "spare" + inst2_br_name.format(i))
|
||||
self.connect_bitline(inst1, inst2, inst1_bl_name.format(self.num_cols + i), "spare" + inst2_bl_name.format(i))
|
||||
self.connect_bitline(inst1, inst2, inst1_br_name.format(self.num_cols + i), "spare" + inst2_br_name.format(i))
|
||||
|
||||
# Connect the replica bitlines
|
||||
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")
|
||||
rbl_bl_names = self.bitcell_array.get_rbl_bitline_names(port)
|
||||
for (array_name, data_name) in zip(rbl_bl_names, ["rbl_bl", "rbl_br"]):
|
||||
self.connect_bitline(inst1, inst2, array_name, data_name)
|
||||
|
||||
def route_port_data_out(self, port):
|
||||
""" Add pins for the port data out """
|
||||
|
|
@ -962,7 +961,35 @@ class bank(design.design):
|
|||
self.add_label(text=data_name,
|
||||
layer="m1",
|
||||
offset=data_pin.center())
|
||||
|
||||
|
||||
def route_unused_wordlines(self):
|
||||
""" Connect the unused RBL and dummy wordlines to gnd """
|
||||
gnd_wl_names = []
|
||||
|
||||
# Connect unused RBL WL to gnd
|
||||
array_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("rbl")])
|
||||
dummy_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("dummy")])
|
||||
rbl_wl_names = set([self.bitcell_array.get_rbl_wordline_names(x) for x in self.all_ports])
|
||||
|
||||
gnd_wl_names = list((array_rbl_names - rbl_wl_names) | dummy_rbl_names)
|
||||
|
||||
for wl_name in gnd_wl_names:
|
||||
pin = self.bitcell_array_inst.get_pin(wl_name)
|
||||
pin_layer = pin.layer
|
||||
layer_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer))
|
||||
left_pin_loc = pin.lc()
|
||||
right_pin_loc = pin.rc()
|
||||
|
||||
# Place the pins a track outside of the array
|
||||
left_loc = left_pin_loc - vector(layer_pitch, 0)
|
||||
right_loc = right_pin_loc + vector(layer_pitch, 0)
|
||||
self.add_power_pin("gnd", left_loc, directions=("H", "H"))
|
||||
self.add_power_pin("gnd", right_loc, directions=("H", "H"))
|
||||
|
||||
# Add a path to connect to the array
|
||||
self.add_path(pin_layer, [left_loc, left_pin_loc])
|
||||
self.add_path(pin_layer, [right_loc, right_pin_loc])
|
||||
|
||||
def route_control_lines(self, port):
|
||||
""" Route the control lines of the entire bank """
|
||||
|
||||
|
|
@ -974,10 +1001,10 @@ class bank(design.design):
|
|||
connection.append((self.prefix + "p_en_bar{}".format(port),
|
||||
self.port_data_inst[port].get_pin("p_en_bar")))
|
||||
|
||||
rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port])
|
||||
rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)
|
||||
connection.append((self.prefix + "wl_en{}".format(port),
|
||||
self.bitcell_array_inst.get_pin(rbl_wl_name)))
|
||||
|
||||
|
||||
if port in self.write_ports:
|
||||
connection.append((self.prefix + "w_en{}".format(port),
|
||||
self.port_data_inst[port].get_pin("w_en")))
|
||||
|
|
@ -1012,7 +1039,7 @@ class bank(design.design):
|
|||
self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=control_pos)
|
||||
|
||||
|
||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
||||
"""Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline"""
|
||||
# Decoder is assumed to have settled before the negative edge of the clock.
|
||||
|
|
@ -1023,7 +1050,7 @@ class bank(design.design):
|
|||
inp_is_rise)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
|
||||
def get_wl_en_cin(self):
|
||||
"""Get the relative capacitance of all the clk connections in the bank"""
|
||||
# wl_en only used in the wordline driver.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class bank_select(design.design):
|
|||
"""
|
||||
|
||||
def __init__(self, name="bank_select", port="rw"):
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
self.port = port
|
||||
|
||||
|
|
|
|||
|
|
@ -13,12 +13,11 @@ from sram_factory import factory
|
|||
|
||||
class bitcell_array(bitcell_base_array):
|
||||
"""
|
||||
Creates a rows x cols array of memory cells. Assumes bit-lines
|
||||
and word line is connected by abutment.
|
||||
Connects the word lines and bit lines.
|
||||
Creates a rows x cols array of memory cells.
|
||||
Assumes bit-lines and word lines are connected by abutment.
|
||||
"""
|
||||
def __init__(self, cols, rows, name, column_offset=0):
|
||||
super().__init__(cols, rows, name, column_offset)
|
||||
def __init__(self, rows, cols, column_offset=0, name=""):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
|
|
@ -27,7 +26,7 @@ class bitcell_array(bitcell_base_array):
|
|||
# We don't offset this because we need to align
|
||||
# the replica bitcell in the control logic
|
||||
# self.offset_all_coordinates()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
""" Create and connect the netlist """
|
||||
self.add_modules()
|
||||
|
|
@ -41,7 +40,7 @@ class bitcell_array(bitcell_base_array):
|
|||
self.add_layout_pins()
|
||||
|
||||
self.add_boundary()
|
||||
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
|
|
@ -57,21 +56,21 @@ class bitcell_array(bitcell_base_array):
|
|||
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.get_bitcell_pins(col, row))
|
||||
|
||||
self.connect_inst(self.get_bitcell_pins(row, col))
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Power of Bitcell array and bitline in nW."""
|
||||
|
||||
|
||||
# Dynamic Power from Bitline
|
||||
bl_wire = self.gen_bl_wire()
|
||||
cell_load = 2 * bl_wire.return_input_cap()
|
||||
bl_swing = OPTS.rbl_delay_percentage
|
||||
freq = spice["default_event_frequency"]
|
||||
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
|
||||
|
||||
|
||||
# Calculate the bitcell power which currently only includes leakage
|
||||
cell_power = self.cell.analytical_power(corner, load)
|
||||
|
||||
|
||||
# Leakage power grows with entire array and bitlines.
|
||||
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
|
||||
cell_power.leakage * self.column_size * self.row_size)
|
||||
|
|
@ -83,7 +82,8 @@ class bitcell_array(bitcell_base_array):
|
|||
else:
|
||||
width = self.width
|
||||
wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1"))
|
||||
wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
|
||||
# 2 access tx gate per cell
|
||||
wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c
|
||||
return wl_wire
|
||||
|
||||
def gen_bl_wire(self):
|
||||
|
|
@ -93,7 +93,8 @@ class bitcell_array(bitcell_base_array):
|
|||
height = self.height
|
||||
bl_pos = 0
|
||||
bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1"))
|
||||
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
||||
# 1 access tx d/s per cell
|
||||
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c
|
||||
return bl_wire
|
||||
|
||||
def get_wordline_cin(self):
|
||||
|
|
@ -102,7 +103,7 @@ class bitcell_array(bitcell_base_array):
|
|||
bitcell_wl_cin = self.cell.get_wl_cin()
|
||||
total_cin = bitcell_wl_cin * self.column_size
|
||||
return total_cin
|
||||
|
||||
|
||||
def graph_exclude_bits(self, targ_row, targ_col):
|
||||
"""Excludes bits in column from being added to graph except target"""
|
||||
# Function is not robust with column mux configurations
|
||||
|
|
@ -111,7 +112,7 @@ class bitcell_array(bitcell_base_array):
|
|||
if row == targ_row and col == targ_col:
|
||||
continue
|
||||
self.graph_inst_exclude.add(self.cell_inst[row, col])
|
||||
|
||||
|
||||
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]
|
||||
|
|
|
|||
|
|
@ -8,14 +8,15 @@
|
|||
import debug
|
||||
import design
|
||||
from tech import cell_properties
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class bitcell_base_array(design.design):
|
||||
"""
|
||||
Abstract base class for bitcell-arrays -- bitcell, dummy
|
||||
Abstract base class for bitcell-arrays -- bitcell, dummy, replica
|
||||
"""
|
||||
def __init__(self, cols, rows, name, column_offset):
|
||||
design.design.__init__(self, name)
|
||||
def __init__(self, name, rows, cols, column_offset):
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
|
||||
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
||||
|
||||
|
|
@ -23,56 +24,52 @@ class bitcell_base_array(design.design):
|
|||
self.row_size = rows
|
||||
self.column_offset = column_offset
|
||||
|
||||
def get_all_bitline_names(self):
|
||||
# Bitcell for port names only
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
|
||||
self.create_all_bitline_names()
|
||||
self.create_all_wordline_names()
|
||||
|
||||
res = list()
|
||||
def get_all_bitline_names(self, prefix=""):
|
||||
return [prefix + x for x in self.bitline_names]
|
||||
|
||||
def create_all_bitline_names(self):
|
||||
self.bitline_names = list()
|
||||
bitline_names = self.cell.get_all_bitline_names()
|
||||
|
||||
# We have to keep the order of self.pins, otherwise we connect
|
||||
# it wrong in the spice netlist
|
||||
for pin in self.pins:
|
||||
for bl_name in bitline_names:
|
||||
if bl_name in pin:
|
||||
res.append(pin)
|
||||
return res
|
||||
for col in range(self.column_size):
|
||||
for cell_column in bitline_names:
|
||||
self.bitline_names.append("{0}_{1}".format(cell_column, col))
|
||||
|
||||
def get_all_wordline_names(self):
|
||||
def get_all_wordline_names(self, prefix=""):
|
||||
return [prefix + x for x in self.wordline_names]
|
||||
|
||||
res = list()
|
||||
def create_all_wordline_names(self):
|
||||
|
||||
self.wordline_names = list()
|
||||
wordline_names = self.cell.get_all_wl_names()
|
||||
|
||||
# We have to keep the order of self.pins, otherwise we connect
|
||||
# it wrong in the spice netlist
|
||||
for pin in self.pins:
|
||||
for wl_name in wordline_names:
|
||||
if wl_name in pin:
|
||||
res.append(pin)
|
||||
return res
|
||||
for row in range(self.row_size):
|
||||
for cell_row in wordline_names:
|
||||
self.wordline_names.append("{0}_{1}".format(cell_row, row))
|
||||
|
||||
def add_pins(self):
|
||||
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), "INOUT")
|
||||
for row in range(self.row_size):
|
||||
for cell_row in row_list:
|
||||
self.add_pin(cell_row+"_{0}".format(row), "INPUT")
|
||||
for bl_name in self.bitline_names:
|
||||
self.add_pin(bl_name, "INOUT")
|
||||
for wl_name in self.wordline_names:
|
||||
self.add_pin(wl_name, "INPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def get_bitcell_pins(self, col, row):
|
||||
def get_bitcell_pins(self, row, col):
|
||||
""" Creates a list of connections in the bitcell,
|
||||
indexed by column and row, for instance use in bitcell_array """
|
||||
|
||||
bitcell_pins = []
|
||||
|
||||
pin_names = self.cell.get_all_bitline_names()
|
||||
for pin in pin_names:
|
||||
bitcell_pins.append(pin + "_{0}".format(col))
|
||||
pin_names = self.cell.get_all_wl_names()
|
||||
for pin in pin_names:
|
||||
bitcell_pins.append(pin + "_{0}".format(row))
|
||||
# bitlines
|
||||
bitcell_pins.extend([x for x in self.bitline_names if x.endswith("_{0}".format(col))])
|
||||
# wordlines
|
||||
bitcell_pins.extend([x for x in self.wordline_names if x.endswith("_{0}".format(row))])
|
||||
bitcell_pins.append("vdd")
|
||||
bitcell_pins.append("gnd")
|
||||
|
||||
|
|
@ -81,22 +78,21 @@ class bitcell_base_array(design.design):
|
|||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
|
||||
row_list = self.cell.get_all_wl_names()
|
||||
column_list = self.cell.get_all_bitline_names()
|
||||
|
||||
bitline_names = self.cell.get_all_bitline_names()
|
||||
for col in range(self.column_size):
|
||||
for cell_column in column_list:
|
||||
bl_pin = self.cell_inst[0, col].get_pin(cell_column)
|
||||
self.add_layout_pin(text=cell_column + "_{0}".format(col),
|
||||
for bl_name in bitline_names:
|
||||
bl_pin = self.cell_inst[0, col].get_pin(bl_name)
|
||||
self.add_layout_pin(text="{0}_{1}".format(bl_name, col),
|
||||
layer=bl_pin.layer,
|
||||
offset=bl_pin.ll().scale(1, 0),
|
||||
width=bl_pin.width(),
|
||||
height=self.height)
|
||||
|
||||
wl_names = self.cell.get_all_wl_names()
|
||||
for row in range(self.row_size):
|
||||
for cell_row in row_list:
|
||||
wl_pin = self.cell_inst[row, 0].get_pin(cell_row)
|
||||
self.add_layout_pin(text=cell_row + "_{0}".format(row),
|
||||
for wl_name in wl_names:
|
||||
wl_pin = self.cell_inst[row, 0].get_pin(wl_name)
|
||||
self.add_layout_pin(text="{0}_{1}".format(wl_name, row),
|
||||
layer=wl_pin.layer,
|
||||
offset=wl_pin.ll().scale(0, 1),
|
||||
width=self.width,
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ class col_cap_array(bitcell_base_array):
|
|||
"""
|
||||
Generate a dummy row/column for the replica array.
|
||||
"""
|
||||
def __init__(self, cols, rows, column_offset=0, mirror=0, name=""):
|
||||
super().__init__(cols, rows, name, column_offset)
|
||||
def __init__(self, rows, cols, column_offset=0, mirror=0, name=""):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
|
||||
self.mirror = mirror
|
||||
|
||||
self.no_instances = True
|
||||
|
|
@ -50,9 +50,9 @@ class col_cap_array(bitcell_base_array):
|
|||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.dummy_cell)
|
||||
self.connect_inst(self.get_bitcell_pins(col, row))
|
||||
self.connect_inst(self.get_bitcell_pins(row, col))
|
||||
|
||||
def get_bitcell_pins(self, col, row):
|
||||
def get_bitcell_pins(self, row, col):
|
||||
"""
|
||||
Creates a list of connections in the bitcell,
|
||||
indexed by column and row, for instance use in bitcell_array
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class control_logic(design.design):
|
|||
def __init__(self, num_rows, words_per_row, word_size, spare_columns=None, sram=None, port_type="rw", name=""):
|
||||
""" Constructor """
|
||||
name = "control_logic_" + port_type
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {}".format(name))
|
||||
self.add_comment("num_rows: {0}".format(num_rows))
|
||||
self.add_comment("words_per_row: {0}".format(words_per_row))
|
||||
|
|
@ -869,7 +869,6 @@ class control_logic(design.design):
|
|||
offset=pin.ll(),
|
||||
height=pin.height(),
|
||||
width=pin.width())
|
||||
|
||||
def get_delays_to_wl(self):
|
||||
"""Get the delay (in delay units) of the clk to a wordline in the bitcell array"""
|
||||
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class delay_chain(design.design):
|
|||
|
||||
def __init__(self, name, fanout_list):
|
||||
"""init function"""
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "creating delay chain {0}".format(str(fanout_list)))
|
||||
self.add_comment("fanouts: {0}".format(str(fanout_list)))
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class dff_array(design.design):
|
|||
|
||||
if name=="":
|
||||
name = "dff_array_{0}x{1}".format(rows, columns)
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns))
|
||||
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class dff_buf(design.design):
|
|||
if name=="":
|
||||
name = "dff_buf_{0}".format(dff_buf.unique_id)
|
||||
dff_buf.unique_id += 1
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class dff_buf_array(design.design):
|
|||
if name=="":
|
||||
name = "dff_buf_array_{0}x{1}_{2}".format(rows, columns, dff_buf_array.unique_id)
|
||||
dff_buf_array.unique_id += 1
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
|
||||
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class dff_inv(design.design):
|
|||
if name=="":
|
||||
name = "dff_inv_{0}".format(dff_inv.unique_id)
|
||||
dff_inv.unique_id += 1
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
self.add_comment("inv: {0}".format(inv_size))
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class dff_inv_array(design.design):
|
|||
if name=="":
|
||||
name = "dff_inv_array_{0}x{1}_{2}".format(rows, columns, dff_inv_array.unique_id)
|
||||
dff_inv_array.unique_id += 1
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
|
||||
self.add_comment("inv1: {0}".format(inv1_size))
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ class dummy_array(bitcell_base_array):
|
|||
"""
|
||||
Generate a dummy row/column for the replica array.
|
||||
"""
|
||||
def __init__(self, cols, rows, column_offset=0, mirror=0, name=""):
|
||||
super().__init__(cols, rows, name, column_offset)
|
||||
def __init__(self, rows, cols, column_offset=0, mirror=0, name=""):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
|
||||
self.mirror = mirror
|
||||
|
||||
self.create_netlist()
|
||||
|
|
@ -51,7 +51,7 @@ class dummy_array(bitcell_base_array):
|
|||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.dummy_cell)
|
||||
self.connect_inst(self.get_bitcell_pins(col, row))
|
||||
self.connect_inst(self.get_bitcell_pins(row, col))
|
||||
|
||||
def input_load(self):
|
||||
wl_wire = self.gen_wl_wire()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
# 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 bitcell_base_array import bitcell_base_array
|
||||
from tech import drc, spice
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
class global_bitcell_array(bitcell_base_array):
|
||||
"""
|
||||
Creates a global bitcell array with a number
|
||||
of local arrays of a sizes given by a tuple in the list.
|
||||
"""
|
||||
def __init__(self, sizes, name=""):
|
||||
# Each bank will have the same number of rows
|
||||
self.rows = sizes[0][0]
|
||||
for (r, c) in sizes:
|
||||
debug.check(r[0] == self.rows, "Cannot have non-uniform number of rows in global array.")
|
||||
# The total of all columns will be the number of columns
|
||||
self.cols = sum(x[1] for x in sizes)
|
||||
self.sizes = sizes
|
||||
super().__init__(rows=self.rows, cols=self.cols, name=name)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
""" Create and connect the netlist """
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_instances()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.place()
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
self.add_boundary()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
self.local_mods = []
|
||||
for (row, col) in self.sizes:
|
||||
la = factory.create(module_type="local_bitcell_array", rows=row, cols=col)
|
||||
self.add_mod(la)
|
||||
self.local_mods.append(la)
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
self.local_inst = {}
|
||||
for i in range(self.sizes):
|
||||
name = "local_array_{0}".format(i)
|
||||
self.local_inst.append(self.add_inst(name=name,
|
||||
mod=self.local_mods[i])
|
||||
self.connect_inst(self.get_bitcell_pins(row, col))
|
||||
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ class hierarchical_decoder(design.design):
|
|||
Dynamically generated hierarchical decoder.
|
||||
"""
|
||||
def __init__(self, name, num_outputs):
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
self.AND_FORMAT = "DEC_AND_{0}"
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class hierarchical_predecode(design.design):
|
|||
self.column_decoder = (height != b.height)
|
||||
|
||||
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
def add_pins(self):
|
||||
for k in range(self.number_of_inputs):
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
Pre 2x4 decoder used in hierarchical_decoder.
|
||||
"""
|
||||
def __init__(self, name, height=None):
|
||||
hierarchical_predecode.__init__(self, name, 2, height)
|
||||
super().__init__( name, 2, height)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
Pre 3x8 decoder used in hierarchical_decoder.
|
||||
"""
|
||||
def __init__(self, name, height=None):
|
||||
hierarchical_predecode.__init__(self, name, 3, height)
|
||||
super().__init__(name, 3, height)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class hierarchical_predecode4x16(hierarchical_predecode):
|
|||
Pre 4x16 decoder used in hierarchical_decoder.
|
||||
"""
|
||||
def __init__(self, name, height=None):
|
||||
hierarchical_predecode.__init__(self, name, 4, height)
|
||||
super().__init__(name, 4, height)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,168 @@
|
|||
# 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 bitcell_base_array
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
from vector import vector
|
||||
from tech import drc
|
||||
import debug
|
||||
|
||||
class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||
"""
|
||||
A local bitcell array is a bitcell array with a wordline driver.
|
||||
This can either be a single aray on its own if there is no hierarchical WL
|
||||
or it can be combined into a larger array with hierarchical WL.
|
||||
"""
|
||||
def __init__(self, rows, cols, ports, left_rbl=0, right_rbl=0, add_replica=True, name=""):
|
||||
super().__init__(name, rows, cols, 0)
|
||||
debug.info(2, "create local array of size {} rows x {} cols words".format(rows,
|
||||
cols + left_rbl + right_rbl))
|
||||
|
||||
self.rows = rows
|
||||
self.cols = cols
|
||||
self.add_replica=add_replica
|
||||
self.all_ports = ports
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
# We don't offset this because we need to align
|
||||
# the replica bitcell in the control logic
|
||||
# self.offset_all_coordinates()
|
||||
|
||||
def create_netlist(self):
|
||||
""" Create and connect the netlist """
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_instances()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.place()
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
self.route()
|
||||
|
||||
self.add_boundary()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
# This is just used for names
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
|
||||
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
|
||||
cols=self.cols,
|
||||
rows=self.rows,
|
||||
left_rbl=1,
|
||||
right_rbl=1 if len(self.all_ports)>1 else 0,
|
||||
bitcell_ports=self.all_ports,
|
||||
add_replica=self.add_replica)
|
||||
self.add_mod(self.bitcell_array)
|
||||
|
||||
self.wl_array = factory.create(module_type="wordline_buffer_array",
|
||||
rows=self.rows + len(self.all_ports),
|
||||
cols=self.cols)
|
||||
self.add_mod(self.wl_array)
|
||||
|
||||
def add_pins(self):
|
||||
|
||||
self.bitline_names = self.bitcell_array.get_all_bitline_names()
|
||||
self.add_pin_list(self.bitline_names, "INOUT")
|
||||
self.driver_wordline_inputs = [x for x in self.bitcell_array.get_all_wordline_names() if not x.startswith("dummy")]
|
||||
self.driver_wordline_outputs = [x + "i" for x in self.driver_wordline_inputs]
|
||||
self.array_wordline_inputs = [x + "i" if not x.startswith("dummy") else "gnd" for x in self.bitcell_array.get_all_wordline_names()]
|
||||
self.add_pin_list(self.wordline_names, "INPUT")
|
||||
self.replica_names = self.bitcell_array.get_rbl_wordline_names()
|
||||
self.add_pin_list(self.replica_names, "INPUT")
|
||||
self.bitline_names = self.bitcell_array.get_inouts()
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
|
||||
self.wl_inst = self.add_inst(name="wl_driver",
|
||||
mod=self.wl_array)
|
||||
self.connect_inst(self.driver_wordline_inputs + self.driver_wordline_outputs + ["vdd", "gnd"])
|
||||
|
||||
self.bitcell_array_inst = self.add_inst(name="array",
|
||||
mod=self.bitcell_array,
|
||||
offset=self.wl_inst.lr())
|
||||
self.connect_inst(self.bitline_names + self.array_wordline_inputs + ["vdd", "gnd"])
|
||||
|
||||
def place(self):
|
||||
""" Place the bitcelll array to the right of the wl driver. """
|
||||
|
||||
self.wl_inst.place(vector(0, self.cell.height))
|
||||
# FIXME: Replace this with a tech specific paramter
|
||||
driver_to_array_spacing = 3 * self.m3_pitch
|
||||
self.bitcell_array_inst.place(vector(self.wl_inst.rx() + driver_to_array_spacing,
|
||||
0))
|
||||
|
||||
self.height = self.bitcell_array.height
|
||||
self.width = self.bitcell_array_inst.rx()
|
||||
|
||||
def route_unused_wordlines(self):
|
||||
""" Connect the unused RBL and dummy wordlines to gnd """
|
||||
gnd_wl_names = []
|
||||
|
||||
# Connect unused RBL WL to gnd
|
||||
array_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("rbl")])
|
||||
dummy_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("dummy")])
|
||||
rbl_wl_names = set([self.bitcell_array.get_rbl_wordline_names(x) for x in self.all_ports])
|
||||
|
||||
gnd_wl_names = list((array_rbl_names - rbl_wl_names) | dummy_rbl_names)
|
||||
|
||||
for wl_name in gnd_wl_names:
|
||||
pin = self.bitcell_array_inst.get_pin(wl_name)
|
||||
pin_layer = pin.layer
|
||||
layer_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer))
|
||||
left_pin_loc = pin.lc()
|
||||
right_pin_loc = pin.rc()
|
||||
|
||||
# Place the pins a track outside of the array
|
||||
left_loc = left_pin_loc - vector(layer_pitch, 0)
|
||||
right_loc = right_pin_loc + vector(layer_pitch, 0)
|
||||
self.add_power_pin("gnd", left_loc, directions=("H", "H"))
|
||||
self.add_power_pin("gnd", right_loc, directions=("H", "H"))
|
||||
|
||||
# Add a path to connect to the array
|
||||
self.add_path(pin_layer, [left_loc, left_pin_loc])
|
||||
self.add_path(pin_layer, [right_loc, right_pin_loc])
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
for (x, y) in zip(self.bitline_names, self.bitcell_array.get_inouts()):
|
||||
self.copy_layout_pin(self.bitcell_array_inst, y, x)
|
||||
|
||||
for (x, y) in zip(self.driver_wordline_inputs, self.wl_array.get_inputs()):
|
||||
self.copy_layout_pin(self.wl_inst, y, x)
|
||||
|
||||
supply_insts = [self.wl_inst, self.bitcell_array_inst]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
for inst in supply_insts:
|
||||
pin_list = inst.get_pins(pin_name)
|
||||
for pin in pin_list:
|
||||
self.add_power_pin(name=pin_name,
|
||||
loc=pin.center(),
|
||||
start_layer=pin.layer)
|
||||
|
||||
def route(self):
|
||||
array_names = [x for x in self.bitcell_array.get_all_wordline_names() if not x.startswith("dummy")]
|
||||
for (driver_name, array_name) in zip(self.wl_array.get_outputs(), array_names):
|
||||
out_pin = self.wl_inst.get_pin(driver_name)
|
||||
in_pin = self.bitcell_array_inst.get_pin(array_name)
|
||||
mid_loc = self.wl_inst.rx() + 1.5 * self.m3_pitch
|
||||
self.add_path(out_pin.layer, [out_pin.rc(), vector(mid_loc, out_pin.cy()), in_pin.lc()])
|
||||
|
||||
self.route_unused_wordlines()
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ class multibank(design.design):
|
|||
|
||||
def __init__(self, name, word_size, num_words, words_per_row, num_banks=1):
|
||||
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(2, "create sram of size {0} with {1} words".format(word_size,num_words))
|
||||
|
||||
self.word_size = word_size
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class port_address(design.design):
|
|||
|
||||
if name == "":
|
||||
name = "port_address_{0}_{1}".format(cols, rows)
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(2, "create data port of cols {0} rows {1}".format(cols, rows))
|
||||
|
||||
self.create_netlist()
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class port_data(design.design):
|
|||
|
||||
if name == "":
|
||||
name = "port_data_{0}".format(self.port)
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(2,
|
||||
"create data port of size {0} with {1} words per row".format(self.word_size,
|
||||
self.words_per_row))
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class precharge_array(design.design):
|
|||
"""
|
||||
|
||||
def __init__(self, name, columns, size=1, bitcell_bl="bl", bitcell_br="br", column_offset=0):
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br))
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@
|
|||
#
|
||||
|
||||
import debug
|
||||
import design
|
||||
import bitcell_base_array
|
||||
from tech import drc, spice, cell_properties
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class replica_bitcell_array(design.design):
|
||||
class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||
"""
|
||||
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
|
||||
|
|
@ -21,8 +21,8 @@ class replica_bitcell_array(design.design):
|
|||
Requires a regular bitcell array, replica bitcell, and dummy
|
||||
bitcell (Bl/BR disconnected).
|
||||
"""
|
||||
def __init__(self, cols, rows, left_rbl, right_rbl, bitcell_ports, name):
|
||||
design.design.__init__(self, name)
|
||||
def __init__(self, rows, cols, left_rbl, right_rbl, bitcell_ports, name, add_replica=True):
|
||||
super().__init__(name, rows, cols, column_offset=0)
|
||||
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
|
||||
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
||||
|
||||
|
|
@ -31,15 +31,26 @@ class replica_bitcell_array(design.design):
|
|||
self.left_rbl = left_rbl
|
||||
self.right_rbl = right_rbl
|
||||
self.bitcell_ports = bitcell_ports
|
||||
# If set to false, we increase the height for the replica wordline row, but don't
|
||||
# actually add the column to this array. This is so the height matches other
|
||||
# banks that have the replica columns.
|
||||
# Number of replica columns to actually add
|
||||
if add_replica:
|
||||
self.add_left_rbl = self.left_rbl
|
||||
self.add_right_rbl = self.right_rbl
|
||||
else:
|
||||
self.add_left_rbl = 0
|
||||
self.add_right_rbl = 0
|
||||
|
||||
debug.check(left_rbl + right_rbl == len(self.all_ports),
|
||||
debug.check(left_rbl + right_rbl <= len(self.all_ports),
|
||||
"Invalid number of RBLs for port configuration.")
|
||||
debug.check(left_rbl + right_rbl == len(self.bitcell_ports),
|
||||
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
|
||||
# Two dummy rows plus replica even if we don't add the column
|
||||
self.extra_rows = 2 + self.left_rbl + self.right_rbl
|
||||
# Two dummy cols plus replica if we add the column
|
||||
self.extra_cols = 2 + self.add_left_rbl + self.add_right_rbl
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
|
|
@ -79,33 +90,30 @@ class replica_bitcell_array(design.design):
|
|||
1 x (rows + 4)
|
||||
"""
|
||||
|
||||
# Bitcell for port names only
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
|
||||
# Bitcell array
|
||||
self.bitcell_array = factory.create(module_type="bitcell_array",
|
||||
column_offset=1 + self.left_rbl,
|
||||
column_offset=1 + self.add_left_rbl,
|
||||
cols=self.column_size,
|
||||
rows=self.row_size)
|
||||
self.add_mod(self.bitcell_array)
|
||||
|
||||
# Replica bitlines
|
||||
self.replica_columns = {}
|
||||
for bit in range(self.left_rbl + self.right_rbl):
|
||||
for bit in range(self.add_left_rbl + self.add_right_rbl):
|
||||
# Creating left_rbl
|
||||
if bit<self.left_rbl:
|
||||
replica_bit = bit + 1
|
||||
# dummy column
|
||||
column_offset = self.left_rbl - bit
|
||||
if bit < self.add_left_rbl:
|
||||
# These go from the top (where the bitcell array starts ) down
|
||||
replica_bit = self.left_rbl - bit
|
||||
# Creating right_rbl
|
||||
else:
|
||||
replica_bit = bit + self.row_size + 1
|
||||
# dummy column + replica column + bitcell colums
|
||||
column_offset = self.left_rbl - bit + self.row_size
|
||||
# These go from the bottom up
|
||||
replica_bit = self.left_rbl + self.row_size + 1 + bit
|
||||
# If we have an odd numer on the bottom
|
||||
column_offset = self.left_rbl + 1
|
||||
self.replica_columns[bit] = factory.create(module_type="replica_column",
|
||||
rows=self.row_size,
|
||||
left_rbl=self.left_rbl,
|
||||
right_rbl=self.right_rbl,
|
||||
left_rbl=self.add_left_rbl,
|
||||
right_rbl=self.add_right_rbl,
|
||||
column_offset=column_offset,
|
||||
replica_bit=replica_bit)
|
||||
self.add_mod(self.replica_columns[bit])
|
||||
|
|
@ -115,7 +123,7 @@ class replica_bitcell_array(design.design):
|
|||
cols=self.column_size,
|
||||
rows=1,
|
||||
# dummy column + left replica column
|
||||
column_offset=1 + self.left_rbl,
|
||||
column_offset=1 + self.add_left_rbl,
|
||||
mirror=0)
|
||||
self.add_mod(self.dummy_row)
|
||||
|
||||
|
|
@ -126,108 +134,134 @@ class replica_bitcell_array(design.design):
|
|||
end_caps_enabled = False
|
||||
|
||||
# Dummy Row or Col Cap, depending on bitcell array properties
|
||||
edge_row_module_type = ("col_cap_array" if end_caps_enabled else "dummy_array")
|
||||
|
||||
self.edge_row = factory.create(module_type=edge_row_module_type,
|
||||
col_cap_module_type = ("col_cap_array" if end_caps_enabled else "dummy_array")
|
||||
self.col_cap = factory.create(module_type=col_cap_module_type,
|
||||
cols=self.column_size,
|
||||
rows=1,
|
||||
# dummy column + left replica column(s)
|
||||
column_offset=1 + self.left_rbl,
|
||||
column_offset=1 + self.add_left_rbl,
|
||||
mirror=0)
|
||||
self.add_mod(self.edge_row)
|
||||
self.add_mod(self.col_cap)
|
||||
|
||||
# Dummy Col or Row Cap, depending on bitcell array properties
|
||||
edge_col_module_type = ("row_cap_array" if end_caps_enabled else "dummy_array")
|
||||
row_cap_module_type = ("row_cap_array" if end_caps_enabled else "dummy_array")
|
||||
|
||||
self.edge_col_left = factory.create(module_type=edge_col_module_type,
|
||||
cols=1,
|
||||
column_offset=0,
|
||||
rows=self.row_size + self.extra_rows,
|
||||
mirror=(self.left_rbl + 1) % 2)
|
||||
self.add_mod(self.edge_col_left)
|
||||
self.row_cap_left = factory.create(module_type=row_cap_module_type,
|
||||
cols=1,
|
||||
column_offset=0,
|
||||
rows=self.row_size + self.extra_rows,
|
||||
mirror=(self.left_rbl + 1) % 2)
|
||||
self.add_mod(self.row_cap_left)
|
||||
|
||||
self.edge_col_right = factory.create(module_type=edge_col_module_type,
|
||||
self.row_cap_right = factory.create(module_type=row_cap_module_type,
|
||||
cols=1,
|
||||
# dummy column
|
||||
# + left replica column(s)
|
||||
# + bitcell columns
|
||||
# + right replica column(s)
|
||||
column_offset = 1 + self.left_rbl + self.column_size + self.right_rbl,
|
||||
column_offset = 1 + self.add_left_rbl + self.column_size + self.add_right_rbl,
|
||||
rows=self.row_size + self.extra_rows,
|
||||
mirror=(self.left_rbl + 1) %2)
|
||||
self.add_mod(self.edge_col_right)
|
||||
self.add_mod(self.row_cap_right)
|
||||
|
||||
def add_pins(self):
|
||||
self.bitcell_array_wl_names = self.bitcell_array.get_all_wordline_names()
|
||||
self.bitcell_array_bl_names = self.bitcell_array.get_all_bitline_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
|
||||
self.add_bitline_pins()
|
||||
self.add_wordline_pins()
|
||||
|
||||
# A dictionary because some ports may have nothing
|
||||
self.rbl_bl_names = {}
|
||||
self.rbl_br_names = {}
|
||||
self.rbl_wl_names = {}
|
||||
|
||||
# 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.cell.get_all_wl_names()))]
|
||||
# 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.cell.get_all_wl_names()))]
|
||||
# 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
|
||||
|
||||
# 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()]
|
||||
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 add_bitline_pins(self):
|
||||
|
||||
# Regular bitline names for all ports
|
||||
self.bitline_names = []
|
||||
# Bitline names for each port
|
||||
self.bitline_names_by_port = [[] for x in self.all_ports]
|
||||
# Replica wordlines by port
|
||||
self.replica_bitline_names = [[] for x in self.all_ports]
|
||||
# Replica wordlines by port (bl only)
|
||||
self.replica_bl_names = [[] for x in self.all_ports]
|
||||
# Dummy wordlines by port
|
||||
self.dummy_bitline_names = []
|
||||
|
||||
# Regular array bitline names
|
||||
self.bitcell_array_bitline_names = self.bitcell_array.get_all_bitline_names()
|
||||
|
||||
# These are the non-indexed names
|
||||
dummy_bitline_names = ["dummy_" + x for x in self.cell.get_all_bitline_names()]
|
||||
self.dummy_bitline_names.append([x + "_left" for x in dummy_bitline_names])
|
||||
self.dummy_bitline_names.append([x + "_right" for x in dummy_bitline_names])
|
||||
|
||||
# Array of all port bitline names
|
||||
for port in range(self.add_left_rbl + self.add_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.replica_bl_names[port]=left_names[self.bitcell_ports[port]]
|
||||
# Interleave the left and right lists
|
||||
bitline_names = [x for t in zip(left_names, right_names) for x in t]
|
||||
self.replica_bitline_names[port] = bitline_names
|
||||
|
||||
# Dummy bitlines are not connected to anything
|
||||
self.bitline_names.extend(self.bitcell_array_bitline_names)
|
||||
|
||||
for port in self.all_ports:
|
||||
self.add_pin_list(self.replica_bitline_names[port], "INOUT")
|
||||
self.add_pin_list(self.bitline_names, "INOUT")
|
||||
|
||||
def add_wordline_pins(self):
|
||||
|
||||
# All wordline names for all ports
|
||||
self.wordline_names = []
|
||||
# Wordline names for each port
|
||||
self.wordline_names_by_port = [[] for x in self.all_ports]
|
||||
# Replica wordlines by port
|
||||
self.replica_wordline_names = [[] for x in self.all_ports]
|
||||
# Dummy wordlines
|
||||
self.dummy_wordline_names = {}
|
||||
|
||||
# Regular array wordline names
|
||||
self.bitcell_array_wordline_names = self.bitcell_array.get_all_wordline_names()
|
||||
|
||||
# These are the non-indexed names
|
||||
dummy_cell_wl_names = ["dummy_" + x for x in self.cell.get_all_wl_names()]
|
||||
|
||||
# Create the full WL names include dummy, replica, and regular bit cells
|
||||
self.wordline_names = []
|
||||
|
||||
self.dummy_wordline_names["bot"] = ["{0}_bot".format(x) for x in dummy_cell_wl_names]
|
||||
self.wordline_names.extend(self.dummy_wordline_names["bot"])
|
||||
|
||||
# Left port WLs
|
||||
for port in range(self.left_rbl):
|
||||
# Make names for all RBLs
|
||||
wl_names=["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()]
|
||||
# Keep track of the pin that is the RBL
|
||||
self.replica_wordline_names[port] = wl_names
|
||||
self.wordline_names.extend(wl_names)
|
||||
|
||||
# Regular WLs
|
||||
self.wordline_names.extend(self.bitcell_array_wordline_names)
|
||||
|
||||
# Right port WLs
|
||||
for port in range(self.left_rbl, self.left_rbl + self.right_rbl):
|
||||
# Make names for all RBLs
|
||||
wl_names=["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()]
|
||||
# Keep track of the pin that is the RBL
|
||||
self.replica_wordline_names[port] = wl_names
|
||||
self.wordline_names.extend(wl_names)
|
||||
|
||||
self.dummy_wordline_names["top"] = ["{0}_top".format(x) for x in dummy_cell_wl_names]
|
||||
self.wordline_names.extend(self.dummy_wordline_names["top"])
|
||||
|
||||
# Array of all port wl names
|
||||
for port in range(self.left_rbl + self.right_rbl):
|
||||
wl_names = ["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()]
|
||||
self.replica_wordline_names[port] = wl_names
|
||||
|
||||
self.add_pin_list(self.wordline_names, "INPUT")
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
|
||||
|
|
@ -239,37 +273,42 @@ class replica_bitcell_array(design.design):
|
|||
# Main array
|
||||
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
|
||||
mod=self.bitcell_array)
|
||||
self.connect_inst(self.bitcell_array_bl_names + self.bitcell_array_wl_names + supplies)
|
||||
self.connect_inst(self.bitcell_array_bitline_names + self.bitcell_array_wordline_names + supplies)
|
||||
|
||||
# Replica columns
|
||||
self.replica_col_inst = {}
|
||||
for port in range(self.left_rbl + self.right_rbl):
|
||||
for port in range(self.add_left_rbl + self.add_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)
|
||||
|
||||
self.connect_inst(self.replica_bitline_names[port] + self.wordline_names + supplies)
|
||||
|
||||
# Dummy rows under the bitcell array (connected with with the replica cell wl)
|
||||
self.dummy_row_replica_inst = {}
|
||||
# Note, this is the number of left and right even if we aren't adding the columns to this bitcell array!
|
||||
for port in range(self.left_rbl + self.right_rbl):
|
||||
self.dummy_row_replica_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)
|
||||
self.connect_inst(self.bitcell_array_bitline_names + self.replica_wordline_names[port] + supplies)
|
||||
|
||||
# Top/bottom dummy rows or col caps
|
||||
self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot",
|
||||
mod=self.edge_row)
|
||||
self.connect_inst(self.dummy_row_bl_names + [x + "_bot" for x in self.dummy_cell_wl_names] + supplies)
|
||||
mod=self.col_cap)
|
||||
self.connect_inst(self.bitcell_array_bitline_names
|
||||
+ self.dummy_wordline_names["bot"]
|
||||
+ supplies)
|
||||
self.dummy_row_top_inst=self.add_inst(name="dummy_row_top",
|
||||
mod=self.edge_row)
|
||||
self.connect_inst(self.dummy_row_bl_names + [x + "_top" for x in self.dummy_cell_wl_names] + supplies)
|
||||
mod=self.col_cap)
|
||||
self.connect_inst(self.bitcell_array_bitline_names
|
||||
+ self.dummy_wordline_names["top"]
|
||||
+ supplies)
|
||||
|
||||
# Left/right Dummy columns
|
||||
self.dummy_col_left_inst=self.add_inst(name="dummy_col_left",
|
||||
mod=self.edge_col_left)
|
||||
self.connect_inst([x + "_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies)
|
||||
mod=self.row_cap_left)
|
||||
self.connect_inst(self.dummy_bitline_names[0] + self.wordline_names + supplies)
|
||||
self.dummy_col_right_inst=self.add_inst(name="dummy_col_right",
|
||||
mod=self.edge_col_right)
|
||||
self.connect_inst([x + "_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies)
|
||||
mod=self.row_cap_right)
|
||||
self.connect_inst(self.dummy_bitline_names[-1] + self.wordline_names + supplies)
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
|
|
@ -277,41 +316,18 @@ class replica_bitcell_array(design.design):
|
|||
self.width = (self.column_size + self.extra_cols) * self.cell.width
|
||||
|
||||
# This is a bitcell x bitcell offset to scale
|
||||
offset = vector(self.cell.width, self.cell.height)
|
||||
self.bitcell_offset = vector(self.cell.width, self.cell.height)
|
||||
|
||||
# Everything is computed with the main array at (0, 0) to start
|
||||
self.bitcell_array_inst.place(offset=[0, 0])
|
||||
|
||||
# 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.add_replica_columns()
|
||||
|
||||
self.add_end_caps()
|
||||
|
||||
# FIXME: These depend on the array size itself
|
||||
# Far top dummy row (first row above array is NOT flipped)
|
||||
flip_dummy = self.right_rbl % 2
|
||||
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")
|
||||
# FIXME: These depend on the array size itself
|
||||
# Far bottom dummy row (first row below array IS flipped)
|
||||
flip_dummy = (self.left_rbl + 1) % 2
|
||||
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))
|
||||
# Array was at (0, 0) but move everything so it is at the lower left
|
||||
# We move DOWN the number of left RBL even if we didn't add the column to this bitcell array
|
||||
self.translate_all(self.bitcell_offset.scale(-1 - self.add_left_rbl, -1 - self.left_rbl))
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
|
|
@ -319,22 +335,70 @@ class replica_bitcell_array(design.design):
|
|||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_replica_columns(self):
|
||||
""" Add replica columns on left and right of array """
|
||||
|
||||
# Grow from left to right, toward the array
|
||||
for bit in range(self.add_left_rbl):
|
||||
offset = self.bitcell_offset.scale(-self.add_left_rbl + bit, -self.add_left_rbl - 1)
|
||||
self.replica_col_inst[bit].place(offset)
|
||||
# Grow to the right of the bitcell array, array outward
|
||||
for bit in range(self.add_right_rbl):
|
||||
offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.add_left_rbl - 1)
|
||||
self.replica_col_inst[self.add_left_rbl + bit].place(offset)
|
||||
|
||||
# Replica dummy rows
|
||||
# Add the dummy rows even if we aren't adding the replica column to this bitcell array
|
||||
# These grow up, toward the array
|
||||
for bit in range(self.left_rbl):
|
||||
self.dummy_row_replica_inst[bit].place(offset=self.bitcell_offset.scale(0, -self.left_rbl + bit + (-self.left_rbl + bit) % 2),
|
||||
mirror="MX" if (-self.left_rbl + bit) % 2 else "R0")
|
||||
# These grow up, away from the array
|
||||
for bit in range(self.right_rbl):
|
||||
self.dummy_row_replica_inst[self.left_rbl + bit].place(offset=self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(),
|
||||
mirror="MX" if bit % 2 else "R0")
|
||||
|
||||
def add_end_caps(self):
|
||||
""" Add dummy cells or end caps around the array """
|
||||
|
||||
# FIXME: These depend on the array size itself
|
||||
# Far top dummy row (first row above array is NOT flipped)
|
||||
flip_dummy = self.right_rbl % 2
|
||||
dummy_row_offset = self.bitcell_offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul()
|
||||
self.dummy_row_top_inst.place(offset=dummy_row_offset,
|
||||
mirror="MX" if flip_dummy else "R0")
|
||||
# FIXME: These depend on the array size itself
|
||||
# Far bottom dummy row (first row below array IS flipped)
|
||||
flip_dummy = (self.left_rbl + 1) % 2
|
||||
dummy_row_offset = self.bitcell_offset.scale(0, -self.left_rbl - 1 + flip_dummy)
|
||||
self.dummy_row_bot_inst.place(offset=dummy_row_offset,
|
||||
mirror="MX" if flip_dummy else "R0")
|
||||
# Far left dummy col
|
||||
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
|
||||
dummy_col_offset = self.bitcell_offset.scale(-self.add_left_rbl - 1, -self.left_rbl - 1)
|
||||
self.dummy_col_left_inst.place(offset=dummy_col_offset)
|
||||
# Far right dummy col
|
||||
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
|
||||
dummy_col_offset = self.bitcell_offset.scale(self.add_right_rbl, -self.left_rbl - 1) + self.bitcell_array_inst.lr()
|
||||
self.dummy_col_right_inst.place(offset=dummy_col_offset)
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
|
||||
# All wordlines
|
||||
# Main array wl and bl/br
|
||||
pin_names = self.bitcell_array.get_pin_names()
|
||||
for pin_name in pin_names:
|
||||
for wl in self.bitcell_array_wl_names:
|
||||
for wl in self.bitcell_array_wordline_names:
|
||||
if wl in pin_name:
|
||||
pin_list = self.bitcell_array_inst.get_pins(pin_name)
|
||||
for pin in pin_list:
|
||||
self.add_layout_pin(text=pin_name,
|
||||
layer=pin.layer,
|
||||
layer=pin.layer,
|
||||
offset=pin.ll().scale(0, 1),
|
||||
width=self.width,
|
||||
height=pin.height())
|
||||
for bitline in self.bitcell_array_bl_names:
|
||||
for bitline in self.bitcell_array_bitline_names:
|
||||
if bitline in pin_name:
|
||||
pin_list = self.bitcell_array_inst.get_pins(pin_name)
|
||||
for pin in pin_list:
|
||||
|
|
@ -344,31 +408,35 @@ class replica_bitcell_array(design.design):
|
|||
width=pin.width(),
|
||||
height=self.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
|
||||
# Dummy wordlines
|
||||
for (name, inst) in [("bot", self.dummy_row_bot_inst), ("top", self.dummy_row_top_inst)]:
|
||||
for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.dummy_wordline_names[name]):
|
||||
# It's always a single row
|
||||
pin = inst.get_pin(pin_name + "_0")
|
||||
self.add_layout_pin(text=wl_name,
|
||||
layer=pin.layer,
|
||||
offset=pin.ll().scale(0, 1),
|
||||
width=self.width,
|
||||
height=pin.height())
|
||||
|
||||
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 wordlines (go by the row instead of replica column because we may have to add a pin
|
||||
# even though the column is in another local bitcell array)
|
||||
for (port, inst) in list(self.dummy_row_replica_inst.items()):
|
||||
for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.replica_wordline_names[port]):
|
||||
pin = inst.get_pin(pin_name + "_0")
|
||||
self.add_layout_pin(text=wl_name,
|
||||
layer=pin.layer,
|
||||
offset=pin.ll().scale(0, 1),
|
||||
width=self.width,
|
||||
height=pin.height())
|
||||
|
||||
# Replica bitlines
|
||||
for port in range(self.left_rbl + self.right_rbl):
|
||||
for port in range(self.add_left_rbl + self.add_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]):
|
||||
for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(), self.replica_bitline_names[port]):
|
||||
pin = inst.get_pin(pin_name)
|
||||
if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names:
|
||||
|
||||
if bl_name in self.replica_bl_names:
|
||||
name = bl_name
|
||||
else:
|
||||
name = "rbl_{0}_{1}".format(pin_name, port)
|
||||
|
|
@ -390,22 +458,42 @@ class replica_bitcell_array(design.design):
|
|||
loc=pin.center(),
|
||||
directions=("V", "V"),
|
||||
start_layer=pin.layer)
|
||||
|
||||
|
||||
for inst in list(self.replica_col_inst.values()):
|
||||
self.copy_layout_pin(inst, pin_name)
|
||||
self.copy_layout_pin(inst, pin_name)
|
||||
|
||||
def get_rbl_wl_name(self, port):
|
||||
""" Return the WL for the given RBL port """
|
||||
return self.rbl_wl_names[port]
|
||||
def get_rbl_wordline_names(self, port=None):
|
||||
"""
|
||||
Return the ACTIVE WL for the given RBL port.
|
||||
Inactive will be set to gnd.
|
||||
"""
|
||||
if port == None:
|
||||
temp = []
|
||||
for port in self.all_ports:
|
||||
temp.extend(self.replica_wordline_names[port])
|
||||
return temp
|
||||
else:
|
||||
wl_names = self.replica_wordline_names[port]
|
||||
return wl_names[port]
|
||||
|
||||
def get_rbl_bl_name(self, port):
|
||||
def get_rbl_bitline_names(self, port=None):
|
||||
""" Return the BL for the given RBL port """
|
||||
return self.rbl_bl_names[port]
|
||||
if port == None:
|
||||
temp = []
|
||||
for port in self.all_ports:
|
||||
temp.extend(self.replica_bitline_names[port])
|
||||
return temp
|
||||
else:
|
||||
bl_names = self.replica_bitline_names[port]
|
||||
return bl_names[2 * port:2 * port + 2]
|
||||
|
||||
def get_rbl_br_name(self, port):
|
||||
""" Return the BR for the given RBL port """
|
||||
return self.rbl_br_names[port]
|
||||
def get_wordline_names(self):
|
||||
""" Return the wordline names """
|
||||
return self.wordline_names
|
||||
|
||||
def get_bitline_names(self):
|
||||
""" Return the bitline names """
|
||||
return self.bitline_names
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Power of Bitcell array and bitline in nW."""
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class replica_column(design.design):
|
|||
|
||||
def __init__(self, name, rows, left_rbl, right_rbl, replica_bit,
|
||||
column_offset=0):
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
self.rows = rows
|
||||
self.left_rbl = left_rbl
|
||||
|
|
@ -102,22 +102,22 @@ class replica_column(design.design):
|
|||
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)
|
||||
self.connect_inst(self.get_bitcell_pins(0, row))
|
||||
self.connect_inst(self.get_bitcell_pins(row, 0))
|
||||
elif row==self.replica_bit:
|
||||
self.cell_inst[row]=self.add_inst(name=name,
|
||||
mod=self.replica_cell)
|
||||
self.connect_inst(self.get_bitcell_pins(0, row))
|
||||
self.connect_inst(self.get_bitcell_pins(row, 0))
|
||||
elif (row == 0 or row == self.total_size - 1):
|
||||
self.cell_inst[row]=self.add_inst(name=name,
|
||||
mod=self.edge_cell)
|
||||
if end_caps_enabled:
|
||||
self.connect_inst(self.get_bitcell_pins_col_cap(0, row))
|
||||
self.connect_inst(self.get_bitcell_pins_col_cap(row, 0))
|
||||
else:
|
||||
self.connect_inst(self.get_bitcell_pins(0, row))
|
||||
self.connect_inst(self.get_bitcell_pins(row, 0))
|
||||
else:
|
||||
self.cell_inst[row]=self.add_inst(name=name,
|
||||
mod=self.dummy_cell)
|
||||
self.connect_inst(self.get_bitcell_pins(0, row))
|
||||
self.connect_inst(self.get_bitcell_pins(row, 0))
|
||||
|
||||
def place_instances(self):
|
||||
from tech import cell_properties
|
||||
|
|
@ -191,7 +191,7 @@ class replica_column(design.design):
|
|||
else:
|
||||
self.copy_layout_pin(inst, pin_name)
|
||||
|
||||
def get_bitcell_pins(self, col, row):
|
||||
def get_bitcell_pins(self, row, col):
|
||||
""" Creates a list of connections in the bitcell,
|
||||
indexed by column and row, for instance use in bitcell_array """
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ class replica_column(design.design):
|
|||
|
||||
return bitcell_pins
|
||||
|
||||
def get_bitcell_pins_col_cap(self, col, row):
|
||||
def get_bitcell_pins_col_cap(self, row, col):
|
||||
""" Creates a list of connections in the bitcell,
|
||||
indexed by column and row, for instance use in bitcell_array """
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ class row_cap_array(bitcell_base_array):
|
|||
"""
|
||||
Generate a dummy row/column for the replica array.
|
||||
"""
|
||||
def __init__(self, cols, rows, column_offset=0, mirror=0, name=""):
|
||||
super().__init__(cols, rows, name, column_offset)
|
||||
def __init__(self, rows, cols, column_offset=0, mirror=0, name=""):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
|
||||
self.mirror = mirror
|
||||
self.no_instances = True
|
||||
self.create_netlist()
|
||||
|
|
@ -49,9 +49,9 @@ class row_cap_array(bitcell_base_array):
|
|||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.dummy_cell)
|
||||
self.connect_inst(self.get_bitcell_pins(col, row))
|
||||
self.connect_inst(self.get_bitcell_pins(row, col))
|
||||
|
||||
def get_bitcell_pins(self, col, row):
|
||||
def get_bitcell_pins(self, row, col):
|
||||
"""
|
||||
Creates a list of connections in the bitcell,
|
||||
indexed by column and row, for instance use in bitcell_array
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class sense_amp(design.design):
|
|||
return props.sense_amp.pin.en
|
||||
|
||||
def __init__(self, name):
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(2, "Create sense_amp")
|
||||
|
||||
self.width = sense_amp.width
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class sense_amp_array(design.design):
|
|||
|
||||
def __init__(self, name, word_size, words_per_row, num_spare_cols=None, column_offset=0):
|
||||
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
self.add_comment("word_size {0}".format(word_size))
|
||||
self.add_comment("words_per_row: {0}".format(words_per_row))
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class single_level_column_mux_array(design.design):
|
|||
"""
|
||||
|
||||
def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br", column_offset=0):
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br))
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class tri_gate_array(design.design):
|
|||
|
||||
def __init__(self, columns, word_size, name):
|
||||
"""Intial function of tri gate array """
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
|
||||
self.columns = columns
|
||||
|
|
|
|||
|
|
@ -0,0 +1,140 @@
|
|||
# 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, layer
|
||||
from vector import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class wordline_buffer_array(design.design):
|
||||
"""
|
||||
Creates a Wordline Buffer/Inverter array
|
||||
"""
|
||||
|
||||
def __init__(self, name, rows, cols):
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
||||
|
||||
self.rows = rows
|
||||
self.cols = cols
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_drivers()
|
||||
|
||||
def create_layout(self):
|
||||
if "li" in layer:
|
||||
self.route_layer = "li"
|
||||
else:
|
||||
self.route_layer = "m1"
|
||||
self.place_drivers()
|
||||
self.route_layout()
|
||||
self.route_vdd_gnd()
|
||||
self.offset_all_coordinates()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
# inputs to wordline_driver.
|
||||
for i in range(self.rows):
|
||||
self.add_pin("in_{0}".format(i), "INPUT")
|
||||
# Outputs from wordline_driver.
|
||||
for i in range(self.rows):
|
||||
self.add_pin("out_{0}".format(i), "OUTPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def add_modules(self):
|
||||
b = factory.create(module_type="bitcell")
|
||||
|
||||
self.wl_driver = factory.create(module_type="inv_dec",
|
||||
size=self.cols,
|
||||
height=b.height)
|
||||
self.add_mod(self.wl_driver)
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
"""
|
||||
Add a pin for each row of vdd/gnd which
|
||||
are must-connects next level up.
|
||||
"""
|
||||
if OPTS.tech_name == "sky130":
|
||||
for name in ["vdd", "gnd"]:
|
||||
supply_pins = self.wld_inst[0].get_pins(name)
|
||||
for pin in supply_pins:
|
||||
self.add_layout_pin_segment_center(text=name,
|
||||
layer=pin.layer,
|
||||
start=pin.bc(),
|
||||
end=vector(pin.cx(), self.height))
|
||||
else:
|
||||
# Find the x offsets for where the vias/pins should be placed
|
||||
xoffset_list = [self.wld_inst[0].rx()]
|
||||
for num in range(self.rows):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
# use the inverter offset even though it will be the and's too
|
||||
(gate_offset, y_dir) = self.get_gate_offset(0,
|
||||
self.wl_driver.height,
|
||||
num)
|
||||
# Route both supplies
|
||||
for name in ["vdd", "gnd"]:
|
||||
supply_pin = self.wld_inst[num].get_pin(name)
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in xoffset_list:
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_power_pin(name, pin_pos)
|
||||
|
||||
def create_drivers(self):
|
||||
self.wld_inst = []
|
||||
for row in range(self.rows):
|
||||
self.wld_inst.append(self.add_inst(name="wld{0}".format(row),
|
||||
mod=self.wl_driver))
|
||||
self.connect_inst(["in_{0}".format(row),
|
||||
"out_{0}".format(row),
|
||||
"vdd", "gnd"])
|
||||
|
||||
def place_drivers(self):
|
||||
|
||||
for row in range(self.rows):
|
||||
if (row % 2):
|
||||
y_offset = self.wl_driver.height * (row + 1)
|
||||
inst_mirror = "MX"
|
||||
else:
|
||||
y_offset = self.wl_driver.height * row
|
||||
inst_mirror = "R0"
|
||||
|
||||
offset = [0, y_offset]
|
||||
|
||||
self.wld_inst[row].place(offset=offset,
|
||||
mirror=inst_mirror)
|
||||
|
||||
self.width = self.wl_driver.width
|
||||
self.height = self.wl_driver.height * self.rows
|
||||
|
||||
def route_layout(self):
|
||||
""" Route all of the signals """
|
||||
|
||||
for row in range(self.rows):
|
||||
inst = self.wld_inst[row]
|
||||
|
||||
self.copy_layout_pin(inst, "A", "in_{0}".format(row))
|
||||
|
||||
# output each WL on the right
|
||||
wl_offset = inst.get_pin("Z").rc()
|
||||
self.add_layout_pin_segment_center(text="out_{0}".format(row),
|
||||
layer=self.route_layer,
|
||||
start=wl_offset,
|
||||
end=wl_offset - vector(self.m1_width, 0))
|
||||
|
|
@ -12,6 +12,7 @@ from vector import vector
|
|||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class wordline_driver_array(design.design):
|
||||
"""
|
||||
Creates a Wordline Driver
|
||||
|
|
@ -19,7 +20,7 @@ class wordline_driver_array(design.design):
|
|||
"""
|
||||
|
||||
def __init__(self, name, rows, cols):
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class write_driver_array(design.design):
|
|||
|
||||
def __init__(self, name, columns, word_size, num_spare_cols=None, write_size=None, column_offset=0):
|
||||
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
self.add_comment("columns: {0}".format(columns))
|
||||
self.add_comment("word_size {0}".format(word_size))
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class write_mask_and_array(design.design):
|
|||
"""
|
||||
|
||||
def __init__(self, name, columns, word_size, write_size, column_offset=0):
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
self.add_comment("columns: {0}".format(columns))
|
||||
self.add_comment("word_size {0}".format(word_size))
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class pand2(pgate.pgate):
|
|||
self.vertical = vertical
|
||||
self.size = size
|
||||
|
||||
pgate.pgate.__init__(self, name, height, add_wells)
|
||||
super().__init__(name, height, add_wells)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class pand3(pgate.pgate):
|
|||
self.size = size
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height, add_wells)
|
||||
super().__init__(name, height, add_wells)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class pbuf(pgate.pgate):
|
|||
self.height = height
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height)
|
||||
super().__init__(name, height)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from vector import vector
|
||||
import pgate
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class pbuf_dec(pgate.pgate):
|
||||
"""
|
||||
This is a simple buffer used for driving wordlines.
|
||||
"""
|
||||
def __init__(self, name, size=4, height=None):
|
||||
|
||||
debug.info(1, "creating {0} with size of {1}".format(name, size))
|
||||
self.add_comment("size: {}".format(size))
|
||||
|
||||
self.stage_effort = 4
|
||||
self.size = size
|
||||
self.height = height
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.create_insts()
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.inv1.width + self.inv2.width
|
||||
self.place_insts()
|
||||
self.add_wires()
|
||||
self.add_layout_pins()
|
||||
self.route_supply_rails()
|
||||
self.add_boundary()
|
||||
|
||||
def add_pins(self):
|
||||
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
|
||||
input_size = max(1, int(self.size / self.stage_effort))
|
||||
self.inv1 = factory.create(module_type="pinv_dec",
|
||||
size=input_size,
|
||||
height=self.height)
|
||||
self.add_mod(self.inv1)
|
||||
|
||||
self.inv2 = factory.create(module_type="pinv_dec",
|
||||
size=self.size,
|
||||
height=self.height)
|
||||
self.add_mod(self.inv2)
|
||||
|
||||
def create_insts(self):
|
||||
self.inv1_inst = self.add_inst(name="buf_inv1",
|
||||
mod=self.inv1)
|
||||
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
|
||||
|
||||
self.inv2_inst = self.add_inst(name="buf_inv2",
|
||||
mod=self.inv2)
|
||||
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||
|
||||
def place_insts(self):
|
||||
# Add INV1 to the right
|
||||
self.inv1_inst.place(vector(0, 0))
|
||||
|
||||
# Add INV2 to the right
|
||||
self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
|
||||
|
||||
def add_wires(self):
|
||||
# inv1 Z to inv2 A
|
||||
z1_pin = self.inv1_inst.get_pin("Z")
|
||||
a2_pin = self.inv2_inst.get_pin("A")
|
||||
mid_loc = vector(a2_pin.cx(), z1_pin.cy())
|
||||
self.add_path(self.route_layer,
|
||||
[z1_pin.rc(), mid_loc, a2_pin.lc()],
|
||||
width=a2_pin.width())
|
||||
|
||||
def route_supply_rails(self):
|
||||
""" Add vdd/gnd rails to the top, (middle), and bottom. """
|
||||
self.copy_layout_pin(self.inv1_inst, "vdd")
|
||||
self.copy_layout_pin(self.inv1_inst, "gnd")
|
||||
self.copy_layout_pin(self.inv2_inst, "vdd")
|
||||
self.copy_layout_pin(self.inv2_inst, "gnd")
|
||||
|
||||
def add_layout_pins(self):
|
||||
z_pin = self.inv2_inst.get_pin("Z")
|
||||
self.add_layout_pin_rect_center(text="Z",
|
||||
layer=z_pin.layer,
|
||||
offset=z_pin.center(),
|
||||
width=z_pin.width(),
|
||||
height=z_pin.height())
|
||||
|
||||
a_pin = self.inv1_inst.get_pin("A")
|
||||
self.add_layout_pin_rect_center(text="A",
|
||||
layer=a_pin.layer,
|
||||
offset=a_pin.center(),
|
||||
width=a_pin.width(),
|
||||
height=a_pin.height())
|
||||
|
||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||
"""Get the stage efforts of the A -> Z path"""
|
||||
stage_effort_list = []
|
||||
stage1_cout = self.inv2.get_cin()
|
||||
stage1 = self.inv1.get_stage_effort(stage1_cout, inp_is_rise)
|
||||
stage_effort_list.append(stage1)
|
||||
last_stage_is_rise = stage1.is_rise
|
||||
|
||||
stage2 = self.inv2.get_stage_effort(external_cout, last_stage_is_rise)
|
||||
stage_effort_list.append(stage2)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
def get_cin(self):
|
||||
"""Returns the relative capacitance of the input"""
|
||||
input_cin = self.inv1.get_cin()
|
||||
return input_cin
|
||||
|
|
@ -35,7 +35,7 @@ class pdriver(pgate.pgate):
|
|||
debug.error("Cannot specify both size_list and inverting.", -1)
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height, add_wells)
|
||||
super().__init__(name, height, add_wells)
|
||||
|
||||
def compute_sizes(self):
|
||||
# size_list specified
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class pgate(design.design):
|
|||
|
||||
def __init__(self, name, height=None, add_wells=True):
|
||||
""" Creates a generic cell """
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
if height:
|
||||
self.height = height
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class pinv(pgate.pgate):
|
|||
self.pmos_size = beta * size
|
||||
self.beta = beta
|
||||
|
||||
pgate.pgate.__init__(self, name, height, add_wells)
|
||||
super().__init__(name, height, add_wells)
|
||||
|
||||
def create_netlist(self):
|
||||
""" Calls all functions related to the generation of the netlist """
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class pinv_dec(pinv.pinv):
|
|||
else:
|
||||
self.supply_layer = "m2"
|
||||
|
||||
pinv.pinv.__init__(self, name, size, beta, self.cell_height, add_wells)
|
||||
super().__init__(name, size, beta, self.cell_height, add_wells)
|
||||
|
||||
def determine_tx_mults(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class pinvbuf(pgate.pgate):
|
|||
self.predriver_size = max(int(self.size / (self.stage_effort / 2)), 1)
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class pnand2(pgate.pgate):
|
|||
self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height, add_wells)
|
||||
super().__init__(name, height, add_wells)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class pnand3(pgate.pgate):
|
|||
self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height, add_wells)
|
||||
super().__init__(name, height, add_wells)
|
||||
|
||||
def add_pins(self):
|
||||
""" Adds pins for spice netlist """
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class pnor2(pgate.pgate):
|
|||
self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height, add_wells)
|
||||
super().__init__(name, height, add_wells)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class precharge(design.design):
|
|||
def __init__(self, name, size=1, bitcell_bl="bl", bitcell_br="br"):
|
||||
|
||||
debug.info(2, "creating precharge cell {0}".format(name))
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.beta = parameter["beta"]
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class ptristate_inv(pgate.pgate):
|
|||
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height)
|
||||
super().__init__(name, height)
|
||||
|
||||
def create_netlist(self):
|
||||
""" Calls all functions related to the generation of the netlist """
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class ptx(design.design):
|
|||
# replace periods with underscore for newer spice compatibility
|
||||
name = name.replace('.', '_')
|
||||
debug.info(3, "creating ptx {0}".format(name))
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
self.tx_type = tx_type
|
||||
self.mults = mults
|
||||
|
|
@ -129,7 +129,7 @@ class ptx(design.design):
|
|||
# be decided in the layout later.
|
||||
area_sd = 2.5 * self.poly_width * self.tx_width
|
||||
perimeter_sd = 2 * self.poly_width + 2 * self.tx_width
|
||||
if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre":
|
||||
if OPTS.tech_name == "sky130":
|
||||
# sky130 simulation cannot use the mult parameter in simulation
|
||||
(self.tx_width, self.mults) = pgate.best_bin(self.tx_type, self.tx_width)
|
||||
main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type],
|
||||
|
|
@ -148,20 +148,19 @@ class ptx(design.design):
|
|||
area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd,
|
||||
area_sd)
|
||||
self.spice_device = main_str + area_str
|
||||
self.spice.append("\n* ptx " + self.spice_device)
|
||||
self.spice.append("\n* spice ptx " + self.spice_device)
|
||||
|
||||
if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre":
|
||||
# sky130 requires mult parameter too
|
||||
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type],
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc("minwidth_poly"))
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc("minwidth_poly"))
|
||||
else:
|
||||
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc("minwidth_poly"))
|
||||
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc("minwidth_poly"))
|
||||
def setup_layout_constants(self):
|
||||
"""
|
||||
Pre-compute some handy layout parameters.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class pwrite_driver(design.design):
|
|||
def __init__(self, name, size=0):
|
||||
debug.error("pwrite_driver not implemented yet.", -1)
|
||||
debug.info(1, "creating pwrite_driver {}".format(name))
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
self.size = size
|
||||
self.beta = parameter["beta"]
|
||||
self.pmos_width = self.beta*self.size*parameter["min_tx_size"]
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class single_level_column_mux(pgate.pgate):
|
|||
self.bitcell_bl = bitcell_bl
|
||||
self.bitcell_br = bitcell_br
|
||||
|
||||
pgate.pgate.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
def get_bl_names(self):
|
||||
return "bl"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class wordline_driver(design.design):
|
|||
def __init__(self, name, size=1, height=None):
|
||||
debug.info(1, "Creating wordline_driver {}".format(name))
|
||||
self.add_comment("size: {}".format(size))
|
||||
design.design.__init__(self, name)
|
||||
super().__init__(name)
|
||||
|
||||
if height is None:
|
||||
b = factory.create(module_type="bitcell")
|
||||
|
|
|
|||
|
|
@ -90,9 +90,9 @@ class sram():
|
|||
|
||||
# Save the LVS file
|
||||
start_time = datetime.datetime.now()
|
||||
spname = OPTS.output_path + self.s.name + ".lvs"
|
||||
debug.print_raw("LVS: Writing to {0}".format(spname))
|
||||
self.lvs_write(spname)
|
||||
lvsname = OPTS.output_path + self.s.name + ".lvs.sp"
|
||||
debug.print_raw("LVS: Writing to {0}".format(lvsname))
|
||||
self.lvs_write(lvsname)
|
||||
print_time("LVS writing", datetime.datetime.now(), start_time)
|
||||
|
||||
# Save the extracted spice file
|
||||
|
|
@ -100,8 +100,10 @@ class sram():
|
|||
import verify
|
||||
start_time = datetime.datetime.now()
|
||||
# Output the extracted design if requested
|
||||
sp_file = OPTS.output_path + "temp_pex.sp"
|
||||
verify.run_pex(self.s.name, gdsname, spname, output=sp_file)
|
||||
pexname = OPTS.output_path + self.s.name + ".pex.sp"
|
||||
spname = OPTS.output_path + self.s.name + ".sp"
|
||||
verify.run_pex(self.s.name, gdsname, spname, output=pexname)
|
||||
sp_file = pexname
|
||||
print_time("Extraction", datetime.datetime.now(), start_time)
|
||||
else:
|
||||
# Use generated spice file for characterization
|
||||
|
|
|
|||
|
|
@ -160,7 +160,10 @@ class sram_1bank(sram_base):
|
|||
port = 0
|
||||
# Add the col address flops below the bank to the right of the control logic
|
||||
x_offset = self.control_logic_insts[port].rx() + self.dff.width
|
||||
y_offset = - self.data_bus_size[port] - self.dff.height
|
||||
# Place it a data bus below the x-axis, but at least as low as the control logic to not block
|
||||
# the control logic signals
|
||||
y_offset = min(-self.data_bus_size[port] - self.dff.height,
|
||||
self.control_logic_insts[port].by())
|
||||
if self.col_addr_dff:
|
||||
self.col_addr_pos[port] = vector(x_offset,
|
||||
y_offset)
|
||||
|
|
@ -201,7 +204,10 @@ class sram_1bank(sram_base):
|
|||
|
||||
# Add the col address flops below the bank to the right of the control logic
|
||||
x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width
|
||||
y_offset = self.bank.height + self.data_bus_size[port] + self.dff.height
|
||||
# Place it a data bus below the x-axis, but at least as high as the control logic to not block
|
||||
# the control logic signals
|
||||
y_offset = max(self.bank.height + self.data_bus_size[port] + self.dff.height,
|
||||
self.control_logic_insts[port].uy() - self.dff.height)
|
||||
if self.col_addr_dff:
|
||||
self.col_addr_pos[port] = vector(x_offset,
|
||||
y_offset)
|
||||
|
|
@ -637,5 +643,8 @@ class sram_1bank(sram_base):
|
|||
"""Gets the spice name of the target bitcell."""
|
||||
# Sanity check in case it was forgotten
|
||||
if inst_name.find('x') != 0:
|
||||
inst_name = 'x' + inst_name
|
||||
return self.bank_inst.mod.get_cell_name(inst_name + '.x' + self.bank_inst.name, row, col)
|
||||
inst_name = 'x'+inst_name
|
||||
return self.bank_inst.mod.get_cell_name(inst_name+'.x'+self.bank_inst.name, row, col)
|
||||
|
||||
def get_bank_num(self, inst_name, row, col):
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ from design import design
|
|||
from verilog import verilog
|
||||
from lef import lef
|
||||
from sram_factory import factory
|
||||
from tech import drc
|
||||
import numpy as np
|
||||
import logical_effort
|
||||
|
||||
|
||||
class sram_base(design, verilog, lef):
|
||||
|
|
@ -84,8 +87,71 @@ class sram_base(design, verilog, lef):
|
|||
for bit in range(self.word_size + self.num_spare_cols):
|
||||
self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT")
|
||||
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
def add_global_pex_labels(self):
|
||||
"""
|
||||
Add pex labels at the sram level for spice analysis
|
||||
"""
|
||||
|
||||
# add pex labels for bitcells
|
||||
for bank_num in range(len(self.bank_insts)):
|
||||
bank = self.bank_insts[bank_num]
|
||||
pex_data = bank.reverse_transformation_bitcell(bank.mod.bitcell.name)
|
||||
|
||||
bank_offset = pex_data[0] # offset bank relative to sram
|
||||
Q_offset = pex_data[1] # offset of storage relative to bank
|
||||
Q_bar_offset = pex_data[2] # offset of storage relative to bank
|
||||
bl_offsets = pex_data[3]
|
||||
br_offsets = pex_data[4]
|
||||
bl_meta = pex_data[5]
|
||||
br_meta = pex_data[6]
|
||||
|
||||
bl = []
|
||||
br = []
|
||||
|
||||
storage_layer_name = "m1"
|
||||
bitline_layer_name = "m2"
|
||||
|
||||
for cell in range(len(bank_offset)):
|
||||
Q = [bank_offset[cell][0] + Q_offset[cell][0], bank_offset[cell][1] + Q_offset[cell][1]]
|
||||
Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], bank_offset[cell][1] + Q_bar_offset[cell][1]]
|
||||
OPTS.words_per_row = self.words_per_row
|
||||
self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))) , storage_layer_name, Q)
|
||||
self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))), storage_layer_name, Q_bar)
|
||||
|
||||
for cell in range(len(bl_offsets)):
|
||||
col = bl_meta[cell][0][2]
|
||||
for bitline in range(len(bl_offsets[cell])):
|
||||
bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0], float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]]
|
||||
bl.append([bitline_location, bl_meta[cell][bitline][3], col])
|
||||
|
||||
for cell in range(len(br_offsets)):
|
||||
col = br_meta[cell][0][2]
|
||||
for bitline in range(len(br_offsets[cell])):
|
||||
bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0], float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]]
|
||||
br.append([bitline_location, br_meta[cell][bitline][3], col])
|
||||
|
||||
for i in range(len(bl)):
|
||||
self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]), bitline_layer_name, bl[i][0])
|
||||
|
||||
for i in range(len(br)):
|
||||
self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]), bitline_layer_name, br[i][0])
|
||||
|
||||
# add pex labels for control logic
|
||||
for i in range (len(self.control_logic_insts)):
|
||||
instance = self.control_logic_insts[i]
|
||||
control_logic_offset = instance.offset
|
||||
for output in instance.mod.output_list:
|
||||
pin = instance.mod.get_pin(output)
|
||||
pin.transform([0,0], instance.mirror, instance.rotate)
|
||||
offset = [control_logic_offset[0] + pin.center()[0], control_logic_offset[1] + pin.center()[1]]
|
||||
self.add_layout_pin_rect_center("{0}{1}".format(pin.name,i), storage_layer_name, offset)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
""" Netlist creation """
|
||||
|
|
@ -124,6 +190,8 @@ class sram_base(design, verilog, lef):
|
|||
highest_coord = self.find_highest_coords()
|
||||
self.width = highest_coord[0]
|
||||
self.height = highest_coord[1]
|
||||
if OPTS.use_pex:
|
||||
self.add_global_pex_labels()
|
||||
self.add_boundary(ll=vector(0, 0),
|
||||
ur=vector(self.width, self.height))
|
||||
|
||||
|
|
@ -346,6 +414,7 @@ class sram_base(design, verilog, lef):
|
|||
temp.append("dout{0}[{1}]".format(port, bit))
|
||||
for port in self.all_ports:
|
||||
temp.append("rbl_bl{0}".format(port))
|
||||
temp.append("rbl_br{0}".format(port))
|
||||
for port in self.write_ports:
|
||||
for bit in range(self.word_size + self.num_spare_cols):
|
||||
temp.append("bank_din{0}[{1}]".format(port, bit))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
#!/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 pbuf_dec_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
globals.setup_bitcell()
|
||||
|
||||
debug.info(2, "Checking 8x size decoder buffer")
|
||||
a = factory.create(module_type="pbuf_dec", size=8)
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -21,7 +21,7 @@ class pbuf_test(openram_test):
|
|||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing inverter/buffer 4x 8x")
|
||||
debug.info(2, "Testing buffer 8x")
|
||||
a = factory.create(module_type="pbuf", size=8)
|
||||
self.local_check(a)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env python3
|
||||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import unittest
|
||||
from testutils import *
|
||||
import sys, os
|
||||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
import globals
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
|
||||
#@unittest.skip("SKIPPING 05_local_bitcell_array_test")
|
||||
class local_bitcell_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing 4x4 local bitcell array for 6t_cell without replica")
|
||||
a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, ports=[0], add_replica=False)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing 4x4 local bitcell array for 6t_cell with replica column")
|
||||
a = factory.create(module_type="local_bitcell_array", cols=4, left_rbl=1, rows=4, ports=[0])
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
# run the test from the command line
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env python3
|
||||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import unittest
|
||||
from testutils import *
|
||||
import sys,os
|
||||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
|
||||
class wordline_buffer_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# check wordline driver for single port
|
||||
debug.info(2, "Checking driver")
|
||||
tx = factory.create(module_type="wordline_buffer_array", rows=8, cols=32)
|
||||
self.local_check(tx)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
# run the test from the command line
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -24,14 +24,35 @@ class replica_bitcell_array_1rw_1r_test(openram_test):
|
|||
OPTS.num_w_ports = 0
|
||||
globals.setup_bitcell()
|
||||
|
||||
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])
|
||||
debug.info(2, "Testing 4x4 non-replica 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],
|
||||
add_replica=False)
|
||||
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)
|
||||
|
||||
|
||||
# Sky 130 has restrictions on the symmetries
|
||||
if OPTS.tech_name != "sky130":
|
||||
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])
|
||||
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)
|
||||
|
||||
globals.end_openram()
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ num_lvs_runs = 0
|
|||
num_pex_runs = 0
|
||||
|
||||
|
||||
def write_calibre_drc_script(cell_name, extract, final_verification):
|
||||
def write_calibre_drc_script(cell_name, extract, final_verification, gds_name):
|
||||
""" Write a Calibre runset file and script to run DRC """
|
||||
# the runset file contains all the options to run calibre
|
||||
from tech import drc
|
||||
|
|
@ -39,7 +39,7 @@ def write_calibre_drc_script(cell_name, extract, final_verification):
|
|||
drc_runset = {
|
||||
'drcRulesFile': drc_rules,
|
||||
'drcRunDir': OPTS.openram_temp,
|
||||
'drcLayoutPaths': cell_name + ".gds",
|
||||
'drcLayoutPaths': gds_name,
|
||||
'drcLayoutPrimary': cell_name,
|
||||
'drcLayoutSystem': 'GDSII',
|
||||
'drcResultsformat': 'ASCII',
|
||||
|
|
@ -68,7 +68,7 @@ def write_calibre_drc_script(cell_name, extract, final_verification):
|
|||
return drc_runset
|
||||
|
||||
|
||||
def write_calibre_lvs_script(cell_name, final_verification):
|
||||
def write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name):
|
||||
""" Write a Calibre runset file and script to run LVS """
|
||||
|
||||
from tech import drc
|
||||
|
|
@ -76,9 +76,9 @@ def write_calibre_lvs_script(cell_name, final_verification):
|
|||
lvs_runset = {
|
||||
'lvsRulesFile': lvs_rules,
|
||||
'lvsRunDir': OPTS.openram_temp,
|
||||
'lvsLayoutPaths': cell_name + ".gds",
|
||||
'lvsLayoutPaths': gds_name,
|
||||
'lvsLayoutPrimary': cell_name,
|
||||
'lvsSourcePath': cell_name + ".sp",
|
||||
'lvsSourcePath': sp_name,
|
||||
'lvsSourcePrimary': cell_name,
|
||||
'lvsSourceSystem': 'SPICE',
|
||||
'lvsSpiceFile': "{}.spice".format(cell_name),
|
||||
|
|
@ -135,11 +135,11 @@ def write_calibre_lvs_script(cell_name, final_verification):
|
|||
def write_calibre_pex_script(cell_name, extract, output, final_verification):
|
||||
""" Write a pex script that can either just extract the netlist or the netlist+parasitics """
|
||||
if output == None:
|
||||
output = name + ".pex.netlist"
|
||||
output = cell_name + ".pex.sp"
|
||||
|
||||
# check if lvs report has been done
|
||||
# if not run drc and lvs
|
||||
if not os.path.isfile(cell_name + ".lvs.report"):
|
||||
if not os.path.isfile(OPTS.openram_temp + cell_name + ".lvs.report"):
|
||||
gds_name = OPTS.openram_temp +"/"+ cell_name + ".gds"
|
||||
sp_name = OPTS.openram_temp +"/"+ cell_name + ".sp"
|
||||
run_drc(cell_name, gds_name)
|
||||
|
|
@ -155,7 +155,7 @@ def write_calibre_pex_script(cell_name, extract, output, final_verification):
|
|||
'pexSourcePath': cell_name + ".sp",
|
||||
'pexSourcePrimary': cell_name,
|
||||
'pexReportFile': cell_name + ".pex.report",
|
||||
'pexPexNetlistFile': cell_name + ".pex.netlist",
|
||||
'pexPexNetlistFile': output,
|
||||
'pexPexReportFile': cell_name + ".pex.report",
|
||||
'pexMaskDBFile': cell_name + ".maskdb",
|
||||
'cmnFDIDEFLayoutPath': cell_name + ".def",
|
||||
|
|
@ -195,11 +195,14 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False):
|
|||
filter_gds(cell_name, OPTS.openram_temp + "temp.gds", OPTS.openram_temp + cell_name + ".gds")
|
||||
else:
|
||||
# Copy file to local dir if it isn't already
|
||||
if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'):
|
||||
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
|
||||
shutil.copy(gds_name, OPTS.openram_temp)
|
||||
|
||||
drc_runset = write_calibre_drc_script(cell_name, extract, final_verification)
|
||||
drc_runset = write_calibre_drc_script(cell_name, extract, final_verification, gds_name)
|
||||
|
||||
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
|
||||
shutil.copy(gds_name, OPTS.openram_temp + os.path.basename(gds_name))
|
||||
|
||||
(outfile, errfile, resultsfile) = run_script(cell_name, "drc")
|
||||
|
||||
# check the result for these lines in the summary:
|
||||
|
|
@ -238,12 +241,12 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
|
|||
global num_lvs_runs
|
||||
num_lvs_runs += 1
|
||||
|
||||
lvs_runset = write_calibre_lvs_script(cell_name, final_verification)
|
||||
lvs_runset = write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name)
|
||||
|
||||
# Copy file to local dir if it isn't already
|
||||
if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'):
|
||||
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
|
||||
shutil.copy(gds_name, OPTS.openram_temp)
|
||||
if os.path.dirname(sp_name)!=OPTS.openram_temp.rstrip('/'):
|
||||
if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)):
|
||||
shutil.copy(sp_name, OPTS.openram_temp)
|
||||
|
||||
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs")
|
||||
|
|
@ -325,8 +328,14 @@ def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False)
|
|||
global num_pex_runs
|
||||
num_pex_runs += 1
|
||||
|
||||
write_calibre_pex_script(cell_name,True,output,final_verification)
|
||||
write_calibre_pex_script(cell_name, True, output, final_verification)
|
||||
|
||||
# Copy file to local dir if it isn't already
|
||||
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
|
||||
shutil.copy(gds_name, OPTS.openram_temp)
|
||||
if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)):
|
||||
shutil.copy(sp_name, OPTS.openram_temp)
|
||||
|
||||
(outfile, errfile, resultsfile) = run_script(cell_name, "pex")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ def write_magic_script(cell_name, extract=False, final_verification=False):
|
|||
f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1]))
|
||||
f.write("gds polygon subcell true\n")
|
||||
f.write("gds warning default\n")
|
||||
# This causes substrate contacts to not be extracted
|
||||
f.write("# gds readonly true\n")
|
||||
f.write("gds read {}.gds\n".format(cell_name))
|
||||
f.write("load {}\n".format(cell_name))
|
||||
# Flatten the cell to get rid of DRCs spanning multiple layers
|
||||
|
|
@ -107,21 +109,21 @@ def write_magic_script(cell_name, extract=False, final_verification=False):
|
|||
# f.write(pre + "ext2spice hierarchy on\n")
|
||||
# f.write(pre + "ext2spice scale off\n")
|
||||
# lvs exists in 8.2.79, but be backword compatible for now
|
||||
# f.write(pre + "ext2spice lvs\n")
|
||||
f.write(pre + "ext2spice hierarchy on\n")
|
||||
f.write(pre + "ext2spice format ngspice\n")
|
||||
f.write(pre + "ext2spice cthresh infinite\n")
|
||||
f.write(pre + "ext2spice rthresh infinite\n")
|
||||
f.write(pre + "ext2spice renumber off\n")
|
||||
f.write(pre + "ext2spice scale off\n")
|
||||
f.write(pre + "ext2spice blackbox on\n")
|
||||
f.write(pre + "ext2spice subcircuit top auto\n")
|
||||
f.write(pre + "ext2spice global off\n")
|
||||
#f.write(pre+"ext2spice lvs\n")
|
||||
f.write(pre+"ext2spice hierarchy on\n")
|
||||
f.write(pre+"ext2spice format ngspice\n")
|
||||
f.write(pre+"ext2spice cthresh infinite\n")
|
||||
f.write(pre+"ext2spice rthresh infinite\n")
|
||||
f.write(pre+"ext2spice renumber off\n")
|
||||
f.write(pre+"ext2spice scale off\n")
|
||||
f.write(pre+"ext2spice blackbox on\n")
|
||||
f.write(pre+"ext2spice subcircuit top on\n")
|
||||
f.write(pre+"ext2spice global off\n")
|
||||
|
||||
# Can choose hspice, ngspice, or spice3,
|
||||
# but they all seem compatible enough.
|
||||
#f.write(pre + "ext2spice format ngspice\n")
|
||||
f.write(pre + "ext2spice {}\n".format(cell_name))
|
||||
f.write(pre+"ext2spice format ngspice\n")
|
||||
f.write(pre+"ext2spice {}\n".format(cell_name))
|
||||
f.write("quit -noprompt\n")
|
||||
f.write("EOF\n")
|
||||
|
||||
|
|
@ -411,13 +413,13 @@ def write_script_pex_rule(gds_name,cell_name,output):
|
|||
else:
|
||||
pre = ""
|
||||
f.write(pre+"extract\n".format(cell_name))
|
||||
#f.write(pre+"ext2spice hierarchy on\n")
|
||||
#f.write(pre+"ext2spice format ngspice\n")
|
||||
#f.write(pre+"ext2spice renumber off\n")
|
||||
#f.write(pre+"ext2spice scale off\n")
|
||||
#f.write(pre+"ext2spice blackbox on\n")
|
||||
f.write(pre+"ext2spice hierarchy off\n")
|
||||
f.write(pre+"ext2spice format ngspice\n")
|
||||
f.write(pre+"ext2spice renumber off\n")
|
||||
f.write(pre+"ext2spice scale off\n")
|
||||
f.write(pre+"ext2spice blackbox on\n")
|
||||
f.write(pre+"ext2spice subcircuit top on\n")
|
||||
#f.write(pre+"ext2spice global off\n")
|
||||
f.write(pre+"ext2spice global off\n")
|
||||
f.write(pre+"ext2spice {}\n".format(cell_name))
|
||||
f.write("quit -noprompt\n")
|
||||
f.write("eof\n")
|
||||
|
|
@ -440,31 +442,49 @@ def correct_port(name, output_file_name, ref_file_name):
|
|||
pex_file = open(output_file_name, "r")
|
||||
contents = pex_file.read()
|
||||
# locate the start of circuit definition line
|
||||
match = re.search(".subckt " + str(name) + ".*", contents)
|
||||
match = re.search(r'^\.subckt+[^M]*', contents, re.MULTILINE)
|
||||
match_index_start = match.start()
|
||||
pex_file.seek(match_index_start)
|
||||
rest_text = pex_file.read()
|
||||
# locate the end of circuit definition line
|
||||
match = re.search(r'\n', rest_text)
|
||||
match_index_end = match.start()
|
||||
match_index_end = match.end()
|
||||
# store the unchanged part of pex file in memory
|
||||
pex_file.seek(0)
|
||||
part1 = pex_file.read(match_index_start)
|
||||
pex_file.seek(match_index_start + match_index_end)
|
||||
pex_file.seek(match_index_end)
|
||||
part2 = pex_file.read()
|
||||
|
||||
bitcell_list = "+ "
|
||||
if OPTS.words_per_row:
|
||||
for bank in range(OPTS.num_banks):
|
||||
for bank in range(OPTS.num_banks):
|
||||
row = int(OPTS.num_words / OPTS.words_per_row) - 1
|
||||
col = int(OPTS.word_size * OPTS.words_per_row) - 1
|
||||
bitcell_list += "bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col)
|
||||
bitcell_list += "bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col)
|
||||
for col in range(OPTS.word_size * OPTS.words_per_row):
|
||||
for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports):
|
||||
bitcell_list += "bl{0}_{1} ".format(bank, col)
|
||||
bitcell_list += "br{0}_{1} ".format(bank, col)
|
||||
bitcell_list += "\n"
|
||||
control_list = "+ "
|
||||
if OPTS.words_per_row:
|
||||
for bank in range(OPTS.num_banks):
|
||||
control_list += "bank_{}/s_en0".format(bank)
|
||||
control_list += '\n'
|
||||
|
||||
part2 = bitcell_list + control_list + part2
|
||||
|
||||
pex_file.close()
|
||||
|
||||
# obtain the correct definition line from the original spice file
|
||||
sp_file = open(ref_file_name, "r")
|
||||
contents = sp_file.read()
|
||||
circuit_title = re.search(".SUBCKT " + str(name) + ".*\n", contents)
|
||||
circuit_title = re.search(".SUBCKT " + str(name) + ".*", contents)
|
||||
circuit_title = circuit_title.group()
|
||||
sp_file.close()
|
||||
|
||||
# write the new pex file with info in the memory
|
||||
output_file = open(output_file_name, "w")
|
||||
output_file.write(part1)
|
||||
output_file.write(circuit_title)
|
||||
output_file.write(circuit_title+'\n')
|
||||
output_file.write(part2)
|
||||
output_file.close()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
|
||||
.SUBCKT cell_6t bl br wl vdd gnd
|
||||
* Inverter 1
|
||||
MM0 Qbar Q gnd gnd NMOS_VTG W=205.00n L=50n
|
||||
MM4 Qbar Q vdd vdd PMOS_VTG W=90n L=50n
|
||||
MM0 Q_bar Q gnd gnd NMOS_VTG W=205.00n L=50n
|
||||
MM4 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n
|
||||
|
||||
* Inverer 2
|
||||
MM1 Q Qbar gnd gnd NMOS_VTG W=205.00n L=50n
|
||||
MM5 Q Qbar vdd vdd PMOS_VTG W=90n L=50n
|
||||
MM1 Q Q_bar gnd gnd NMOS_VTG W=205.00n L=50n
|
||||
MM5 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n
|
||||
|
||||
* Access transistors
|
||||
MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n
|
||||
MM2 br wl Qbar gnd NMOS_VTG W=135.00n L=50n
|
||||
MM2 br wl Q_bar gnd NMOS_VTG W=135.00n L=50n
|
||||
.ENDS cell_6t
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
|
||||
.SUBCKT dummy_cell_6t bl br wl vdd gnd
|
||||
* Inverter 1
|
||||
MM0 Qbar Q gnd gnd NMOS_VTG W=205.00n L=50n
|
||||
MM4 Qbar Q vdd vdd PMOS_VTG W=90n L=50n
|
||||
MM0 Q_bar Q gnd gnd NMOS_VTG W=205.00n L=50n
|
||||
MM4 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n
|
||||
|
||||
* Inverer 2
|
||||
MM1 Q Qbar gnd gnd NMOS_VTG W=205.00n L=50n
|
||||
MM5 Q Qbar vdd vdd PMOS_VTG W=90n L=50n
|
||||
MM1 Q Q_bar gnd gnd NMOS_VTG W=205.00n L=50n
|
||||
MM5 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n
|
||||
|
||||
* Access transistors
|
||||
MM3 bl_noconn wl Q gnd NMOS_VTG W=135.00n L=50n
|
||||
MM2 br_noconn wl Qbar gnd NMOS_VTG W=135.00n L=50n
|
||||
MM2 br_noconn wl Q_bar gnd NMOS_VTG W=135.00n L=50n
|
||||
.ENDS cell_6t
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
|||
magic
|
||||
tech scmos
|
||||
timestamp 1542220294
|
||||
timestamp 1577066542
|
||||
<< nwell >>
|
||||
rect 0 46 54 75
|
||||
<< pwell >>
|
||||
|
|
@ -47,12 +47,17 @@ rect 17 33 21 37
|
|||
rect 25 29 29 37
|
||||
rect 33 33 37 37
|
||||
rect 41 33 45 37
|
||||
rect 9 17 13 21
|
||||
rect 25 17 29 23
|
||||
rect 41 17 45 21
|
||||
<< pdcontact >>
|
||||
rect 17 54 21 58
|
||||
rect 25 54 29 58
|
||||
rect 33 54 37 58
|
||||
<< psubstratepcontact >>
|
||||
rect 25 9 29 13
|
||||
<< nsubstratencontact >>
|
||||
rect 25 68 29 72
|
||||
<< polysilicon >>
|
||||
rect 22 57 24 60
|
||||
rect 30 57 32 60
|
||||
|
|
@ -103,19 +108,16 @@ rect 0 2 16 6
|
|||
rect 20 2 34 6
|
||||
rect 38 2 54 6
|
||||
<< m2contact >>
|
||||
rect 25 68 29 72
|
||||
rect 25 54 29 58
|
||||
rect 2 33 6 37
|
||||
rect 48 33 52 37
|
||||
rect 16 24 20 28
|
||||
rect 34 24 38 28
|
||||
rect 16 2 20 6
|
||||
rect 34 2 38 6
|
||||
<< pdm12contact >>
|
||||
rect 25 54 29 58
|
||||
<< ndm12contact >>
|
||||
rect 9 17 13 21
|
||||
rect 41 17 45 21
|
||||
<< nsm12contact >>
|
||||
rect 25 68 29 72
|
||||
rect 16 2 20 6
|
||||
rect 34 2 38 6
|
||||
<< metal2 >>
|
||||
rect 2 37 6 72
|
||||
rect 2 0 6 33
|
||||
|
|
@ -139,4 +141,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
|||
rlabel metal2 11 7 11 7 1 bl1
|
||||
rlabel metal2 43 7 43 7 1 br1
|
||||
rlabel metal2 50 7 50 7 8 br0
|
||||
rlabel polycontact 29 49 29 49 1 Q
|
||||
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||
<< end >>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
magic
|
||||
tech scmos
|
||||
timestamp 1542220294
|
||||
timestamp 1577067400
|
||||
<< nwell >>
|
||||
rect 0 46 54 75
|
||||
<< pwell >>
|
||||
|
|
@ -47,12 +47,17 @@ rect 17 33 21 37
|
|||
rect 25 29 29 37
|
||||
rect 33 33 37 37
|
||||
rect 41 33 45 37
|
||||
rect 9 17 13 21
|
||||
rect 25 17 29 23
|
||||
rect 41 17 45 21
|
||||
<< pdcontact >>
|
||||
rect 17 54 21 58
|
||||
rect 25 54 29 58
|
||||
rect 33 54 37 58
|
||||
<< psubstratepcontact >>
|
||||
rect 25 9 29 13
|
||||
<< nsubstratencontact >>
|
||||
rect 25 68 29 72
|
||||
<< polysilicon >>
|
||||
rect 22 57 24 60
|
||||
rect 30 57 32 60
|
||||
|
|
@ -103,19 +108,16 @@ rect 0 2 16 6
|
|||
rect 20 2 34 6
|
||||
rect 38 2 54 6
|
||||
<< m2contact >>
|
||||
rect 25 68 29 72
|
||||
rect 25 54 29 58
|
||||
rect 2 33 6 37
|
||||
rect 48 33 52 37
|
||||
rect 16 24 20 28
|
||||
rect 34 24 38 28
|
||||
rect 16 2 20 6
|
||||
rect 34 2 38 6
|
||||
<< pdm12contact >>
|
||||
rect 25 54 29 58
|
||||
<< ndm12contact >>
|
||||
rect 9 17 13 21
|
||||
rect 41 17 45 21
|
||||
<< nsm12contact >>
|
||||
rect 25 68 29 72
|
||||
rect 16 2 20 6
|
||||
rect 34 2 38 6
|
||||
<< metal2 >>
|
||||
rect 2 37 6 72
|
||||
rect 2 0 6 33
|
||||
|
|
@ -139,4 +141,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
|||
rlabel metal2 11 7 11 7 1 bl1
|
||||
rlabel metal2 43 7 43 7 1 br1
|
||||
rlabel metal2 50 7 50 7 8 br0
|
||||
rlabel polycontact 29 49 29 49 1 Q
|
||||
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||
<< end >>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
magic
|
||||
tech scmos
|
||||
timestamp 1560809302
|
||||
timestamp 1577163318
|
||||
<< nwell >>
|
||||
rect -8 35 42 57
|
||||
<< pwell >>
|
||||
|
|
@ -115,4 +115,6 @@ rlabel m2contact 17 52 17 52 5 vdd
|
|||
rlabel metal2 8 49 8 49 1 bl
|
||||
rlabel metal2 26 49 26 49 1 br
|
||||
rlabel metal1 4 13 4 13 1 wl
|
||||
rlabel polycontact 17 37 17 37 1 Q
|
||||
rlabel polycontact 23 28 23 28 1 Q_bar
|
||||
<< end >>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
magic
|
||||
tech scmos
|
||||
timestamp 1562188987
|
||||
timestamp 1577067400
|
||||
<< nwell >>
|
||||
rect 0 46 54 75
|
||||
<< pwell >>
|
||||
|
|
@ -133,4 +133,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
|||
rlabel metal2 11 7 11 7 1 bl1
|
||||
rlabel metal2 43 7 43 7 1 br1
|
||||
rlabel metal2 50 7 50 7 8 br0
|
||||
rlabel polycontact 29 49 29 49 1 Q
|
||||
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||
<< end >>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
magic
|
||||
tech scmos
|
||||
timestamp 1562189027
|
||||
timestamp 1577067400
|
||||
<< nwell >>
|
||||
rect 0 46 54 75
|
||||
<< pwell >>
|
||||
|
|
@ -133,4 +133,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
|||
rlabel metal2 11 7 11 7 1 bl1
|
||||
rlabel metal2 43 7 43 7 1 br1
|
||||
rlabel metal2 50 7 50 7 8 br0
|
||||
rlabel polycontact 29 49 29 49 1 Q
|
||||
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||
<< end >>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
magic
|
||||
tech scmos
|
||||
timestamp 1560809362
|
||||
timestamp 1577067400
|
||||
<< nwell >>
|
||||
rect -8 35 42 57
|
||||
<< pwell >>
|
||||
|
|
@ -112,4 +112,6 @@ rlabel m2contact 17 52 17 52 5 vdd
|
|||
rlabel metal2 8 49 8 49 1 bl
|
||||
rlabel metal2 26 49 26 49 1 br
|
||||
rlabel metal1 4 13 4 13 1 wl
|
||||
rlabel polycontact 17 37 17 37 1 Q
|
||||
rlabel polycontact 23 28 23 28 1 Q_bar
|
||||
<< end >>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
magic
|
||||
tech scmos
|
||||
timestamp 1542221056
|
||||
timestamp 1577067400
|
||||
<< nwell >>
|
||||
rect 0 46 54 75
|
||||
<< pwell >>
|
||||
|
|
@ -142,4 +142,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
|||
rlabel metal2 11 7 11 7 1 bl1
|
||||
rlabel metal2 43 7 43 7 1 br1
|
||||
rlabel metal2 50 7 50 7 8 br0
|
||||
rlabel polycontact 29 49 29 49 1 Q
|
||||
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||
<< end >>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
magic
|
||||
tech scmos
|
||||
timestamp 1542221056
|
||||
timestamp 1577067446
|
||||
<< nwell >>
|
||||
rect 0 46 54 75
|
||||
<< pwell >>
|
||||
|
|
@ -142,4 +142,6 @@ rlabel metal2 4 7 4 7 2 bl0
|
|||
rlabel metal2 11 7 11 7 1 bl1
|
||||
rlabel metal2 43 7 43 7 1 br1
|
||||
rlabel metal2 50 7 50 7 8 br0
|
||||
rlabel polycontact 29 49 29 49 1 Q
|
||||
rlabel polycontact 25 42 25 42 1 Q_bar
|
||||
<< end >>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
magic
|
||||
tech scmos
|
||||
timestamp 1560809329
|
||||
timestamp 1577067503
|
||||
<< nwell >>
|
||||
rect -8 35 42 57
|
||||
<< pwell >>
|
||||
|
|
@ -116,4 +116,6 @@ rlabel m2contact 17 52 17 52 5 vdd
|
|||
rlabel metal2 8 49 8 49 1 bl
|
||||
rlabel metal2 26 49 26 49 1 br
|
||||
rlabel metal1 4 13 4 13 1 wl
|
||||
rlabel polycontact 17 37 17 37 1 Q
|
||||
rlabel polycontact 23 28 23 28 1 Q_bar
|
||||
<< end >>
|
||||
|
|
|
|||
|
|
@ -4,15 +4,15 @@
|
|||
* SPICE3 file created from cell_6t.ext - technology: scmos
|
||||
|
||||
* Inverter 1
|
||||
M1000 Q Qbar vdd vdd p w=0.6u l=0.8u
|
||||
M1002 Q Qbar gnd gnd n w=1.6u l=0.4u
|
||||
M1000 Q Q_bar vdd vdd p w=0.6u l=0.8u
|
||||
M1002 Q Q_bar gnd gnd n w=1.6u l=0.4u
|
||||
|
||||
* Inverter 2
|
||||
M1001 vdd Q Qbar vdd p w=0.6u l=0.8u
|
||||
M1003 gnd Q Qbar gnd n w=1.6u l=0.4u
|
||||
M1001 vdd Q Q_bar vdd p w=0.6u l=0.8u
|
||||
M1003 gnd Q Q_bar gnd n w=1.6u l=0.4u
|
||||
|
||||
* Access transistors
|
||||
M1004 Q wl bl gnd n w=0.8u l=0.4u
|
||||
M1005 Qbar wl br gnd n w=0.8u l=0.4u
|
||||
M1005 Q_bar wl br gnd n w=0.8u l=0.4u
|
||||
|
||||
.ENDS
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
.SUBCKT dummy_cell_6t bl br wl vdd gnd
|
||||
|
||||
* Inverter 1
|
||||
M1000 Q Qbar vdd vdd p w=0.6u l=0.8u
|
||||
M1002 Q Qbar gnd gnd n w=1.6u l=0.4u
|
||||
M1000 Q Q_bar vdd vdd p w=0.6u l=0.8u
|
||||
M1002 Q Q_bar gnd gnd n w=1.6u l=0.4u
|
||||
|
||||
* Inverter 2
|
||||
M1001 vdd Q Qbar vdd p w=0.6u l=0.8u
|
||||
M1003 gnd Q Qbar gnd n w=1.6u l=0.4u
|
||||
M1001 vdd Q Q_bar vdd p w=0.6u l=0.8u
|
||||
M1003 gnd Q Q_bar gnd n w=1.6u l=0.4u
|
||||
|
||||
* Access transistors
|
||||
M1004 Q wl bl_noconn gnd n w=0.8u l=0.4u
|
||||
M1005 Qbar wl br_noconn gnd n w=0.8u l=0.4u
|
||||
M1005 Q_bar wl br_noconn gnd n w=0.8u l=0.4u
|
||||
|
||||
.ENDS
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue