mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' of https://github.com/VLSIDA/PrivateRAM into dev
This commit is contained in:
commit
3260468477
|
|
@ -0,0 +1,6 @@
|
|||
freepdk45:
|
||||
script: "/home/gitlab-runner/regress_freepdk45.sh"
|
||||
|
||||
scn3me_subm:
|
||||
script: "/home/gitlab-runner/regress_scn3me_subm.sh"
|
||||
|
||||
16
README.md
16
README.md
|
|
@ -6,7 +6,7 @@ https://github.com/mguthaus/OpenRAM/blob/master/OpenRAM_ICCAD_2016_presentation.
|
|||
|
||||
The OpenRAM compiler has very few dependencies:
|
||||
* ngspice-26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later)
|
||||
* Python 2.7 and higher (currently excludes Python 3 and up)
|
||||
* Python 3.5 and higher
|
||||
* Python numpy
|
||||
* a setup script for each technology
|
||||
* a technology directory for each technology with the base cells
|
||||
|
|
@ -28,6 +28,10 @@ For example, in csh/tcsh, add to your .cshrc/.tcshrc:
|
|||
setenv OPENRAM_HOME "$HOME/OpenRAM/compiler"
|
||||
setenv OPENRAM_TECH "$HOME/OpenRAM/technology"
|
||||
```
|
||||
|
||||
We include the tech files necessary for FreePDK and SCMOS. The SCMOS
|
||||
spice models, however, are generic and should be replaced with foundry
|
||||
models.
|
||||
If you are using FreePDK, you should also have that set up and have the
|
||||
environment variable point to the PDK.
|
||||
For example, in bash, add to your .bashrc:
|
||||
|
|
@ -44,13 +48,16 @@ We do not distribute the PDK, but you may get it from:
|
|||
If you are using SCMOS, you should install Magic and netgen from:
|
||||
http://opencircuitdesign.com/magic/
|
||||
http://opencircuitdesign.com/netgen/
|
||||
In addition, you will need to install the MOSIS SCMOS rules for scn3me_subm
|
||||
that are part of QFlow:
|
||||
We have included the SCN3ME design rules from QFlow:
|
||||
http://opencircuitdesign.com/qflow/
|
||||
|
||||
# DIRECTORY STRUCTURE
|
||||
|
||||
* compiler - openram compiler itself (pointed to by OPENRAM_HOME)
|
||||
* compiler/base - base data structure modules
|
||||
* compiler/pgates - parameterized cells (e.g. logic gates)
|
||||
* compiler/modules - high-level modules (e.g. decoders, etc.)
|
||||
* compiler/verify - DRC and LVS verification wrappers
|
||||
* compiler/characterizer - timing characterization code
|
||||
* compiler/gdsMill - GDSII reader/writer
|
||||
* compiler/router - detailed router
|
||||
|
|
@ -59,6 +66,8 @@ that are part of QFlow:
|
|||
* technology/freepdk45 - example configuration library for freepdk45 technology node
|
||||
* technology/scn3me_subm - example configuration library SCMOS technology node
|
||||
* technology/setup_scripts - setup scripts to customize your PDKs and OpenRAM technologies
|
||||
* docs - LaTeX manual (likely outdated)
|
||||
* lib - IP library of pregenerated memories
|
||||
|
||||
|
||||
# UNIT TESTS
|
||||
|
|
@ -232,3 +241,4 @@ ui().importCds("default",
|
|||
When you import a gds file, make sure to attach the correct tech lib
|
||||
or you will get incorrect layers in the resulting library.
|
||||
|
||||
TEST
|
||||
|
|
|
|||
|
|
@ -40,10 +40,12 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
'hierarchical_predecode3x8']
|
||||
if name not in design.name_map:
|
||||
design.name_map.append(name)
|
||||
elif self.__class__.__name__ in ok_list:
|
||||
pass
|
||||
else:
|
||||
debug.error("Duplicate layout reference name {0} of class {1}. GDS2 requires names be unique.".format(name,self.__class__),-1)
|
||||
for ok_names in ok_list:
|
||||
if ok_names in self.__class__.__name__:
|
||||
break
|
||||
else:
|
||||
debug.error("Duplicate layout reference name {0} of class {1}. GDS2 requires names be unique.".format(name,self.__class__),-1)
|
||||
|
||||
def setup_drc_constants(self):
|
||||
""" These are some DRC constants used in many places in the compiler."""
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ def relative_compare(value1,value2,error_tolerance=0.001):
|
|||
return (abs(value1 - value2) / max(value1,value2) <= error_tolerance)
|
||||
|
||||
|
||||
def parse_output(filename, key):
|
||||
def parse_spice_list(filename, key):
|
||||
"""Parses a hspice output.lis file for a key value"""
|
||||
if OPTS.spice_name == "xa" :
|
||||
# customsim has a different output file name
|
||||
|
|
@ -22,6 +22,7 @@ def parse_output(filename, key):
|
|||
except IOError:
|
||||
debug.error("Unable to open spice output file: {0}".format(full_filename),1)
|
||||
contents = f.read()
|
||||
f.close()
|
||||
# val = re.search(r"{0}\s*=\s*(-?\d+.?\d*\S*)\s+.*".format(key), contents)
|
||||
val = re.search(r"{0}\s*=\s*(-?\d+.?\d*[e]?[-+]?[0-9]*\S*)\s+.*".format(key), contents)
|
||||
|
||||
|
|
|
|||
|
|
@ -339,16 +339,16 @@ class delay():
|
|||
# Checking from not data_value to data_value
|
||||
self.write_delay_stimulus()
|
||||
self.stim.run_sim()
|
||||
delay_hl = parse_output("timing", "delay_hl")
|
||||
delay_lh = parse_output("timing", "delay_lh")
|
||||
slew_hl = parse_output("timing", "slew_hl")
|
||||
slew_lh = parse_output("timing", "slew_lh")
|
||||
delay_hl = parse_spice_list("timing", "delay_hl")
|
||||
delay_lh = parse_spice_list("timing", "delay_lh")
|
||||
slew_hl = parse_spice_list("timing", "slew_hl")
|
||||
slew_lh = parse_spice_list("timing", "slew_lh")
|
||||
delays = (delay_hl, delay_lh, slew_hl, slew_lh)
|
||||
|
||||
read0_power=parse_output("timing", "read0_power")
|
||||
write0_power=parse_output("timing", "write0_power")
|
||||
read1_power=parse_output("timing", "read1_power")
|
||||
write1_power=parse_output("timing", "write1_power")
|
||||
read0_power=parse_spice_list("timing", "read0_power")
|
||||
write0_power=parse_spice_list("timing", "write0_power")
|
||||
read1_power=parse_spice_list("timing", "read1_power")
|
||||
write1_power=parse_spice_list("timing", "write1_power")
|
||||
|
||||
if not self.check_valid_delays(delays):
|
||||
return (False,{})
|
||||
|
|
@ -378,13 +378,13 @@ class delay():
|
|||
|
||||
self.write_power_stimulus(trim=False)
|
||||
self.stim.run_sim()
|
||||
leakage_power=parse_output("timing", "leakage_power")
|
||||
leakage_power=parse_spice_list("timing", "leakage_power")
|
||||
debug.check(leakage_power!="Failed","Could not measure leakage power.")
|
||||
|
||||
|
||||
self.write_power_stimulus(trim=True)
|
||||
self.stim.run_sim()
|
||||
trim_leakage_power=parse_output("timing", "leakage_power")
|
||||
trim_leakage_power=parse_spice_list("timing", "leakage_power")
|
||||
debug.check(trim_leakage_power!="Failed","Could not measure leakage power.")
|
||||
|
||||
# For debug, you sometimes want to inspect each simulation.
|
||||
|
|
@ -473,10 +473,10 @@ class delay():
|
|||
# Checking from not data_value to data_value
|
||||
self.write_delay_stimulus()
|
||||
self.stim.run_sim()
|
||||
delay_hl = parse_output("timing", "delay_hl")
|
||||
delay_lh = parse_output("timing", "delay_lh")
|
||||
slew_hl = parse_output("timing", "slew_hl")
|
||||
slew_lh = parse_output("timing", "slew_lh")
|
||||
delay_hl = parse_spice_list("timing", "delay_hl")
|
||||
delay_lh = parse_spice_list("timing", "delay_lh")
|
||||
slew_hl = parse_spice_list("timing", "slew_hl")
|
||||
slew_lh = parse_spice_list("timing", "slew_lh")
|
||||
# if it failed or the read was longer than a period
|
||||
if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float:
|
||||
debug.info(2,"Invalid measures: Period {0}, delay_hl={1}ns, delay_lh={2}ns slew_hl={3}ns slew_lh={4}ns".format(self.period,
|
||||
|
|
|
|||
|
|
@ -186,8 +186,8 @@ class setup_hold():
|
|||
target_time=feasible_bound,
|
||||
correct_value=correct_value)
|
||||
self.stim.run_sim()
|
||||
ideal_clk_to_q = convert_to_float(parse_output("timing", "clk2q_delay"))
|
||||
setuphold_time = convert_to_float(parse_output("timing", "setup_hold_time"))
|
||||
ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
||||
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
|
||||
debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time))
|
||||
|
||||
if type(ideal_clk_to_q)!=float or type(setuphold_time)!=float:
|
||||
|
|
@ -219,8 +219,8 @@ class setup_hold():
|
|||
|
||||
|
||||
self.stim.run_sim()
|
||||
clk_to_q = convert_to_float(parse_output("timing", "clk2q_delay"))
|
||||
setuphold_time = convert_to_float(parse_output("timing", "setup_hold_time"))
|
||||
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
||||
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
|
||||
if type(clk_to_q)==float and (clk_to_q<1.1*ideal_clk_to_q) and type(setuphold_time)==float:
|
||||
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
||||
setuphold_time *= -1e9
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class trim_spice():
|
|||
# Load the file into a buffer for performance
|
||||
sp = open(self.sp_file, "r")
|
||||
self.spice = sp.readlines()
|
||||
sp.close()
|
||||
for i in range(len(self.spice)):
|
||||
self.spice[i] = self.spice[i].rstrip(" \n")
|
||||
|
||||
|
|
@ -97,6 +98,7 @@ class trim_spice():
|
|||
# Finally, write out the buffer as the new reduced file
|
||||
sp = open(self.reduced_spfile, "w")
|
||||
sp.write("\n".join(self.sp_buffer))
|
||||
sp.close()
|
||||
|
||||
|
||||
def remove_insts(self, subckt_name, keep_inst_list):
|
||||
|
|
|
|||
|
|
@ -89,11 +89,13 @@ def print_banner():
|
|||
def check_versions():
|
||||
""" Run some checks of required software versions. """
|
||||
|
||||
# Now require python >=3.6
|
||||
# Now require python >=3.5
|
||||
major_python_version = sys.version_info.major
|
||||
minor_python_version = sys.version_info.minor
|
||||
if not (major_python_version == 3 and minor_python_version >= 6):
|
||||
debug.error("Python 3.6 or greater is required.",-1)
|
||||
major_required = 3
|
||||
minor_required = 5
|
||||
if not (major_python_version == major_required and minor_python_version >= minor_required):
|
||||
debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1)
|
||||
|
||||
# FIXME: Check versions of other tools here??
|
||||
# or, this could be done in each module (e.g. verify, characterizer, etc.)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class bitcell(design.design):
|
|||
library.
|
||||
"""
|
||||
|
||||
pin_names = ["BL", "BR", "WL", "vdd", "gnd"]
|
||||
pin_names = ["bl", "br", "wl", "vdd", "gnd"]
|
||||
(width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"], layer["boundary"])
|
||||
|
||||
|
|
@ -34,7 +34,59 @@ class bitcell(design.design):
|
|||
c_para = spice["min_tx_drain_c"]
|
||||
result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = swing)
|
||||
return result
|
||||
|
||||
|
||||
def list_bitcell_pins(self, col, row):
|
||||
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
|
||||
bitcell_pins = ["bl[{0}]".format(col),
|
||||
"br[{0}]".format(col),
|
||||
"wl[{0}]".format(row),
|
||||
"vdd",
|
||||
"gnd"]
|
||||
return bitcell_pins
|
||||
|
||||
|
||||
def list_row_pins(self):
|
||||
""" Creates a list of all row pins (except for gnd and vdd) """
|
||||
row_pins = ["wl"]
|
||||
return row_pins
|
||||
|
||||
def list_read_row_pins(self):
|
||||
""" Creates a list of row pins associated with read ports """
|
||||
row_pins = ["wl"]
|
||||
return row_pins
|
||||
|
||||
def list_write_row_pins(self):
|
||||
""" Creates a list of row pins associated with write ports """
|
||||
row_pins = ["wl"]
|
||||
return row_pins
|
||||
|
||||
|
||||
def list_column_pins(self):
|
||||
""" Creates a list of all column pins (except for gnd and vdd) """
|
||||
column_pins = ["bl", "br"]
|
||||
return column_pins
|
||||
|
||||
def list_read_column_pins(self):
|
||||
""" Creates a list of column pins associated with read ports """
|
||||
column_pins = ["bl"]
|
||||
return column_pins
|
||||
|
||||
def list_read_bar_column_pins(self):
|
||||
""" Creates a list of column pins associated with read_bar ports """
|
||||
column_pins = ["br"]
|
||||
return column_pins
|
||||
|
||||
def list_write_column_pins(self):
|
||||
""" Creates a list of column pins associated with write ports """
|
||||
column_pins = ["bl"]
|
||||
return column_pins
|
||||
|
||||
def list_write_bar_column_pins(self):
|
||||
""" Creates a list of column pins asscociated with write_bar ports"""
|
||||
column_pins = ["br"]
|
||||
return column_pins
|
||||
|
||||
def analytical_power(self, proc, vdd, temp, load):
|
||||
"""Bitcell power in nW. Only characterizes leakage."""
|
||||
from tech import spice
|
||||
|
|
@ -42,3 +94,4 @@ class bitcell(design.design):
|
|||
dynamic = 0 #temporary
|
||||
total_power = self.return_power(dynamic, leakage)
|
||||
return total_power
|
||||
|
||||
|
|
|
|||
|
|
@ -1,204 +1,202 @@
|
|||
import debug
|
||||
import design
|
||||
from tech import drc, spice
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
|
||||
class bitcell_array(design.design):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
def __init__(self, cols, rows, name="bitcell_array"):
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
|
||||
|
||||
|
||||
self.column_size = cols
|
||||
self.row_size = rows
|
||||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.cell = self.mod_bitcell()
|
||||
self.add_mod(self.cell)
|
||||
|
||||
# We increase it by a well enclosure so the precharges don't overlap our wells
|
||||
self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width
|
||||
self.width = self.column_size*self.cell.width + self.m1_width
|
||||
|
||||
self.add_pins()
|
||||
self.create_layout()
|
||||
self.add_layout_pins()
|
||||
|
||||
# We don't offset this because we need to align
|
||||
# the replica bitcell in the control logic
|
||||
#self.offset_all_coordinates()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for col in range(self.column_size):
|
||||
self.add_pin("bl[{0}]".format(col))
|
||||
self.add_pin("br[{0}]".format(col))
|
||||
for row in range(self.row_size):
|
||||
self.add_pin("wl[{0}]".format(row))
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_layout(self):
|
||||
xoffset = 0.0
|
||||
self.cell_inst = {}
|
||||
for col in range(self.column_size):
|
||||
yoffset = 0.0
|
||||
for row in range(self.row_size):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
|
||||
if row % 2:
|
||||
tempy = yoffset + self.cell.height
|
||||
dir_key = "MX"
|
||||
else:
|
||||
tempy = yoffset
|
||||
dir_key = ""
|
||||
|
||||
self.cell_inst[row,col]=self.add_inst(name=name,
|
||||
mod=self.cell,
|
||||
offset=[xoffset, tempy],
|
||||
mirror=dir_key)
|
||||
self.connect_inst(["bl[{0}]".format(col),
|
||||
"br[{0}]".format(col),
|
||||
"wl[{0}]".format(row),
|
||||
"vdd",
|
||||
"gnd"])
|
||||
yoffset += self.cell.height
|
||||
xoffset += self.cell.width
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
|
||||
offset = vector(0.0, 0.0)
|
||||
for col in range(self.column_size):
|
||||
# get the pin of the lower row cell and make it the full width
|
||||
bl_pin = self.cell_inst[0,col].get_pin("BL")
|
||||
br_pin = self.cell_inst[0,col].get_pin("BR")
|
||||
self.add_layout_pin(text="bl[{0}]".format(col),
|
||||
layer="metal2",
|
||||
offset=bl_pin.ll(),
|
||||
width=bl_pin.width(),
|
||||
height=self.height)
|
||||
self.add_layout_pin(text="br[{0}]".format(col),
|
||||
layer="metal2",
|
||||
offset=br_pin.ll(),
|
||||
width=br_pin.width(),
|
||||
height=self.height)
|
||||
|
||||
# increments to the next column width
|
||||
offset.x += self.cell.width
|
||||
|
||||
offset.x = 0.0
|
||||
for row in range(self.row_size):
|
||||
wl_pin = self.cell_inst[row,0].get_pin("WL")
|
||||
self.add_layout_pin(text="wl[{0}]".format(row),
|
||||
layer="metal1",
|
||||
offset=wl_pin.ll(),
|
||||
width=self.width,
|
||||
height=wl_pin.height())
|
||||
|
||||
# increments to the next row height
|
||||
offset.y += self.cell.height
|
||||
|
||||
# For every second row and column, add a via for vdd
|
||||
for row in range(self.row_size):
|
||||
for col in range(self.column_size):
|
||||
inst = self.cell_inst[row,col]
|
||||
for vdd_pin in inst.get_pins("vdd"):
|
||||
# Drop to M1 if needed
|
||||
if vdd_pin.layer == "metal1":
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=vdd_pin.center(),
|
||||
rotate=90)
|
||||
# Always drop to M2
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=vdd_pin.center())
|
||||
self.add_layout_pin_rect_center(text="vdd",
|
||||
layer="metal3",
|
||||
offset=vdd_pin.center())
|
||||
|
||||
|
||||
# For every second row and column (+1), add a via for gnd
|
||||
for row in range(self.row_size):
|
||||
for col in range(self.column_size):
|
||||
inst = self.cell_inst[row,col]
|
||||
for gnd_pin in inst.get_pins("gnd"):
|
||||
# Drop to M1 if needed
|
||||
if gnd_pin.layer == "metal1":
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=gnd_pin.center(),
|
||||
rotate=90)
|
||||
# Always drop to M2
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=gnd_pin.center())
|
||||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer="metal3",
|
||||
offset=gnd_pin.center())
|
||||
|
||||
def analytical_delay(self, slew, load=0):
|
||||
from tech import drc
|
||||
wl_wire = self.gen_wl_wire()
|
||||
wl_wire.return_delay_over_wire(slew)
|
||||
|
||||
wl_to_cell_delay = wl_wire.return_delay_over_wire(slew)
|
||||
# hypothetical delay from cell to bl end without sense amp
|
||||
bl_wire = self.gen_bl_wire()
|
||||
cell_load = 2 * bl_wire.return_input_cap() # we ingore the wire r
|
||||
# hence just use the whole c
|
||||
bl_swing = 0.1
|
||||
cell_delay = self.cell.analytical_delay(wl_to_cell_delay.slew, cell_load, swing = bl_swing)
|
||||
|
||||
#we do not consider the delay over the wire for now
|
||||
return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay,
|
||||
wl_to_cell_delay.slew)
|
||||
|
||||
def analytical_power(self, proc, vdd, temp, load):
|
||||
"""Power of Bitcell array and bitline in nW."""
|
||||
from tech import drc
|
||||
|
||||
# Dynamic Power from Bitline
|
||||
bl_wire = self.gen_bl_wire()
|
||||
cell_load = 2 * bl_wire.return_input_cap()
|
||||
bl_swing = 0.1 #This should probably be defined in the tech file or input
|
||||
freq = spice["default_event_rate"]
|
||||
bitline_dynamic = bl_swing*cell_load*vdd*vdd*freq #not sure if calculation is correct
|
||||
|
||||
#Calculate the bitcell power which currently only includes leakage
|
||||
cell_power = self.cell.analytical_power(proc, vdd, temp, 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)
|
||||
return total_power
|
||||
|
||||
def gen_wl_wire(self):
|
||||
wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc["minwidth_metal1"])
|
||||
wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
|
||||
return wl_wire
|
||||
|
||||
def gen_bl_wire(self):
|
||||
bl_pos = 0
|
||||
bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), self.height, drc["minwidth_metal1"])
|
||||
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
||||
return bl_wire
|
||||
|
||||
def output_load(self, bl_pos=0):
|
||||
bl_wire = self.gen_bl_wire()
|
||||
return bl_wire.wire_c # sense amp only need to charge small portion of the bl
|
||||
# set as one segment for now
|
||||
|
||||
def input_load(self):
|
||||
wl_wire = self.gen_wl_wire()
|
||||
return wl_wire.return_input_cap()
|
||||
import debug
|
||||
import design
|
||||
from tech import drc, spice
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
|
||||
class bitcell_array(design.design):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
def __init__(self, cols, rows, name="bitcell_array"):
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
|
||||
|
||||
|
||||
self.column_size = cols
|
||||
self.row_size = rows
|
||||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.cell = self.mod_bitcell()
|
||||
self.add_mod(self.cell)
|
||||
|
||||
# We increase it by a well enclosure so the precharges don't overlap our wells
|
||||
self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width
|
||||
self.width = self.column_size*self.cell.width + self.m1_width
|
||||
|
||||
self.add_pins()
|
||||
self.create_layout()
|
||||
self.add_layout_pins()
|
||||
|
||||
# We don't offset this because we need to align
|
||||
# the replica bitcell in the control logic
|
||||
#self.offset_all_coordinates()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
row_list = self.cell.list_row_pins()
|
||||
column_list = self.cell.list_column_pins()
|
||||
for col in range(self.column_size):
|
||||
for cell_column in column_list:
|
||||
self.add_pin(cell_column+"[{0}]".format(col))
|
||||
for row in range(self.row_size):
|
||||
for cell_row in row_list:
|
||||
self.add_pin(cell_row+"[{0}]".format(row))
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_layout(self):
|
||||
xoffset = 0.0
|
||||
self.cell_inst = {}
|
||||
for col in range(self.column_size):
|
||||
yoffset = 0.0
|
||||
for row in range(self.row_size):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
|
||||
if row % 2:
|
||||
tempy = yoffset + self.cell.height
|
||||
dir_key = "MX"
|
||||
else:
|
||||
tempy = yoffset
|
||||
dir_key = ""
|
||||
|
||||
self.cell_inst[row,col]=self.add_inst(name=name,
|
||||
mod=self.cell,
|
||||
offset=[xoffset, tempy],
|
||||
mirror=dir_key)
|
||||
self.connect_inst(self.cell.list_bitcell_pins(col, row))
|
||||
|
||||
yoffset += self.cell.height
|
||||
xoffset += self.cell.width
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
|
||||
row_list = self.cell.list_row_pins()
|
||||
column_list = self.cell.list_column_pins()
|
||||
|
||||
offset = vector(0.0, 0.0)
|
||||
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),
|
||||
layer="metal2",
|
||||
offset=bl_pin.ll(),
|
||||
width=bl_pin.width(),
|
||||
height=self.height)
|
||||
|
||||
# increments to the next column width
|
||||
offset.x += self.cell.width
|
||||
|
||||
offset.x = 0.0
|
||||
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),
|
||||
layer="metal1",
|
||||
offset=wl_pin.ll(),
|
||||
width=self.width,
|
||||
height=wl_pin.height())
|
||||
|
||||
# increments to the next row height
|
||||
offset.y += self.cell.height
|
||||
|
||||
# For every second row and column, add a via for vdd
|
||||
for row in range(self.row_size):
|
||||
for col in range(self.column_size):
|
||||
inst = self.cell_inst[row,col]
|
||||
for vdd_pin in inst.get_pins("vdd"):
|
||||
# Drop to M1 if needed
|
||||
if vdd_pin.layer == "metal1":
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=vdd_pin.center(),
|
||||
rotate=90)
|
||||
# Always drop to M2
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=vdd_pin.center())
|
||||
self.add_layout_pin_rect_center(text="vdd",
|
||||
layer="metal3",
|
||||
offset=vdd_pin.center())
|
||||
|
||||
|
||||
# For every second row and column (+1), add a via for gnd
|
||||
for row in range(self.row_size):
|
||||
for col in range(self.column_size):
|
||||
inst = self.cell_inst[row,col]
|
||||
for gnd_pin in inst.get_pins("gnd"):
|
||||
# Drop to M1 if needed
|
||||
if gnd_pin.layer == "metal1":
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=gnd_pin.center(),
|
||||
rotate=90)
|
||||
# Always drop to M2
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=gnd_pin.center())
|
||||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer="metal3",
|
||||
offset=gnd_pin.center())
|
||||
|
||||
def analytical_delay(self, slew, load=0):
|
||||
from tech import drc
|
||||
wl_wire = self.gen_wl_wire()
|
||||
wl_wire.return_delay_over_wire(slew)
|
||||
|
||||
wl_to_cell_delay = wl_wire.return_delay_over_wire(slew)
|
||||
# hypothetical delay from cell to bl end without sense amp
|
||||
bl_wire = self.gen_bl_wire()
|
||||
cell_load = 2 * bl_wire.return_input_cap() # we ingore the wire r
|
||||
# hence just use the whole c
|
||||
bl_swing = 0.1
|
||||
cell_delay = self.cell.analytical_delay(wl_to_cell_delay.slew, cell_load, swing = bl_swing)
|
||||
|
||||
#we do not consider the delay over the wire for now
|
||||
return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay,
|
||||
wl_to_cell_delay.slew)
|
||||
|
||||
def analytical_power(self, proc, vdd, temp, load):
|
||||
"""Power of Bitcell array and bitline in nW."""
|
||||
from tech import drc
|
||||
|
||||
# Dynamic Power from Bitline
|
||||
bl_wire = self.gen_bl_wire()
|
||||
cell_load = 2 * bl_wire.return_input_cap()
|
||||
bl_swing = 0.1 #This should probably be defined in the tech file or input
|
||||
freq = spice["default_event_rate"]
|
||||
bitline_dynamic = bl_swing*cell_load*vdd*vdd*freq #not sure if calculation is correct
|
||||
|
||||
#Calculate the bitcell power which currently only includes leakage
|
||||
cell_power = self.cell.analytical_power(proc, vdd, temp, 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)
|
||||
return total_power
|
||||
|
||||
def gen_wl_wire(self):
|
||||
wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc["minwidth_metal1"])
|
||||
wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
|
||||
return wl_wire
|
||||
|
||||
def gen_bl_wire(self):
|
||||
bl_pos = 0
|
||||
bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), self.height, drc["minwidth_metal1"])
|
||||
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
||||
return bl_wire
|
||||
|
||||
def output_load(self, bl_pos=0):
|
||||
bl_wire = self.gen_bl_wire()
|
||||
return bl_wire.wire_c # sense amp only need to charge small portion of the bl
|
||||
# set as one segment for now
|
||||
|
||||
def input_load(self):
|
||||
wl_wire = self.gen_wl_wire()
|
||||
return wl_wire.return_input_cap()
|
||||
|
|
|
|||
|
|
@ -33,9 +33,8 @@ class sense_amp_array(design.design):
|
|||
|
||||
def add_pins(self):
|
||||
|
||||
for i in range(0,self.row_size,self.words_per_row):
|
||||
index = int(i/self.words_per_row)
|
||||
self.add_pin("data[{0}]".format(index))
|
||||
for i in range(0,self.word_size):
|
||||
self.add_pin("data[{0}]".format(i))
|
||||
self.add_pin("bl[{0}]".format(i))
|
||||
self.add_pin("br[{0}]".format(i))
|
||||
|
||||
|
|
@ -55,23 +54,22 @@ class sense_amp_array(design.design):
|
|||
br_pin = self.amp.get_pin("br")
|
||||
dout_pin = self.amp.get_pin("dout")
|
||||
|
||||
for i in range(0,self.row_size,self.words_per_row):
|
||||
amp_spacing = self.amp.width * self.words_per_row
|
||||
for i in range(0,self.word_size):
|
||||
|
||||
name = "sa_d{0}".format(i)
|
||||
amp_position = vector(self.amp.width * i, 0)
|
||||
amp_position = vector(amp_spacing * i, 0)
|
||||
|
||||
bl_offset = amp_position + bl_pin.ll().scale(1,0)
|
||||
br_offset = amp_position + br_pin.ll().scale(1,0)
|
||||
dout_offset = amp_position + dout_pin.ll()
|
||||
|
||||
index = int(i/self.words_per_row)
|
||||
|
||||
inst = self.add_inst(name=name,
|
||||
mod=self.amp,
|
||||
offset=amp_position)
|
||||
self.connect_inst(["bl[{0}]".format(i),
|
||||
"br[{0}]".format(i),
|
||||
"data[{0}]".format(index),
|
||||
"data[{0}]".format(i),
|
||||
"en", "vdd", "gnd"])
|
||||
|
||||
|
||||
|
|
@ -100,7 +98,7 @@ class sense_amp_array(design.design):
|
|||
width=br_pin.width(),
|
||||
height=br_pin.height())
|
||||
|
||||
self.add_layout_pin(text="data[{0}]".format(index),
|
||||
self.add_layout_pin(text="data[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=dout_offset,
|
||||
width=dout_pin.width(),
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ class options(optparse.Values):
|
|||
# These are the configuration parameters
|
||||
rw_ports = 1
|
||||
r_ports = 0
|
||||
w_ports = 0
|
||||
# These will get initialized by the the file
|
||||
supply_voltages = ""
|
||||
temperatures = ""
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -82,12 +82,12 @@ class precharge(pgate.pgate):
|
|||
"""Adds both the upper_pmos and lower_pmos to the module"""
|
||||
# adds the lower pmos to layout
|
||||
#base = vector(self.width - 2*self.pmos.width + self.overlap_offset.x, 0)
|
||||
self.lower_pmos_position = vector(self.bitcell.get_pin("BL").lx(),
|
||||
self.lower_pmos_position = vector(self.bitcell.get_pin("bl").lx(),
|
||||
self.pmos.active_offset.y)
|
||||
self.lower_pmos_inst=self.add_inst(name="lower_pmos",
|
||||
mod=self.pmos,
|
||||
offset=self.lower_pmos_position)
|
||||
self.connect_inst(["bl", "en", "BR", "vdd"])
|
||||
self.connect_inst(["bl", "en", "br", "vdd"])
|
||||
|
||||
# adds the upper pmos(s) to layout
|
||||
ydiff = self.pmos.height + 2*self.m1_space + contact.poly.width
|
||||
|
|
@ -159,7 +159,7 @@ class precharge(pgate.pgate):
|
|||
def add_bitlines(self):
|
||||
"""Adds both bit-line and bit-line-bar to the module"""
|
||||
# adds the BL on metal 2
|
||||
offset = vector(self.bitcell.get_pin("BL").cx(),0) - vector(0.5 * self.m2_width,0)
|
||||
offset = vector(self.bitcell.get_pin("bl").cx(),0) - vector(0.5 * self.m2_width,0)
|
||||
self.add_layout_pin(text="bl",
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
|
|
@ -167,7 +167,7 @@ class precharge(pgate.pgate):
|
|||
height=self.height)
|
||||
|
||||
# adds the BR on metal 2
|
||||
offset = vector(self.bitcell.get_pin("BR").cx(),0) - vector(0.5 * self.m2_width,0)
|
||||
offset = vector(self.bitcell.get_pin("br").cx(),0) - vector(0.5 * self.m2_width,0)
|
||||
self.add_layout_pin(text="br",
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class single_level_column_mux(design.design):
|
|||
self.add_ptx()
|
||||
self.pin_height = 2*self.m2_width
|
||||
self.width = self.bitcell.width
|
||||
self.height = self.nmos2.uy() + self.pin_height
|
||||
self.height = self.nmos_upper.uy() + self.pin_height
|
||||
self.connect_poly()
|
||||
self.add_bitline_pins()
|
||||
self.connect_bitlines()
|
||||
|
|
@ -40,8 +40,8 @@ class single_level_column_mux(design.design):
|
|||
def add_bitline_pins(self):
|
||||
""" Add the top and bottom pins to this cell """
|
||||
|
||||
bl_pos = vector(self.bitcell.get_pin("BL").lx(), 0)
|
||||
br_pos = vector(self.bitcell.get_pin("BR").lx(), 0)
|
||||
bl_pos = vector(self.bitcell.get_pin("bl").lx(), 0)
|
||||
br_pos = vector(self.bitcell.get_pin("br").lx(), 0)
|
||||
|
||||
# bl and br
|
||||
self.add_layout_pin(text="bl",
|
||||
|
|
@ -67,32 +67,32 @@ class single_level_column_mux(design.design):
|
|||
def add_ptx(self):
|
||||
""" Create the two pass gate NMOS transistors to switch the bitlines"""
|
||||
|
||||
# Adds nmos1,nmos2 to the module
|
||||
# Adds nmos_lower,nmos_upper to the module
|
||||
self.nmos = ptx(width=self.ptx_width)
|
||||
self.add_mod(self.nmos)
|
||||
|
||||
# Space it in the center
|
||||
nmos1_position = self.nmos.active_offset.scale(0,1) + vector(0.5*self.bitcell.width-0.5*self.nmos.active_width,0)
|
||||
self.nmos1=self.add_inst(name="mux_tx1",
|
||||
nmos_lower_position = self.nmos.active_offset.scale(0,1) + vector(0.5*self.bitcell.width-0.5*self.nmos.active_width,0)
|
||||
self.nmos_lower=self.add_inst(name="mux_tx1",
|
||||
mod=self.nmos,
|
||||
offset=nmos1_position)
|
||||
offset=nmos_lower_position)
|
||||
self.connect_inst(["bl", "sel", "bl_out", "gnd"])
|
||||
|
||||
# This aligns it directly above the other tx with gates abutting
|
||||
nmos2_position = nmos1_position + vector(0,self.nmos.active_height + self.poly_space)
|
||||
self.nmos2=self.add_inst(name="mux_tx2",
|
||||
nmos_upper_position = nmos_lower_position + vector(0,self.nmos.active_height + self.poly_space)
|
||||
self.nmos_upper=self.add_inst(name="mux_tx2",
|
||||
mod=self.nmos,
|
||||
offset=nmos2_position)
|
||||
offset=nmos_upper_position)
|
||||
self.connect_inst(["br", "sel", "br_out", "gnd"])
|
||||
|
||||
|
||||
def connect_poly(self):
|
||||
""" Connect the poly gate of the two pass transistors """
|
||||
|
||||
height=self.nmos2.get_pin("G").uy() - self.nmos1.get_pin("G").by()
|
||||
height=self.nmos_upper.get_pin("G").uy() - self.nmos_lower.get_pin("G").by()
|
||||
self.add_layout_pin(text="sel",
|
||||
layer="poly",
|
||||
offset=self.nmos1.get_pin("G").ll(),
|
||||
offset=self.nmos_lower.get_pin("G").ll(),
|
||||
height=height)
|
||||
|
||||
|
||||
|
|
@ -105,36 +105,36 @@ class single_level_column_mux(design.design):
|
|||
br_out_pin = self.get_pin("br_out")
|
||||
|
||||
# These are on metal1
|
||||
nmos1_s_pin = self.nmos1.get_pin("S")
|
||||
nmos1_d_pin = self.nmos1.get_pin("D")
|
||||
nmos2_s_pin = self.nmos2.get_pin("S")
|
||||
nmos2_d_pin = self.nmos2.get_pin("D")
|
||||
nmos_lower_s_pin = self.nmos_lower.get_pin("S")
|
||||
nmos_lower_d_pin = self.nmos_lower.get_pin("D")
|
||||
nmos_upper_s_pin = self.nmos_upper.get_pin("S")
|
||||
nmos_upper_d_pin = self.nmos_upper.get_pin("D")
|
||||
|
||||
# Add vias to bl, br_out, nmos2/S, nmos1/D
|
||||
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=bl_pin.bc())
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=br_out_pin.uc())
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=nmos2_s_pin.center())
|
||||
offset=nmos_upper_s_pin.center())
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=nmos1_d_pin.center())
|
||||
offset=nmos_lower_d_pin.center())
|
||||
|
||||
# bl -> nmos2/D on metal1
|
||||
# bl_out -> nmos2/S on metal2
|
||||
self.add_path("metal1",[bl_pin.ll(), vector(nmos2_d_pin.cx(),bl_pin.by()), nmos2_d_pin.center()])
|
||||
# bl -> nmos_upper/D on metal1
|
||||
# bl_out -> nmos_upper/S on metal2
|
||||
self.add_path("metal1",[bl_pin.ll(), vector(nmos_upper_d_pin.cx(),bl_pin.by()), nmos_upper_d_pin.center()])
|
||||
# halfway up, move over
|
||||
mid1 = bl_out_pin.uc().scale(1,0.5)+nmos2_s_pin.bc().scale(0,0.5)
|
||||
mid2 = bl_out_pin.uc().scale(0,0.5)+nmos2_s_pin.bc().scale(1,0.5)
|
||||
self.add_path("metal2",[bl_out_pin.uc(), mid1, mid2, nmos2_s_pin.bc()])
|
||||
mid1 = bl_out_pin.uc().scale(1,0.5)+nmos_upper_s_pin.bc().scale(0,0.5)
|
||||
mid2 = bl_out_pin.uc().scale(0,0.5)+nmos_upper_s_pin.bc().scale(1,0.5)
|
||||
self.add_path("metal2",[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()])
|
||||
|
||||
# br -> nmos1/D on metal2
|
||||
# br_out -> nmos1/S on metal1
|
||||
self.add_path("metal1",[br_out_pin.uc(), vector(nmos1_s_pin.cx(),br_out_pin.uy()), nmos1_s_pin.center()])
|
||||
# br -> nmos_lower/D on metal2
|
||||
# br_out -> nmos_lower/S on metal1
|
||||
self.add_path("metal1",[br_out_pin.uc(), vector(nmos_lower_s_pin.cx(),br_out_pin.uy()), nmos_lower_s_pin.center()])
|
||||
# halfway up, move over
|
||||
mid1 = br_pin.bc().scale(1,0.5)+nmos1_d_pin.uc().scale(0,0.5)
|
||||
mid2 = br_pin.bc().scale(0,0.5)+nmos1_d_pin.uc().scale(1,0.5)
|
||||
self.add_path("metal2",[br_pin.bc(), mid1, mid2, nmos1_d_pin.uc()])
|
||||
mid1 = br_pin.bc().scale(1,0.5)+nmos_lower_d_pin.uc().scale(0,0.5)
|
||||
mid2 = br_pin.bc().scale(0,0.5)+nmos_lower_d_pin.uc().scale(1,0.5)
|
||||
self.add_path("metal2",[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()])
|
||||
|
||||
|
||||
def add_wells(self):
|
||||
|
|
@ -144,11 +144,11 @@ class single_level_column_mux(design.design):
|
|||
"""
|
||||
|
||||
# Add it to the right, aligned in between the two tx
|
||||
active_pos = self.nmos2.lr().scale(0,0.5) + self.nmos1.ur().scale(1,0.5)
|
||||
self.add_via_center(layers=("active", "contact", "metal1"),
|
||||
offset=active_pos,
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
active_pos = vector(self.bitcell.width,self.nmos_upper.by())
|
||||
active_via = self.add_via_center(layers=("active", "contact", "metal1"),
|
||||
offset=active_pos,
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
|
||||
|
||||
# Add the M1->M2->M3 stack
|
||||
|
|
@ -159,6 +159,12 @@ class single_level_column_mux(design.design):
|
|||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer="metal3",
|
||||
offset=active_pos)
|
||||
|
||||
# Add well enclosure over all the tx and contact
|
||||
self.add_rect(layer="pwell",
|
||||
offset=vector(0,0),
|
||||
width=self.bitcell.width,
|
||||
height=self.height)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,154 +0,0 @@
|
|||
#!/usr/bin/env python2.7
|
||||
"Run a regresion test on a basic parameterized transistors"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
import sys,os
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
import debug
|
||||
|
||||
class ptx_test(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import ptx
|
||||
import tech
|
||||
|
||||
debug.info(2, "Checking three fingers PMOS")
|
||||
fet = ptx.ptx(width=tech.drc["minwidth_tx"],
|
||||
mults=4,
|
||||
tx_type="pmos",
|
||||
connect_active=True,
|
||||
connect_poly=True)
|
||||
self.local_check(fet)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def add_mods(self, fet):
|
||||
self.create_contacts()
|
||||
self.add_well_extension(fet)
|
||||
self.add_wire_extension(fet)
|
||||
self.add_well_tiedown(fet)
|
||||
self.add_poly_tiedown(fet)
|
||||
|
||||
def create_contacts(self):
|
||||
layer_stack = ("active", "contact", "metal1")
|
||||
self.well_contact = contact.contact(layer_stack)
|
||||
|
||||
layer_stack = ("poly", "contact", "metal1")
|
||||
self.poly_contact = contact.contact(layer_stack)
|
||||
|
||||
def add_well_tiedown(self, fet):
|
||||
offset = [fet.active_contact_positions[0][0],
|
||||
fet.active_contact_positions[0][1] + fet.well_height]
|
||||
fet.add_inst(name="well_tap",
|
||||
mod=self.well_contact,
|
||||
offset=offset,
|
||||
mirror="R0",
|
||||
rotate=0)
|
||||
fet.well_contact = self.well_contact
|
||||
fet.well_tiedown_location = offset
|
||||
|
||||
def add_well_extension(self, fet):
|
||||
well_define = {"pmos": "nwell",
|
||||
"nmos": "pwell"}
|
||||
well_type = well_define[fet.tx_type]
|
||||
offset = getattr(fet,"{}_position".format(well_type))
|
||||
if tech.info["has_{0}".format(well_type)]:
|
||||
fet.add_rect(layerNumber=tech.layer[well_type],
|
||||
offset=offset,
|
||||
width=fet.well_width,
|
||||
height=2 * fet.well_height)
|
||||
fet.add_rect(layerNumber=tech.layer["{0}implant".format(fet.tx_type[0])],
|
||||
offset=offset,
|
||||
width=fet.well_width,
|
||||
height=2 * fet.well_height)
|
||||
fet.add_rect(layerNumber=tech.layer["vtg"],
|
||||
offset=offset,
|
||||
width=fet.well_width,
|
||||
height=2 * fet.well_height)
|
||||
|
||||
well_type = "{0}well".format(fet.tx_type[0])
|
||||
offset[1] = offset[1] - 3 * fet.well_height
|
||||
if tech.info["has_{0}".format(well_type)]:
|
||||
fet.add_rect(layerNumber=tech.layer[well_type],
|
||||
offset=offset,
|
||||
width=fet.well_width,
|
||||
height=3 * fet.well_height)
|
||||
fet.add_rect(layerNumber=tech.layer["{0}implant".format(well_define[fet.tx_type][
|
||||
0])],
|
||||
offset=offset,
|
||||
width=fet.well_width,
|
||||
height=3 * fet.well_height)
|
||||
fet.add_rect(layerNumber=tech.layer["vtg"],
|
||||
offset=offset,
|
||||
width=fet.well_width,
|
||||
height=3 * fet.well_height)
|
||||
|
||||
def add_wire_extension(self, fet):
|
||||
xcorrect = (fet.active_contact.width / 2) - (tech.drc["minwidth_metal1"] / 2)
|
||||
offset = [fet.active_contact_positions[0][0] + xcorrect,
|
||||
fet.active_contact_positions[0][1]]
|
||||
fet.add_rect(layerNumber=tech.layer["metal1"],
|
||||
offset=offset,
|
||||
width=tech.drc["minwidth_metal1"],
|
||||
height=fet.well_height)
|
||||
|
||||
offset = [fet.active_contact_positions[-1][0] + xcorrect,
|
||||
fet.active_contact_positions[-1][1] - 2 * fet.well_height]
|
||||
fet.add_rect(layerNumber=tech.layer["metal1"],
|
||||
offset=offset,
|
||||
width=tech.drc["minwidth_metal1"],
|
||||
height=2 * fet.well_height)
|
||||
|
||||
offset = [fet.poly_positions[-1][0],
|
||||
fet.poly_positions[-1][1] - (fet.well_height)]
|
||||
fet.add_rect(layerNumber=tech.layer["poly"],
|
||||
offset=offset,
|
||||
width=tech.drc["minwidth_poly"],
|
||||
height=fet.well_height)
|
||||
|
||||
def add_poly_tiedown(self, fet):
|
||||
xcorrect = abs(self.poly_contact.upper_layer_vertical_enclosure -
|
||||
self.poly_contact.lower_layer_vertical_enclosure)
|
||||
offset = [fet.poly_positions[-1][0] - xcorrect,
|
||||
fet.poly_positions[-1][1] - (fet.well_height)]
|
||||
fet.add_inst(name="poly_contact",
|
||||
mod=self.poly_contact,
|
||||
offset=offset,
|
||||
mirror="R270")
|
||||
|
||||
|
||||
offset = [fet.active_contact_positions[-1][0], fet.active_contact_positions
|
||||
[-1][1] - 2 * fet.well_height - self.well_contact.height]
|
||||
fet.poly_tiedown_location = offset
|
||||
fet.add_inst(name="n_tiedown",
|
||||
mod=self.well_contact,
|
||||
offset=offset)
|
||||
tech.ptx_port.add_custom_layer(fet)
|
||||
|
||||
def local_check(self, fet):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
fet.sp_write(tempspice)
|
||||
fet.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(fet.name, tempgds))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# instantiate a copy 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()
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#!/usr/bin/env python2.7
|
||||
"""
|
||||
Run regresion tests on a parameterized bitcell
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,openram_test
|
||||
import sys,os
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
import debug
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 04_pbitcell_test")
|
||||
|
||||
|
||||
class pbitcell_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import pbitcell
|
||||
import tech
|
||||
|
||||
debug.info(2, "Bitcell with 1 of each port: read/write, write, and read")
|
||||
tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=1)
|
||||
self.local_check(tx)
|
||||
|
||||
debug.info(2, "Bitcell with 0 read/write ports")
|
||||
tx = pbitcell.pbitcell(num_readwrite=0,num_write=1,num_read=1)
|
||||
self.local_check(tx)
|
||||
|
||||
debug.info(2, "Bitcell with 0 write ports")
|
||||
tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=1)
|
||||
self.local_check(tx)
|
||||
|
||||
debug.info(2, "Bitcell with 0 read ports")
|
||||
tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=0)
|
||||
self.local_check(tx)
|
||||
|
||||
debug.info(2, "Bitcell with 0 read ports and 0 write ports")
|
||||
tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=0)
|
||||
self.local_check(tx)
|
||||
|
||||
debug.info(2, "Bitcell with 2 of each port: read/write, write, and read")
|
||||
tx = pbitcell.pbitcell(num_readwrite=2,num_write=2,num_read=2)
|
||||
self.local_check(tx)
|
||||
|
||||
debug.info(2, "Bitcell with 0 read/write ports")
|
||||
tx = pbitcell.pbitcell(num_readwrite=0,num_write=2,num_read=2)
|
||||
self.local_check(tx)
|
||||
|
||||
debug.info(2, "Bitcell with 0 write ports")
|
||||
tx = pbitcell.pbitcell(num_readwrite=2,num_write=0,num_read=2)
|
||||
self.local_check(tx)
|
||||
|
||||
debug.info(2, "Bitcell with 0 read ports")
|
||||
tx = pbitcell.pbitcell(num_readwrite=2,num_write=2,num_read=0)
|
||||
self.local_check(tx)
|
||||
|
||||
debug.info(2, "Bitcell with 0 read ports and 0 write ports")
|
||||
tx = pbitcell.pbitcell(num_readwrite=2,num_write=0,num_read=0)
|
||||
self.local_check(tx)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
OPTS.bitcell = "bitcell"
|
||||
|
||||
|
||||
# instantiate a copy 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()
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Run a regression test on a basic array
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,openram_test
|
||||
import sys,os
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
import debug
|
||||
|
||||
#@unittest.skip("SKIPPING 05_array_multiport_test")
|
||||
|
||||
class array_multiport_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import bitcell_array
|
||||
|
||||
OPTS.bitcell = "pbitcell"
|
||||
OPTS.rw_ports = 2
|
||||
OPTS.r_ports = 2
|
||||
OPTS.w_ports = 2
|
||||
|
||||
debug.info(2, "Testing 4x4 array for multiport bitcell, with read ports at the edge of the bit cell")
|
||||
a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4)
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.rw_ports = 2
|
||||
OPTS.r_ports = 0
|
||||
OPTS.w_ports = 2
|
||||
|
||||
debug.info(2, "Testing 4x4 array for multiport bitcell, with read/write ports at the edge of the bit cell")
|
||||
a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4)
|
||||
self.local_check(a)
|
||||
|
||||
|
||||
OPTS.bitcell = "bitcell"
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
# instantiate a copy 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()
|
||||
|
|
@ -45,7 +45,7 @@ class timing_sram_test(openram_test):
|
|||
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
d = delay.delay(s,tempspice,corner)
|
||||
d = delay(s,tempspice,corner)
|
||||
import tech
|
||||
loads = [tech.spice["msflop_in_cap"]*4]
|
||||
slews = [tech.spice["rise_time"]*2]
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class timing_setup_test(openram_test):
|
|||
slews = [tech.spice["rise_time"]*2]
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
sh = setup_hold.setup_hold(corner)
|
||||
sh = setup_hold(corner)
|
||||
data = sh.analyze(slews,slews)
|
||||
#print data
|
||||
if OPTS.tech_name == "freepdk45":
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class timing_sram_test(openram_test):
|
|||
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
d = delay.delay(s,tempspice,corner)
|
||||
d = delay(s,tempspice,corner)
|
||||
import tech
|
||||
loads = [tech.spice["msflop_in_cap"]*4]
|
||||
slews = [tech.spice["rise_time"]*2]
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class timing_setup_test(openram_test):
|
|||
slews = [tech.spice["rise_time"]*2]
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
sh = setup_hold.setup_hold(corner)
|
||||
sh = setup_hold(corner)
|
||||
data = sh.analyze(slews,slews)
|
||||
#print data
|
||||
if OPTS.tech_name == "freepdk45":
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class lib_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
lib.lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=True)
|
||||
lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=True)
|
||||
|
||||
# get all of the .lib files generated
|
||||
files = os.listdir(OPTS.openram_temp)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class lib_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
lib.lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False)
|
||||
lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False)
|
||||
|
||||
# get all of the .lib files generated
|
||||
files = os.listdir(OPTS.openram_temp)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class lib_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
lib.lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False)
|
||||
lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False)
|
||||
|
||||
# get all of the .lib files generated
|
||||
files = os.listdir(OPTS.openram_temp)
|
||||
|
|
|
|||
|
|
@ -91,25 +91,25 @@ class openram_test(unittest.TestCase):
|
|||
import re
|
||||
import debug
|
||||
|
||||
numeric_const_pattern = r"""
|
||||
[-+]? # optional sign
|
||||
(?:
|
||||
(?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc
|
||||
|
|
||||
(?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc
|
||||
)
|
||||
# followed by optional exponent part if desired
|
||||
(?: [Ee] [+-]? \d+ ) ?
|
||||
"""
|
||||
rx = re.compile(numeric_const_pattern, re.VERBOSE)
|
||||
with open(f1, 'rb') as fp1, open(f2, 'rb') as fp2:
|
||||
while True:
|
||||
b1 = fp1.readline()
|
||||
b2 = fp2.readline()
|
||||
b1 = fp1.readline().decode('utf-8')
|
||||
b2 = fp2.readline().decode('utf-8')
|
||||
#print "b1:",b1,
|
||||
#print "b2:",b2,
|
||||
|
||||
# 1. Find all of the floats using a regex
|
||||
numeric_const_pattern = r"""
|
||||
[-+]? # optional sign
|
||||
(?:
|
||||
(?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc
|
||||
|
|
||||
(?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc
|
||||
)
|
||||
# followed by optional exponent part if desired
|
||||
(?: [Ee] [+-]? \d+ ) ?
|
||||
"""
|
||||
rx = re.compile(numeric_const_pattern, re.VERBOSE)
|
||||
b1_floats=rx.findall(b1)
|
||||
b2_floats=rx.findall(b2)
|
||||
debug.info(3,"b1_floats: "+str(b1_floats))
|
||||
|
|
@ -117,9 +117,9 @@ class openram_test(unittest.TestCase):
|
|||
|
||||
# 2. Remove the floats from the string
|
||||
for f in b1_floats:
|
||||
b1=b1.replace(str(f),"",1)
|
||||
b1=b1.replace(f,"",1)
|
||||
for f in b2_floats:
|
||||
b2=b2.replace(str(f),"",1)
|
||||
b2=b2.replace(f,"",1)
|
||||
#print "b1:",b1,
|
||||
#print "b2:",b2,
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ def run_drc(cell_name, gds_name):
|
|||
|
||||
# write the runset file
|
||||
f = open(OPTS.openram_temp + "drc_runset", "w")
|
||||
for k in sorted(drc_runset.iterkeys()):
|
||||
for k in sorted(iter(drc_runset.keys())):
|
||||
f.write("*{0}: {1}\n".format(k, drc_runset[k]))
|
||||
f.close()
|
||||
|
||||
|
|
@ -177,7 +177,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
|
|||
|
||||
# write the runset file
|
||||
f = open(OPTS.openram_temp + "lvs_runset", "w")
|
||||
for k in sorted(lvs_runset.iterkeys()):
|
||||
for k in sorted(iter(lvs_runset.keys())):
|
||||
f.write("*{0}: {1}\n".format(k, lvs_runset[k]))
|
||||
f.close()
|
||||
|
||||
|
|
@ -286,7 +286,7 @@ def run_pex(cell_name, gds_name, sp_name, output=None):
|
|||
|
||||
# write the runset file
|
||||
f = open(OPTS.openram_temp + "pex_runset", "w")
|
||||
for k in sorted(pex_runset.iterkeys()):
|
||||
for k in sorted(iter(pex_runset.keys())):
|
||||
f.write("*{0}: {1}\n".format(k, pex_runset[k]))
|
||||
f.close()
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue