merge dev

This commit is contained in:
jcirimel 2020-08-19 14:25:41 -07:00
commit 854d51c721
101 changed files with 1707 additions and 520 deletions

View File

@ -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).

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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]

View File

@ -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,

View File

@ -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

View File

@ -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"],

View File

@ -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.

View File

@ -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 """

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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

View File

@ -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) """

View File

@ -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

View File

@ -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"

View File

@ -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.

View File

@ -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

View File

@ -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]

View File

@ -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,

View File

@ -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

View File

@ -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.")

View File

@ -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)))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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()

View File

@ -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))

View File

@ -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}"

View File

@ -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):

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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))

View File

@ -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))

View File

@ -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."""

View File

@ -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 """

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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))

View File

@ -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

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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()

View File

@ -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()

View File

@ -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()

125
compiler/pgates/pbuf_dec.py Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 """

View File

@ -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):
"""

View File

@ -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()

View File

@ -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()

View File

@ -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 """

View File

@ -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()

View File

@ -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"]

View File

@ -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 """

View File

@ -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.

View File

@ -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"]

View File

@ -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"

View File

@ -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")

View File

@ -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

View File

@ -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;

View File

@ -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))

View File

@ -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())

View File

@ -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)

View File

@ -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())

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys,os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class 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())

View File

@ -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()

View File

@ -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")

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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 >>

View File

@ -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 >>

View File

@ -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 >>

View File

@ -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 >>

View File

@ -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 >>

View File

@ -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 >>

View File

@ -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 >>

View File

@ -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 >>

View File

@ -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 >>

View File

@ -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

View File

@ -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