Merge branch 'dev' of github.com:VLSIDA/PrivateRAM into dev

This commit is contained in:
mrg 2020-08-18 09:01:41 -07:00
commit 17504a7da3
32 changed files with 511 additions and 124 deletions

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

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

@ -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"],
@ -51,7 +51,7 @@ class bitcell(bitcell_base.bitcell_base):
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells")
# debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells")
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """

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

@ -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,8 +180,12 @@ 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
@ -228,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
@ -271,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):
"""
@ -315,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):
@ -331,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]
@ -422,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):

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

@ -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):
@ -1050,7 +1049,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

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

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

View File

@ -109,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")
@ -413,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")
@ -442,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