mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'master' of https://github.com/mguthaus/OpenRAM into multiport
This commit is contained in:
commit
ce83b67350
2
LICENSE
2
LICENSE
|
|
@ -1,4 +1,4 @@
|
|||
Copyright 2017 Regents of the University of California and The Board
|
||||
Copyright 2018 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)
|
||||
|
||||
|
|
|
|||
61
README.md
61
README.md
|
|
@ -7,9 +7,14 @@ 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 numpy
|
||||
* a setup script for each technology
|
||||
* a technology directory for each technology with the base cells
|
||||
|
||||
If you want to perform DRC and LVS, you will need either:
|
||||
* Calibre (for FreePDK45 or SCMOS)
|
||||
* Magic + Netgen (for SCMOS only)
|
||||
|
||||
You must set two environment variables: OPENRAM_HOME should point to
|
||||
the compiler source directory. OPENERAM_TECH should point to a root
|
||||
technology directory that contains subdirs of all other technologies.
|
||||
|
|
@ -36,6 +41,12 @@ For example, in csh/tcsh, add to your .tcshrc:
|
|||
We do not distribute the PDK, but you may get it from:
|
||||
https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
|
||||
|
||||
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:
|
||||
http://opencircuitdesign.com/qflow/
|
||||
|
||||
# DIRECTORY STRUCTURE
|
||||
|
||||
|
|
@ -120,27 +131,34 @@ This is where simulations and DRC/LVS get run so there is no network
|
|||
traffic. The directory name is unique for each person and run of
|
||||
OpenRAM to not clobber any files and allow simultaneous runs. If it
|
||||
passes, the files are deleted. If it fails, you will see these files:
|
||||
* _calibreDRC.rul_ is the DRC rule file.
|
||||
* dc_runset is the command file for caliber.
|
||||
* temp.gds is the layout
|
||||
* test1.drc.err is the std err output of the command
|
||||
* test1.drc.out is the standard output of the command
|
||||
* test1.drc.db is the DRC results file
|
||||
* (.mag files if using SCMOS)
|
||||
* temp.sp is the netlist
|
||||
* test1.drc.err is the std err output of the DRC command
|
||||
* test1.drc.out is the standard output of the DRC command
|
||||
* test1.drc.results is the DRC results file
|
||||
* test1.lvs.err is the std err output of the LVS command
|
||||
* test1.lvs.out is the standard output of the LVS command
|
||||
* test1.lvs.results is the DRC results file
|
||||
|
||||
Depending on your DRC/LVS tools, there will also be:
|
||||
* _calibreDRC.rul_ is the DRC rule file (Calibre)
|
||||
* dc_runset is the command file (Calibre)
|
||||
* extracted.sp (Calibre)
|
||||
* run_lvs.sh is a Netgen script for LVS (Netgen)
|
||||
* run_drc.sh is a Magic script for DRC (Magic)
|
||||
* <topcell>.spice (Magic)
|
||||
|
||||
If DRC/LVS fails, the first thing is to check if it ran in the .out and
|
||||
.err file. This shows the standard output and error output from
|
||||
running DRC/LVS. If there is a setup problem it will be shown here.
|
||||
|
||||
If DRC/LVS runs, but doesn't pass, you then should look at the .db
|
||||
If DRC/LVS runs, but doesn't pass, you then should look at the .results
|
||||
file. If the DRC fails, it will typically show you the command that was used
|
||||
to run caliber. It is something like this:
|
||||
```
|
||||
calibre -gui -drc /tmp/openram_mrg_28781_temp/drc_runset -batch 2>
|
||||
/tmp/openram_mrg_28781_temp/test1.drc.err 1>
|
||||
/tmp/openram_mrg_28781_temp/test1.drc.out
|
||||
```
|
||||
To debug, you will need a layout viewer. I prefer to use glade on my
|
||||
Mac, but you can also use Calibre, Magic, etc.
|
||||
to run Calibre or Magic+Netgen.
|
||||
|
||||
To debug, you will need a layout viewer. I prefer to use Glade
|
||||
on my Mac, but you can also use Calibre, Magic, etc.
|
||||
|
||||
1. Calibre
|
||||
|
||||
|
|
@ -184,12 +202,19 @@ ui().importCds("default",
|
|||
between processes, you have to change the importCds command (or you
|
||||
can manually run the command each time you start glade).
|
||||
|
||||
To load the errors, you simply do Verify->Import Caliber Errors select
|
||||
the .db file from calibre.
|
||||
To load the errors, you simply do Verify->Import Calibre Errors select
|
||||
the .results file from Calibre.
|
||||
|
||||
3. It is possible to use other viewers as well, such as:
|
||||
3. Magic
|
||||
|
||||
Magic is only supported in SCMOS. You will need to install the MOSIS SCMOS rules
|
||||
and Magic from: http://opencircuitdesign.com/
|
||||
|
||||
When running DRC or extraction, OpenRAM will load the GDS file, save
|
||||
the .ext/.mag files, and export an extracted netlist (.spice).
|
||||
|
||||
4. It is possible to use other viewers as well, such as:
|
||||
* LayoutEditor http://www.layouteditor.net/
|
||||
* Magic http://opencircuitdesign.com/magic/
|
||||
|
||||
|
||||
# Example to output/input .gds layout files from/to Cadence
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
CUR_DIR = $(shell pwd)
|
||||
TEST_DIR = ${CUR_DIR}/tests
|
||||
|
||||
MAKEFLAGS += -j 2
|
||||
|
||||
# Library test
|
||||
LIBRARY_TESTS = \
|
||||
01_library_drc_test.py \
|
||||
02_library_lvs_test.py
|
||||
|
||||
# Technology and DRC tests (along with ptx)
|
||||
TECH_TESTS = \
|
||||
03_contact_test.py \
|
||||
03_ptx_1finger_pmos_test.py \
|
||||
03_ptx_4finger_nmos_test.py \
|
||||
03_path_test.py \
|
||||
03_ptx_3finger_nmos_test.py \
|
||||
03_ptx_4finger_pmos_test.py \
|
||||
03_ptx_1finger_nmos_test.py \
|
||||
03_ptx_3finger_pmos_test.py \
|
||||
03_wire_test.py
|
||||
|
||||
# Parameterized cells
|
||||
PCELLS_TESTS = \
|
||||
04_pinv_1x_test.py \
|
||||
04_pinv_1x_beta_test.py \
|
||||
04_pinv_2x_test.py \
|
||||
04_pinv_10x_test.py \
|
||||
04_pnand2_test.py \
|
||||
04_pnor2_test.py \
|
||||
04_pnand3_test.py\
|
||||
04_wordline_driver_test.py \
|
||||
04_precharge_test.py
|
||||
|
||||
# Dynamically generated modules and arrays
|
||||
MODULE_TESTS = \
|
||||
05_bitcell_array_test.py \
|
||||
06_hierarchical_decoder_test.py \
|
||||
06_hierarchical_predecode2x4_test.py \
|
||||
06_hierarchical_predecode3x8_test.py \
|
||||
07_single_level_column_mux_array_test.py \
|
||||
08_precharge_array_test.py \
|
||||
09_sense_amp_array_test.py \
|
||||
10_write_driver_array_test.py \
|
||||
11_ms_flop_array_test.py \
|
||||
12_tri_gate_array_test.py \
|
||||
13_delay_chain_test.py \
|
||||
14_replica_bitline_test.py \
|
||||
16_control_logic_test.py
|
||||
|
||||
# Top-level SRAM configurations
|
||||
TOP_TESTS = \
|
||||
19_multi_bank_test.py \
|
||||
19_single_bank_test.py \
|
||||
20_sram_1bank_test.py \
|
||||
20_sram_2bank_test.py \
|
||||
20_sram_4bank_test.py
|
||||
|
||||
# All simulation tests.
|
||||
CHAR_TESTS = \
|
||||
21_hspice_delay_test.py \
|
||||
21_ngspice_delay_test.py \
|
||||
21_ngspice_setuphold_test.py \
|
||||
21_hspice_setuphold_test.py \
|
||||
22_sram_func_test.py \
|
||||
22_pex_func_test_with_pinv.py \
|
||||
23_lib_sram_prune_test.py \
|
||||
23_lib_sram_test.py
|
||||
|
||||
# Keep the model lib test here since it is fast
|
||||
# and doesn't need simulation.
|
||||
USAGE_TESTS = \
|
||||
23_lib_sram_model_test.py \
|
||||
24_lef_sram_test.py \
|
||||
25_verilog_sram_test.py
|
||||
|
||||
ALL_FILES = \
|
||||
${LIBRARY_TESTS} \
|
||||
${TECH_TESTS} \
|
||||
${PCELLS_TESTS} \
|
||||
${MODULES_TESTS} \
|
||||
${TOP_TESTS} \
|
||||
${CHAR_TESTS} \
|
||||
${USAGE_TESTS}
|
||||
|
||||
default all:
|
||||
|
||||
$(ALL_FILES):
|
||||
python ${TEST_DIR}/$@ -t freepdk45
|
||||
python ${TEST_DIR}/$@ -t scn3me_subm
|
||||
|
||||
# Library tests
|
||||
lib: ${LIBRARY_TESTS}
|
||||
|
||||
# Transistor and wire tests
|
||||
tech: ${TECH_TESTS}
|
||||
|
||||
# Dynamically generated cells
|
||||
pcells: ${PCELLS_TESTS}
|
||||
|
||||
# Dynamically generated modules
|
||||
modules: ${MODULES_TESTS}
|
||||
|
||||
# Top level SRAM tests
|
||||
top: ${TOP_TESTS}
|
||||
|
||||
# Timing and characterization tests
|
||||
char: ${CHAR_TESTS}
|
||||
|
||||
# Usage and file generation
|
||||
usage: ${USAGE_TESTS}
|
||||
|
||||
clean:
|
||||
find . -name \*.pyc -exec rm {} \;
|
||||
find . -name \*~ -exec rm {} \;
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ class bank(design.design):
|
|||
"bitcell_array", "sense_amp_array", "precharge_array",
|
||||
"column_mux_array", "write_driver_array", "tri_gate_array"]
|
||||
for mod_name in mod_list:
|
||||
config_mod_name = getattr(OPTS.config, mod_name)
|
||||
config_mod_name = getattr(OPTS, mod_name)
|
||||
class_file = reload(__import__(config_mod_name))
|
||||
mod_class = getattr(class_file , config_mod_name)
|
||||
setattr (self, "mod_"+mod_name, mod_class)
|
||||
|
|
@ -99,8 +99,10 @@ class bank(design.design):
|
|||
self.add_precharge_array()
|
||||
|
||||
if self.col_addr_size > 0:
|
||||
# The m2 width is because the 6T cell may have vias on the boundary edge for
|
||||
# overlapping when making the array
|
||||
self.column_mux_height = self.column_mux_array.height + 0.5*self.m2_width
|
||||
self.add_column_mux_array()
|
||||
self.column_mux_height = self.column_mux_array.height
|
||||
else:
|
||||
self.column_mux_height = 0
|
||||
if self.col_addr_size > 1: # size 1 is from addr FF
|
||||
|
|
@ -234,8 +236,8 @@ class bank(design.design):
|
|||
""" Adding Precharge """
|
||||
|
||||
# The wells must be far enough apart
|
||||
# We use two well spacings because the bitcells tend to have a shared rail in the height
|
||||
y_offset = self.bitcell_array.height + 2*drc["pwell_to_nwell"]
|
||||
# The enclosure is for the well and the spacig is to the bitcell wells
|
||||
y_offset = self.bitcell_array.height + 2*drc["pwell_to_nwell"] + drc["well_enclosure_active"]
|
||||
self.precharge_array_inst=self.add_inst(name="precharge_array",
|
||||
mod=self.precharge_array,
|
||||
offset=vector(0,y_offset))
|
||||
|
|
@ -249,7 +251,7 @@ class bank(design.design):
|
|||
def add_column_mux_array(self):
|
||||
""" Adding Column Mux when words_per_row > 1 . """
|
||||
|
||||
y_offset = self.column_mux_array.height
|
||||
y_offset = self.column_mux_height
|
||||
self.col_mux_array_inst=self.add_inst(name="column_mux_array",
|
||||
mod=self.column_mux_array,
|
||||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
|
|
@ -434,7 +436,8 @@ class bank(design.design):
|
|||
|
||||
|
||||
# Place the col decoder just to the left of the control bus
|
||||
x_off = self.m2_pitch + self.overall_central_bus_width + self.col_decoder.width
|
||||
gap = max(drc["pwell_to_nwell"], 2*self.m2_pitch)
|
||||
x_off = gap + self.overall_central_bus_width + self.col_decoder.width
|
||||
# Place the col decoder below the the address flops which are below the row decoder (lave some space for wells)
|
||||
vertical_gap = max(drc["pwell_to_nwell"], 2*self.m2_pitch)
|
||||
y_off = self.decoder.predecoder_height + self.msf_address.width + self.col_decoder.height + 2*vertical_gap
|
||||
|
|
|
|||
|
|
@ -21,12 +21,13 @@ class bitcell_array(design.design):
|
|||
self.column_size = cols
|
||||
self.row_size = rows
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.cell = self.mod_bitcell()
|
||||
self.add_mod(self.cell)
|
||||
|
||||
self.height = self.row_size*self.cell.height
|
||||
# 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.width = self.column_size*self.cell.width
|
||||
|
||||
self.add_pins()
|
||||
|
|
@ -115,8 +116,8 @@ class bitcell_array(design.design):
|
|||
gnd_pins = self.cell_inst[0,col].get_pins("gnd")
|
||||
for gnd_pin in gnd_pins:
|
||||
# avoid duplicates by only doing even rows
|
||||
# also skip if it is not the full height (a through rail)
|
||||
if gnd_pin.layer=="metal2" and col%2 == 0 and gnd_pin.height()>=self.cell.height:
|
||||
# also skip if it isn't the pin that spans the entire cell down to the bottom
|
||||
if gnd_pin.layer=="metal2" and gnd_pin.by()==lower_y:
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal2",
|
||||
offset=gnd_pin.ll(),
|
||||
|
|
|
|||
|
|
@ -8,22 +8,22 @@ import setup_hold
|
|||
|
||||
debug.info(2,"Initializing characterizer...")
|
||||
|
||||
spice_exe = ""
|
||||
OPTS.spice_exe = ""
|
||||
|
||||
if not OPTS.analytical_delay:
|
||||
if OPTS.spice_name != "":
|
||||
spice_exe=find_exe(OPTS.spice_name)
|
||||
if spice_exe=="":
|
||||
OPTS.spice_exe=find_exe(OPTS.spice_name)
|
||||
if OPTS.spice_exe=="":
|
||||
debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_name),1)
|
||||
else:
|
||||
(choice,spice_exe) = get_tool("spice",["xa", "hspice", "ngspice", "ngspice.exe"])
|
||||
OPTS.spice_name = choice
|
||||
(OPTS.spice_name,OPTS.spice_exe) = get_tool("spice",["xa", "hspice", "ngspice", "ngspice.exe"])
|
||||
|
||||
# set the input dir for spice files if using ngspice
|
||||
if OPTS.spice_name == "ngspice":
|
||||
os.environ["NGSPICE_INPUT_DIR"] = "{0}".format(OPTS.openram_temp)
|
||||
|
||||
if spice_exe == "":
|
||||
if OPTS.spice_exe == "":
|
||||
debug.error("No recognizable spice version found. Unable to perform characterization.",1)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from globals import OPTS
|
|||
|
||||
class delay():
|
||||
"""
|
||||
Functions to measure the delay of the SRAM at a given address and
|
||||
Functions to measure the delay of an SRAM at a given address and
|
||||
data bit.
|
||||
"""
|
||||
|
||||
|
|
@ -40,9 +40,9 @@ class delay():
|
|||
|
||||
|
||||
def write_stimulus(self, period, load, slew):
|
||||
"""Creates a stimulus file for simulations to probe a certain bitcell, given an address and data-position of the data-word
|
||||
(probe-address form: '111010000' LSB=0, MSB=1)
|
||||
(probe_data form: number corresponding to the bit position of data-bus, begins with position 0)
|
||||
""" Creates a stimulus file for simulations to probe a bitcell at a given clock period.
|
||||
Address and bit were previously set with set_probe().
|
||||
Input slew (in ns) and output capacitive load (in fF) are required for charaterization.
|
||||
"""
|
||||
self.check_arguments()
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ class delay():
|
|||
# creates and opens stimulus file for writing
|
||||
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
||||
self.sf = open(temp_stim, "w")
|
||||
self.sf.write("* Stimulus for period of {0}n load={1} slew={2}\n\n".format(period,load,slew))
|
||||
self.sf.write("* Stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(period,load,slew))
|
||||
|
||||
# include files in stimulus file
|
||||
model_list = tech.spice["fet_models"] + [self.sram_sp_file]
|
||||
|
|
@ -60,76 +60,83 @@ class delay():
|
|||
|
||||
# add vdd/gnd statements
|
||||
|
||||
self.sf.write("* Global Power Supplies\n")
|
||||
self.sf.write("\n* Global Power Supplies\n")
|
||||
stimuli.write_supply(self.sf)
|
||||
|
||||
# instantiate the sram
|
||||
self.sf.write("* Instantiation of the SRAM\n")
|
||||
self.sf.write("\n* Instantiation of the SRAM\n")
|
||||
stimuli.inst_sram(stim_file=self.sf,
|
||||
abits=self.addr_size,
|
||||
dbits=self.word_size,
|
||||
sram_name=self.name)
|
||||
|
||||
self.sf.write("* SRAM output loads\n")
|
||||
self.sf.write("\n* SRAM output loads\n")
|
||||
for i in range(self.word_size):
|
||||
self.sf.write("CD{0} d[{0}] 0 {1}f\n".format(i,load))
|
||||
|
||||
# add access transistors for data-bus
|
||||
self.sf.write("* Transmission Gates for data-bus and control signals\n")
|
||||
self.sf.write("\n* Transmission Gates for data-bus and control signals\n")
|
||||
stimuli.inst_accesstx(stim_file=self.sf, dbits=self.word_size)
|
||||
|
||||
# generate data and addr signals
|
||||
self.sf.write("* Generation of data and address signals\n")
|
||||
self.sf.write("\n* Generation of data and address signals\n")
|
||||
for i in range(self.word_size):
|
||||
if i == self.probe_data:
|
||||
stimuli.gen_data(stim_file=self.sf,
|
||||
clk_times=self.cycle_times,
|
||||
sig_name="data[{0}]".format(i),
|
||||
period=period,
|
||||
slew=slew)
|
||||
self.gen_data(clk_times=self.cycle_times,
|
||||
sig_name="data[{0}]".format(i),
|
||||
period=period,
|
||||
slew=slew)
|
||||
else:
|
||||
stimuli.gen_constant(stim_file=self.sf,
|
||||
sig_name="d[{0}]".format(i),
|
||||
v_val=self.gnd)
|
||||
|
||||
stimuli.gen_addr(self.sf,
|
||||
clk_times=self.cycle_times,
|
||||
self.gen_addr(clk_times=self.cycle_times,
|
||||
addr=self.probe_address,
|
||||
period=period,
|
||||
slew=slew)
|
||||
|
||||
# generate control signals
|
||||
self.sf.write("* Generation of control signals\n")
|
||||
stimuli.gen_csb(self.sf, self.cycle_times, period, slew)
|
||||
stimuli.gen_web(self.sf, self.cycle_times, period, slew)
|
||||
stimuli.gen_oeb(self.sf, self.cycle_times, period, slew)
|
||||
self.sf.write("\n* Generation of control signals\n")
|
||||
self.gen_csb(self.cycle_times, period, slew)
|
||||
self.gen_web(self.cycle_times, period, slew)
|
||||
self.gen_oeb(self.cycle_times, period, slew)
|
||||
|
||||
self.sf.write("* Generation of global clock signal\n")
|
||||
self.sf.write("\n* Generation of global clock signal\n")
|
||||
stimuli.gen_pulse(stim_file=self.sf,
|
||||
sig_name="CLK",
|
||||
v1=self.gnd,
|
||||
v2=self.vdd,
|
||||
offset=period,
|
||||
period=period,
|
||||
t_rise = slew,
|
||||
t_fall = slew)
|
||||
t_rise=slew,
|
||||
t_fall=slew)
|
||||
|
||||
self.write_measures(period)
|
||||
|
||||
# run until the last cycle time
|
||||
stimuli.write_control(self.sf,self.cycle_times[-1])
|
||||
# run until the end of the cycle time
|
||||
stimuli.write_control(self.sf,self.cycle_times[-1] + period)
|
||||
|
||||
self.sf.close()
|
||||
|
||||
def write_measures(self,period):
|
||||
# meas statement for delay and power measurements
|
||||
self.sf.write("* Measure statements for delay and power\n")
|
||||
"""
|
||||
Write the measure statements to quantify the delay and power results.
|
||||
"""
|
||||
|
||||
self.sf.write("\n* Measure statements for delay and power\n")
|
||||
|
||||
# Output some comments to aid where cycles start and
|
||||
# what is happening
|
||||
for comment in self.cycle_comments:
|
||||
self.sf.write("* {}\n".format(comment))
|
||||
|
||||
# Trigger on the clk of the appropriate cycle
|
||||
trig_name = "clk"
|
||||
targ_name = "{0}".format("d[{0}]".format(self.probe_data))
|
||||
trig_val = targ_val = 0.5 * self.vdd
|
||||
# add measure statments for delay0
|
||||
# delay the target to measure after the negetive edge
|
||||
|
||||
# Delay the target to measure after the negative edge
|
||||
stimuli.gen_meas_delay(stim_file=self.sf,
|
||||
meas_name="DELAY0",
|
||||
trig_name=trig_name,
|
||||
|
|
@ -138,7 +145,8 @@ class delay():
|
|||
targ_val=targ_val,
|
||||
trig_dir="FALL",
|
||||
targ_dir="FALL",
|
||||
td=self.cycle_times[self.read0_cycle]+0.5*period)
|
||||
trig_td=self.cycle_times[self.read0_cycle],
|
||||
targ_td=self.cycle_times[self.read0_cycle]+0.5*period)
|
||||
|
||||
stimuli.gen_meas_delay(stim_file=self.sf,
|
||||
meas_name="DELAY1",
|
||||
|
|
@ -148,7 +156,8 @@ class delay():
|
|||
targ_val=targ_val,
|
||||
trig_dir="FALL",
|
||||
targ_dir="RISE",
|
||||
td=self.cycle_times[self.read1_cycle]+0.5*period)
|
||||
trig_td=self.cycle_times[self.read1_cycle],
|
||||
targ_td=self.cycle_times[self.read1_cycle]+0.5*period)
|
||||
|
||||
stimuli.gen_meas_delay(stim_file=self.sf,
|
||||
meas_name="SLEW0",
|
||||
|
|
@ -158,7 +167,8 @@ class delay():
|
|||
targ_val=0.1*self.vdd,
|
||||
trig_dir="FALL",
|
||||
targ_dir="FALL",
|
||||
td=self.cycle_times[self.read0_cycle]+0.5*period)
|
||||
trig_td=self.cycle_times[self.read0_cycle],
|
||||
targ_td=self.cycle_times[self.read0_cycle]+0.5*period)
|
||||
|
||||
stimuli.gen_meas_delay(stim_file=self.sf,
|
||||
meas_name="SLEW1",
|
||||
|
|
@ -168,7 +178,8 @@ class delay():
|
|||
targ_val=0.9*self.vdd,
|
||||
trig_dir="RISE",
|
||||
targ_dir="RISE",
|
||||
td=self.cycle_times[self.read1_cycle]+0.5*period)
|
||||
trig_td=self.cycle_times[self.read1_cycle],
|
||||
targ_td=self.cycle_times[self.read1_cycle]+0.5*period)
|
||||
|
||||
# add measure statements for power
|
||||
t_initial = self.cycle_times[self.write0_cycle]
|
||||
|
|
@ -200,11 +211,13 @@ class delay():
|
|||
t_final=t_final)
|
||||
|
||||
def find_feasible_period(self, load, slew):
|
||||
"""Uses an initial period and finds a feasible period before we
|
||||
"""
|
||||
Uses an initial period and finds a feasible period before we
|
||||
run the binary search algorithm to find min period. We check if
|
||||
the given clock period is valid and if it's not, we continue to
|
||||
double the period until we find a valid period to use as a
|
||||
starting point. """
|
||||
starting point.
|
||||
"""
|
||||
|
||||
feasible_period = tech.spice["feasible_period"]
|
||||
time_out = 8
|
||||
|
|
@ -220,13 +233,19 @@ class delay():
|
|||
feasible_period = 2 * feasible_period
|
||||
continue
|
||||
|
||||
debug.info(1, "Found feasible_period: {0}ns feasible_delay1/0 {1}ns/{2}ns slew {3}ns/{4}ns".format(feasible_period,feasible_delay1,feasible_delay0,feasible_slew1,feasible_slew0))
|
||||
debug.info(1, "Found feasible_period: {0}ns feasible_delay1/0 {1}ns/{2}ns slew {3}ns/{4}ns".format(feasible_period,
|
||||
feasible_delay1,
|
||||
feasible_delay0,
|
||||
feasible_slew1,
|
||||
feasible_slew0))
|
||||
return (feasible_period, feasible_delay1, feasible_delay0)
|
||||
|
||||
|
||||
def run_simulation(self, period, load, slew):
|
||||
""" This tries to simulate a period and checks if the result
|
||||
works. If so, it returns True and the delays and slews."""
|
||||
"""
|
||||
This tries to simulate a period and checks if the result
|
||||
works. If so, it returns True and the delays and slews.
|
||||
"""
|
||||
|
||||
# Checking from not data_value to data_value
|
||||
self.write_stimulus(period, load, slew)
|
||||
|
|
@ -238,15 +257,37 @@ class delay():
|
|||
|
||||
# if it failed or the read was longer than a period
|
||||
if type(delay0)!=float or type(delay1)!=float or type(slew1)!=float or type(slew0)!=float:
|
||||
debug.info(2,"Failed simulation: period {0} load {1} slew {2}, delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period,
|
||||
load,
|
||||
slew,
|
||||
delay0,
|
||||
delay1,
|
||||
slew0,
|
||||
slew1))
|
||||
return (False,0,0,0,0)
|
||||
# Scale delays to ns (they previously could have not been floats)
|
||||
delay0 *= 1e9
|
||||
delay1 *= 1e9
|
||||
slew0 *= 1e9
|
||||
slew1 *= 1e9
|
||||
if delay0>period or delay1>period or slew0>period or slew1>period:
|
||||
debug.info(2,"UNsuccessful simulation: period {0} load {1} slew {2}, delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period,
|
||||
load,
|
||||
slew,
|
||||
delay0,
|
||||
delay1,
|
||||
slew0,
|
||||
slew1))
|
||||
return (False,0,0,0,0)
|
||||
else:
|
||||
debug.info(2,"Successful simulation: period {0} load {1} slew {2}, delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period,load,slew,delay0,delay1,slew0,slew1))
|
||||
debug.info(2,"Successful simulation: period {0} load {1} slew {2}, delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period,
|
||||
load,
|
||||
slew,
|
||||
delay0,
|
||||
delay1,
|
||||
slew0,
|
||||
slew1))
|
||||
# For debug, you sometimes want to inspect each simulation.
|
||||
#key=raw_input("press return to continue")
|
||||
|
||||
# The delay is from the negative edge for our SRAM
|
||||
|
|
@ -255,8 +296,10 @@ class delay():
|
|||
|
||||
|
||||
def find_min_period(self,feasible_period, load, slew, feasible_delay1, feasible_delay0):
|
||||
"""Searches for the smallest period with output delays being within 5% of
|
||||
long period. """
|
||||
"""
|
||||
Searches for the smallest period with output delays being within 5% of
|
||||
long period.
|
||||
"""
|
||||
|
||||
previous_period = ub_period = feasible_period
|
||||
lb_period = 0.0
|
||||
|
|
@ -284,8 +327,10 @@ class delay():
|
|||
|
||||
|
||||
def try_period(self, period, load, slew, feasible_delay1, feasible_delay0):
|
||||
""" This tries to simulate a period and checks if the result
|
||||
works. If it does and the delay is within 5% still, it returns True."""
|
||||
"""
|
||||
This tries to simulate a period and checks if the result
|
||||
works. If it does and the delay is within 5% still, it returns True.
|
||||
"""
|
||||
|
||||
# Checking from not data_value to data_value
|
||||
self.write_stimulus(period,load,slew)
|
||||
|
|
@ -296,14 +341,22 @@ class delay():
|
|||
slew1 = ch.convert_to_float(ch.parse_output("timing", "slew1"))
|
||||
# if it failed or the read was longer than a period
|
||||
if type(delay0)!=float or type(delay1)!=float or type(slew1)!=float or type(slew0)!=float:
|
||||
debug.info(2,"Invalid measures: Period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1))
|
||||
debug.info(2,"Invalid measures: Period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period,
|
||||
delay0,
|
||||
delay1,
|
||||
slew0,
|
||||
slew1))
|
||||
return False
|
||||
delay0 *= 1e9
|
||||
delay1 *= 1e9
|
||||
slew0 *= 1e9
|
||||
slew1 *= 1e9
|
||||
if delay0>period or delay1>period or slew0>period or slew1>period:
|
||||
debug.info(2,"Too long delay/slew: Period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1))
|
||||
debug.info(2,"Too long delay/slew: Period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period,
|
||||
delay0,
|
||||
delay1,
|
||||
slew0,
|
||||
slew1))
|
||||
return False
|
||||
else:
|
||||
if not ch.relative_compare(delay1,feasible_delay1,error_tolerance=0.05):
|
||||
|
|
@ -316,7 +369,11 @@ class delay():
|
|||
|
||||
#key=raw_input("press return to continue")
|
||||
|
||||
debug.info(2,"Successful period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1))
|
||||
debug.info(2,"Successful period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period,
|
||||
delay0,
|
||||
delay1,
|
||||
slew0,
|
||||
slew1))
|
||||
return True
|
||||
|
||||
def set_probe(self,probe_address, probe_data):
|
||||
|
|
@ -364,7 +421,7 @@ class delay():
|
|||
for slew in slews:
|
||||
for load in loads:
|
||||
(success, delay1, slew1, delay0, slew0) = self.run_simulation(feasible_period, load, slew)
|
||||
debug.check(success,"Couldn't run a simulation properly.\n")
|
||||
debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(slew,load))
|
||||
LH_delay.append(delay1)
|
||||
HL_delay.append(delay0)
|
||||
LH_slew.append(slew1)
|
||||
|
|
@ -394,47 +451,96 @@ class delay():
|
|||
of the cycles to do a timing evaluation. The last time is the end of the simulation
|
||||
and does not need a rising edge."""
|
||||
|
||||
# idle cycle, no operation
|
||||
t_current = period
|
||||
self.cycle_comments = []
|
||||
self.cycle_times = []
|
||||
|
||||
# cycle0: W data 1 address 1111 to initialize cell to a value
|
||||
t_current = 0
|
||||
|
||||
# idle cycle, no operation
|
||||
msg = "Idle cycle (no clock)"
|
||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(0,
|
||||
t_current,
|
||||
msg))
|
||||
self.cycle_times.append(t_current)
|
||||
t_current += period
|
||||
|
||||
# cycle1: W data 0 address 1111 (to ensure a write of value works)
|
||||
# One period
|
||||
msg = "W data 1 address 11..11 to initialize cell"
|
||||
self.cycle_times.append(t_current)
|
||||
self.write0_cycle=1
|
||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
||||
t_current,
|
||||
msg))
|
||||
t_current += period
|
||||
|
||||
# One period
|
||||
msg = "W data 0 address 11..11 (to ensure a write of value works)"
|
||||
self.cycle_times.append(t_current)
|
||||
self.write0_cycle=len(self.cycle_times)-1
|
||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
||||
t_current,
|
||||
msg))
|
||||
t_current += period
|
||||
|
||||
# cycle2: W data 1 address 0000 (to clear the data bus cap)
|
||||
# One period
|
||||
msg = "W data 1 address 00..00 (to clear bus caps)"
|
||||
self.cycle_times.append(t_current)
|
||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
||||
t_current,
|
||||
msg))
|
||||
t_current += period
|
||||
|
||||
# One period
|
||||
msg = "R data 0 address 11..11 to check W0 worked"
|
||||
self.cycle_times.append(t_current)
|
||||
self.read0_cycle=len(self.cycle_times)-1
|
||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
||||
t_current,
|
||||
msg))
|
||||
t_current += period
|
||||
|
||||
# One period
|
||||
msg = "Idle cycle"
|
||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
||||
t_current,
|
||||
msg))
|
||||
self.cycle_times.append(t_current)
|
||||
t_current += period
|
||||
|
||||
# cycle3: R data 0 address 1111 to check W0 works
|
||||
# One period
|
||||
msg = "W data 1 address 11..11 (to ensure a write of value worked)"
|
||||
self.cycle_times.append(t_current)
|
||||
self.read0_cycle=3
|
||||
self.write1_cycle=len(self.cycle_times)-1
|
||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
||||
t_current,
|
||||
msg))
|
||||
t_current += period
|
||||
|
||||
# cycle4: W data 1 address 1111 (to ensure a write of value works)
|
||||
self.cycle_times.append(t_current)
|
||||
self.write1_cycle=4
|
||||
t_current += period
|
||||
|
||||
# cycle5: W data 0 address 0000 (to clear the data bus cap)
|
||||
# One period
|
||||
msg = "W data 0 address 00..00 (to clear bus caps)"
|
||||
self.cycle_times.append(t_current)
|
||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
||||
t_current,
|
||||
msg))
|
||||
t_current += period
|
||||
|
||||
# cycle6: R data 1 address 1111 to check W1 works
|
||||
# One period
|
||||
msg = "R data 1 address 11..11 to check W1 worked"
|
||||
self.cycle_times.append(t_current)
|
||||
self.read1_cycle=6
|
||||
self.read1_cycle=len(self.cycle_times)-1
|
||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
||||
t_current,
|
||||
msg))
|
||||
t_current += period
|
||||
|
||||
# cycle7: wait a clock period to end the simulation
|
||||
# One period
|
||||
msg = "Idle cycle"
|
||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
||||
t_current,
|
||||
msg))
|
||||
self.cycle_times.append(t_current)
|
||||
t_current += period
|
||||
|
||||
|
||||
|
||||
def analytical_model(self,sram, slews, loads):
|
||||
""" Just return the analytical model results for the SRAM.
|
||||
"""
|
||||
|
|
@ -463,3 +569,54 @@ class delay():
|
|||
}
|
||||
return data
|
||||
|
||||
def gen_data(self, clk_times, sig_name, period, slew):
|
||||
""" Generates the PWL data inputs for a simulation timing test. """
|
||||
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP
|
||||
# we are asserting the opposite value on the other side of the tx gate during
|
||||
# the read to be "worst case". Otherwise, it can actually assist the read.
|
||||
values = [0, 1, 0, 1, 1, 1, 1, 0, 0, 0 ]
|
||||
stimuli.gen_pwl(self.sf, sig_name, clk_times, values, period, slew, 0.05)
|
||||
|
||||
def gen_addr(self, clk_times, addr, period, slew):
|
||||
"""
|
||||
Generates the address inputs for a simulation timing test.
|
||||
This alternates between all 1's and all 0's for the address.
|
||||
"""
|
||||
|
||||
zero_values = [0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ]
|
||||
ones_values = [1, 1, 1, 0, 1, 0, 1, 0, 1, 1 ]
|
||||
|
||||
for i in range(len(addr)):
|
||||
sig_name = "A[{0}]".format(i)
|
||||
if addr[i]=="1":
|
||||
stimuli.gen_pwl(self.sf, sig_name, clk_times, ones_values, period, slew, 0.05)
|
||||
else:
|
||||
stimuli.gen_pwl(self.sf, sig_name, clk_times, zero_values, period, slew, 0.05)
|
||||
|
||||
|
||||
def gen_csb(self, clk_times, period, slew):
|
||||
""" Generates the PWL CSb signal """
|
||||
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP
|
||||
# Keep CSb asserted in NOP for measuring >1 period
|
||||
values = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
stimuli.gen_pwl(self.sf, "csb", clk_times, values, period, slew, 0.05)
|
||||
|
||||
def gen_web(self, clk_times, period, slew):
|
||||
""" Generates the PWL WEb signal """
|
||||
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP
|
||||
# Keep WEb deasserted in NOP for measuring >1 period
|
||||
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
|
||||
stimuli.gen_pwl(self.sf, "web", clk_times, values, period, slew, 0.05)
|
||||
|
||||
# Keep acc_en deasserted in NOP for measuring >1 period
|
||||
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
|
||||
stimuli.gen_pwl(self.sf, "acc_en", clk_times, values, period, slew, 0)
|
||||
values = [0, 1, 1, 1, 0, 0, 1, 1, 0, 0]
|
||||
stimuli.gen_pwl(self.sf, "acc_en_inv", clk_times, values, period, slew, 0)
|
||||
|
||||
def gen_oeb(self, clk_times, period, slew):
|
||||
""" Generates the PWL WEb signal """
|
||||
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
|
||||
# Keep OEb asserted in NOP for measuring >1 period
|
||||
values = [1, 1, 1, 1, 0, 0, 1, 1, 0, 0]
|
||||
stimuli.gen_pwl(self.sf, "oeb", clk_times, values, period, slew, 0.05)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
import os
|
||||
import sys
|
||||
import re
|
||||
import os,sys,re,shutil
|
||||
import debug
|
||||
import tech
|
||||
import math
|
||||
import setup_hold
|
||||
import delay
|
||||
|
|
@ -34,7 +31,9 @@ class lib:
|
|||
self.sram.word_size)
|
||||
else:
|
||||
# Else, use the non-reduced netlist file for simulation
|
||||
self.sim_sp_file = self.sp_file
|
||||
self.sim_sp_file = "{}sram.sp".format(OPTS.openram_temp)
|
||||
# Make a copy in temp for debugging
|
||||
shutil.copy(self.sp_file, self.sim_sp_file)
|
||||
|
||||
# These are the parameters to determine the table sizes
|
||||
#self.load_scales = np.array([0.1, 0.25, 0.5, 1, 2, 4, 8])
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class setup_hold():
|
|||
self.write_header(correct_value)
|
||||
|
||||
# instantiate the master-slave d-flip-flop
|
||||
self.sf.write("* Instantiation of the Master-Slave D-flip-flop\n")
|
||||
self.sf.write("\n* Instantiation of the Master-Slave D-flip-flop\n")
|
||||
stimuli.inst_model(stim_file=self.sf,
|
||||
pins=self.pins,
|
||||
model_name=self.model_name)
|
||||
|
|
@ -58,7 +58,7 @@ class setup_hold():
|
|||
|
||||
def write_header(self, correct_value):
|
||||
""" Write the header file with all the models and the power supplies. """
|
||||
self.sf.write("* Stimulus for setup/hold: data {0} period {1}n\n".format(correct_value, self.period))
|
||||
self.sf.write("\n* Stimulus for setup/hold: data {0} period {1}n\n".format(correct_value, self.period))
|
||||
|
||||
# include files in stimulus file
|
||||
self.model_list = tech.spice["fet_models"] + [self.model_location]
|
||||
|
|
@ -66,7 +66,7 @@ class setup_hold():
|
|||
models=self.model_list)
|
||||
|
||||
# add vdd/gnd statements
|
||||
self.sf.write("* Global Power Supplies\n")
|
||||
self.sf.write("\n* Global Power Supplies\n")
|
||||
stimuli.write_supply(self.sf)
|
||||
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ class setup_hold():
|
|||
characterization.
|
||||
|
||||
"""
|
||||
self.sf.write("* Generation of the data and clk signals\n")
|
||||
self.sf.write("\n* Generation of the data and clk signals\n")
|
||||
incorrect_value = stimuli.get_inverse_value(correct_value)
|
||||
if mode=="HOLD":
|
||||
init_value = incorrect_value
|
||||
|
|
@ -89,7 +89,7 @@ class setup_hold():
|
|||
|
||||
stimuli.gen_pwl(stim_file=self.sf,
|
||||
sig_name="data",
|
||||
clk_times=[self.period, target_time],
|
||||
clk_times=[0, self.period, target_time],
|
||||
data_values=[init_value, start_value, end_value],
|
||||
period=target_time,
|
||||
slew=self.constrained_input_slew,
|
||||
|
|
@ -105,7 +105,7 @@ class setup_hold():
|
|||
# without using .IC on an internal node.
|
||||
# Return input to value after one period.
|
||||
# The second pulse is the characterization one at 2*period
|
||||
clk_times=[0.1*self.period,self.period,2*self.period],
|
||||
clk_times=[0, 0.1*self.period,self.period,2*self.period],
|
||||
data_values=[0, 1, 0, 1],
|
||||
period=2*self.period,
|
||||
slew=self.constrained_input_slew,
|
||||
|
|
@ -132,7 +132,7 @@ class setup_hold():
|
|||
din_rise_or_fall = "RISE"
|
||||
|
||||
|
||||
self.sf.write("* Measure statements for pass/fail verification\n")
|
||||
self.sf.write("\n* Measure statements for pass/fail verification\n")
|
||||
trig_name = "clk"
|
||||
targ_name = "dout"
|
||||
trig_val = targ_val = 0.5 * self.vdd
|
||||
|
|
@ -145,7 +145,8 @@ class setup_hold():
|
|||
targ_val=targ_val,
|
||||
trig_dir="RISE",
|
||||
targ_dir=dout_rise_or_fall,
|
||||
td=1.9*self.period)
|
||||
trig_td=1.9*self.period,
|
||||
targ_td=1.9*self.period)
|
||||
|
||||
targ_name = "data"
|
||||
# Start triggers right after initialize value is returned to normal
|
||||
|
|
@ -158,7 +159,8 @@ class setup_hold():
|
|||
targ_val=targ_val,
|
||||
trig_dir="RISE",
|
||||
targ_dir=din_rise_or_fall,
|
||||
td=1.2*self.period)
|
||||
trig_td=1.2*self.period,
|
||||
targ_td=1.2*self.period)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"""
|
||||
This file generates the test structure and stimulus for an sram
|
||||
simulation. There are various functions that can be be used to
|
||||
generate stimulus for other simulations as well.
|
||||
This file generates simple spice cards for simulation. There are
|
||||
various functions that can be be used to generate stimulus for other
|
||||
simulations as well.
|
||||
"""
|
||||
|
||||
import tech
|
||||
|
|
@ -22,7 +22,7 @@ tx_width = tech.spice["minwidth_tx"]
|
|||
tx_length = tech.spice["channel"]
|
||||
|
||||
def inst_sram(stim_file, abits, dbits, sram_name):
|
||||
"""function to instatiate the sram subckt"""
|
||||
""" Function to instatiate an SRAM subckt. """
|
||||
stim_file.write("Xsram ")
|
||||
for i in range(dbits):
|
||||
stim_file.write("D[{0}] ".format(i))
|
||||
|
|
@ -32,11 +32,11 @@ def inst_sram(stim_file, abits, dbits, sram_name):
|
|||
stim_file.write("{0} ".format(i))
|
||||
stim_file.write("{0} ".format(tech.spice["clk"]))
|
||||
stim_file.write("{0} {1} ".format(vdd_name, gnd_name))
|
||||
stim_file.write("{0}\n\n".format(sram_name))
|
||||
stim_file.write("{0}\n".format(sram_name))
|
||||
|
||||
|
||||
def inst_model(stim_file, pins, model_name):
|
||||
"""function to instantiate a model"""
|
||||
""" Function to instantiate a generic model with a set of pins """
|
||||
stim_file.write("X{0} ".format(model_name))
|
||||
for pin in pins:
|
||||
stim_file.write("{0} ".format(pin))
|
||||
|
|
@ -44,7 +44,7 @@ def inst_model(stim_file, pins, model_name):
|
|||
|
||||
|
||||
def create_inverter(stim_file, size=1, beta=2.5):
|
||||
"""Generates inverter for the top level signals (only for sim purposes)"""
|
||||
""" Generates inverter for the top level signals (only for sim purposes) """
|
||||
stim_file.write(".SUBCKT test_inv in out {0} {1}\n".format(vdd_name, gnd_name))
|
||||
stim_file.write("mpinv out in {0} {0} {1} w={2}u l={3}u\n".format(vdd_name,
|
||||
pmos_name,
|
||||
|
|
@ -58,9 +58,10 @@ def create_inverter(stim_file, size=1, beta=2.5):
|
|||
|
||||
|
||||
def create_buffer(stim_file, buffer_name, size=[1,3], beta=2.5):
|
||||
"""Generates buffer for top level signals (only for sim
|
||||
purposes). Size is pair for PMOS, NMOS width multiple. It includes
|
||||
a beta of 3."""
|
||||
"""
|
||||
Generates buffer for top level signals (only for sim
|
||||
purposes). Size is pair for PMOS, NMOS width multiple.
|
||||
"""
|
||||
|
||||
stim_file.write(".SUBCKT test_{2} in out {0} {1}\n".format(vdd_name,
|
||||
gnd_name,
|
||||
|
|
@ -85,7 +86,7 @@ def create_buffer(stim_file, buffer_name, size=[1,3], beta=2.5):
|
|||
|
||||
|
||||
def inst_buffer(stim_file, buffer_name, signal_list):
|
||||
"""Adds buffers to each top level signal that is in signal_list (only for sim purposes)"""
|
||||
""" Adds buffers to each top level signal that is in signal_list (only for sim purposes) """
|
||||
for signal in signal_list:
|
||||
stim_file.write("X{0}_buffer {0} {0}_buf {1} {2} test_{3}\n".format(signal,
|
||||
"test"+vdd_name,
|
||||
|
|
@ -94,7 +95,7 @@ def inst_buffer(stim_file, buffer_name, signal_list):
|
|||
|
||||
|
||||
def inst_inverter(stim_file, signal_list):
|
||||
"""Adds inv for each signal that needs its inverted version (only for sim purposes)"""
|
||||
""" Adds inv for each signal that needs its inverted version (only for sim purposes) """
|
||||
for signal in signal_list:
|
||||
stim_file.write("X{0}_inv {0} {0}_inv {1} {2} test_inv\n".format(signal,
|
||||
"test"+vdd_name,
|
||||
|
|
@ -102,7 +103,7 @@ def inst_inverter(stim_file, signal_list):
|
|||
|
||||
|
||||
def inst_accesstx(stim_file, dbits):
|
||||
"""Adds transmission gate for inputs to data-bus (only for sim purposes)"""
|
||||
""" Adds transmission gate for inputs to data-bus (only for sim purposes) """
|
||||
stim_file.write("* Tx Pin-list: Drain Gate Source Body\n")
|
||||
for i in range(dbits):
|
||||
pmos_access_string="mp{0} DATA[{0}] acc_en D[{0}] {1} {2} w={3}u l={4}u\n"
|
||||
|
|
@ -119,8 +120,11 @@ def inst_accesstx(stim_file, dbits):
|
|||
tx_length))
|
||||
|
||||
def gen_pulse(stim_file, sig_name, v1=gnd_voltage, v2=vdd_voltage, offset=0, period=1, t_rise=0, t_fall=0):
|
||||
"""Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
|
||||
from 50% to 50%."""
|
||||
"""
|
||||
Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
|
||||
from 50% to 50%.
|
||||
"""
|
||||
stim_file.write("* PULSE: period={0}\n".format(period))
|
||||
pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n"
|
||||
stim_file.write(pulse_string.format(sig_name,
|
||||
v1,
|
||||
|
|
@ -133,74 +137,32 @@ def gen_pulse(stim_file, sig_name, v1=gnd_voltage, v2=vdd_voltage, offset=0, per
|
|||
|
||||
|
||||
def gen_pwl(stim_file, sig_name, clk_times, data_values, period, slew, setup):
|
||||
"""
|
||||
Generate a PWL stimulus given a signal name and data values at each period.
|
||||
Automatically creates slews and ensures each data occurs a setup before the clock
|
||||
edge. The first clk_time should be 0 and is the initial time that corresponds
|
||||
to the initial value.
|
||||
"""
|
||||
# the initial value is not a clock time
|
||||
debug.check(len(clk_times)+1==len(data_values),"Clock and data value lengths don't match.")
|
||||
debug.check(len(clk_times)==len(data_values),"Clock and data value lengths don't match.")
|
||||
|
||||
# shift signal times earlier for setup time
|
||||
times = np.array(clk_times) - setup*period
|
||||
values = np.array(data_values) * vdd_voltage
|
||||
half_slew = 0.5 * slew
|
||||
stim_file.write("* (time, data): {}\n".format(zip(clk_times, data_values)))
|
||||
stim_file.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0]))
|
||||
for i in range(len(times)):
|
||||
for i in range(1,len(times)):
|
||||
stim_file.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew,
|
||||
values[i],
|
||||
values[i-1],
|
||||
times[i]+half_slew,
|
||||
values[i+1]))
|
||||
values[i]))
|
||||
stim_file.write(")\n")
|
||||
|
||||
def gen_data(stim_file, clk_times, sig_name, period, slew):
|
||||
"""Generates the PWL data inputs for a simulation timing test."""
|
||||
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
|
||||
# we are asserting the opposite value on the other side of the tx gate during
|
||||
# the read to be "worst case". Otherwise, it can actually assist the read.
|
||||
values = [0, 1, 0, 1, 1, 1, 0, 0, 0 ]
|
||||
gen_pwl(stim_file, sig_name, clk_times, values, period, slew, 0.05)
|
||||
|
||||
|
||||
def gen_addr(stim_file, clk_times, addr, period, slew):
|
||||
"""Generates the address inputs for a simulation timing test.
|
||||
One cycle is different to clear the bus
|
||||
"""
|
||||
|
||||
zero_values = [0, 0, 0, 1, 0, 0, 1, 0, 0 ]
|
||||
ones_values = [1, 1, 1, 0, 1, 1, 0, 1, 1 ]
|
||||
|
||||
for i in range(len(addr)):
|
||||
sig_name = "A[{0}]".format(i)
|
||||
if addr[i]=="1":
|
||||
gen_pwl(stim_file, sig_name, clk_times, ones_values, period, slew, 0.05)
|
||||
else:
|
||||
gen_pwl(stim_file, sig_name, clk_times, zero_values, period, slew, 0.05)
|
||||
|
||||
def gen_constant(stim_file, sig_name, v_val):
|
||||
"""Generates a constant signal with reference voltage and the voltage value"""
|
||||
""" Generates a constant signal with reference voltage and the voltage value """
|
||||
stim_file.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val))
|
||||
|
||||
def gen_csb(stim_file, clk_times, period, slew):
|
||||
""" Generates the PWL CSb signal"""
|
||||
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
|
||||
values = [1, 0, 0, 0, 0, 0, 0, 0, 1]
|
||||
gen_pwl(stim_file, "csb", clk_times, values, period, slew, 0.05)
|
||||
|
||||
def gen_web(stim_file, clk_times, period, slew):
|
||||
""" Generates the PWL WEb signal"""
|
||||
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
|
||||
values = [1, 0, 0, 0, 1, 0, 0, 1, 1]
|
||||
gen_pwl(stim_file, "web", clk_times, values, period, slew, 0.05)
|
||||
|
||||
values = [1, 0, 0, 0, 1, 0, 0, 1, 1]
|
||||
gen_pwl(stim_file, "acc_en", clk_times, values, period, slew, 0)
|
||||
values = [0, 1, 1, 1, 0, 1, 1, 0, 0]
|
||||
gen_pwl(stim_file, "acc_en_inv", clk_times, values, period, slew, 0)
|
||||
|
||||
def gen_oeb(stim_file, clk_times, period, slew):
|
||||
""" Generates the PWL WEb signal"""
|
||||
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
|
||||
values = [1, 1, 1, 1, 0, 1, 1, 0, 1]
|
||||
gen_pwl(stim_file, "oeb", clk_times, values, period, slew, 0.05)
|
||||
|
||||
|
||||
|
||||
|
||||
def get_inverse_voltage(value):
|
||||
if value > 0.5*vdd_voltage:
|
||||
return gnd_voltage
|
||||
|
|
@ -218,20 +180,21 @@ def get_inverse_value(value):
|
|||
debug.error("Invalid value to get an inverse of: {0}".format(value))
|
||||
|
||||
|
||||
def gen_meas_delay(stim_file, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, td):
|
||||
"""Creates the .meas statement for the measurement of delay"""
|
||||
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={7}n TARG v({4}) VAL={5} {6}=1 TD={7}n\n\n"
|
||||
def gen_meas_delay(stim_file, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
|
||||
""" Creates the .meas statement for the measurement of delay """
|
||||
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
|
||||
stim_file.write(measure_string.format(meas_name,
|
||||
trig_name,
|
||||
trig_val,
|
||||
trig_dir,
|
||||
trig_td,
|
||||
targ_name,
|
||||
targ_val,
|
||||
targ_dir,
|
||||
td))
|
||||
targ_td))
|
||||
|
||||
def gen_meas_power(stim_file, meas_name, t_initial, t_final):
|
||||
"""Creates the .meas statement for the measurement of avg power"""
|
||||
""" Creates the .meas statement for the measurement of avg power """
|
||||
# power mea cmd is different in different spice:
|
||||
if OPTS.spice_name == "hspice":
|
||||
power_exp = "power"
|
||||
|
|
@ -241,12 +204,20 @@ def gen_meas_power(stim_file, meas_name, t_initial, t_final):
|
|||
power_exp,
|
||||
t_initial,
|
||||
t_final))
|
||||
stim_file.write("\n")
|
||||
|
||||
def write_control(stim_file, end_time):
|
||||
""" Write the control cards to run and end the simulation """
|
||||
# UIC is needed for ngspice to converge
|
||||
stim_file.write(".TRAN 5p {0}n UIC\n".format(end_time))
|
||||
stim_file.write(".OPTIONS POST=1 RUNLVL=4 PROBE\n")
|
||||
if OPTS.spice_name == "ngspice":
|
||||
# ngspice sometimes has convergence problems if not using gear method
|
||||
# which is more accurate, but slower than the default trapezoid method
|
||||
# Do not remove this or it may not converge due to some "pa_00" nodes
|
||||
# unless you figure out what these are.
|
||||
stim_file.write(".OPTIONS POST=1 RUNLVL=4 PROBE method=gear\n")
|
||||
else:
|
||||
stim_file.write(".OPTIONS POST=1 RUNLVL=4 PROBE\n")
|
||||
|
||||
# create plots for all signals
|
||||
stim_file.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
|
||||
if OPTS.debug_level>0:
|
||||
|
|
@ -265,43 +236,46 @@ def write_control(stim_file, end_time):
|
|||
def write_include(stim_file, models):
|
||||
"""Writes include statements, inputs are lists of model files"""
|
||||
for item in list(models):
|
||||
stim_file.write(".include \"{0}\"\n\n".format(item))
|
||||
if os.path.isfile(item):
|
||||
stim_file.write(".include \"{0}\"\n".format(item))
|
||||
else:
|
||||
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item))
|
||||
|
||||
|
||||
def write_supply(stim_file):
|
||||
"""Writes supply voltage statements"""
|
||||
""" Writes supply voltage statements """
|
||||
stim_file.write("V{0} {0} 0.0 {1}\n".format(vdd_name, vdd_voltage))
|
||||
stim_file.write("V{0} {0} 0.0 {1}\n".format(gnd_name, gnd_voltage))
|
||||
# This is for the test power supply
|
||||
stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+vdd_name, vdd_voltage))
|
||||
stim_file.write("V{0} {0} 0.0 {1}\n\n".format("test"+gnd_name, gnd_voltage))
|
||||
stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+gnd_name, gnd_voltage))
|
||||
|
||||
|
||||
def run_sim():
|
||||
"""Run hspice in batch mode and output rawfile to parse."""
|
||||
""" Run hspice in batch mode and output rawfile to parse. """
|
||||
temp_stim = "{0}stim.sp".format(OPTS.openram_temp)
|
||||
import datetime
|
||||
start_time = datetime.datetime.now()
|
||||
debug.check(OPTS.spice_exe!="","No spice simulator has been found.")
|
||||
|
||||
from characterizer import spice_exe
|
||||
if OPTS.spice_name == "xa":
|
||||
# Output the xa configurations here. FIXME: Move this to write it once.
|
||||
xa_cfg = open("{}xa.cfg".format(OPTS.openram_temp), "w")
|
||||
xa_cfg.write("set_sim_level -level 7\n")
|
||||
xa_cfg.write("set_powernet_level 7 -node vdd\n")
|
||||
xa_cfg.close()
|
||||
cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt 20".format(spice_exe,
|
||||
cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt 2".format(OPTS.spice_exe,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
valid_retcode=0
|
||||
elif OPTS.spice_name == "hspice":
|
||||
# TODO: Should make multithreading parameter a configuration option
|
||||
cmd = "{0} -mt 2 -i {1} -o {2}timing".format(spice_exe,
|
||||
cmd = "{0} -mt 2 -i {1} -o {2}timing".format(OPTS.spice_exe,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
valid_retcode=0
|
||||
else:
|
||||
cmd = "{0} -b -o {2}timing.lis {1}".format(spice_exe,
|
||||
cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
# for some reason, ngspice-25 returns 1 when it only has acceptable warnings
|
||||
|
|
|
|||
|
|
@ -45,27 +45,38 @@ class trim_spice():
|
|||
|
||||
# Always start fresh if we do multiple reductions
|
||||
self.sp_buffer = self.spice
|
||||
|
||||
# Find the row and column indices for the removals
|
||||
# Convert address froms tring to int
|
||||
address = int(address,2)
|
||||
array_row = address >> self.col_addr_size
|
||||
# Which word in the array (0 if only one word)
|
||||
if self.col_addr_size>0:
|
||||
lower_mask = int(self.col_addr_size-1)
|
||||
lower_address = address & lower_mask
|
||||
else:
|
||||
lower_address=0
|
||||
# Which bit in the array
|
||||
array_bit = lower_address*self.word_size + data_bit
|
||||
|
||||
# Split up the address and convert to an int
|
||||
wl_address = int(address[self.col_addr_size:],2)
|
||||
if self.col_addr_size>1:
|
||||
col_address = int(address[0:self.col_addr_size],2)
|
||||
else:
|
||||
col_address = 0
|
||||
# 1. Keep cells in the bitcell array based on WL and BL
|
||||
wl_name = "wl[{}]".format(array_row)
|
||||
bl_name = "bl[{}]".format(array_bit)
|
||||
wl_name = "wl[{}]".format(wl_address)
|
||||
bl_name = "bl[{}]".format(self.words_per_row*data_bit + col_address)
|
||||
|
||||
# Prepend info about the trimming
|
||||
addr_msg = "Keeping {} address".format(address)
|
||||
self.sp_buffer.insert(0, "* "+addr_msg)
|
||||
debug.info(1,addr_msg)
|
||||
data_msg = "Keeping {} data bit".format(data_bit)
|
||||
self.sp_buffer.insert(0, "* "+data_msg)
|
||||
debug.info(1,data_msg)
|
||||
bl_msg = "Keeping {} (trimming other BLs)".format(bl_name)
|
||||
wl_msg = "Keeping {} (trimming other WLs)".format(wl_name)
|
||||
self.sp_buffer.insert(0, "* "+bl_msg)
|
||||
debug.info(1,bl_msg)
|
||||
self.sp_buffer.insert(0, "* "+wl_msg)
|
||||
debug.info(1,wl_msg)
|
||||
self.sp_buffer.insert(0, "* It should NOT be used for LVS!!")
|
||||
self.sp_buffer.insert(0, "* WARNING: This is a TRIMMED NETLIST.")
|
||||
|
||||
self.remove_insts("bitcell_array",[wl_name,bl_name])
|
||||
|
||||
# 2. Keep sense amps basd on BL
|
||||
self.remove_insts("sense_amp_array",[bl_name])
|
||||
# FIXME: The bit lines are not indexed the same in sense_amp_array
|
||||
#self.remove_insts("sense_amp_array",[bl_name])
|
||||
|
||||
# 3. Keep column muxes basd on BL
|
||||
self.remove_insts("column_mux_array",[bl_name])
|
||||
|
|
|
|||
|
|
@ -11,19 +11,34 @@ class contact(design.design):
|
|||
Creates a contact array minimum active or poly enclosure and metal1 enclosure.
|
||||
This class has enclosure on multiple sides of the contact whereas a via may
|
||||
have extension on two or four sides.
|
||||
The well/implant_type is an option to add a select/implant layer enclosing the contact. This is
|
||||
necessary to import layouts into Magic which requires the select to be in the same GDS
|
||||
hierarchy as the contact.
|
||||
"""
|
||||
def __init__(self, layer_stack, dimensions=[1,1]):
|
||||
name = "{0}_{1}_{2}_{3}x{4}".format(layer_stack[0],
|
||||
layer_stack[1],
|
||||
layer_stack[2],
|
||||
dimensions[0],
|
||||
dimensions[1])
|
||||
def __init__(self, layer_stack, dimensions=[1,1], implant_type=None, well_type=None):
|
||||
if implant_type or well_type:
|
||||
name = "{0}_{1}_{2}_{3}x{4}_{5}{6}".format(layer_stack[0],
|
||||
layer_stack[1],
|
||||
layer_stack[2],
|
||||
dimensions[0],
|
||||
dimensions[1],
|
||||
implant_type,
|
||||
well_type)
|
||||
else:
|
||||
name = "{0}_{1}_{2}_{3}x{4}".format(layer_stack[0],
|
||||
layer_stack[1],
|
||||
layer_stack[2],
|
||||
dimensions[0],
|
||||
dimensions[1])
|
||||
|
||||
design.design.__init__(self, name)
|
||||
debug.info(4, "create contact object {0}".format(name))
|
||||
|
||||
self.layer_stack = layer_stack
|
||||
self.dimensions = dimensions
|
||||
self.offset = vector(0,0)
|
||||
self.implant_type = implant_type
|
||||
self.well_type = well_type
|
||||
self.pins = [] # used for matching parm lengths
|
||||
self.create_layout()
|
||||
|
||||
|
|
@ -37,74 +52,112 @@ class contact(design.design):
|
|||
self.height = max(obj.offset.y + obj.height for obj in self.objs)
|
||||
self.width = max(obj.offset.x + obj.width for obj in self.objs)
|
||||
|
||||
# Do not include the select layer in the height/width
|
||||
if self.implant_type and self.well_type:
|
||||
self.create_implant_well_enclosures()
|
||||
elif self.implant_type or self.well_type:
|
||||
debug.error(-1,"Must define both implant and well type or none at all.")
|
||||
|
||||
def setup_layers(self):
|
||||
(first_layer, via_layer, second_layer) = self.layer_stack
|
||||
self.first_layer_name = first_layer
|
||||
self.via_layer_name = via_layer
|
||||
# Some technologies have a separate active contact from the poly contact
|
||||
# We will use contact for DRC, but active_contact for output
|
||||
if first_layer=="active" or second_layer=="active":
|
||||
self.via_layer_name_expanded = "active_"+via_layer
|
||||
else:
|
||||
self.via_layer_name_expanded = via_layer
|
||||
self.second_layer_name = second_layer
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.contact_width = drc["minwidth_{0}". format(self.via_layer_name)]
|
||||
self.contact_to_contact = drc["{0}_to_{0}".format(self.via_layer_name)]
|
||||
self.contact_pitch = self.contact_width + self.contact_to_contact
|
||||
contact_to_contact = drc["{0}_to_{0}".format(self.via_layer_name)]
|
||||
self.contact_pitch = self.contact_width + contact_to_contact
|
||||
self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch
|
||||
self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch
|
||||
|
||||
# FIME break this up
|
||||
self.first_layer_horizontal_enclosure = max((drc["minwidth_{0}".format(self.first_layer_name)] - self.contact_array_width) / 2,
|
||||
drc["{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)])
|
||||
self.first_layer_vertical_enclosure = max(utils.ceil((drc["minarea_{0}".format(self.first_layer_name)]
|
||||
/ (self.contact_array_width + 2 * self.first_layer_horizontal_enclosure) - self.contact_array_height) / 2),
|
||||
(drc["minheight_{0}".format(self.first_layer_name)] - self.contact_array_height) / 2,
|
||||
drc["{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)])
|
||||
# DRC rules
|
||||
first_layer_minwidth = drc["minwidth_{0}".format(self.first_layer_name)]
|
||||
first_layer_minarea = drc["minarea_{0}".format(self.first_layer_name)]
|
||||
first_layer_enclosure = drc["{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)]
|
||||
first_layer_extend = drc["{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)]
|
||||
second_layer_minwidth = drc["minwidth_{0}".format(self.second_layer_name)]
|
||||
second_layer_minarea = drc["minarea_{0}".format(self.second_layer_name)]
|
||||
second_layer_enclosure = drc["{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)]
|
||||
second_layer_extend = drc["{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)]
|
||||
|
||||
self.second_layer_horizontal_enclosure = max((drc["minwidth_{0}".format(self.second_layer_name)] - self.contact_array_width) / 2,
|
||||
drc["{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)])
|
||||
self.second_layer_vertical_enclosure = max(utils.ceil((drc["minarea_{0}".format(self.second_layer_name)]
|
||||
/ (self.contact_array_width + 2 * self.second_layer_horizontal_enclosure) - self.contact_array_height) / 2),
|
||||
(drc["minheight_{0}".format(self.second_layer_name)] - self.contact_array_height) / 2,
|
||||
drc["{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)])
|
||||
self.first_layer_horizontal_enclosure = max((first_layer_minwidth - self.contact_array_width) / 2,
|
||||
first_layer_enclosure)
|
||||
self.first_layer_vertical_enclosure = max(utils.ceil((first_layer_minarea
|
||||
/ (self.contact_array_width + 2*self.first_layer_horizontal_enclosure)
|
||||
- self.contact_array_height)/2),
|
||||
(first_layer_minwidth - self.contact_array_height)/2,
|
||||
first_layer_extend)
|
||||
|
||||
self.second_layer_horizontal_enclosure = max((second_layer_minwidth - self.contact_array_width)/2,
|
||||
second_layer_enclosure)
|
||||
self.second_layer_vertical_enclosure = max(utils.ceil((second_layer_minarea
|
||||
/ (self.contact_array_width + 2*self.second_layer_horizontal_enclosure)
|
||||
- self.contact_array_height)/2),
|
||||
(second_layer_minwidth - self.contact_array_height)/2,
|
||||
second_layer_extend)
|
||||
|
||||
def create_contact_array(self):
|
||||
""" Create the contact array at the origin"""
|
||||
# offset for the via array
|
||||
self.via_layer_position =vector(max(self.first_layer_horizontal_enclosure,self.second_layer_horizontal_enclosure),
|
||||
max(self.first_layer_vertical_enclosure,self.second_layer_vertical_enclosure))
|
||||
# this is if the first and second layers are different
|
||||
self.first_layer_position = vector(max(self.second_layer_horizontal_enclosure - self.first_layer_horizontal_enclosure,0),
|
||||
max(self.second_layer_vertical_enclosure - self.first_layer_vertical_enclosure,0))
|
||||
self.second_layer_position = vector(max(self.first_layer_horizontal_enclosure - self.second_layer_horizontal_enclosure,0),
|
||||
max(self.first_layer_vertical_enclosure - self.second_layer_vertical_enclosure,0))
|
||||
|
||||
def create_contact_array(self):
|
||||
""" Create the contact array at the origin"""
|
||||
for i in range(self.dimensions[1]):
|
||||
offset = self.via_layer_position + vector(0, self.contact_pitch * i)
|
||||
for j in range(self.dimensions[0]):
|
||||
self.add_rect(layer=self.via_layer_name,
|
||||
self.add_rect(layer=self.via_layer_name_expanded,
|
||||
offset=offset,
|
||||
width=self.contact_width,
|
||||
height=self.contact_width)
|
||||
offset = offset + vector(self.contact_pitch,0)
|
||||
|
||||
def create_first_layer_enclosure(self):
|
||||
width = self.first_layer_width = self.contact_array_width \
|
||||
+ 2 * self.first_layer_horizontal_enclosure
|
||||
height = self.first_layer_height = self.contact_array_height \
|
||||
+ 2 * self.first_layer_vertical_enclosure
|
||||
# this is if the first and second layers are different
|
||||
self.first_layer_position = vector(max(self.second_layer_horizontal_enclosure - self.first_layer_horizontal_enclosure,0),
|
||||
max(self.second_layer_vertical_enclosure - self.first_layer_vertical_enclosure,0))
|
||||
|
||||
self.first_layer_width = self.contact_array_width + 2*self.first_layer_horizontal_enclosure
|
||||
self.first_layer_height = self.contact_array_height + 2*self.first_layer_vertical_enclosure
|
||||
self.add_rect(layer=self.first_layer_name,
|
||||
offset=self.first_layer_position,
|
||||
width=width,
|
||||
height=height)
|
||||
width=self.first_layer_width,
|
||||
height=self.first_layer_height)
|
||||
|
||||
def create_second_layer_enclosure(self):
|
||||
width = self.second_layer_width = self.contact_array_width \
|
||||
+ 2 * self.second_layer_horizontal_enclosure
|
||||
height = self.second_layer_height = self.contact_array_height \
|
||||
+ 2 * self.second_layer_vertical_enclosure
|
||||
# this is if the first and second layers are different
|
||||
self.second_layer_position = vector(max(self.first_layer_horizontal_enclosure - self.second_layer_horizontal_enclosure,0),
|
||||
max(self.first_layer_vertical_enclosure - self.second_layer_vertical_enclosure,0))
|
||||
|
||||
self.second_layer_width = self.contact_array_width + 2*self.second_layer_horizontal_enclosure
|
||||
self.second_layer_height = self.contact_array_height + 2*self.second_layer_vertical_enclosure
|
||||
self.add_rect(layer=self.second_layer_name,
|
||||
offset=self.second_layer_position,
|
||||
width=width,
|
||||
height=height)
|
||||
width=self.second_layer_width,
|
||||
height=self.second_layer_height)
|
||||
|
||||
def create_implant_well_enclosures(self):
|
||||
implant_position = self.first_layer_position - [drc["implant_enclosure_active"]]*2
|
||||
implant_width = self.first_layer_width + 2*drc["implant_enclosure_active"]
|
||||
implant_height = self.first_layer_height + 2*drc["implant_enclosure_active"]
|
||||
self.add_rect(layer="{}implant".format(self.implant_type),
|
||||
offset=implant_position,
|
||||
width=implant_width,
|
||||
height=implant_height)
|
||||
well_position = self.first_layer_position - [drc["well_enclosure_active"]]*2
|
||||
well_width = self.first_layer_width + 2*drc["well_enclosure_active"]
|
||||
well_height = self.first_layer_height + 2*drc["well_enclosure_active"]
|
||||
self.add_rect(layer="{}well".format(self.well_type),
|
||||
offset=well_position,
|
||||
width=well_width,
|
||||
height=well_height)
|
||||
|
||||
|
||||
|
||||
# This is not instantiated and used for calculations only.
|
||||
|
|
|
|||
|
|
@ -59,16 +59,19 @@ class control_logic(design.design):
|
|||
self.inv16 = pinv(16)
|
||||
self.add_mod(self.inv16)
|
||||
|
||||
c = reload(__import__(OPTS.config.ms_flop_array))
|
||||
ms_flop_array = getattr(c, OPTS.config.ms_flop_array)
|
||||
c = reload(__import__(OPTS.ms_flop_array))
|
||||
ms_flop_array = getattr(c, OPTS.ms_flop_array)
|
||||
self.msf_control = ms_flop_array(name="msf_control",
|
||||
columns=3,
|
||||
word_size=3)
|
||||
self.add_mod(self.msf_control)
|
||||
|
||||
c = reload(__import__(OPTS.config.replica_bitline))
|
||||
replica_bitline = getattr(c, OPTS.config.replica_bitline)
|
||||
self.replica_bitline = replica_bitline(rows=int(math.ceil(self.num_rows / 10.0)))
|
||||
c = reload(__import__(OPTS.replica_bitline))
|
||||
replica_bitline = getattr(c, OPTS.replica_bitline)
|
||||
# FIXME: These should be tuned according to the size!
|
||||
FO4_stages = 4
|
||||
bitcell_loads = int(math.ceil(self.num_rows / 10.0))
|
||||
self.replica_bitline = replica_bitline(FO4_stages, bitcell_loads)
|
||||
self.add_mod(self.replica_bitline)
|
||||
|
||||
|
||||
|
|
@ -77,8 +80,6 @@ class control_logic(design.design):
|
|||
# These aren't for instantiating, but we use them to get the dimensions
|
||||
self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height)
|
||||
|
||||
# For different layer width vias
|
||||
self.m1m2_offset_fix = vector(0,0.5*(drc["minwidth_metal2"]-drc["minwidth_metal1"]))
|
||||
# M1/M2 routing pitch is based on contacted pitch
|
||||
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
|
||||
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
|
||||
|
|
@ -87,10 +88,6 @@ class control_logic(design.design):
|
|||
# Some cells may have pwell/nwell spacing problems too when the wells are different heights.
|
||||
self.cell_gap = max(self.m2_pitch,drc["pwell_to_nwell"])
|
||||
|
||||
# Amount to shift a 90 degree rotated via from center-line path routing to it's offset
|
||||
self.m1m2_via_offset = vector(contact.m1m2.first_layer_height,-0.5*drc["minwidth_metal2"])
|
||||
self.m2m3_via_offset = vector(contact.m2m3.first_layer_height,-0.5*drc["minwidth_metal3"])
|
||||
|
||||
# First RAIL Parameters: gnd, oe, oebar, cs, we, clk_buf, clk_bar
|
||||
self.rail_1_start_x = 0
|
||||
self.num_rails_1 = 8
|
||||
|
|
@ -275,7 +272,7 @@ class control_logic(design.design):
|
|||
mod=self.nand2,
|
||||
offset=self.tri_en_bar_offset,
|
||||
mirror="MX")
|
||||
self.connect_inst(["oe", "clk_bar", "tri_en_bar", "vdd", "gnd"])
|
||||
self.connect_inst(["clk_bar", "oe", "tri_en_bar", "vdd", "gnd"])
|
||||
x_off += self.nand2.width
|
||||
|
||||
x_off += self.inv1.width + self.cell_gap
|
||||
|
|
@ -326,6 +323,7 @@ class control_logic(design.design):
|
|||
x_off += self.inv1.width
|
||||
|
||||
# BUFFER INVERTERS FOR W_EN
|
||||
# FIXME: Can we remove these two invs and size the previous one?
|
||||
self.pre_w_en_bar_offset = vector(x_off, y_off)
|
||||
self.pre_w_en_bar=self.add_inst(name="inv_pre_w_en_bar",
|
||||
mod=self.inv1,
|
||||
|
|
@ -512,11 +510,25 @@ class control_logic(design.design):
|
|||
offset=clk_buf_rail_position,
|
||||
rotate=90)
|
||||
|
||||
# clk_bar
|
||||
self.connect_rail_from_left_m2m3(self.clk_bar,"Z","clk_bar")
|
||||
# clk_bar, routes over the clock buffer vdd rail
|
||||
clk_pin = self.clk_bar.get_pin("Z")
|
||||
vdd_pin = self.clk_bar.get_pin("vdd")
|
||||
# move the output pin up to metal2
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=self.clk_bar.get_pin("Z").rc(),
|
||||
offset=clk_pin.rc(),
|
||||
rotate=90)
|
||||
# route to a position over the supply rail
|
||||
in_pos = vector(clk_pin.rx(), vdd_pin.cy())
|
||||
self.add_path("metal2",[clk_pin.rc(), in_pos])
|
||||
# connect that position to the control bus
|
||||
rail_pos = vector(self.rail_1_x_offsets["clk_bar"], in_pos.y)
|
||||
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=in_pos,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=rail_pos,
|
||||
rotate=90)
|
||||
|
||||
# clk_buf to msf control flops
|
||||
msf_clk_pos = self.msf_inst.get_pin("clk").bc()
|
||||
|
|
|
|||
|
|
@ -14,14 +14,13 @@ def check(check,str):
|
|||
index) = inspect.getouterframes(inspect.currentframe())[1]
|
||||
if not check:
|
||||
print("ERROR: file {0}: line {1}: {2}".format(os.path.basename(filename),line_number,str))
|
||||
sys.exit(-1)
|
||||
assert 0
|
||||
|
||||
def error(str,return_value=None):
|
||||
def error(str,return_value=0):
|
||||
(frame, filename, line_number, function_name, lines,
|
||||
index) = inspect.getouterframes(inspect.currentframe())[1]
|
||||
print("ERROR: file {0}: line {1}: {2}".format(os.path.basename(filename),line_number,str))
|
||||
if return_value:
|
||||
sys.exit(return_value)
|
||||
assert return_value==0
|
||||
|
||||
def warning(str):
|
||||
(frame, filename, line_number, function_name, lines,
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ class delay_chain(design.design):
|
|||
self.num_inverters = 1 + sum(fanout_list)
|
||||
self.num_top_half = round(self.num_inverters / 2.0)
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
self.add_pins()
|
||||
|
|
|
|||
|
|
@ -27,8 +27,13 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
|
||||
# Check if the name already exists, if so, give an error
|
||||
# because each reference must be a unique name.
|
||||
ok_list = ['ms_flop.ms_flop', 'bitcell.bitcell', 'contact.contact',
|
||||
'ptx.ptx', 'sram.sram',
|
||||
# These modules ensure unique names or have no changes if they
|
||||
# aren't unique
|
||||
ok_list = ['ms_flop.ms_flop',
|
||||
'bitcell.bitcell',
|
||||
'contact.contact',
|
||||
'ptx.ptx',
|
||||
'sram.sram',
|
||||
'hierarchical_predecode2x4.hierarchical_predecode2x4',
|
||||
'hierarchical_predecode3x8.hierarchical_predecode3x8']
|
||||
if name not in design.name_map:
|
||||
|
|
@ -41,6 +46,7 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
def setup_drc_constants(self):
|
||||
""" These are some DRC constants used in many places in the compiler."""
|
||||
from tech import drc
|
||||
self.well_width = drc["minwidth_well"]
|
||||
self.poly_width = drc["minwidth_poly"]
|
||||
self.poly_space = drc["poly_to_poly"]
|
||||
self.m1_width = drc["minwidth_metal1"]
|
||||
|
|
@ -49,7 +55,16 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
self.m2_space = drc["metal2_to_metal2"]
|
||||
self.m3_width = drc["minwidth_metal3"]
|
||||
self.m3_space = drc["metal3_to_metal3"]
|
||||
|
||||
self.active_width = drc["minwidth_active"]
|
||||
self.contact_width = drc["minwidth_contact"]
|
||||
|
||||
self.poly_to_active = drc["poly_to_active"]
|
||||
self.poly_extend_active = drc["poly_extend_active"]
|
||||
self.contact_to_gate = drc["contact_to_gate"]
|
||||
self.well_enclose_active = drc["well_enclosure_active"]
|
||||
self.implant_enclose_active = drc["implant_enclosure_active"]
|
||||
self.implant_space = drc["implant_to_implant"]
|
||||
|
||||
def get_layout_pins(self,inst):
|
||||
""" Return a map of pin locations of the instance offset """
|
||||
# find the instance
|
||||
|
|
@ -62,7 +77,7 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
return inst_map
|
||||
|
||||
|
||||
def DRC_LVS(self):
|
||||
def DRC_LVS(self, final_verification=False):
|
||||
"""Checks both DRC and LVS for a module"""
|
||||
if OPTS.check_lvsdrc:
|
||||
tempspice = OPTS.openram_temp + "/temp.sp"
|
||||
|
|
@ -70,7 +85,7 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
self.sp_write(tempspice)
|
||||
self.gds_write(tempgds)
|
||||
debug.check(verify.run_drc(self.name, tempgds) == 0,"DRC failed for {0}".format(self.name))
|
||||
debug.check(verify.run_lvs(self.name, tempgds, tempspice) == 0,"LVS failed for {0}".format(self.name))
|
||||
debug.check(verify.run_lvs(self.name, tempgds, tempspice, final_verification) == 0,"LVS failed for {0}".format(self.name))
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
|
|
@ -82,14 +97,14 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
debug.check(verify.run_drc(self.name, tempgds) == 0,"DRC failed for {0}".format(self.name))
|
||||
os.remove(tempgds)
|
||||
|
||||
def LVS(self):
|
||||
def LVS(self, final_verification=False):
|
||||
"""Checks LVS for a module"""
|
||||
if OPTS.check_lvsdrc:
|
||||
tempspice = OPTS.openram_temp + "/temp.sp"
|
||||
tempgds = OPTS.openram_temp + "/temp.gds"
|
||||
self.sp_write(tempspice)
|
||||
self.gds_write(tempgds)
|
||||
debug.check(verify.run_lvs(self.name, tempgds, tempspice) == 0,"LVS failed for {0}".format(self.name))
|
||||
debug.check(verify.run_lvs(self.name, tempgds, tempspice, final_verification) == 0,"LVS failed for {0}".format(self.name))
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,24 +4,6 @@ num_banks = 1
|
|||
|
||||
tech_name = "freepdk45"
|
||||
|
||||
output_path = "/tmp/mysram"
|
||||
output_path = "temp"
|
||||
output_name = "sram_2_16_1_freepdk45"
|
||||
|
||||
decoder = "hierarchical_decoder"
|
||||
ms_flop = "ms_flop"
|
||||
ms_flop_array = "ms_flop_array"
|
||||
control_logic = "control_logic"
|
||||
bitcell_array = "bitcell_array"
|
||||
sense_amp = "sense_amp"
|
||||
sense_amp_array = "sense_amp_array"
|
||||
precharge_array = "precharge_array"
|
||||
column_mux_array = "single_level_column_mux_array"
|
||||
write_driver = "write_driver"
|
||||
write_driver_array = "write_driver_array"
|
||||
tri_gate = "tri_gate"
|
||||
tri_gate_array = "tri_gate_array"
|
||||
wordline_driver = "wordline_driver"
|
||||
replica_bitline = "replica_bitline"
|
||||
replica_bitcell = "replica_bitcell"
|
||||
bitcell = "bitcell"
|
||||
delay_chain = "delay_chain"
|
||||
|
|
|
|||
|
|
@ -1,27 +1,9 @@
|
|||
word_size = 1
|
||||
word_size = 2
|
||||
num_words = 16
|
||||
num_banks = 1
|
||||
|
||||
tech_name = "scn3me_subm"
|
||||
|
||||
output_path = "/tmp/mysram"
|
||||
output_path = "temp"
|
||||
output_name = "sram_2_16_1_scn3me_subm"
|
||||
|
||||
decoder = "hierarchical_decoder"
|
||||
ms_flop = "ms_flop"
|
||||
ms_flop_array = "ms_flop_array"
|
||||
control_logic = "control_logic"
|
||||
bitcell_array = "bitcell_array"
|
||||
sense_amp = "sense_amp"
|
||||
sense_amp_array = "sense_amp_array"
|
||||
precharge_array = "precharge_array"
|
||||
column_mux_array = "single_level_column_mux_array"
|
||||
write_driver = "write_driver"
|
||||
write_driver_array = "write_driver_array"
|
||||
tri_gate = "tri_gate"
|
||||
tri_gate_array = "tri_gate_array"
|
||||
wordline_driver = "wordline_driver"
|
||||
replica_bitline = "replica_bitline"
|
||||
replica_bitcell = "replica_bitcell"
|
||||
bitcell = "bitcell"
|
||||
delay_chain = "delay_chain"
|
||||
|
|
|
|||
|
|
@ -11,9 +11,6 @@ import sys
|
|||
import re
|
||||
import importlib
|
||||
|
||||
# Current version of OpenRAM.
|
||||
VERSION = "1.01"
|
||||
|
||||
USAGE = "Usage: openram.py [options] <config file>\nUse -h for help.\n"
|
||||
|
||||
# Anonymous object that will be the options
|
||||
|
|
@ -50,22 +47,26 @@ def parse_args():
|
|||
optparse.make_option("-r", "--remove_netlist_trimming", action="store_false", dest="trim_netlist",
|
||||
help="Disable removal of noncritical memory cells during characterization"),
|
||||
optparse.make_option("-c", "--characterize", action="store_false", dest="analytical_delay",
|
||||
help="Perform characterization to calculate delays (default is analytical models)")
|
||||
help="Perform characterization to calculate delays (default is analytical models)"),
|
||||
optparse.make_option("-d", "--dontpurge", action="store_false", dest="purge_temp",
|
||||
help="Don't purge the contents of the temp directory after a successful run")
|
||||
# -h --help is implicit.
|
||||
}
|
||||
|
||||
parser = optparse.OptionParser(option_list=option_list,
|
||||
description="Compile and/or characterize an SRAM.",
|
||||
usage=USAGE,
|
||||
version="OpenRAM v" + VERSION)
|
||||
version="OpenRAM")
|
||||
|
||||
(options, args) = parser.parse_args(values=OPTS)
|
||||
|
||||
# If we don't specify a tech, assume freepdk45.
|
||||
# This may be overridden when we read a config file though...
|
||||
if OPTS.tech_name == "":
|
||||
OPTS.tech_name = "freepdk45"
|
||||
|
||||
# Alias SCMOS to AMI 0.5um
|
||||
if OPTS.tech_name == "scmos":
|
||||
OPTS.tech_name = "scn3me_subm"
|
||||
|
||||
return (options, args)
|
||||
|
||||
def print_banner():
|
||||
|
|
@ -75,7 +76,7 @@ def print_banner():
|
|||
return
|
||||
|
||||
print("|==============================================================================|")
|
||||
name = "OpenRAM Compiler v"+VERSION
|
||||
name = "OpenRAM Compiler"
|
||||
print("|=========" + name.center(60) + "=========|")
|
||||
print("|=========" + " ".center(60) + "=========|")
|
||||
print("|=========" + "VLSI Design and Automation Lab".center(60) + "=========|")
|
||||
|
|
@ -99,13 +100,13 @@ def init_openram(config_file):
|
|||
|
||||
import_tech()
|
||||
|
||||
|
||||
def get_tool(tool_type, preferences):
|
||||
"""
|
||||
Find which tool we have from a list of preferences and return the
|
||||
one selected and its full path.
|
||||
"""
|
||||
debug.info(2,"Finding {} tool...".format(tool_type))
|
||||
global OPTS
|
||||
|
||||
for name in preferences:
|
||||
exe_name = find_exe(name)
|
||||
|
|
@ -125,6 +126,8 @@ def read_config(config_file):
|
|||
config file is just a Python file that defines some config
|
||||
options.
|
||||
"""
|
||||
global OPTS
|
||||
|
||||
# Create a full path relative to current dir unless it is already an abs path
|
||||
if not os.path.isabs(config_file):
|
||||
config_file = os.getcwd() + "/" + config_file
|
||||
|
|
@ -140,18 +143,17 @@ def read_config(config_file):
|
|||
# Import the configuration file of which modules to use
|
||||
debug.info(1, "Configuration file is " + config_file + ".py")
|
||||
try:
|
||||
OPTS.config = importlib.import_module(file_name)
|
||||
config = importlib.import_module(file_name)
|
||||
except:
|
||||
debug.error("Unable to read configuration file: {0}".format(config_file),2)
|
||||
|
||||
# This path must be setup after the config file.
|
||||
try:
|
||||
# If path not set on command line, try config file.
|
||||
if OPTS.output_path=="":
|
||||
OPTS.output_path=OPTS.config.output_path
|
||||
except:
|
||||
# Default to current directory.
|
||||
OPTS.output_path="."
|
||||
for k,v in config.__dict__.items():
|
||||
# The command line will over-ride the config file
|
||||
# except in the case of the tech name! This is because the tech name
|
||||
# is sometimes used to specify the config file itself (e.g. unit tests)
|
||||
if not k in OPTS.__dict__ or k=="tech_name":
|
||||
OPTS.__dict__[k]=v
|
||||
|
||||
if not OPTS.output_path.endswith('/'):
|
||||
OPTS.output_path += "/"
|
||||
debug.info(1, "Output saved in " + OPTS.output_path)
|
||||
|
|
@ -172,16 +174,15 @@ def end_openram():
|
|||
""" Clean up openram for a proper exit """
|
||||
cleanup_paths()
|
||||
|
||||
# Reset the static duplicate name checker for unit tests.
|
||||
# This is needed for running unit tests.
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
def cleanup_paths():
|
||||
"""
|
||||
We should clean up the temp directory after execution.
|
||||
"""
|
||||
if not OPTS.purge_temp:
|
||||
debug.info(0,"Preserving temp directory: {}".format(OPTS.openram_temp))
|
||||
return
|
||||
if os.path.exists(OPTS.openram_temp):
|
||||
shutil.rmtree(OPTS.openram_temp, ignore_errors=True)
|
||||
|
||||
|
|
@ -242,7 +243,7 @@ def import_tech():
|
|||
debug.info(2,"Importing technology: " + OPTS.tech_name)
|
||||
|
||||
# Set the tech to the config file we read in instead of the command line value.
|
||||
OPTS.tech_name = OPTS.config.tech_name
|
||||
OPTS.tech_name = OPTS.tech_name
|
||||
|
||||
|
||||
# environment variable should point to the technology dir
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ class hierarchical_decoder(design.design):
|
|||
def __init__(self, rows):
|
||||
design.design.__init__(self, "hierarchical_decoder_{0}rows".format(rows))
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell_height = self.mod_bitcell.height
|
||||
|
||||
self.pre2x4_inst = []
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ class hierarchical_predecode(design.design):
|
|||
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
||||
design.design.__init__(self, name="pre{0}x{1}".format(self.number_of_inputs,self.number_of_outputs))
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell_height = self.mod_bitcell.height
|
||||
|
||||
|
||||
|
|
@ -252,7 +252,7 @@ class hierarchical_predecode(design.design):
|
|||
if self.number_of_inputs == 2:
|
||||
gate_lst = ["A","B"]
|
||||
else:
|
||||
gate_lst = ["A","B","C"]
|
||||
gate_lst = ["A","B","C"]
|
||||
|
||||
# this will connect pins A,B or A,B,C
|
||||
for rail_pin,gate_pin in zip(index_lst,gate_lst):
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
self.create_rails()
|
||||
self.add_input_inverters()
|
||||
self.add_output_inverters()
|
||||
connections =[["in[0]", "in[1]", "Z[3]", "vdd", "gnd"],
|
||||
["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"],
|
||||
connections =[["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"],
|
||||
["in[0]", "inbar[1]", "Z[1]", "vdd", "gnd"],
|
||||
["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"]]
|
||||
["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"],
|
||||
["in[0]", "in[1]", "Z[3]", "vdd", "gnd"]]
|
||||
self.add_nand(connections)
|
||||
self.route()
|
||||
|
||||
|
|
|
|||
|
|
@ -27,26 +27,26 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
self.create_rails()
|
||||
self.add_input_inverters()
|
||||
self.add_output_inverters()
|
||||
connections=[["in[0]", "in[1]", "in[2]", "Z[7]", "vdd", "gnd"],
|
||||
["in[0]", "in[1]", "inbar[2]", "Z[6]", "vdd", "gnd"],
|
||||
["in[0]", "inbar[1]", "in[2]", "Z[5]", "vdd", "gnd"],
|
||||
["in[0]", "inbar[1]", "inbar[2]", "Z[4]", "vdd", "gnd"],
|
||||
["inbar[0]", "in[1]", "in[2]", "Z[3]", "vdd", "gnd"],
|
||||
connections=[["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"],
|
||||
["in[0]", "inbar[1]", "inbar[2]", "Z[1]", "vdd", "gnd"],
|
||||
["inbar[0]", "in[1]", "inbar[2]", "Z[2]", "vdd", "gnd"],
|
||||
["inbar[0]", "inbar[1]", "in[2]", "Z[1]", "vdd", "gnd"],
|
||||
["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"]]
|
||||
["in[0]", "in[1]", "inbar[2]", "Z[3]", "vdd", "gnd"],
|
||||
["inbar[0]", "inbar[1]", "in[2]", "Z[4]", "vdd", "gnd"],
|
||||
["in[0]", "inbar[1]", "in[2]", "Z[5]", "vdd", "gnd"],
|
||||
["inbar[0]", "in[1]", "in[2]", "Z[6]", "vdd", "gnd"],
|
||||
["in[0]", "in[1]", "in[2]", "Z[7]", "vdd", "gnd"]]
|
||||
self.add_nand(connections)
|
||||
self.route()
|
||||
|
||||
def get_nand_input_line_combination(self):
|
||||
""" These are the decoder connections of the NAND gates to the A,B,C pins """
|
||||
combination = [["Abar[0]", "Abar[1]", "Abar[2]"],
|
||||
["Abar[0]", "Abar[1]", "A[2]"],
|
||||
["Abar[0]", "A[1]", "Abar[2]"],
|
||||
["Abar[0]", "A[1]", "A[2]"],
|
||||
["A[0]", "Abar[1]", "Abar[2]"],
|
||||
["A[0]", "Abar[1]", "A[2]"],
|
||||
["Abar[0]", "A[1]", "Abar[2]"],
|
||||
["A[0]", "A[1]", "Abar[2]"],
|
||||
["Abar[0]", "Abar[1]", "A[2]"],
|
||||
["A[0]", "Abar[1]", "A[2]"],
|
||||
["Abar[0]", "A[1]", "A[2]"],
|
||||
["A[0]", "A[1]", "A[2]"]]
|
||||
return combination
|
||||
|
||||
|
|
|
|||
|
|
@ -60,21 +60,44 @@ class layout(lef.lef):
|
|||
def find_lowest_coords(self):
|
||||
"""Finds the lowest set of 2d cartesian coordinates within
|
||||
this layout"""
|
||||
|
||||
lowestx1 = min(obj.lx() for obj in self.objs if obj.name!="label")
|
||||
lowesty1 = min(obj.by() for obj in self.objs if obj.name!="label")
|
||||
lowestx2 = min(inst.lx() for inst in self.insts)
|
||||
lowesty2 = min(inst.by() for inst in self.insts)
|
||||
return vector(min(lowestx1, lowestx2), min(lowesty1, lowesty2))
|
||||
|
||||
if len(self.objs)>0:
|
||||
lowestx1 = min(obj.lx() for obj in self.objs if obj.name!="label")
|
||||
lowesty1 = min(obj.by() for obj in self.objs if obj.name!="label")
|
||||
else:
|
||||
lowestx1=lowesty1=None
|
||||
if len(self.insts)>0:
|
||||
lowestx2 = min(inst.lx() for inst in self.insts)
|
||||
lowesty2 = min(inst.by() for inst in self.insts)
|
||||
else:
|
||||
lowestx2=lowesty2=None
|
||||
if lowestx1==None:
|
||||
return vector(lowestx2,lowesty2)
|
||||
elif lowestx2==None:
|
||||
return vector(lowestx1,lowesty1)
|
||||
else:
|
||||
return vector(min(lowestx1, lowestx2), min(lowesty1, lowesty2))
|
||||
|
||||
def find_highest_coords(self):
|
||||
"""Finds the highest set of 2d cartesian coordinates within
|
||||
this layout"""
|
||||
highestx1 = min(obj.rx() for obj in self.objs if obj.name!="label")
|
||||
highesty1 = min(obj.uy() for obj in self.objs if obj.name!="label")
|
||||
highestx2 = min(inst.rx() for inst in self.insts)
|
||||
highesty2 = min(inst.uy() for inst in self.insts)
|
||||
return vector(min(highestx1, highestx2), min(highesty1, highesty2))
|
||||
|
||||
if len(self.objs)>0:
|
||||
highestx1 = max(obj.rx() for obj in self.objs if obj.name!="label")
|
||||
highesty1 = max(obj.uy() for obj in self.objs if obj.name!="label")
|
||||
else:
|
||||
highestx1=highesty1=None
|
||||
if len(self.insts)>0:
|
||||
highestx2 = max(inst.rx() for inst in self.insts)
|
||||
highesty2 = max(inst.uy() for inst in self.insts)
|
||||
else:
|
||||
highestx2=highesty2=None
|
||||
if highestx1==None:
|
||||
return vector(highestx2,highesty2)
|
||||
elif highestx2==None:
|
||||
return vector(highestx1,highesty1)
|
||||
else:
|
||||
return vector(max(highestx1, highestx2), max(highesty1, highesty2))
|
||||
|
||||
|
||||
def translate_all(self, offset):
|
||||
|
|
@ -143,9 +166,11 @@ class layout(lef.lef):
|
|||
debug.error("Nonrectilinear center rect!",-1)
|
||||
elif start.x!=end.x:
|
||||
offset = vector(0,0.5*minwidth_layer)
|
||||
return self.add_rect(layer,start-offset,end.x-start.x,minwidth_layer)
|
||||
else:
|
||||
offset = vector(0.5*minwidth_layer,0)
|
||||
return self.add_rect(layer,start-offset,end.x-start.x,minwidth_layer)
|
||||
return self.add_rect(layer,start-offset,minwidth_layer,end.y-start.y)
|
||||
|
||||
|
||||
|
||||
def get_pin(self, text):
|
||||
|
|
@ -308,42 +333,50 @@ class layout(lef.lef):
|
|||
layer_stack=layers,
|
||||
position_list=coordinates)
|
||||
|
||||
def add_contact(self, layers, offset, size=[1,1], mirror="R0", rotate=0):
|
||||
def add_contact(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None):
|
||||
""" This is just an alias for a via."""
|
||||
return self.add_via(layers=layers,
|
||||
offset=offset,
|
||||
size=size,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
rotate=rotate,
|
||||
implant_type=implant_type,
|
||||
well_type=well_type)
|
||||
|
||||
def add_contact_center(self, layers, offset, size=[1,1], mirror="R0", rotate=0):
|
||||
def add_contact_center(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None):
|
||||
""" This is just an alias for a via."""
|
||||
return self.add_via_center(layers=layers,
|
||||
offset=offset,
|
||||
size=size,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
rotate=rotate,
|
||||
implant_type=implant_type,
|
||||
well_type=well_type)
|
||||
|
||||
def add_via(self, layers, offset, size=[1,1], mirror="R0", rotate=0):
|
||||
def add_via(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None):
|
||||
""" Add a three layer via structure. """
|
||||
import contact
|
||||
via = contact.contact(layer_stack=layers,
|
||||
dimensions=size)
|
||||
dimensions=size,
|
||||
implant_type=implant_type,
|
||||
well_type=well_type)
|
||||
self.add_mod(via)
|
||||
self.add_inst(name=via.name,
|
||||
mod=via,
|
||||
offset=offset,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
inst=self.add_inst(name=via.name,
|
||||
mod=via,
|
||||
offset=offset,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
# We don't model the logical connectivity of wires/paths
|
||||
self.connect_inst([])
|
||||
return via
|
||||
return inst
|
||||
|
||||
def add_via_center(self, layers, offset, size=[1,1], mirror="R0", rotate=0):
|
||||
def add_via_center(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None):
|
||||
""" Add a three layer via structure by the center coordinate accounting for mirroring and rotation. """
|
||||
import contact
|
||||
via = contact.contact(layer_stack=layers,
|
||||
dimensions=size)
|
||||
dimensions=size,
|
||||
implant_type=implant_type,
|
||||
well_type=well_type)
|
||||
|
||||
debug.check(mirror=="R0","Use rotate to rotate vias instead of mirror.")
|
||||
|
||||
|
|
@ -363,14 +396,14 @@ class layout(lef.lef):
|
|||
|
||||
|
||||
self.add_mod(via)
|
||||
self.add_inst(name=via.name,
|
||||
mod=via,
|
||||
offset=corrected_offset,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
inst=self.add_inst(name=via.name,
|
||||
mod=via,
|
||||
offset=corrected_offset,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
# We don't model the logical connectivity of wires/paths
|
||||
self.connect_inst([])
|
||||
return via
|
||||
return inst
|
||||
|
||||
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
|
||||
"""Adds a ptx module to the design."""
|
||||
|
|
@ -379,12 +412,12 @@ class layout(lef.lef):
|
|||
mults=mults,
|
||||
tx_type=tx_type)
|
||||
self.add_mod(mos)
|
||||
self.add_inst(name=mos.name,
|
||||
mod=mos,
|
||||
offset=offset,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
return mos
|
||||
inst=self.add_inst(name=mos.name,
|
||||
mod=mos,
|
||||
offset=offset,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
return inst
|
||||
|
||||
|
||||
|
||||
|
|
@ -488,6 +521,26 @@ class layout(lef.lef):
|
|||
|
||||
return blockages
|
||||
|
||||
def add_enclosure(self, insts, layer="nwell"):
|
||||
""" Add a layer that surrounds the given instances. Useful
|
||||
for creating wells, for example. Doesn't check for minimum widths or
|
||||
spacings."""
|
||||
|
||||
xmin=insts[0].lx()
|
||||
ymin=insts[0].by()
|
||||
xmax=insts[0].rx()
|
||||
ymax=insts[0].uy()
|
||||
for inst in insts:
|
||||
xmin = min(xmin, inst.lx())
|
||||
ymin = min(ymin, inst.by())
|
||||
xmax = max(xmax, inst.rx())
|
||||
ymax = max(ymax, inst.uy())
|
||||
|
||||
self.add_rect(layer=layer,
|
||||
offset=vector(xmin,ymin),
|
||||
width=xmax-xmin,
|
||||
height=ymax-ymin)
|
||||
|
||||
def pdf_write(self, pdf_name):
|
||||
# NOTE: Currently does not work (Needs further research)
|
||||
#self.pdf_name = self.name + ".pdf"
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ class spice(verilog.verilog):
|
|||
return
|
||||
if self.pins == []:
|
||||
return
|
||||
|
||||
|
||||
# write out the first spice line (the subcircuit)
|
||||
sp.write("\n.SUBCKT {0} {1}\n".format(self.name,
|
||||
" ".join(self.pins)))
|
||||
|
|
@ -146,9 +148,15 @@ class spice(verilog.verilog):
|
|||
# these are wires and paths
|
||||
if self.conns[i] == []:
|
||||
continue
|
||||
sp.write("X{0} {1} {2}\n".format(self.insts[i].name,
|
||||
" ".join(self.conns[i]),
|
||||
self.insts[i].mod.name))
|
||||
if hasattr(self.insts[i].mod,"spice_device"):
|
||||
sp.write(self.insts[i].mod.spice_device.format(self.insts[i].name,
|
||||
" ".join(self.conns[i])))
|
||||
sp.write("\n")
|
||||
|
||||
else:
|
||||
sp.write("X{0} {1} {2}\n".format(self.insts[i].name,
|
||||
" ".join(self.conns[i]),
|
||||
self.insts[i].mod.name))
|
||||
|
||||
sp.write(".ENDS {0}\n".format(self.name))
|
||||
|
||||
|
|
@ -158,6 +166,7 @@ class spice(verilog.verilog):
|
|||
#if os.path.isfile(self.sp_file):
|
||||
# sp.write("\n* {0}\n".format(self.sp_file))
|
||||
sp.write("\n".join(self.spice))
|
||||
|
||||
sp.write("\n")
|
||||
|
||||
def sp_write(self, spname):
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ class ms_flop_array(design.design):
|
|||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
|
||||
c = reload(__import__(OPTS.config.ms_flop))
|
||||
self.mod_ms_flop = getattr(c, OPTS.config.ms_flop)
|
||||
c = reload(__import__(OPTS.ms_flop))
|
||||
self.mod_ms_flop = getattr(c, OPTS.ms_flop)
|
||||
self.ms = self.mod_ms_flop("ms_flop")
|
||||
self.add_mod(self.ms)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,19 +39,19 @@ globals.print_banner()
|
|||
globals.init_openram(args[0])
|
||||
|
||||
# Check if all arguments are integers for bits, size, banks
|
||||
if type(OPTS.config.word_size)!=int:
|
||||
debug.error("{0} is not an integer in config file.".format(OPTS.config.word_size))
|
||||
if type(OPTS.config.num_words)!=int:
|
||||
debug.error("{0} is not an integer in config file.".format(OPTS.config.sram_size))
|
||||
if type(OPTS.config.num_banks)!=int:
|
||||
debug.error("{0} is not an integer in config file.".format(OPTS.config.num_banks))
|
||||
if type(OPTS.word_size)!=int:
|
||||
debug.error("{0} is not an integer in config file.".format(OPTS.word_size))
|
||||
if type(OPTS.num_words)!=int:
|
||||
debug.error("{0} is not an integer in config file.".format(OPTS.sram_size))
|
||||
if type(OPTS.num_banks)!=int:
|
||||
debug.error("{0} is not an integer in config file.".format(OPTS.num_banks))
|
||||
|
||||
if not OPTS.config.tech_name:
|
||||
if not OPTS.tech_name:
|
||||
debug.error("Tech name must be specified in config file.")
|
||||
|
||||
word_size = OPTS.config.word_size
|
||||
num_words = OPTS.config.num_words
|
||||
num_banks = OPTS.config.num_banks
|
||||
word_size = OPTS.word_size
|
||||
num_words = OPTS.num_words
|
||||
num_banks = OPTS.num_banks
|
||||
|
||||
if (OPTS.output_name == ""):
|
||||
OPTS.output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,
|
||||
|
|
@ -79,10 +79,6 @@ s = sram.sram(word_size=word_size,
|
|||
num_banks=num_banks,
|
||||
name=OPTS.output_name)
|
||||
last_time=print_time("SRAM creation", datetime.datetime.now(), last_time)
|
||||
# Measure design area
|
||||
# Not working?
|
||||
#cell_size = s.gds.measureSize(s.name)
|
||||
#print("Area:", cell_size[0] * cell_size[1])
|
||||
|
||||
# Output the files for the resulting SRAM
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import os
|
|||
|
||||
class options(optparse.Values):
|
||||
"""
|
||||
Class for holding all of the OpenRAM options.
|
||||
Class for holding all of the OpenRAM options. All of these options can be over-riden in a configuration file
|
||||
that is the sole required command-line positional argument for openram.py.
|
||||
"""
|
||||
|
||||
# This is the technology directory.
|
||||
|
|
@ -13,6 +14,7 @@ class options(optparse.Values):
|
|||
tech_name = ""
|
||||
# This is the temp directory where all intermediate results are stored.
|
||||
openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid())
|
||||
#openram_temp = "/Users/{}/openram_temp/".format(getpass.getuser())
|
||||
# This is the verbosity level to control debug information. 0 is none, 1
|
||||
# is minimal, etc.
|
||||
debug_level = 0
|
||||
|
|
@ -23,9 +25,9 @@ class options(optparse.Values):
|
|||
# Should we print out the banner at startup
|
||||
print_banner = True
|
||||
# The DRC/LVS/PEX executable being used which is derived from the user PATH.
|
||||
drc_exe = ""
|
||||
lvs_exe = ""
|
||||
pex_exe = ""
|
||||
drc_exe = None
|
||||
lvs_exe = None
|
||||
pex_exe = None
|
||||
# The spice executable being used which is derived from the user PATH.
|
||||
spice_exe = ""
|
||||
# Run with extracted parasitics
|
||||
|
|
@ -35,8 +37,32 @@ class options(optparse.Values):
|
|||
# Use detailed LEF blockages
|
||||
detailed_blockages = True
|
||||
# Define the output file paths
|
||||
output_path = ""
|
||||
output_path = "."
|
||||
# Define the output file base name
|
||||
output_name = ""
|
||||
output_name = "sram"
|
||||
# Use analytical delay models by default rather than (slow) characterization
|
||||
analytical_delay = True
|
||||
# Purge the temp directory after a successful run (doesn't purge on errors, anyhow)
|
||||
purge_temp = True
|
||||
|
||||
|
||||
# These are the default modules that can be over-riden
|
||||
decoder = "hierarchical_decoder"
|
||||
ms_flop = "ms_flop"
|
||||
ms_flop_array = "ms_flop_array"
|
||||
control_logic = "control_logic"
|
||||
bitcell_array = "bitcell_array"
|
||||
sense_amp = "sense_amp"
|
||||
sense_amp_array = "sense_amp_array"
|
||||
precharge_array = "precharge_array"
|
||||
column_mux_array = "single_level_column_mux_array"
|
||||
write_driver = "write_driver"
|
||||
write_driver_array = "write_driver_array"
|
||||
tri_gate = "tri_gate"
|
||||
tri_gate_array = "tri_gate_array"
|
||||
wordline_driver = "wordline_driver"
|
||||
replica_bitline = "replica_bitline"
|
||||
replica_bitcell = "replica_bitcell"
|
||||
bitcell = "bitcell"
|
||||
delay_chain = "delay_chain"
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class pgate(design.design):
|
|||
debug.check(nmos_gate_pin.ll().x==pmos_gate_pin.ll().x, "Connecting unaligned gates not supported.")
|
||||
|
||||
# Pick point on the left of NMOS and connect down to PMOS
|
||||
nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5*drc["minwidth_poly"],0)
|
||||
nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5*self.poly_width,0)
|
||||
pmos_gate_pos = vector(nmos_gate_pos.x,pmos_gate_pin.bc().y)
|
||||
self.add_path("poly",[nmos_gate_pos,pmos_gate_pos])
|
||||
|
||||
|
|
@ -97,93 +97,117 @@ class pgate(design.design):
|
|||
def extend_wells(self, middle_position):
|
||||
""" Extend the n/p wells to cover whole cell """
|
||||
|
||||
nwell_height = self.height - middle_position.y
|
||||
# Add a rail width to extend the well to the top of the rail
|
||||
max_y_offset = self.height + 0.5*self.m1_width
|
||||
self.nwell_position = middle_position
|
||||
nwell_height = max_y_offset - middle_position.y
|
||||
if info["has_nwell"]:
|
||||
self.add_rect(layer="nwell",
|
||||
offset=middle_position,
|
||||
width=self.well_width,
|
||||
height=nwell_height)
|
||||
self.add_rect(layer="vtg",
|
||||
offset=middle_position,
|
||||
offset=self.nwell_position,
|
||||
width=self.well_width,
|
||||
height=nwell_height)
|
||||
|
||||
pwell_position = vector(0,-0.5*self.m1_width)
|
||||
pwell_height = middle_position.y-pwell_position.y
|
||||
if info["has_pwell"]:
|
||||
self.add_rect(layer="pwell",
|
||||
offset=vector(0,0),
|
||||
offset=pwell_position,
|
||||
width=self.well_width,
|
||||
height=middle_position.y)
|
||||
height=pwell_height)
|
||||
self.add_rect(layer="vtg",
|
||||
offset=vector(0,0),
|
||||
offset=pwell_position,
|
||||
width=self.well_width,
|
||||
height=middle_position.y)
|
||||
height=pwell_height)
|
||||
|
||||
def add_nwell_contact(self, nmos, nmos_pos):
|
||||
""" Add an nwell contact next to the given nmos device. """
|
||||
def add_nwell_contact(self, pmos, pmos_pos):
|
||||
""" Add an nwell contact next to the given pmos device. """
|
||||
|
||||
layer_stack = ("active", "contact", "metal1")
|
||||
|
||||
# To the right a spacing away from the nmos right active edge
|
||||
nwell_contact_xoffset = nmos_pos.x + nmos.active_width + drc["active_to_body_active"]
|
||||
nwell_contact_yoffset = nmos_pos.y
|
||||
nwell_offset = vector(nwell_contact_xoffset, nwell_contact_yoffset)
|
||||
# Offset by half a contact in x and y
|
||||
nwell_offset += vector(0.5*nmos.active_contact.first_layer_width,
|
||||
0.5*nmos.active_contact.first_layer_height)
|
||||
self.nwell_contact=self.add_contact_center(layers=layer_stack,
|
||||
offset=nwell_offset)
|
||||
self.add_rect_center(layer="metal1",
|
||||
offset=nwell_offset.scale(1,0.5),
|
||||
width=self.nwell_contact.second_layer_width,
|
||||
height=nwell_offset.y)
|
||||
# Now add the full active and implant for the NMOS
|
||||
nwell_offset = nmos_pos + vector(nmos.active_width,0)
|
||||
nwell_contact_width = drc["active_to_body_active"] + nmos.active_contact.width
|
||||
self.add_rect(layer="active",
|
||||
offset=nwell_offset,
|
||||
width=nwell_contact_width,
|
||||
height=nmos.active_height)
|
||||
|
||||
implant_offset = nwell_offset + vector(drc["implant_to_implant"],0)
|
||||
implant_width = nwell_contact_width - drc["implant_to_implant"]
|
||||
self.add_rect(layer="pimplant",
|
||||
offset=implant_offset,
|
||||
width=implant_width,
|
||||
height=nmos.active_height)
|
||||
|
||||
|
||||
def add_pwell_contact(self, pmos, pmos_pos):
|
||||
""" Add an pwell contact next to the given pmos device. """
|
||||
|
||||
layer_stack = ("active", "contact", "metal1")
|
||||
|
||||
|
||||
# To the right a spacing away from the pmos right active edge
|
||||
pwell_contact_xoffset = pmos_pos.x + pmos.active_width + drc["active_to_body_active"]
|
||||
pwell_contact_yoffset = pmos_pos.y + pmos.active_height - pmos.active_contact.height
|
||||
pwell_offset = vector(pwell_contact_xoffset, pwell_contact_yoffset)
|
||||
# Offset by half a contact
|
||||
pwell_offset += vector(0.5*pmos.active_contact.first_layer_width,
|
||||
contact_xoffset = pmos_pos.x + pmos.active_width + drc["active_to_body_active"]
|
||||
# Must be at least an well enclosure of active down from the top of the well
|
||||
# OR align the active with the top of PMOS active.
|
||||
max_y_offset = self.height + 0.5*self.m1_width
|
||||
contact_yoffset = min(pmos_pos.y + pmos.active_height - pmos.active_contact.first_layer_height,
|
||||
max_y_offset - pmos.active_contact.first_layer_height/2 - self.well_enclose_active)
|
||||
contact_offset = vector(contact_xoffset, contact_yoffset)
|
||||
# Offset by half a contact in x and y
|
||||
contact_offset += vector(0.5*pmos.active_contact.first_layer_width,
|
||||
0.5*pmos.active_contact.first_layer_height)
|
||||
self.pwell_contact=self.add_contact_center(layers=layer_stack,
|
||||
offset=pwell_offset)
|
||||
self.nwell_contact=self.add_contact_center(layers=layer_stack,
|
||||
offset=contact_offset,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
self.add_rect_center(layer="metal1",
|
||||
offset=pwell_offset + vector(0,0.5*(self.height-pwell_offset.y)),
|
||||
width=self.pwell_contact.second_layer_width,
|
||||
height=self.height - pwell_offset.y)
|
||||
# Now add the full active and implant for the PMOS
|
||||
pwell_offset = pmos_pos + vector(pmos.active_width,0)
|
||||
pwell_contact_width = drc["active_to_body_active"] + pmos.active_contact.width
|
||||
self.add_rect(layer="active",
|
||||
offset=pwell_offset,
|
||||
width=pwell_contact_width,
|
||||
height=pmos.active_height)
|
||||
|
||||
|
||||
implant_offset = pwell_offset + vector(drc["implant_to_implant"],0)
|
||||
implant_width = pwell_contact_width - drc["implant_to_implant"]
|
||||
self.add_rect(layer="nimplant",
|
||||
offset=implant_offset,
|
||||
width=implant_width,
|
||||
height=pmos.active_height)
|
||||
offset=contact_offset + vector(0,0.5*(self.height-contact_offset.y)),
|
||||
width=self.nwell_contact.mod.second_layer_width,
|
||||
height=self.height - contact_offset.y)
|
||||
|
||||
# Now add the full active and implant for the PMOS
|
||||
#active_offset = pmos_pos + vector(pmos.active_width,0)
|
||||
# This might be needed if the spacing between the actives is not satisifed
|
||||
# self.add_rect(layer="active",
|
||||
# offset=active_offset,
|
||||
# width=pmos.active_contact.width,
|
||||
# height=pmos.active_height)
|
||||
|
||||
# we need to ensure implants don't overlap and are spaced far enough apart
|
||||
# implant_spacing = self.implant_space+self.implant_enclose_active
|
||||
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active)
|
||||
# implant_width = pmos.active_contact.width + 2*self.implant_enclose_active
|
||||
# implant_height = pmos.active_height + 2*self.implant_enclose_active
|
||||
# self.add_rect(layer="nimplant",
|
||||
# offset=implant_offset,
|
||||
# width=implant_width,
|
||||
# height=implant_height)
|
||||
|
||||
# Return the top of the well
|
||||
|
||||
def add_pwell_contact(self, nmos, nmos_pos):
|
||||
""" Add an pwell contact next to the given nmos device. """
|
||||
|
||||
layer_stack = ("active", "contact", "metal1")
|
||||
|
||||
pwell_position = vector(0,-0.5*self.m1_width)
|
||||
|
||||
# To the right a spacing away from the nmos right active edge
|
||||
contact_xoffset = nmos_pos.x + nmos.active_width + drc["active_to_body_active"]
|
||||
# Must be at least an well enclosure of active up from the bottom of the well
|
||||
contact_yoffset = max(nmos_pos.y,
|
||||
self.well_enclose_active - nmos.active_contact.first_layer_height/2)
|
||||
contact_offset = vector(contact_xoffset, contact_yoffset)
|
||||
|
||||
# Offset by half a contact
|
||||
contact_offset += vector(0.5*nmos.active_contact.first_layer_width,
|
||||
0.5*nmos.active_contact.first_layer_height)
|
||||
self.pwell_contact=self.add_contact_center(layers=layer_stack,
|
||||
offset=contact_offset,
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
self.add_rect_center(layer="metal1",
|
||||
offset=contact_offset.scale(1,0.5),
|
||||
width=self.pwell_contact.mod.second_layer_width,
|
||||
height=contact_offset.y)
|
||||
|
||||
# Now add the full active and implant for the NMOS
|
||||
# active_offset = nmos_pos + vector(nmos.active_width,0)
|
||||
# This might be needed if the spacing between the actives is not satisifed
|
||||
# self.add_rect(layer="active",
|
||||
# offset=active_offset,
|
||||
# width=nmos.active_contact.width,
|
||||
# height=nmos.active_height)
|
||||
|
||||
# implant_spacing = self.implant_space+self.implant_enclose_active
|
||||
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active)
|
||||
# implant_width = nmos.active_contact.width + 2*self.implant_enclose_active
|
||||
# implant_height = nmos.active_height + 2*self.implant_enclose_active
|
||||
# self.add_rect(layer="pimplant",
|
||||
# offset=implant_offset,
|
||||
# width=implant_width,
|
||||
# height=implant_height)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ class pinv(pgate.pgate):
|
|||
from center of rail to rail.. The route_output will route the
|
||||
output to the right side of the cell for easier access.
|
||||
"""
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
bitcell = getattr(c, OPTS.bitcell)
|
||||
|
||||
unique_id = 1
|
||||
|
||||
|
|
@ -57,8 +57,8 @@ class pinv(pgate.pgate):
|
|||
self.setup_layout_constants()
|
||||
self.add_supply_rails()
|
||||
self.add_ptx()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.connect_rails()
|
||||
self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", rotate=0)
|
||||
self.route_outputs()
|
||||
|
|
@ -128,9 +128,6 @@ class pinv(pgate.pgate):
|
|||
self.width = self.well_width
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
# This will help with the wells
|
||||
self.well_pos = vector(0,0.4*self.height)
|
||||
|
||||
|
||||
|
||||
def create_ptx(self):
|
||||
|
|
@ -183,9 +180,14 @@ class pinv(pgate.pgate):
|
|||
self.connect_inst(["Z", "A", "gnd", "gnd"])
|
||||
|
||||
|
||||
# Output position will be in between the PMOS and NMOS
|
||||
self.output_pos = vector(0,0.5*(self.pmos_pos.y+self.nmos_pos.y+self.nmos.active_height))
|
||||
# Output position will be in between the PMOS and NMOS drains
|
||||
pmos_drain_pos = self.pmos_inst.get_pin("D").ll()
|
||||
nmos_drain_pos = self.nmos_inst.get_pin("D").ul()
|
||||
self.output_pos = vector(0,0.5*(pmos_drain_pos.y+nmos_drain_pos.y))
|
||||
|
||||
# This will help with the wells
|
||||
self.well_pos = vector(0,self.nmos_inst.uy())
|
||||
|
||||
|
||||
|
||||
def route_outputs(self):
|
||||
|
|
@ -196,8 +198,8 @@ class pinv(pgate.pgate):
|
|||
pmos_drain_pin = self.pmos_inst.get_pin("D")
|
||||
|
||||
# Pick point at right most of NMOS and connect down to PMOS
|
||||
nmos_drain_pos = nmos_drain_pin.ur() - vector(0.5*self.m1_width,0)
|
||||
pmos_drain_pos = vector(nmos_drain_pos.x,pmos_drain_pin.bc().y)
|
||||
nmos_drain_pos = nmos_drain_pin.lr() - vector(0.5*self.m1_width,0)
|
||||
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.bc().y)
|
||||
self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos])
|
||||
|
||||
# Remember the mid for the output
|
||||
|
|
@ -220,9 +222,9 @@ class pinv(pgate.pgate):
|
|||
def add_well_contacts(self):
|
||||
""" Add n/p well taps to the layout and connect to supplies """
|
||||
|
||||
self.add_nwell_contact(self.nmos, self.nmos_pos)
|
||||
self.add_nwell_contact(self.pmos, self.pmos_pos)
|
||||
|
||||
self.add_pwell_contact(self.pmos, self.pmos_pos)
|
||||
self.add_pwell_contact(self.nmos, self.nmos_pos)
|
||||
|
||||
def connect_rails(self):
|
||||
""" Connect the nmos and pmos to its respective power rails """
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ class pnand2(pgate.pgate):
|
|||
This model use ptx to generate a 2-input nand within a cetrain height.
|
||||
"""
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
bitcell = getattr(c, OPTS.bitcell)
|
||||
|
||||
unique_id = 1
|
||||
|
||||
|
|
@ -50,8 +50,8 @@ class pnand2(pgate.pgate):
|
|||
self.setup_layout_constants()
|
||||
self.add_supply_rails()
|
||||
self.add_ptx()
|
||||
self.add_well_contacts()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
|
@ -94,9 +94,6 @@ class pnand2(pgate.pgate):
|
|||
self.width = self.well_width
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
# This will help with the wells
|
||||
self.well_pos = vector(0,0.4*self.height)
|
||||
|
||||
# This is the extra space needed to ensure DRC rules to the active contacts
|
||||
extra_contact_space = max(-self.nmos.get_pin("D").by(),0)
|
||||
# This is a poly-to-poly of a flipped cell
|
||||
|
|
@ -139,22 +136,25 @@ class pnand2(pgate.pgate):
|
|||
self.nmos1_inst=self.add_inst(name="pnand2_nmos1",
|
||||
mod=self.nmos,
|
||||
offset=nmos1_pos)
|
||||
self.connect_inst(["Z", "A", "net1", "gnd"])
|
||||
self.connect_inst(["Z", "B", "net1", "gnd"])
|
||||
|
||||
self.nmos2_pos = nmos1_pos + self.overlap_offset
|
||||
self.nmos2_inst=self.add_inst(name="pnand2_nmos2",
|
||||
mod=self.nmos,
|
||||
offset=self.nmos2_pos)
|
||||
self.connect_inst(["net1", "B", "gnd", "gnd"])
|
||||
self.connect_inst(["net1", "A", "gnd", "gnd"])
|
||||
|
||||
# Output position will be in between the PMOS and NMOS
|
||||
self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height))
|
||||
|
||||
# This will help with the wells
|
||||
self.well_pos = vector(0,self.nmos1_inst.uy())
|
||||
|
||||
def add_well_contacts(self):
|
||||
""" Add n/p well taps to the layout and connect to supplies """
|
||||
""" Add n/p well taps to the layout and connect to supplies AFTER the wells are created """
|
||||
|
||||
self.add_nwell_contact(self.nmos, self.nmos2_pos)
|
||||
self.add_pwell_contact(self.pmos, self.pmos2_pos)
|
||||
self.add_nwell_contact(self.pmos, self.pmos2_pos)
|
||||
self.add_pwell_contact(self.nmos, self.nmos2_pos)
|
||||
|
||||
|
||||
def connect_rails(self):
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ class pnand3(pgate.pgate):
|
|||
This model use ptx to generate a 2-input nand within a cetrain height.
|
||||
"""
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
bitcell = getattr(c, OPTS.bitcell)
|
||||
|
||||
unique_id = 1
|
||||
|
||||
|
|
@ -24,7 +24,9 @@ class pnand3(pgate.pgate):
|
|||
pgate.pgate.__init__(self, name)
|
||||
debug.info(2, "create pnand3 structure {0} with size of {1}".format(name, size))
|
||||
|
||||
self.nmos_size = 3*size
|
||||
# We have trouble pitch matching a 3x sizes to the bitcell...
|
||||
# If we relax this, we could size this better.
|
||||
self.nmos_size = 2*size
|
||||
self.pmos_size = parameter["beta"]*size
|
||||
self.nmos_width = self.nmos_size*drc["minwidth_tx"]
|
||||
self.pmos_width = self.pmos_size*drc["minwidth_tx"]
|
||||
|
|
@ -50,8 +52,8 @@ class pnand3(pgate.pgate):
|
|||
self.setup_layout_constants()
|
||||
self.add_supply_rails()
|
||||
self.add_ptx()
|
||||
self.add_well_contacts()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
|
@ -88,13 +90,13 @@ class pnand3(pgate.pgate):
|
|||
|
||||
# This will help with the wells and the input/output placement
|
||||
self.output_pos = vector(0,0.5*self.height)
|
||||
self.well_pos = vector(0,0.4*self.height)
|
||||
|
||||
# This is the extra space needed to ensure DRC rules to the active contacts
|
||||
nmos = ptx(tx_type="nmos")
|
||||
extra_contact_space = max(-nmos.get_pin("D").by(),0)
|
||||
# This is a poly-to-poly of a flipped cell
|
||||
# This is extra liberal for pnand3 because we know there are big transistor sizes
|
||||
# and so contacts won't interfere with the rails. Therefore, ignore metal spacing.
|
||||
# We need to do this to fit the 3 inputs in with M2M3 via accessibility.
|
||||
self.top_bottom_space = max(drc["poly_extend_active"], self.poly_space)
|
||||
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
|
||||
drc["poly_extend_active"], self.poly_space)
|
||||
|
||||
def add_supply_rails(self):
|
||||
""" Add vdd/gnd rails to the top and bottom. """
|
||||
|
|
@ -138,7 +140,7 @@ class pnand3(pgate.pgate):
|
|||
self.nmos1_inst=self.add_inst(name="pnand3_nmos1",
|
||||
mod=self.nmos,
|
||||
offset=nmos1_pos)
|
||||
self.connect_inst(["Z", "A", "net1", "gnd"])
|
||||
self.connect_inst(["Z", "C", "net1", "gnd"])
|
||||
|
||||
nmos2_pos = nmos1_pos + self.overlap_offset
|
||||
self.nmos2_inst=self.add_inst(name="pnand3_nmos2",
|
||||
|
|
@ -151,14 +153,16 @@ class pnand3(pgate.pgate):
|
|||
self.nmos3_inst=self.add_inst(name="pnand3_nmos3",
|
||||
mod=self.nmos,
|
||||
offset=self.nmos3_pos)
|
||||
self.connect_inst(["net2", "C", "gnd", "gnd"])
|
||||
self.connect_inst(["net2", "A", "gnd", "gnd"])
|
||||
|
||||
# This should be placed at the top of the NMOS well
|
||||
self.well_pos = vector(0,self.nmos1_inst.uy())
|
||||
|
||||
def add_well_contacts(self):
|
||||
""" Add n/p well taps to the layout and connect to supplies """
|
||||
|
||||
self.add_nwell_contact(self.nmos, self.nmos3_pos)
|
||||
self.add_pwell_contact(self.pmos, self.pmos3_pos)
|
||||
self.add_nwell_contact(self.pmos, self.pmos3_pos)
|
||||
self.add_pwell_contact(self.nmos, self.nmos3_pos)
|
||||
|
||||
|
||||
def connect_rails(self):
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ class pnor2(pgate.pgate):
|
|||
This model use ptx to generate a 2-input nor within a cetrain height.
|
||||
"""
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
bitcell = getattr(c, OPTS.bitcell)
|
||||
|
||||
unique_id = 1
|
||||
|
||||
|
|
@ -51,8 +51,8 @@ class pnor2(pgate.pgate):
|
|||
self.setup_layout_constants()
|
||||
self.add_supply_rails()
|
||||
self.add_ptx()
|
||||
self.add_well_contacts()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
|
@ -98,9 +98,6 @@ class pnor2(pgate.pgate):
|
|||
self.width = self.well_width
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
# This will help with the wells
|
||||
self.well_pos = vector(0,0.4*self.height)
|
||||
|
||||
# This is the extra space needed to ensure DRC rules to the active contacts
|
||||
extra_contact_space = max(-self.nmos.get_pin("D").by(),0)
|
||||
# This is a poly-to-poly of a flipped cell
|
||||
|
|
@ -154,11 +151,14 @@ class pnor2(pgate.pgate):
|
|||
# Output position will be in between the PMOS and NMOS
|
||||
self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height))
|
||||
|
||||
# This will help with the wells
|
||||
self.well_pos = vector(0,self.nmos1_inst.uy())
|
||||
|
||||
def add_well_contacts(self):
|
||||
""" Add n/p well taps to the layout and connect to supplies """
|
||||
|
||||
self.add_nwell_contact(self.nmos, self.nmos2_pos)
|
||||
self.add_pwell_contact(self.pmos, self.pmos2_pos)
|
||||
self.add_nwell_contact(self.pmos, self.pmos2_pos)
|
||||
self.add_pwell_contact(self.nmos, self.nmos2_pos)
|
||||
|
||||
|
||||
def connect_rails(self):
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ class precharge(pgate.pgate):
|
|||
pgate.pgate.__init__(self, name)
|
||||
debug.info(2, "create single precharge cell: {0}".format(name))
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
self.beta = parameter["beta"]
|
||||
|
|
@ -128,15 +128,12 @@ class precharge(pgate.pgate):
|
|||
"""Adds a nwell tap to connect to the vdd rail"""
|
||||
# adds the contact from active to metal1
|
||||
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \
|
||||
+ vector(0, self.upper_pmos1_pos.y + self.pmos.height + drc["well_extend_active"])
|
||||
+ vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc["well_extend_active"])
|
||||
self.add_contact_center(layers=("active", "contact", "metal1"),
|
||||
offset=well_contact_pos)
|
||||
offset=well_contact_pos,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
|
||||
# adds the implant to turn the contact into a nwell tap
|
||||
self.add_rect_center(layer="nimplant",
|
||||
offset=well_contact_pos,
|
||||
width=contact.well.first_layer_width,
|
||||
height=contact.well.first_layer_height)
|
||||
|
||||
self.height = well_contact_pos.y + contact.well.height
|
||||
|
||||
|
|
@ -175,19 +172,19 @@ class precharge(pgate.pgate):
|
|||
def add_bitline_contacts(self):
|
||||
"""Adds contacts/via from metal1 to metal2 for bit-lines"""
|
||||
|
||||
for stack in [("active","contact","metal1"),("metal1", "via1", "metal2")]:
|
||||
pos = self.lower_pmos_inst.get_pin("S").center()
|
||||
self.add_contact_center(layers=stack,
|
||||
offset=pos)
|
||||
pos = self.lower_pmos_inst.get_pin("D").center()
|
||||
self.add_contact_center(layers=stack,
|
||||
offset=pos)
|
||||
pos = self.upper_pmos1_inst.get_pin("S").center()
|
||||
self.add_contact_center(layers=stack,
|
||||
offset=pos)
|
||||
pos = self.upper_pmos2_inst.get_pin("D").center()
|
||||
self.add_contact_center(layers=stack,
|
||||
offset=pos)
|
||||
stack=("metal1", "via1", "metal2")
|
||||
pos = self.lower_pmos_inst.get_pin("S").center()
|
||||
self.add_contact_center(layers=stack,
|
||||
offset=pos)
|
||||
pos = self.lower_pmos_inst.get_pin("D").center()
|
||||
self.add_contact_center(layers=stack,
|
||||
offset=pos)
|
||||
pos = self.upper_pmos1_inst.get_pin("S").center()
|
||||
self.add_contact_center(layers=stack,
|
||||
offset=pos)
|
||||
pos = self.upper_pmos2_inst.get_pin("D").center()
|
||||
self.add_contact_center(layers=stack,
|
||||
offset=pos)
|
||||
|
||||
def connect_pmos(self, pmos_pin, bit_pin):
|
||||
""" Connect pmos pin to bitline pin """
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class precharge_array(design.design):
|
|||
|
||||
self.columns = columns
|
||||
|
||||
self.pc_cell = precharge(name="precharge_cell", size=size)
|
||||
self.pc_cell = precharge(name="precharge", size=size)
|
||||
self.add_mod(self.pc_cell)
|
||||
|
||||
self.width = self.columns * self.pc_cell.width
|
||||
|
|
|
|||
|
|
@ -61,15 +61,19 @@ class ptx(design.design):
|
|||
def create_spice(self):
|
||||
self.add_pin_list(["D", "G", "S", "B"])
|
||||
|
||||
self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
|
||||
" ".join(self.pins)))
|
||||
self.spice.append("M{0} {1} {2} m={3} w={4}u l={5}u".format(self.tx_type,
|
||||
" ".join(self.pins),
|
||||
spice[self.tx_type],
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc["minwidth_poly"]))
|
||||
self.spice.append(".ENDS {0}".format(self.name))
|
||||
# self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
|
||||
# " ".join(self.pins)))
|
||||
# Just make a guess since these will actually be decided in the layout later.
|
||||
area_sd = 2.5*drc["minwidth_poly"]*self.tx_width
|
||||
perimeter_sd = 2*drc["minwidth_poly"] + 2*self.tx_width
|
||||
self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4}u ps={4}u as={5}p ad={5}p".format(spice[self.tx_type],
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc["minwidth_poly"],
|
||||
perimeter_sd,
|
||||
area_sd)
|
||||
self.spice.append("\n* ptx " + self.spice_device)
|
||||
# self.spice.append(".ENDS {0}".format(self.name))
|
||||
|
||||
def setup_layout_constants(self):
|
||||
"""
|
||||
|
|
@ -94,25 +98,19 @@ class ptx(design.design):
|
|||
self.active_contact = contact(layer_stack=("active", "contact", "metal1"),
|
||||
dimensions=(1, self.num_contacts))
|
||||
|
||||
# Standard DRC rules
|
||||
self.min_active_width = drc["minwidth_active"]
|
||||
self.contact_width = drc["minwidth_contact"]
|
||||
self.poly_width = drc["minwidth_poly"]
|
||||
self.poly_to_active = drc["poly_to_active"]
|
||||
self.poly_extend_active = drc["poly_extend_active"]
|
||||
|
||||
# The contacted poly pitch (or uncontacted in an odd technology)
|
||||
self.poly_pitch = max(2*drc["contact_to_poly"] + self.contact_width + self.poly_width,
|
||||
drc["poly_to_poly"])
|
||||
self.poly_pitch = max(2*self.contact_to_gate + self.contact_width + self.poly_width,
|
||||
self.poly_space)
|
||||
|
||||
# The contacted poly pitch (or uncontacted in an odd technology)
|
||||
self.contact_pitch = 2*drc["contact_to_poly"] + self.contact_width + self.poly_width
|
||||
self.contact_pitch = 2*self.contact_to_gate + self.contact_width + self.poly_width
|
||||
|
||||
# The enclosure of an active contact. Not sure about second term.
|
||||
active_enclose_contact = max(drc["active_enclosure_contact"],
|
||||
(self.min_active_width - self.contact_width)/2)
|
||||
(self.active_width - self.contact_width)/2)
|
||||
# This is the distance from the edge of poly to the contacted end of active
|
||||
self.end_to_poly = active_enclose_contact + self.contact_width + drc["contact_to_poly"]
|
||||
self.end_to_poly = active_enclose_contact + self.contact_width + self.contact_to_gate
|
||||
|
||||
|
||||
# Active width is determined by enclosure on both ends and contacted pitch,
|
||||
|
|
@ -125,22 +123,25 @@ class ptx(design.design):
|
|||
# Poly height must include poly extension over active
|
||||
self.poly_height = self.tx_width + 2*self.poly_extend_active
|
||||
|
||||
# The active offset is due to the well extension
|
||||
self.active_offset = vector([self.well_enclose_active]*2)
|
||||
|
||||
# Well enclosure of active, ensure minwidth as well
|
||||
if info["has_{}well".format(self.well_type)]:
|
||||
self.well_width = max(self.active_width + 2*drc["well_enclosure_active"],
|
||||
drc["minwidth_well"])
|
||||
self.well_height = max(self.tx_width + 2*drc["well_enclosure_active"],
|
||||
drc["minwidth_well"])
|
||||
|
||||
self.height = self.well_height
|
||||
self.width = self.well_width
|
||||
self.cell_well_width = max(self.active_width + 2*self.well_enclose_active,
|
||||
self.well_width)
|
||||
self.cell_well_height = max(self.tx_width + 2*self.well_enclose_active,
|
||||
self.well_width)
|
||||
# We are going to shift the 0,0, so include that in the width and height
|
||||
self.height = self.cell_well_height - self.active_offset.y
|
||||
self.width = self.cell_well_width - self.active_offset.x
|
||||
else:
|
||||
# If no well, use the boundary of the active and poly
|
||||
self.height = self.poly_height
|
||||
self.width = self.active_width
|
||||
|
||||
# The active offset is due to the well extension
|
||||
self.active_offset = vector([drc["well_enclosure_active"]]*2)
|
||||
self.active_offset = vector([self.well_enclose_active]*2)
|
||||
|
||||
# This is the center of the first active contact offset (centered vertically)
|
||||
self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5*self.contact_width,
|
||||
|
|
@ -189,9 +190,9 @@ class ptx(design.design):
|
|||
# This is the distance that we must route up or down from the center
|
||||
# of the contacts to avoid DRC violations to the other contacts
|
||||
pin_offset = vector(0, 0.5*self.active_contact.second_layer_height \
|
||||
+ drc["metal1_to_metal1"] + 0.5*drc["minwidth_metal1"])
|
||||
# This is the width of a contact to extend the ends of the pin
|
||||
end_offset = vector(self.active_contact.second_layer_width/2,0)
|
||||
+ self.m1_space + 0.5*self.m1_width)
|
||||
# This is the width of a m1 extend the ends of the pin
|
||||
end_offset = vector(self.m1_width/2,0)
|
||||
|
||||
# drains always go to the MIDDLE of the cell, so top of NMOS, bottom of PMOS
|
||||
# so reverse the directions for NMOS compared to PMOS.
|
||||
|
|
@ -264,6 +265,14 @@ class ptx(design.design):
|
|||
offset=self.active_offset,
|
||||
width=self.active_width,
|
||||
height=self.active_height)
|
||||
# If the implant must enclose the active, shift offset
|
||||
# and increase width/height
|
||||
enclose_width = drc["implant_enclosure_active"]
|
||||
enclose_offset = [enclose_width]*2
|
||||
self.add_rect(layer="{}implant".format(self.implant_type),
|
||||
offset=self.active_offset - enclose_offset,
|
||||
width=self.active_width + 2*enclose_width,
|
||||
height=self.active_height + 2*enclose_width)
|
||||
|
||||
def add_well_implant(self):
|
||||
"""
|
||||
|
|
@ -272,16 +281,12 @@ class ptx(design.design):
|
|||
if info["has_{}well".format(self.well_type)]:
|
||||
self.add_rect(layer="{}well".format(self.well_type),
|
||||
offset=(0,0),
|
||||
width=self.well_width,
|
||||
height=self.well_height)
|
||||
width=self.cell_well_width,
|
||||
height=self.cell_well_height)
|
||||
self.add_rect(layer="vtg",
|
||||
offset=(0,0),
|
||||
width=self.well_width,
|
||||
height=self.well_height)
|
||||
self.add_rect(layer="{}implant".format(self.implant_type),
|
||||
offset=self.active_offset,
|
||||
width=self.active_width,
|
||||
height=self.active_height)
|
||||
width=self.cell_well_width,
|
||||
height=self.cell_well_height)
|
||||
|
||||
|
||||
def calculate_num_contacts(self):
|
||||
|
|
@ -321,23 +326,27 @@ class ptx(design.design):
|
|||
for pos in source_positions:
|
||||
contact=self.add_contact_center(layers=("active", "contact", "metal1"),
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts))
|
||||
size=(1, self.num_contacts),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_center_rect(text="S",
|
||||
layer="metal1",
|
||||
offset=pos,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
|
||||
|
||||
for pos in drain_positions:
|
||||
contact=self.add_contact_center(layers=("active", "contact", "metal1"),
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts))
|
||||
size=(1, self.num_contacts),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_center_rect(text="D",
|
||||
layer="metal1",
|
||||
offset=pos,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
|
||||
if self.connect_active:
|
||||
self.connect_fingered_active(drain_positions, source_positions)
|
||||
|
|
|
|||
|
|
@ -10,26 +10,27 @@ from globals import OPTS
|
|||
|
||||
class replica_bitline(design.design):
|
||||
"""
|
||||
Generate a module that simulate the delay of control logic
|
||||
and bit line charging.
|
||||
Used for memory timing control
|
||||
Generate a module that simulates the delay of control logic
|
||||
and bit line charging. Stages is the depth of the FO4 delay
|
||||
line and rows is the height of the replica bit loads.
|
||||
"""
|
||||
|
||||
def __init__(self, rows, name="replica_bitline"):
|
||||
def __init__(self, FO4_stages, bitcell_loads, name="replica_bitline"):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
g = reload(__import__(OPTS.config.delay_chain))
|
||||
self.mod_delay_chain = getattr(g, OPTS.config.delay_chain)
|
||||
g = reload(__import__(OPTS.delay_chain))
|
||||
self.mod_delay_chain = getattr(g, OPTS.delay_chain)
|
||||
|
||||
g = reload(__import__(OPTS.config.replica_bitcell))
|
||||
self.mod_replica_bitcell = getattr(g, OPTS.config.replica_bitcell)
|
||||
g = reload(__import__(OPTS.replica_bitcell))
|
||||
self.mod_replica_bitcell = getattr(g, OPTS.replica_bitcell)
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
|
||||
for pin in ["en", "out", "vdd", "gnd"]:
|
||||
self.add_pin(pin)
|
||||
self.rows = rows
|
||||
self.bitcell_loads = bitcell_loads
|
||||
self.FO4_stages = FO4_stages
|
||||
|
||||
self.create_modules()
|
||||
self.calculate_module_offsets()
|
||||
|
|
@ -78,10 +79,11 @@ class replica_bitline(design.design):
|
|||
self.add_mod(self.bitcell)
|
||||
|
||||
# This is the replica bitline load column that is the height of our array
|
||||
self.rbl = bitcell_array(name="bitline_load", cols=1, rows=self.rows)
|
||||
self.rbl = bitcell_array(name="bitline_load", cols=1, rows=self.bitcell_loads)
|
||||
self.add_mod(self.rbl)
|
||||
|
||||
self.delay_chain = self.mod_delay_chain([1, 1, 1])
|
||||
|
||||
# FIXME: The FO and depth of this should be tuned
|
||||
self.delay_chain = self.mod_delay_chain([4]*self.FO4_stages)
|
||||
self.add_mod(self.delay_chain)
|
||||
|
||||
self.inv = pinv()
|
||||
|
|
@ -123,7 +125,7 @@ class replica_bitline(design.design):
|
|||
self.rbl_inst=self.add_inst(name="load",
|
||||
mod=self.rbl,
|
||||
offset=self.rbl_offset)
|
||||
self.connect_inst(["bl[0]", "br[0]"] + ["gnd"]*self.rows + ["vdd", "gnd"])
|
||||
self.connect_inst(["bl[0]", "br[0]"] + ["gnd"]*self.bitcell_loads + ["vdd", "gnd"])
|
||||
|
||||
|
||||
|
||||
|
|
@ -239,56 +241,61 @@ class replica_bitline(design.design):
|
|||
def route_gnd(self):
|
||||
""" Route all signals connected to gnd """
|
||||
|
||||
# Add a rail in M1 from bottom to two along delay chain
|
||||
gnd_start = self.rbl_inv_inst.get_pin("gnd").ll() - self.offset_fix
|
||||
gnd_start = self.rbl_inv_inst.get_pin("gnd").bc()
|
||||
gnd_end = vector(gnd_start.x, self.rbl_inst.uy()+2*self.m2_pitch)
|
||||
|
||||
# It is the height of the entire RBL and bitcell
|
||||
self.add_rect(layer="metal2",
|
||||
offset=gnd_start,
|
||||
width=self.m2_width,
|
||||
height=self.rbl.height+self.bitcell.height+self.inv.width+self.m2_pitch)
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_start.scale(1,0),
|
||||
width=self.m2_width,
|
||||
height=2*self.inv.width)
|
||||
# Add a rail in M1 from bottom of delay chain to two above the RBL
|
||||
# This prevents DRC errors with vias for the WL
|
||||
dc_top = self.dc_inst.ur()
|
||||
self.add_segment_center(layer="metal1",
|
||||
start=vector(gnd_start.x, dc_top.y),
|
||||
end=gnd_end)
|
||||
|
||||
# Add a rail in M2 from RBL inverter to two above the RBL
|
||||
self.add_segment_center(layer="metal2",
|
||||
start=gnd_start,
|
||||
end=gnd_end)
|
||||
|
||||
# Add pin from bottom to RBL inverter
|
||||
self.add_layout_pin_center_segment(text="gnd",
|
||||
layer="metal1",
|
||||
start=gnd_start.scale(1,0),
|
||||
end=gnd_start)
|
||||
|
||||
# Connect the WL pins directly to gnd
|
||||
for row in range(self.rows):
|
||||
gnd_pin = self.get_pin("gnd").rc()
|
||||
for row in range(self.bitcell_loads):
|
||||
wl = "wl[{}]".format(row)
|
||||
pin = self.rbl_inst.get_pin(wl)
|
||||
offset = vector(gnd_start.x,pin.by())
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=self.rbl_offset.x-gnd_start.x,
|
||||
height=self.m1_width)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset)
|
||||
start = vector(gnd_pin.x,pin.cy())
|
||||
self.add_segment_center(layer="metal1",
|
||||
start=start,
|
||||
end=pin.lc())
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=start)
|
||||
|
||||
# Add via for the delay chain
|
||||
offset = self.delay_chain_offset - vector(0.5*self.m1_width,0) - self.offset_fix
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset)
|
||||
offset = self.dc_inst.get_pins("gnd")[0].bc() + vector(0.5*contact.m1m2.width,0.5*contact.m1m2.height)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset)
|
||||
|
||||
# Add via for the inverter
|
||||
offset = self.rbl_inv_offset - vector(0.5*self.m1_width,contact.m1m2.height) - self.offset_fix
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset)
|
||||
offset = self.rbl_inv_inst.get_pin("gnd").bc() - vector(0,0.5*contact.m1m2.height)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset)
|
||||
|
||||
# Connect the bitcell gnd pin to the rail
|
||||
# Connect the bitcell gnd pins to the rail
|
||||
gnd_pins = self.get_pins("gnd")
|
||||
gnd_start = gnd_pins[0].uc()
|
||||
gnd_start = gnd_pins[0].ul()
|
||||
rbl_gnd_pins = self.rbl_inst.get_pins("gnd")
|
||||
# Find the left most rail on M2
|
||||
gnd_pin = None
|
||||
# Add L shapes to each vertical gnd rail
|
||||
for pin in rbl_gnd_pins:
|
||||
if gnd_pin == None or (pin.layer=="metal2" and pin.lx()<gnd_pin.lx()):
|
||||
gnd_pin = pin
|
||||
gnd_end = gnd_pin.uc()
|
||||
# Add a couple midpoints so that the wire will drop a via and then route horizontal on M1
|
||||
gnd_mid1 = gnd_start + vector(0,self.m2_pitch)
|
||||
gnd_mid2 = gnd_end + vector(0,self.m2_pitch)
|
||||
self.add_wire(("metal1","via1","metal2"), [gnd_start, gnd_mid1, gnd_mid2, gnd_end])
|
||||
if pin.layer != "metal2":
|
||||
continue
|
||||
gnd_end = pin.uc()
|
||||
gnd_mid = vector(gnd_end.x, gnd_start.y)
|
||||
self.add_wire(("metal1","via1","metal2"), [gnd_start, gnd_mid, gnd_end])
|
||||
gnd_start = gnd_mid
|
||||
|
||||
|
||||
# Add a second gnd pin to the second delay chain rail. No need for full length.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from vector3d import vector3d
|
|||
|
||||
class route():
|
||||
"""
|
||||
Object route
|
||||
Object route (used by the router module)
|
||||
Add a route of minimium metal width between a set of points.
|
||||
The wire must be completely rectilinear and the
|
||||
z-dimension of the points refers to the layers (plus via)
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ class sense_amp_array(design.design):
|
|||
design.design.__init__(self, "sense_amp_array")
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
|
||||
c = reload(__import__(OPTS.config.sense_amp))
|
||||
self.mod_sense_amp = getattr(c, OPTS.config.sense_amp)
|
||||
c = reload(__import__(OPTS.sense_amp))
|
||||
self.mod_sense_amp = getattr(c, OPTS.sense_amp)
|
||||
self.amp = self.mod_sense_amp("sense_amp")
|
||||
self.add_mod(self.amp)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@ class single_level_column_mux(design.design):
|
|||
Creates a single columnmux cell.
|
||||
"""
|
||||
|
||||
def __init__(self, name, tx_size):
|
||||
def __init__(self, tx_size):
|
||||
name="single_level_column_mux_{}".format(tx_size)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "create single columnmux cell: {0}".format(name))
|
||||
debug.info(2, "create single column mux cell: {0}".format(name))
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
self.ptx_width = tx_size * drc["minwidth_tx"]
|
||||
|
|
@ -27,9 +28,9 @@ class single_level_column_mux(design.design):
|
|||
def create_layout(self):
|
||||
|
||||
self.add_ptx()
|
||||
|
||||
self.pin_height = 2*self.m2_width
|
||||
self.width = self.bitcell.width
|
||||
self.height = self.nmos2.uy()
|
||||
self.height = self.nmos2.uy() + self.pin_height
|
||||
self.connect_poly()
|
||||
self.add_gnd_rail()
|
||||
self.add_bitline_pins()
|
||||
|
|
@ -42,26 +43,25 @@ class single_level_column_mux(design.design):
|
|||
bl_pos = vector(self.bitcell.get_pin("BL").lx(), 0)
|
||||
br_pos = vector(self.bitcell.get_pin("BR").lx(), 0)
|
||||
|
||||
pin_height = 2*self.m2_width
|
||||
# bl and br
|
||||
self.add_layout_pin(text="bl",
|
||||
layer="metal2",
|
||||
offset=bl_pos + vector(0,self.height - pin_height),
|
||||
height=pin_height)
|
||||
offset=bl_pos + vector(0,self.height - self.pin_height),
|
||||
height=self.pin_height)
|
||||
self.add_layout_pin(text="br",
|
||||
layer="metal2",
|
||||
offset=br_pos + vector(0,self.height - pin_height),
|
||||
height=pin_height)
|
||||
offset=br_pos + vector(0,self.height - self.pin_height),
|
||||
height=self.pin_height)
|
||||
|
||||
# bl_out and br_out
|
||||
self.add_layout_pin(text="bl_out",
|
||||
layer="metal2",
|
||||
offset=bl_pos,
|
||||
height=pin_height)
|
||||
height=self.pin_height)
|
||||
self.add_layout_pin(text="br_out",
|
||||
layer="metal2",
|
||||
offset=br_pos,
|
||||
height=pin_height)
|
||||
height=self.pin_height)
|
||||
|
||||
|
||||
def add_ptx(self):
|
||||
|
|
@ -152,7 +152,6 @@ class single_level_column_mux(design.design):
|
|||
|
||||
def add_wells(self):
|
||||
""" Add a well and implant over the whole cell. Also, add the pwell contact (if it exists) """
|
||||
|
||||
|
||||
# find right most gnd rail
|
||||
gnd_pins = self.bitcell.get_pins("gnd")
|
||||
|
|
@ -167,26 +166,8 @@ class single_level_column_mux(design.design):
|
|||
offset=m1m2_offset)
|
||||
active_offset = right_gnd.bc() + vector(0,0.5*self.nmos.poly_height)
|
||||
self.add_via_center(layers=("active", "contact", "metal1"),
|
||||
offset=active_offset)
|
||||
offset=active_offset,
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
|
||||
# implant must surround the active area
|
||||
active_correct = vector(contact.well.width,contact.well.height).scale(0.5,0.5)
|
||||
offset_implant = active_offset - vector([drc["implant_to_contact"]]*2) - active_correct
|
||||
implant_width = 2*drc["implant_to_contact"] + contact.well.width
|
||||
implant_height = 2*drc["implant_to_contact"] + contact.well.height
|
||||
self.add_rect(layer="pimplant",
|
||||
offset=offset_implant,
|
||||
width=implant_width,
|
||||
height=implant_height)
|
||||
|
||||
# Add a well around the whole cell
|
||||
if info["has_pwell"]:
|
||||
self.add_rect(layer="pwell",
|
||||
offset=vector(0,0),
|
||||
width=self.width + contact.well.width + drc["well_enclosure_active"],
|
||||
height=self.height)
|
||||
self.add_rect(layer="vtg",
|
||||
offset=vector(0,0),
|
||||
width=self.width + contact.well.width,
|
||||
height=self.height)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,26 +40,30 @@ class single_level_column_mux_array(design.design):
|
|||
self.setup_layout_constants()
|
||||
self.create_array()
|
||||
self.add_routing()
|
||||
# Find the highest shapes to determine height before adding well
|
||||
highest = self.find_highest_coords()
|
||||
self.height = highest.y
|
||||
self.add_layout_pins()
|
||||
self.add_enclosure(self.mux_inst, "pwell")
|
||||
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
self.mux = single_level_column_mux(name="single_level_column_mux",
|
||||
tx_size=8)
|
||||
# FIXME: Why is this 8x?
|
||||
self.mux = single_level_column_mux(tx_size=8)
|
||||
self.add_mod(self.mux)
|
||||
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.column_addr_size = num_of_inputs = int(self.words_per_row / 2)
|
||||
self.width = self.columns * self.mux.width
|
||||
|
||||
self.m1_pitch = contact.m1m2.width + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
|
||||
# To correct the offset between M1 and M2 via enclosures
|
||||
self.offset_fix = vector(0,0.5*(drc["minwidth_metal2"]-drc["minwidth_metal1"]))
|
||||
# one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br
|
||||
# one extra route pitch is to space from the sense amp
|
||||
self.route_height = (self.words_per_row + 3)*self.m1_pitch
|
||||
# mux height plus routing signal height plus well spacing at the top
|
||||
self.height = self.mux.height + self.route_height + drc["pwell_to_nwell"]
|
||||
|
||||
|
||||
|
||||
def create_array(self):
|
||||
self.mux_inst = []
|
||||
|
||||
|
|
@ -70,28 +74,6 @@ class single_level_column_mux_array(design.design):
|
|||
self.mux_inst.append(self.add_inst(name=name,
|
||||
mod=self.mux,
|
||||
offset=x_off))
|
||||
|
||||
offset = self.mux_inst[-1].get_pin("bl").ll()
|
||||
self.add_layout_pin(text="bl[{}]".format(col_num),
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
height=self.height-offset.y)
|
||||
|
||||
offset = self.mux_inst[-1].get_pin("br").ll()
|
||||
self.add_layout_pin(text="br[{}]".format(col_num),
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
height=self.height-offset.y)
|
||||
|
||||
gnd_pins = self.mux_inst[-1].get_pins("gnd")
|
||||
for gnd_pin in gnd_pins:
|
||||
# only do even colums to avoid duplicates
|
||||
offset = gnd_pin.ll()
|
||||
if col_num % 2 == 0:
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal2",
|
||||
offset=offset.scale(1,0),
|
||||
height=self.height)
|
||||
|
||||
self.connect_inst(["bl[{}]".format(col_num),
|
||||
"br[{}]".format(col_num),
|
||||
|
|
@ -100,7 +82,34 @@ class single_level_column_mux_array(design.design):
|
|||
"sel[{}]".format(col_num % self.words_per_row),
|
||||
"gnd"])
|
||||
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the pins after we determine the height. """
|
||||
# For every column, add a pass gate
|
||||
for col_num in range(self.columns):
|
||||
mux_inst = self.mux_inst[col_num]
|
||||
offset = mux_inst.get_pin("bl").ll()
|
||||
self.add_layout_pin(text="bl[{}]".format(col_num),
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
height=self.height-offset.y)
|
||||
|
||||
offset = mux_inst.get_pin("br").ll()
|
||||
self.add_layout_pin(text="br[{}]".format(col_num),
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
height=self.height-offset.y)
|
||||
|
||||
gnd_pins = mux_inst.get_pins("gnd")
|
||||
for gnd_pin in gnd_pins:
|
||||
# only do even colums to avoid duplicates
|
||||
offset = gnd_pin.ll()
|
||||
if col_num % 2 == 0:
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal2",
|
||||
offset=offset.scale(1,0),
|
||||
height=self.height)
|
||||
|
||||
|
||||
def add_routing(self):
|
||||
self.add_horizontal_input_rail()
|
||||
|
|
|
|||
|
|
@ -19,18 +19,18 @@ class sram(design.design):
|
|||
|
||||
def __init__(self, word_size, num_words, num_banks, name):
|
||||
|
||||
c = reload(__import__(OPTS.config.control_logic))
|
||||
self.mod_control_logic = getattr(c, OPTS.config.control_logic)
|
||||
c = reload(__import__(OPTS.control_logic))
|
||||
self.mod_control_logic = getattr(c, OPTS.control_logic)
|
||||
|
||||
c = reload(__import__(OPTS.config.ms_flop_array))
|
||||
self.mod_ms_flop_array = getattr(c, OPTS.config.ms_flop_array)
|
||||
c = reload(__import__(OPTS.ms_flop_array))
|
||||
self.mod_ms_flop_array = getattr(c, OPTS.ms_flop_array)
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
c = reload(__import__(OPTS.config.ms_flop))
|
||||
self.mod_ms_flop = getattr(c, OPTS.config.ms_flop)
|
||||
c = reload(__import__(OPTS.ms_flop))
|
||||
self.mod_ms_flop = getattr(c, OPTS.ms_flop)
|
||||
self.ms_flop = self.mod_ms_flop()
|
||||
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ class sram(design.design):
|
|||
self.width = sizes[0]
|
||||
self.height = sizes[1]
|
||||
|
||||
self.DRC_LVS()
|
||||
self.DRC_LVS(final_verification=True)
|
||||
|
||||
def compute_sizes(self):
|
||||
""" Computes the organization of the memory using bitcell size by trying to make it square."""
|
||||
|
|
@ -650,7 +650,7 @@ class sram(design.design):
|
|||
# Connect the output bar to select 0
|
||||
msb_out_pin = self.msb_address_inst.get_pin("dout_bar[0]")
|
||||
msb_out_pos = msb_out_pin.rc()
|
||||
out_extend_right_pos = msb_out_pos + vector(self.m2_pitch,0)
|
||||
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
|
||||
out_extend_up_pos = out_extend_right_pos + vector(0,self.m2_width)
|
||||
rail_pos = vector(self.vert_control_bus_positions["bank_sel[0]"].x,out_extend_up_pos.y)
|
||||
self.add_path("metal2",[msb_out_pos,out_extend_right_pos,out_extend_up_pos])
|
||||
|
|
@ -660,7 +660,7 @@ class sram(design.design):
|
|||
# Connect the output to select 1
|
||||
msb_out_pin = self.msb_address_inst.get_pin("dout[0]")
|
||||
msb_out_pos = msb_out_pin.rc()
|
||||
out_extend_right_pos = msb_out_pos + vector(self.m2_pitch,0)
|
||||
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
|
||||
out_extend_down_pos = out_extend_right_pos - vector(0,2*self.m1_pitch)
|
||||
rail_pos = vector(self.vert_control_bus_positions["bank_sel[1]"].x,out_extend_down_pos.y)
|
||||
self.add_path("metal2",[msb_out_pos,out_extend_right_pos,out_extend_down_pos])
|
||||
|
|
@ -989,7 +989,13 @@ class sram(design.design):
|
|||
############################################################
|
||||
sp = open(sp_name, 'w')
|
||||
|
||||
sp.write("**************************************************\n")
|
||||
sp.write("* OpenRAM generated memory.\n")
|
||||
sp.write("* Words: {}\n".format(self.num_words))
|
||||
sp.write("* Data bits: {}\n".format(self.word_size))
|
||||
sp.write("* Banks: {}\n".format(self.num_banks))
|
||||
sp.write("* Column mux: {}:1\n".format(self.words_per_row))
|
||||
sp.write("**************************************************\n")
|
||||
# This causes unit test mismatch
|
||||
# sp.write("* Created: {0}\n".format(datetime.datetime.now()))
|
||||
# sp.write("* User: {0}\n".format(getpass.getuser()))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,154 @@
|
|||
#!/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()
|
||||
|
|
@ -1,18 +1,13 @@
|
|||
#!/usr/bin/env python2.7
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
import sys,os
|
||||
from testutils import header,openram_test
|
||||
import sys,os,re
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
import debug
|
||||
import verify
|
||||
import re
|
||||
|
||||
#@unittest.skip("SKIPPING 00_format check test")
|
||||
|
||||
|
||||
class code_format_test(unittest.TestCase):
|
||||
class code_format_test(openram_test):
|
||||
"Run a test to check for tabs instead of spaces in the all source files."
|
||||
|
||||
def runTest(self):
|
||||
|
|
|
|||
|
|
@ -2,23 +2,19 @@
|
|||
"Run a regresion test the library cells for DRC"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
import sys,os
|
||||
from testutils import header,openram_test
|
||||
import sys,os,re
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
import debug
|
||||
import verify
|
||||
import re
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 01_library_drc_test")
|
||||
|
||||
|
||||
class library_drc_test(unittest.TestCase):
|
||||
class library_drc_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
global verify
|
||||
import verify
|
||||
|
||||
(gds_dir, gds_files) = setup_files()
|
||||
drc_errors = 0
|
||||
|
|
|
|||
|
|
@ -2,24 +2,18 @@
|
|||
"Run a regresion test the library cells for LVS"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
import sys,os
|
||||
from testutils import header,openram_test
|
||||
import sys,os,re
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
import debug
|
||||
import verify
|
||||
import re
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 02_lvs_test")
|
||||
|
||||
|
||||
class library_lvs_test(unittest.TestCase):
|
||||
class library_lvs_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
|
||||
import verify
|
||||
(gds_dir, sp_dir, allnames) = setup_files()
|
||||
lvs_errors = 0
|
||||
debug.info(1, "Performing LVS on: " + ", ".join(allnames))
|
||||
|
|
@ -34,7 +28,7 @@ class library_lvs_test(unittest.TestCase):
|
|||
lvs_errors += 1
|
||||
debug.error("Missing SPICE file {}".format(gds_name))
|
||||
lvs_errors += verify.run_lvs(f, gds_name, sp_name)
|
||||
|
||||
self.assertEqual(lvs_errors, 0)
|
||||
# fail if the error count is not zero
|
||||
self.assertEqual(lvs_errors, 0)
|
||||
globals.end_openram()
|
||||
|
|
|
|||
|
|
@ -2,22 +2,19 @@
|
|||
"Run a regresion test for DRC on basic contacts of different array sizes"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 03_contact_test")
|
||||
|
||||
|
||||
class contact_test(unittest.TestCase):
|
||||
class contact_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 contact
|
||||
|
|
@ -29,31 +26,26 @@ class contact_test(unittest.TestCase):
|
|||
# Check single 1 x 1 contact"
|
||||
debug.info(2, "1 x 1 {} test".format(stack_name))
|
||||
c = contact.contact(layer_stack, (1, 1))
|
||||
self.local_check(c)
|
||||
self.local_drc_check(c)
|
||||
|
||||
# check vertical array with one in the middle and two ends
|
||||
debug.info(2, "1 x 3 {} test".format(stack_name))
|
||||
c = contact.contact(layer_stack, (1, 3))
|
||||
self.local_check(c)
|
||||
self.local_drc_check(c)
|
||||
|
||||
# check horizontal array with one in the middle and two ends
|
||||
debug.info(2, "3 x 1 {} test".format(stack_name))
|
||||
c = contact.contact(layer_stack, (3, 1))
|
||||
self.local_check(c)
|
||||
self.local_drc_check(c)
|
||||
|
||||
# check 3x3 array for all possible neighbors
|
||||
debug.info(2, "3 x 3 {} test".format(stack_name))
|
||||
c = contact.contact(layer_stack, (3, 3))
|
||||
self.local_check(c)
|
||||
self.local_drc_check(c)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, c):
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
c.gds_write(tempgds)
|
||||
self.assertFalse(verify.run_drc(c.name, tempgds))
|
||||
os.remove(tempgds)
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
|
|
|
|||
|
|
@ -2,22 +2,19 @@
|
|||
"Run a regresion test on a basic path"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 03_path_test")
|
||||
|
||||
|
||||
class path_test(unittest.TestCase):
|
||||
class path_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 path
|
||||
|
|
@ -35,7 +32,7 @@ class path_test(unittest.TestCase):
|
|||
[0, 6 * min_space ]]
|
||||
w = design.design("path_test0")
|
||||
path.path(w,layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
self.local_drc_check(w)
|
||||
|
||||
|
||||
min_space = 2 * tech.drc["minwidth_metal1"]
|
||||
|
|
@ -52,7 +49,7 @@ class path_test(unittest.TestCase):
|
|||
position_list = [[x+min_space, y+min_space] for x,y in old_position_list]
|
||||
w = design.design("path_test1")
|
||||
path.path(w,layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
self.local_drc_check(w)
|
||||
|
||||
min_space = 2 * tech.drc["minwidth_metal2"]
|
||||
layer_stack = ("metal2")
|
||||
|
|
@ -68,7 +65,7 @@ class path_test(unittest.TestCase):
|
|||
position_list = [[x-min_space, y-min_space] for x,y in old_position_list]
|
||||
w = design.design("path_test2")
|
||||
path.path(w, layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
self.local_drc_check(w)
|
||||
|
||||
min_space = 2 * tech.drc["minwidth_metal3"]
|
||||
layer_stack = ("metal3")
|
||||
|
|
@ -85,17 +82,12 @@ class path_test(unittest.TestCase):
|
|||
position_list.reverse()
|
||||
w = design.design("path_test3")
|
||||
path.path(w, layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
self.local_drc_check(w)
|
||||
|
||||
# return it back to it's normal state
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, w):
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
w.gds_write(tempgds)
|
||||
self.assertFalse(verify.run_drc(w.name, tempgds))
|
||||
os.remove(tempgds)
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
|
|
|
|||
|
|
@ -2,23 +2,19 @@
|
|||
"Run a regresion test on a basic parameterized transistors"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 03_ptx_test")
|
||||
|
||||
|
||||
class ptx_test(unittest.TestCase):
|
||||
class ptx_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import ptx
|
||||
|
|
@ -28,126 +24,12 @@ class ptx_test(unittest.TestCase):
|
|||
fet = ptx.ptx(width=tech.drc["minwidth_tx"],
|
||||
mults=1,
|
||||
tx_type="nmos")
|
||||
self.local_check(fet)
|
||||
self.local_drc_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))
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -2,23 +2,19 @@
|
|||
"Run a regresion test on a basic parameterized transistors"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 03_ptx_test")
|
||||
|
||||
|
||||
class ptx_test(unittest.TestCase):
|
||||
class ptx_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import ptx
|
||||
|
|
@ -28,127 +24,12 @@ class ptx_test(unittest.TestCase):
|
|||
fet = ptx.ptx(width=tech.drc["minwidth_tx"],
|
||||
mults=1,
|
||||
tx_type="pmos")
|
||||
self.local_check(fet)
|
||||
self.local_drc_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()
|
||||
|
|
|
|||
|
|
@ -2,23 +2,19 @@
|
|||
"Run a regresion test on a basic parameterized transistors"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 03_ptx_test")
|
||||
|
||||
|
||||
class ptx_test(unittest.TestCase):
|
||||
class ptx_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import ptx
|
||||
|
|
@ -30,125 +26,11 @@ class ptx_test(unittest.TestCase):
|
|||
tx_type="nmos",
|
||||
connect_active=True,
|
||||
connect_poly=True)
|
||||
self.local_check(fet)
|
||||
self.local_drc_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__":
|
||||
|
|
|
|||
|
|
@ -2,23 +2,19 @@
|
|||
"Run a regresion test on a basic parameterized transistors"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 03_ptx_test")
|
||||
|
||||
|
||||
class ptx_test(unittest.TestCase):
|
||||
class ptx_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import ptx
|
||||
|
|
@ -30,125 +26,11 @@ class ptx_test(unittest.TestCase):
|
|||
tx_type="pmos",
|
||||
connect_active=True,
|
||||
connect_poly=True)
|
||||
self.local_check(fet)
|
||||
self.local_drc_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__":
|
||||
|
|
|
|||
|
|
@ -2,23 +2,19 @@
|
|||
"Run a regresion test on a basic parameterized transistors"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 03_ptx_test")
|
||||
|
||||
|
||||
class ptx_test(unittest.TestCase):
|
||||
class ptx_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import ptx
|
||||
|
|
@ -30,125 +26,11 @@ class ptx_test(unittest.TestCase):
|
|||
tx_type="nmos",
|
||||
connect_active=True,
|
||||
connect_poly=True)
|
||||
self.local_check(fet)
|
||||
self.local_drc_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__":
|
||||
|
|
|
|||
|
|
@ -2,23 +2,19 @@
|
|||
"Run a regresion test on a basic parameterized transistors"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 03_ptx_test")
|
||||
|
||||
|
||||
class ptx_test(unittest.TestCase):
|
||||
class ptx_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import ptx
|
||||
|
|
@ -30,125 +26,11 @@ class ptx_test(unittest.TestCase):
|
|||
tx_type="pmos",
|
||||
connect_active=True,
|
||||
connect_poly=True)
|
||||
self.local_check(fet)
|
||||
self.local_drc_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__":
|
||||
|
|
|
|||
|
|
@ -2,22 +2,19 @@
|
|||
"Run a regresion test on a basic wire"
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 03_wire_test")
|
||||
|
||||
|
||||
class wire_test(unittest.TestCase):
|
||||
class wire_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 wire
|
||||
|
|
@ -39,7 +36,7 @@ class wire_test(unittest.TestCase):
|
|||
position_list = [[x-min_space, y-min_space] for x,y in old_position_list]
|
||||
w = design.design("wire_test1")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
self.local_drc_check(w)
|
||||
|
||||
min_space = 2 * (tech.drc["minwidth_poly"] +
|
||||
tech.drc["minwidth_metal1"])
|
||||
|
|
@ -56,7 +53,7 @@ class wire_test(unittest.TestCase):
|
|||
position_list = [[x+min_space, y+min_space] for x,y in old_position_list]
|
||||
w = design.design("wire_test2")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
self.local_drc_check(w)
|
||||
|
||||
min_space = 2 * (tech.drc["minwidth_metal2"] +
|
||||
tech.drc["minwidth_metal1"])
|
||||
|
|
@ -72,7 +69,7 @@ class wire_test(unittest.TestCase):
|
|||
[-1 * min_space, 0]]
|
||||
w = design.design("wire_test3")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
self.local_drc_check(w)
|
||||
|
||||
|
||||
min_space = 2 * (tech.drc["minwidth_metal2"] +
|
||||
|
|
@ -89,7 +86,7 @@ class wire_test(unittest.TestCase):
|
|||
[-1 * min_space, 0]]
|
||||
w = design.design("wire_test4")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
self.local_drc_check(w)
|
||||
|
||||
min_space = 2 * (tech.drc["minwidth_metal2"] +
|
||||
tech.drc["minwidth_metal3"])
|
||||
|
|
@ -106,7 +103,7 @@ class wire_test(unittest.TestCase):
|
|||
position_list.reverse()
|
||||
w = design.design("wire_test5")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
self.local_drc_check(w)
|
||||
|
||||
min_space = 2 * (tech.drc["minwidth_metal2"] +
|
||||
tech.drc["minwidth_metal3"])
|
||||
|
|
@ -123,18 +120,12 @@ class wire_test(unittest.TestCase):
|
|||
position_list.reverse()
|
||||
w = design.design("wire_test6")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
self.local_drc_check(w)
|
||||
|
||||
# return it back to it's normal state
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, w):
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
w.gds_write(tempgds)
|
||||
self.assertFalse(verify.run_drc(w.name, tempgds))
|
||||
os.remove(tempgds)
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -4,22 +4,19 @@ Run regresion tests on a parameterized inverter
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 04_pinv_test")
|
||||
|
||||
|
||||
class pinv_test(unittest.TestCase):
|
||||
class pinv_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 pinv
|
||||
|
|
@ -32,25 +29,6 @@ class pinv_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
tx.sp_write(tempspice)
|
||||
tx.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(tx.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -4,22 +4,19 @@ Run regresion tests on a parameterized inverter
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 04_pinv_test")
|
||||
|
||||
|
||||
class pinv_test(unittest.TestCase):
|
||||
class pinv_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 pinv
|
||||
|
|
@ -32,26 +29,6 @@ class pinv_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
tx.sp_write(tempspice)
|
||||
tx.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(tx.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -1,26 +1,23 @@
|
|||
#!/usr/bin/env python2.7
|
||||
"""
|
||||
Run regresion tests on a parameterized inverter
|
||||
Run regression tests on a parameterized inverter
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 04_pinv_test")
|
||||
|
||||
|
||||
class pinv_test(unittest.TestCase):
|
||||
class pinv_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 pinv
|
||||
import tech
|
||||
|
|
@ -32,26 +29,6 @@ class pinv_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
tx.sp_write(tempspice)
|
||||
tx.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(tx.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -4,22 +4,19 @@ Run regresion tests on a parameterized inverter
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 04_pinv_test")
|
||||
|
||||
|
||||
class pinv_test(unittest.TestCase):
|
||||
class pinv_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 pinv
|
||||
|
|
@ -32,25 +29,6 @@ class pinv_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
tx.sp_write(tempspice)
|
||||
tx.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(tx.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -6,24 +6,19 @@ size 2-input nand gate.
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
import sys
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 04_pnand2_test")
|
||||
|
||||
|
||||
class pnand2_test(unittest.TestCase):
|
||||
class pnand2_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import pnand2
|
||||
|
|
@ -37,20 +32,6 @@ class pnand2_test(unittest.TestCase):
|
|||
globals.end_openram()
|
||||
|
||||
|
||||
def local_check(self, tx):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
tx.sp_write(tempspice)
|
||||
tx.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(tx.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -6,21 +6,19 @@ It generates only a minimum size 3-input nand gate.
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 04_pnand3_test")
|
||||
class pnand3_test(unittest.TestCase):
|
||||
class pnand3_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import pnand3
|
||||
|
|
@ -33,19 +31,6 @@ class pnand3_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
tx.sp_write(tempspice)
|
||||
tx.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(tx.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -6,24 +6,19 @@ size 2-input nor gate.
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
import sys
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 04_pnor2_test")
|
||||
|
||||
|
||||
class pnor2_test(unittest.TestCase):
|
||||
class pnor2_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import pnor2
|
||||
|
|
@ -36,21 +31,6 @@ class pnor2_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
def local_check(self, tx):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
tx.sp_write(tempspice)
|
||||
tx.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(tx.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -4,21 +4,19 @@ Run a regresion test on a precharge cell
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
import sys
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
class precharge_test(unittest.TestCase):
|
||||
class precharge_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import precharge
|
||||
|
|
@ -31,20 +29,6 @@ class precharge_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
tx.sp_write(tempspice)
|
||||
tx.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(tx.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -4,24 +4,21 @@ Run a regresion test on a wordline_driver array
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
import sys
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 04_driver_test")
|
||||
|
||||
|
||||
class wordline_driver_test(unittest.TestCase):
|
||||
class wordline_driver_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import wordline_driver
|
||||
|
|
@ -34,20 +31,6 @@ class wordline_driver_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
tx.sp_write(tempspice)
|
||||
tx.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(tx.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -4,23 +4,21 @@ Run a regresion test on a basic array
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 05_array_test")
|
||||
|
||||
|
||||
class array_test(unittest.TestCase):
|
||||
class array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import bitcell_array
|
||||
|
|
@ -32,21 +30,6 @@ class array_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
temppdf = OPTS.openram_temp + "temp.pdf"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -4,20 +4,19 @@ Run a regresion test on a hierarchical_decoder.
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
|
||||
class hierarchical_decoder_test(unittest.TestCase):
|
||||
class hierarchical_decoder_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 hierarchical_decoder
|
||||
|
|
@ -33,6 +32,10 @@ class hierarchical_decoder_test(unittest.TestCase):
|
|||
# a = hierarchical_decoder.hierarchical_decoder(rows=8)
|
||||
# self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
|
||||
a = hierarchical_decoder.hierarchical_decoder(rows=16)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 32 row sample for hierarchical_decoder")
|
||||
a = hierarchical_decoder.hierarchical_decoder(rows=32)
|
||||
self.local_check(a)
|
||||
|
|
@ -48,24 +51,6 @@ class hierarchical_decoder_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -4,21 +4,19 @@ Run a regresion test on a hierarchical_predecode2x4.
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
|
||||
class hierarchical_predecode2x4_test(unittest.TestCase):
|
||||
class hierarchical_predecode2x4_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import hierarchical_predecode2x4 as pre
|
||||
|
|
@ -31,19 +29,6 @@ class hierarchical_predecode2x4_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -4,21 +4,19 @@ Run a regresion test on a hierarchical_predecode3x8.
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
|
||||
class hierarchical_predecode3x8_test(unittest.TestCase):
|
||||
class hierarchical_predecode3x8_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import hierarchical_predecode3x8 as pre
|
||||
|
|
@ -31,19 +29,6 @@ class hierarchical_predecode3x8_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -3,21 +3,19 @@
|
|||
Run a regresion test on a single transistor column_mux.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
from testutils import header,openram_test,unittest
|
||||
import sys,os
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
import debug
|
||||
import verify
|
||||
|
||||
|
||||
class single_level_column_mux_test(unittest.TestCase):
|
||||
class single_level_column_mux_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import single_level_column_mux_array
|
||||
|
|
@ -37,22 +35,6 @@ class single_level_column_mux_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -4,22 +4,19 @@ Run a regresion test on a precharge array
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
|
||||
#@unittest.skip("SKIPPING 08_precharge_test")
|
||||
|
||||
|
||||
class precharge_test(unittest.TestCase):
|
||||
class precharge_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 precharge_array
|
||||
|
|
@ -32,19 +29,6 @@ class precharge_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, pc):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
pc.sp_write(tempspice)
|
||||
pc.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(pc.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(pc.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -4,21 +4,19 @@ Run a regresion test on a sense amp array
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 09_sense_amp_test")
|
||||
|
||||
|
||||
class sense_amp_test(unittest.TestCase):
|
||||
class sense_amp_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 sense_amp_array
|
||||
|
|
@ -35,24 +33,6 @@ class sense_amp_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -4,21 +4,19 @@ Run a regresion test on a write driver array
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 10_write_driver_test")
|
||||
|
||||
|
||||
class write_driver_test(unittest.TestCase):
|
||||
class write_driver_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 write_driver_array
|
||||
|
|
@ -34,24 +32,6 @@ class write_driver_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -4,22 +4,19 @@ Run a regresion test on a dff_array.
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
import importlib
|
||||
|
||||
#@unittest.skip("SKIPPING 20_sram_test")
|
||||
|
||||
|
||||
class dff_array_test(unittest.TestCase):
|
||||
class dff_array_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 ms_flop_array
|
||||
|
|
@ -35,25 +32,6 @@ class dff_array_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -4,20 +4,19 @@ Run a regresion test on a tri_gate_array.
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
|
||||
class tri_gate_array_test(unittest.TestCase):
|
||||
class tri_gate_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import tri_gate_array
|
||||
|
|
@ -33,24 +32,6 @@ class tri_gate_array_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -4,20 +4,19 @@ Run a test on a delay chain
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 14_delay_chain_test")
|
||||
class delay_chain_test(unittest.TestCase):
|
||||
class delay_chain_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import delay_chain
|
||||
|
|
@ -29,19 +28,6 @@ class delay_chain_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -4,47 +4,38 @@ Run a test on a delay chain
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
import importlib
|
||||
|
||||
#@unittest.skip("SKIPPING 14_delay_chain_test")
|
||||
|
||||
|
||||
class replica_bitline_test(unittest.TestCase):
|
||||
class replica_bitline_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import replica_bitline
|
||||
|
||||
debug.info(2, "Testing RBL")
|
||||
a = replica_bitline.replica_bitline(13)
|
||||
stages=4
|
||||
rows=13
|
||||
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
|
||||
a = replica_bitline.replica_bitline(stages,rows)
|
||||
self.local_check(a)
|
||||
|
||||
stages=8
|
||||
rows=100
|
||||
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
|
||||
a = replica_bitline.replica_bitline(stages,rows)
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -4,19 +4,19 @@ Run a regresion test on a control_logic
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
class control_logic_test(unittest.TestCase):
|
||||
class control_logic_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import control_logic
|
||||
|
|
@ -29,20 +29,6 @@ class control_logic_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -4,19 +4,19 @@ Run a regresion test on various srams
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
class multi_bank_test(unittest.TestCase):
|
||||
class multi_bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import bank
|
||||
|
|
@ -41,23 +41,6 @@ class multi_bank_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -4,22 +4,19 @@ Run a regresion test on various srams
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 20_sram_test")
|
||||
|
||||
|
||||
class single_bank_test(unittest.TestCase):
|
||||
class single_bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import bank
|
||||
|
|
@ -44,23 +41,6 @@ class single_bank_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -4,59 +4,42 @@ Run a regresion test on a 1 bank SRAM
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 20_sram_test")
|
||||
|
||||
|
||||
class sram_1bank_test(unittest.TestCase):
|
||||
class sram_1bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import sram
|
||||
|
||||
debug.info(1, "Single bank, no column mux with control logic")
|
||||
a = sram.sram(word_size=4, num_words=16, num_banks=1, name="sram1")
|
||||
self.local_check(a)
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Single bank two way column mux with control logic")
|
||||
a = sram.sram(word_size=4, num_words=32, num_banks=1, name="sram2")
|
||||
self.local_check(a)
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Single bank, four way column mux with control logic")
|
||||
a = sram.sram(word_size=4, num_words=64, num_banks=1, name="sram3")
|
||||
self.local_check(a)
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
# debug.info(1, "Single bank, eight way column mux with control logic")
|
||||
# a = sram.sram(word_size=2, num_words=128, num_banks=1, name="sram4")
|
||||
# self.local_check(a)
|
||||
# self.local_check(a, final_verification=True)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
#self.assertFalse(verify.run_pex(a.name, tempgds, tempspice, output=OPTS.openram_temp+"temp_pex.sp"))
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -4,59 +4,42 @@ Run a regresion test on a 2 bank SRAM
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 20_sram_test")
|
||||
|
||||
|
||||
class sram_2bank_test(unittest.TestCase):
|
||||
class sram_2bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import sram
|
||||
|
||||
debug.info(1, "Two bank, no column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=32, num_banks=2, name="sram1")
|
||||
self.local_check(a)
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Two bank two way column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=64, num_banks=2, name="sram2")
|
||||
self.local_check(a)
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Two bank, four way column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=128, num_banks=2, name="sram3")
|
||||
self.local_check(a)
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
# debug.info(1, "Two bank, eight way column mux with control logic")
|
||||
# a = sram.sram(word_size=2, num_words=256 num_banks=2, name="sram4")
|
||||
# self.local_check(a)
|
||||
# self.local_check(a, final_verification=True)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
#self.assertFalse(verify.run_pex(a.name, tempgds, tempspice, output=OPTS.openram_temp+"temp_pex.sp"))
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -4,59 +4,42 @@ Run a regresion test on a 4 bank SRAM
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 20_sram_test")
|
||||
|
||||
|
||||
class sram_4bank_test(unittest.TestCase):
|
||||
class sram_4bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
global verify
|
||||
import verify
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import sram
|
||||
|
||||
debug.info(1, "Four bank, no column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=64, num_banks=4, name="sram1")
|
||||
self.local_check(a)
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Four bank two way column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=128, num_banks=4, name="sram2")
|
||||
self.local_check(a)
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
debug.info(1, "Four bank, four way column mux with control logic")
|
||||
a = sram.sram(word_size=16, num_words=256, num_banks=4, name="sram3")
|
||||
self.local_check(a)
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
# debug.info(1, "Four bank, eight way column mux with control logic")
|
||||
# a = sram.sram(word_size=2, num_words=256, num_banks=4, name="sram4")
|
||||
# self.local_check(a)
|
||||
# self.local_check(a, final_verification=True)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
a.sp_write(tempspice)
|
||||
a.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(verify.run_drc(a.name, tempgds))
|
||||
self.assertFalse(verify.run_lvs(a.name, tempgds, tempspice))
|
||||
#self.assertFalse(verify.run_pex(a.name, tempgds, tempspice, output=OPTS.openram_temp+"temp_pex.sp"))
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -4,36 +4,34 @@ Run a regresion test on various srams
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,isclose
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 21_timing_sram_test")
|
||||
|
||||
|
||||
class timing_sram_test(unittest.TestCase):
|
||||
class timing_sram_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
OPTS.check_lvsdrc = False
|
||||
OPTS.spice_name="hspice"
|
||||
OPTS.analytical_delay = False
|
||||
|
||||
# This is a hack to reload the characterizer __init__ with the spice version
|
||||
import characterizer
|
||||
reload(characterizer)
|
||||
from characterizer import delay
|
||||
if not OPTS.spice_exe:
|
||||
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
||||
|
||||
import sram
|
||||
|
||||
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
|
||||
s = sram.sram(word_size=OPTS.config.word_size,
|
||||
num_words=OPTS.config.num_words,
|
||||
num_banks=OPTS.config.num_banks,
|
||||
s = sram.sram(word_size=OPTS.word_size,
|
||||
num_words=OPTS.num_words,
|
||||
num_banks=OPTS.num_banks,
|
||||
name="sram1")
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
|
|
@ -50,26 +48,27 @@ class timing_sram_test(unittest.TestCase):
|
|||
loads = [tech.spice["FF_in_cap"]*4]
|
||||
slews = [tech.spice["rise_time"]*2]
|
||||
data = d.analyze(probe_address, probe_data,slews,loads)
|
||||
#print data
|
||||
if OPTS.tech_name == "freepdk45":
|
||||
golden_data = {'read1_power': 0.0296933,
|
||||
'read0_power': 0.029897899999999998,
|
||||
'write0_power': 0.0258029,
|
||||
'delay1': [0.049100700000000004],
|
||||
'delay0': [0.13766139999999996],
|
||||
'min_period': 0.322,
|
||||
'write1_power': 0.0260398,
|
||||
'slew0': [0.0265264],
|
||||
'slew1': [0.0195507]}
|
||||
golden_data = {'read1_power': 0.0345742,
|
||||
'read0_power': 0.03526189999999999,
|
||||
'write0_power': 0.0270014,
|
||||
'delay1': [0.0573107],
|
||||
'delay0': [0.07055809999999998],
|
||||
'min_period': 0.234,
|
||||
'write1_power': 0.0376625,
|
||||
'slew0': [0.0284344],
|
||||
'slew1': [0.0189185]}
|
||||
elif OPTS.tech_name == "scn3me_subm":
|
||||
golden_data = {'read1_power': 4.3678,
|
||||
'read0_power': 4.3914,
|
||||
'write0_power': 2.9394,
|
||||
'delay1': [0.8901521],
|
||||
'delay0': [2.001],
|
||||
'min_period': 5.781,
|
||||
'write1_power': 2.7163,
|
||||
'slew0': [1.3044000000000002],
|
||||
'slew1': [0.9904079]}
|
||||
golden_data = {'read1_power': 11.2474,
|
||||
'read0_power': 11.3148,
|
||||
'write0_power': 6.9064,
|
||||
'delay1': [1.0298],
|
||||
'delay0': [1.4102],
|
||||
'min_period': 4.063,
|
||||
'write1_power': 11.6964,
|
||||
'slew0': [1.3118],
|
||||
'slew1': [0.9816656]}
|
||||
else:
|
||||
self.assertTrue(False) # other techs fail
|
||||
# Check if no too many or too few results
|
||||
|
|
@ -78,9 +77,9 @@ class timing_sram_test(unittest.TestCase):
|
|||
for k in data.keys():
|
||||
if type(data[k])==list:
|
||||
for i in range(len(data[k])):
|
||||
self.assertTrue(isclose(data[k][i],golden_data[k][i],0.15))
|
||||
self.isclose(data[k][i],golden_data[k][i],0.15)
|
||||
else:
|
||||
self.assertTrue(isclose(data[k],golden_data[k],0.15))
|
||||
self.isclose(data[k],golden_data[k],0.15)
|
||||
|
||||
|
||||
# reset these options
|
||||
|
|
|
|||
|
|
@ -4,29 +4,28 @@ Run a regresion test on various srams
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,isclose
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 21_timing_sram_test")
|
||||
|
||||
|
||||
class timing_setup_test(unittest.TestCase):
|
||||
class timing_setup_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
|
||||
# we will manually run lvs/drc
|
||||
OPTS.check_lvsdrc = False
|
||||
OPTS.spice_name="hspice"
|
||||
OPTS.analytical_delay = False
|
||||
|
||||
# This is a hack to reload the characterizer __init__ with the spice version
|
||||
import characterizer
|
||||
reload(characterizer)
|
||||
from characterizer import setup_hold
|
||||
if not OPTS.spice_exe:
|
||||
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
||||
|
||||
|
||||
import sram
|
||||
import tech
|
||||
|
|
@ -54,9 +53,9 @@ class timing_setup_test(unittest.TestCase):
|
|||
for k in data.keys():
|
||||
if type(data[k])==list:
|
||||
for i in range(len(data[k])):
|
||||
self.assertTrue(isclose(data[k][i],golden_data[k][i],0.15))
|
||||
self.isclose(data[k][i],golden_data[k][i],0.15)
|
||||
else:
|
||||
self.assertTrue(isclose(data[k],golden_data[k],0.15))
|
||||
self.isclose(data[k],golden_data[k],0.15)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
OPTS.analytical_delay = True
|
||||
|
|
|
|||
|
|
@ -4,34 +4,34 @@ Run a regresion test on various srams
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,isclose
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 21_ngspice_delay_test")
|
||||
class timing_sram_test(unittest.TestCase):
|
||||
class timing_sram_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
OPTS.check_lvsdrc = False
|
||||
OPTS.spice_name="ngspice"
|
||||
OPTS.analytical_delay = False
|
||||
|
||||
# This is a hack to reload the characterizer __init__ with the spice version
|
||||
import characterizer
|
||||
reload(characterizer)
|
||||
from characterizer import delay
|
||||
if not OPTS.spice_exe:
|
||||
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
||||
|
||||
import sram
|
||||
|
||||
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
|
||||
s = sram.sram(word_size=OPTS.config.word_size,
|
||||
num_words=OPTS.config.num_words,
|
||||
num_banks=OPTS.config.num_banks,
|
||||
s = sram.sram(word_size=OPTS.word_size,
|
||||
num_words=OPTS.num_words,
|
||||
num_banks=OPTS.num_banks,
|
||||
name="sram1")
|
||||
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
|
|
@ -46,26 +46,27 @@ class timing_sram_test(unittest.TestCase):
|
|||
loads = [tech.spice["FF_in_cap"]*4]
|
||||
slews = [tech.spice["rise_time"]*2]
|
||||
data = d.analyze(probe_address, probe_data,slews,loads)
|
||||
#print data
|
||||
if OPTS.tech_name == "freepdk45":
|
||||
golden_data = {'read1_power': 0.026660760000000002,
|
||||
'read0_power': 0.02711731,
|
||||
'write0_power': 0.02501428,
|
||||
'delay1': [0.04867702],
|
||||
'delay0': [0.1423633],
|
||||
'min_period': 0.332,
|
||||
'write1_power': 0.024162890000000003,
|
||||
'slew0': [0.02733451],
|
||||
'slew1': [0.02121624]}
|
||||
golden_data = {'read1_power': 0.03308298,
|
||||
'read0_power': 0.03866541,
|
||||
'write0_power': 0.02695139,
|
||||
'delay1': [0.05840294000000001],
|
||||
'delay0': [0.40787249999999997],
|
||||
'min_period': 0.781,
|
||||
'write1_power': 0.037257830000000006,
|
||||
'slew0': [0.035826199999999996],
|
||||
'slew1': [0.02059459]}
|
||||
elif OPTS.tech_name == "scn3me_subm":
|
||||
golden_data = {'read1_power': 4.250786000000001,
|
||||
'read0_power': 4.093461,
|
||||
'write0_power': 2.762675,
|
||||
'delay1': [0.920068],
|
||||
'delay0': [2.051821],
|
||||
'min_period': 6.563,
|
||||
'write1_power': 2.4545719999999998,
|
||||
'slew0': [1.342015],
|
||||
'slew1': [1.040868]}
|
||||
golden_data = {'read1_power': 10.31395,
|
||||
'read0_power': 10.0321,
|
||||
'write0_power': 6.072756,
|
||||
'delay1': [1.042564],
|
||||
'delay0': [1.412224],
|
||||
'min_period': 4.688,
|
||||
'write1_power': 10.53758,
|
||||
'slew0': [1.355812],
|
||||
'slew1': [1.03401]}
|
||||
else:
|
||||
self.assertTrue(False) # other techs fail
|
||||
|
||||
|
|
@ -75,9 +76,9 @@ class timing_sram_test(unittest.TestCase):
|
|||
for k in data.keys():
|
||||
if type(data[k])==list:
|
||||
for i in range(len(data[k])):
|
||||
self.assertTrue(isclose(data[k][i],golden_data[k][i],0.15))
|
||||
self.isclose(data[k][i],golden_data[k][i],0.15)
|
||||
else:
|
||||
self.assertTrue(isclose(data[k],golden_data[k],0.15))
|
||||
self.isclose(data[k],golden_data[k],0.15)
|
||||
|
||||
# reset these options
|
||||
OPTS.check_lvsdrc = True
|
||||
|
|
|
|||
|
|
@ -4,30 +4,27 @@ Run a regresion test on various srams
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,isclose
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 21_timing_sram_test")
|
||||
|
||||
|
||||
class timing_setup_test(unittest.TestCase):
|
||||
class timing_setup_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
|
||||
# we will manually run lvs/drc
|
||||
OPTS.check_lvsdrc = False
|
||||
OPTS.spice_name="ngspice"
|
||||
OPTS.analytical_delay = False
|
||||
|
||||
# This is a hack to reload the characterizer __init__ with the spice version
|
||||
import characterizer
|
||||
reload(characterizer)
|
||||
from characterizer import setup_hold
|
||||
if not OPTS.spice_exe:
|
||||
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
||||
|
||||
import sram
|
||||
import tech
|
||||
|
|
@ -55,9 +52,9 @@ class timing_setup_test(unittest.TestCase):
|
|||
for k in data.keys():
|
||||
if type(data[k])==list:
|
||||
for i in range(len(data[k])):
|
||||
self.assertTrue(isclose(data[k][i],golden_data[k][i],0.15))
|
||||
self.isclose(data[k][i],golden_data[k][i],0.15)
|
||||
else:
|
||||
self.assertTrue(isclose(data[k],golden_data[k],0.15))
|
||||
self.isclose(data[k],golden_data[k],0.15)
|
||||
|
||||
# reset these options
|
||||
OPTS.check_lvsdrc = True
|
||||
|
|
|
|||
|
|
@ -4,20 +4,20 @@ Run a regression test on an extracted SRAM to ensure functionality.
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
|
||||
@unittest.skip("SKIPPING 22_sram_func_test")
|
||||
class sram_func_test(unittest.TestCase):
|
||||
class sram_func_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
global verify
|
||||
import verify
|
||||
|
||||
self.func_test(bank_num=1)
|
||||
self.func_test(bank_num=2)
|
||||
|
|
@ -33,9 +33,9 @@ class sram_func_test(unittest.TestCase):
|
|||
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
|
||||
OPTS.check_lvsdrc = False
|
||||
OPTS.use_pex = True
|
||||
s = sram.sram(word_size=OPTS.config.word_size,
|
||||
num_words=OPTS.config.num_words,
|
||||
num_banks=OPTS.config.num_banks,
|
||||
s = sram.sram(word_size=OPTS.word_size,
|
||||
num_words=OPTS.num_words,
|
||||
num_banks=OPTS.num_banks,
|
||||
name="test_sram1")
|
||||
OPTS.check_lvsdrc = True
|
||||
OPTS.use_pex = False
|
||||
|
|
|
|||
|
|
@ -4,36 +4,34 @@ Run a regresion test on various srams
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
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
|
||||
import verify
|
||||
|
||||
#@unittest.skip("SKIPPING 21_timing_sram_test")
|
||||
|
||||
|
||||
class sram_func_test(unittest.TestCase):
|
||||
class sram_func_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
|
||||
# we will manually run lvs/drc
|
||||
OPTS.check_lvsdrc = False
|
||||
OPTS.spice_name="hspice"
|
||||
OPTS.spice_name="" # Unset to use any simulator
|
||||
OPTS.analytical_delay = False
|
||||
|
||||
# This is a hack to reload the characterizer __init__ with the spice version
|
||||
import characterizer
|
||||
reload(characterizer)
|
||||
from characterizer import delay
|
||||
if not OPTS.spice_exe:
|
||||
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
||||
|
||||
import sram
|
||||
|
||||
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
|
||||
s = sram.sram(word_size=OPTS.config.word_size,
|
||||
num_words=OPTS.config.num_words,
|
||||
num_banks=OPTS.config.num_banks,
|
||||
s = sram.sram(word_size=OPTS.word_size,
|
||||
num_words=OPTS.num_words,
|
||||
num_banks=OPTS.num_banks,
|
||||
name="sram_func_test")
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
|
|
|
|||
|
|
@ -4,19 +4,17 @@ Check the .lib file for an SRAM
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,isapproxdiff
|
||||
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
|
||||
import verify
|
||||
|
||||
class lib_test(unittest.TestCase):
|
||||
class lib_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import sram
|
||||
|
|
@ -24,8 +22,8 @@ class lib_test(unittest.TestCase):
|
|||
|
||||
debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank")
|
||||
s = sram.sram(word_size=2,
|
||||
num_words=OPTS.config.num_words,
|
||||
num_banks=OPTS.config.num_banks,
|
||||
num_words=OPTS.num_words,
|
||||
num_banks=OPTS.num_banks,
|
||||
name="sram_2_16_1_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
|
|
@ -38,7 +36,7 @@ class lib_test(unittest.TestCase):
|
|||
|
||||
# let's diff the result with a golden model
|
||||
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename)
|
||||
self.assertEqual(isapproxdiff(libname,golden,0.15),True)
|
||||
self.isapproxdiff(libname,golden,0.15)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -4,32 +4,35 @@ Check the .lib file for an SRAM with pruning
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,isapproxdiff
|
||||
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
|
||||
import verify
|
||||
|
||||
class lib_test(unittest.TestCase):
|
||||
class lib_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
OPTS.check_lvsdrc = False
|
||||
OPTS.spice_name="hspice"
|
||||
OPTS.spice_name="" # Unset to use any simulator
|
||||
OPTS.analytical_delay = False
|
||||
OPTS.trim_netlist = True
|
||||
|
||||
# This is a hack to reload the characterizer __init__ with the spice version
|
||||
import characterizer
|
||||
reload(characterizer)
|
||||
from characterizer import lib
|
||||
if not OPTS.spice_exe:
|
||||
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
||||
|
||||
import sram
|
||||
|
||||
debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank")
|
||||
s = sram.sram(word_size=2,
|
||||
num_words=OPTS.config.num_words,
|
||||
num_banks=OPTS.config.num_banks,
|
||||
num_words=OPTS.num_words,
|
||||
num_banks=OPTS.num_banks,
|
||||
name="sram_2_16_1_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
|
|
@ -42,7 +45,7 @@ class lib_test(unittest.TestCase):
|
|||
|
||||
# let's diff the result with a golden model
|
||||
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename)
|
||||
self.assertEqual(isapproxdiff(libname,golden,0.30),True)
|
||||
self.isapproxdiff(libname,golden,0.30)
|
||||
|
||||
OPTS.analytical_delay = True
|
||||
reload(characterizer)
|
||||
|
|
|
|||
|
|
@ -4,32 +4,35 @@ Check the .lib file for an SRAM
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,isapproxdiff
|
||||
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
|
||||
import verify
|
||||
|
||||
class lib_test(unittest.TestCase):
|
||||
class lib_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
OPTS.check_lvsdrc = False
|
||||
OPTS.spice_name="" # Unset to use any simulator
|
||||
OPTS.analytical_delay = False
|
||||
OPTS.trim_netlist = False
|
||||
|
||||
# This is a hack to reload the characterizer __init__ with the spice version
|
||||
import characterizer
|
||||
reload(characterizer)
|
||||
from characterizer import lib
|
||||
if not OPTS.spice_exe:
|
||||
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
||||
|
||||
import sram
|
||||
|
||||
debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank")
|
||||
s = sram.sram(word_size=2,
|
||||
num_words=OPTS.config.num_words,
|
||||
num_banks=OPTS.config.num_banks,
|
||||
num_words=OPTS.num_words,
|
||||
num_banks=OPTS.num_banks,
|
||||
name="sram_2_16_1_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
|
|
@ -42,7 +45,7 @@ class lib_test(unittest.TestCase):
|
|||
|
||||
# let's diff the result with a golden model
|
||||
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename)
|
||||
self.assertEqual(isapproxdiff(libname,golden,0.15),True)
|
||||
self.isapproxdiff(libname,golden,0.15)
|
||||
|
||||
OPTS.analytical_delay = True
|
||||
OPTS.trim_netlist = True
|
||||
|
|
|
|||
|
|
@ -4,16 +4,14 @@ Check the LEF file for an SRMA
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,isdiff
|
||||
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
|
||||
import verify
|
||||
|
||||
|
||||
class lef_test(unittest.TestCase):
|
||||
class lef_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
|
|
@ -24,8 +22,8 @@ class lef_test(unittest.TestCase):
|
|||
|
||||
debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank")
|
||||
s = sram.sram(word_size=2,
|
||||
num_words=OPTS.config.num_words,
|
||||
num_banks=OPTS.config.num_banks,
|
||||
num_words=OPTS.num_words,
|
||||
num_banks=OPTS.num_banks,
|
||||
name="sram_2_16_1_{0}".format(OPTS.tech_name))
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
|
|
@ -39,7 +37,7 @@ class lef_test(unittest.TestCase):
|
|||
|
||||
# let's diff the result with a golden model
|
||||
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),leffile)
|
||||
self.assertEqual(isdiff(lefname,golden),True)
|
||||
self.isdiff(lefname,golden)
|
||||
|
||||
os.system("rm {0}".format(gdsname))
|
||||
os.system("rm {0}".format(lefname))
|
||||
|
|
|
|||
|
|
@ -4,16 +4,14 @@ Check the .v file for an SRAM
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,isdiff
|
||||
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
|
||||
import verify
|
||||
|
||||
|
||||
class verilog_test(unittest.TestCase):
|
||||
class verilog_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
|
|
@ -24,8 +22,8 @@ class verilog_test(unittest.TestCase):
|
|||
|
||||
debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank")
|
||||
s = sram.sram(word_size=2,
|
||||
num_words=OPTS.config.num_words,
|
||||
num_banks=OPTS.config.num_banks,
|
||||
num_words=OPTS.num_words,
|
||||
num_banks=OPTS.num_banks,
|
||||
name="sram_2_16_1_{0}".format(OPTS.tech_name))
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
|
|
@ -37,7 +35,7 @@ class verilog_test(unittest.TestCase):
|
|||
|
||||
# let's diff the result with a golden model
|
||||
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),vfile)
|
||||
self.assertEqual(isdiff(vname,golden),True)
|
||||
self.isdiff(vname,golden)
|
||||
|
||||
os.system("rm {0}".format(vname))
|
||||
|
||||
|
|
|
|||
|
|
@ -6,25 +6,22 @@ check that these files are right.
|
|||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
import sys,os
|
||||
from testutils import header,openram_test
|
||||
import sys,os,re,shutil
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
import debug
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
|
||||
class openram_test(unittest.TestCase):
|
||||
class openram_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
|
||||
|
||||
debug.info(1, "Testing top-level openram.py with 2-bit, 16 word SRAM.")
|
||||
out_file = "testsram"
|
||||
# make a temp directory for output
|
||||
out_path = OPTS.openram_temp + out_file
|
||||
# make a temp directory for output
|
||||
out_path = "/tmp/testsram_{0}".format(OPTS.tech_name)
|
||||
|
||||
# make sure we start without the files existing
|
||||
if os.path.exists(out_path):
|
||||
|
|
@ -70,7 +67,10 @@ class openram_test(unittest.TestCase):
|
|||
shutil.rmtree(out_path, ignore_errors=True)
|
||||
self.assertEqual(os.path.exists(out_path),False)
|
||||
|
||||
globals.end_openram()
|
||||
# The default was on, so disable it.
|
||||
OPTS.check_lvsdrc=False
|
||||
globals.end_openram()
|
||||
OPTS.check_lvsdrc=True
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -4,25 +4,4 @@ num_banks = 1
|
|||
|
||||
tech_name = "freepdk45"
|
||||
|
||||
# Optional, will be over-ridden on command line.
|
||||
output_path = "/tmp/freepdk45_sram"
|
||||
output_name = "sram_2_16_1_freepdk45"
|
||||
|
||||
decoder = "hierarchical_decoder"
|
||||
ms_flop = "ms_flop"
|
||||
ms_flop_array = "ms_flop_array"
|
||||
control_logic = "control_logic"
|
||||
bitcell_array = "bitcell_array"
|
||||
sense_amp = "sense_amp"
|
||||
sense_amp_array = "sense_amp_array"
|
||||
precharge_array = "precharge_array"
|
||||
column_mux_array = "single_level_column_mux_array"
|
||||
write_driver = "write_driver"
|
||||
write_driver_array = "write_driver_array"
|
||||
tri_gate = "tri_gate"
|
||||
tri_gate_array = "tri_gate_array"
|
||||
wordline_driver = "wordline_driver"
|
||||
replica_bitline = "replica_bitline"
|
||||
replica_bitcell = "replica_bitcell"
|
||||
bitcell = "bitcell"
|
||||
delay_chain = "delay_chain"
|
||||
|
|
|
|||
|
|
@ -4,25 +4,3 @@ num_banks = 1
|
|||
|
||||
tech_name = "scn3me_subm"
|
||||
|
||||
# Optional, will be over-ridden on command line.
|
||||
output_path = "/tmp/scn3me_subm_mysram"
|
||||
output_name = "sram_2_16_1_scn3me_subm"
|
||||
|
||||
decoder = "hierarchical_decoder"
|
||||
ms_flop = "ms_flop"
|
||||
ms_flop_array = "ms_flop_array"
|
||||
control_logic = "control_logic"
|
||||
bitcell_array = "bitcell_array"
|
||||
sense_amp = "sense_amp"
|
||||
sense_amp_array = "sense_amp_array"
|
||||
precharge_array = "precharge_array"
|
||||
column_mux_array = "single_level_column_mux_array"
|
||||
write_driver = "write_driver"
|
||||
write_driver_array = "write_driver_array"
|
||||
tri_gate = "tri_gate"
|
||||
tri_gate_array = "tri_gate_array"
|
||||
wordline_driver = "wordline_driver"
|
||||
replica_bitline = "replica_bitline"
|
||||
replica_bitcell = "replica_bitcell"
|
||||
bitcell = "bitcell"
|
||||
delay_chain = "delay_chain"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -74,7 +74,7 @@ cell (sram_2_16_1_freepdk45){
|
|||
dont_use : true;
|
||||
map_only : true;
|
||||
dont_touch : true;
|
||||
area : 0.023625;
|
||||
area : 918.5120625;
|
||||
|
||||
bus(DATA){
|
||||
bus_type : DATA;
|
||||
|
|
@ -92,19 +92,19 @@ cell (sram_2_16_1_freepdk45){
|
|||
internal_power(){
|
||||
when : "OEb & !clk";
|
||||
rise_power(scalar){
|
||||
values("0.027781");
|
||||
values("0.04024341");
|
||||
}
|
||||
fall_power(scalar){
|
||||
values("0.026752");
|
||||
values("0.029869287");
|
||||
}
|
||||
}
|
||||
timing(){
|
||||
timing_type : setup_rising;
|
||||
related_pin : "clk";
|
||||
rise_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027");
|
||||
values("0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021");
|
||||
}
|
||||
fall_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.009, 0.015",\
|
||||
|
|
@ -129,10 +129,10 @@ cell (sram_2_16_1_freepdk45){
|
|||
internal_power(){
|
||||
when : "!OEb & !clk";
|
||||
rise_power(scalar){
|
||||
values("0.031198");
|
||||
values("0.050563718");
|
||||
}
|
||||
fall_power(scalar){
|
||||
values("0.031252");
|
||||
values("0.055867096");
|
||||
}
|
||||
}
|
||||
timing(){
|
||||
|
|
@ -140,24 +140,24 @@ cell (sram_2_16_1_freepdk45){
|
|||
related_pin : "clk";
|
||||
timing_type : falling_edge;
|
||||
cell_rise(CELL_TABLE) {
|
||||
values("0.046, 0.047, 0.054",\
|
||||
"0.047, 0.047, 0.054",\
|
||||
"0.052, 0.052, 0.059");
|
||||
values("0.055, 0.056, 0.063",\
|
||||
"0.056, 0.057, 0.063",\
|
||||
"0.061, 0.062, 0.069");
|
||||
}
|
||||
cell_fall(CELL_TABLE) {
|
||||
values("0.132, 0.133, 0.142",\
|
||||
"0.133, 0.134, 0.142",\
|
||||
"0.138, 0.139, 0.147");
|
||||
values("0.442, 0.443, 0.452",\
|
||||
"0.442, 0.443, 0.453",\
|
||||
"0.448, 0.449, 0.458");
|
||||
}
|
||||
rise_transition(CELL_TABLE) {
|
||||
values("0.014, 0.015, 0.027",\
|
||||
"0.014, 0.015, 0.027",\
|
||||
"0.014, 0.015, 0.027");
|
||||
values("0.013, 0.015, 0.026",\
|
||||
"0.013, 0.015, 0.026",\
|
||||
"0.013, 0.015, 0.026");
|
||||
}
|
||||
fall_transition(CELL_TABLE) {
|
||||
values("0.018, 0.02, 0.036",\
|
||||
"0.019, 0.02, 0.036",\
|
||||
"0.019, 0.02, 0.036");
|
||||
values("0.029, 0.031, 0.044",\
|
||||
"0.029, 0.031, 0.044",\
|
||||
"0.029, 0.031, 0.044");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -174,9 +174,9 @@ cell (sram_2_16_1_freepdk45){
|
|||
timing_type : setup_rising;
|
||||
related_pin : "clk";
|
||||
rise_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027");
|
||||
values("0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021");
|
||||
}
|
||||
fall_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.009, 0.015",\
|
||||
|
|
@ -208,9 +208,9 @@ cell (sram_2_16_1_freepdk45){
|
|||
timing_type : setup_rising;
|
||||
related_pin : "clk";
|
||||
rise_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027");
|
||||
values("0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021");
|
||||
}
|
||||
fall_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.009, 0.015",\
|
||||
|
|
@ -241,9 +241,9 @@ cell (sram_2_16_1_freepdk45){
|
|||
timing_type : setup_rising;
|
||||
related_pin : "clk";
|
||||
rise_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027");
|
||||
values("0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021");
|
||||
}
|
||||
fall_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.009, 0.015",\
|
||||
|
|
@ -274,9 +274,9 @@ cell (sram_2_16_1_freepdk45){
|
|||
timing_type : setup_rising;
|
||||
related_pin : "clk";
|
||||
rise_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027");
|
||||
values("0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021");
|
||||
}
|
||||
fall_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.009, 0.015",\
|
||||
|
|
@ -308,20 +308,20 @@ cell (sram_2_16_1_freepdk45){
|
|||
timing_type :"min_pulse_width";
|
||||
related_pin : clk;
|
||||
rise_constraint(scalar) {
|
||||
values("0.1955");
|
||||
values("0.449");
|
||||
}
|
||||
fall_constraint(scalar) {
|
||||
values("0.1955");
|
||||
values("0.449");
|
||||
}
|
||||
}
|
||||
timing(){
|
||||
timing_type :"minimum_period";
|
||||
related_pin : clk;
|
||||
rise_constraint(scalar) {
|
||||
values("0.391");
|
||||
values("0.898");
|
||||
}
|
||||
fall_constraint(scalar) {
|
||||
values("0.391");
|
||||
values("0.898");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ cell (sram_2_16_1_freepdk45){
|
|||
dont_use : true;
|
||||
map_only : true;
|
||||
dont_touch : true;
|
||||
area : 0.023625;
|
||||
area : 918.5120625;
|
||||
|
||||
bus(DATA){
|
||||
bus_type : DATA;
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ cell (sram_2_16_1_freepdk45){
|
|||
dont_use : true;
|
||||
map_only : true;
|
||||
dont_touch : true;
|
||||
area : 0.023625;
|
||||
area : 918.5120625;
|
||||
|
||||
bus(DATA){
|
||||
bus_type : DATA;
|
||||
|
|
@ -92,19 +92,19 @@ cell (sram_2_16_1_freepdk45){
|
|||
internal_power(){
|
||||
when : "OEb & !clk";
|
||||
rise_power(scalar){
|
||||
values("0.027781");
|
||||
values("0.0370166");
|
||||
}
|
||||
fall_power(scalar){
|
||||
values("0.026752");
|
||||
values("0.026622831");
|
||||
}
|
||||
}
|
||||
timing(){
|
||||
timing_type : setup_rising;
|
||||
related_pin : "clk";
|
||||
rise_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027");
|
||||
values("0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021");
|
||||
}
|
||||
fall_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.009, 0.015",\
|
||||
|
|
@ -129,10 +129,10 @@ cell (sram_2_16_1_freepdk45){
|
|||
internal_power(){
|
||||
when : "!OEb & !clk";
|
||||
rise_power(scalar){
|
||||
values("0.031198");
|
||||
values("0.034203045");
|
||||
}
|
||||
fall_power(scalar){
|
||||
values("0.031252");
|
||||
values("0.039377859");
|
||||
}
|
||||
}
|
||||
timing(){
|
||||
|
|
@ -140,24 +140,24 @@ cell (sram_2_16_1_freepdk45){
|
|||
related_pin : "clk";
|
||||
timing_type : falling_edge;
|
||||
cell_rise(CELL_TABLE) {
|
||||
values("0.046, 0.047, 0.054",\
|
||||
"0.047, 0.047, 0.054",\
|
||||
"0.052, 0.052, 0.059");
|
||||
values("0.054, 0.055, 0.061",\
|
||||
"0.055, 0.055, 0.062",\
|
||||
"0.06, 0.061, 0.067");
|
||||
}
|
||||
cell_fall(CELL_TABLE) {
|
||||
values("0.132, 0.133, 0.142",\
|
||||
"0.133, 0.134, 0.142",\
|
||||
"0.138, 0.139, 0.147");
|
||||
values("0.438, 0.439, 0.449",\
|
||||
"0.439, 0.44, 0.449",\
|
||||
"0.445, 0.446, 0.455");
|
||||
}
|
||||
rise_transition(CELL_TABLE) {
|
||||
values("0.014, 0.015, 0.027",\
|
||||
"0.014, 0.015, 0.027",\
|
||||
"0.014, 0.015, 0.027");
|
||||
values("0.013, 0.014, 0.026",\
|
||||
"0.013, 0.014, 0.026",\
|
||||
"0.013, 0.015, 0.026");
|
||||
}
|
||||
fall_transition(CELL_TABLE) {
|
||||
values("0.018, 0.02, 0.036",\
|
||||
"0.019, 0.02, 0.036",\
|
||||
"0.019, 0.02, 0.036");
|
||||
values("0.027, 0.029, 0.043",\
|
||||
"0.027, 0.029, 0.043",\
|
||||
"0.027, 0.029, 0.043");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -174,9 +174,9 @@ cell (sram_2_16_1_freepdk45){
|
|||
timing_type : setup_rising;
|
||||
related_pin : "clk";
|
||||
rise_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027");
|
||||
values("0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021");
|
||||
}
|
||||
fall_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.009, 0.015",\
|
||||
|
|
@ -208,9 +208,9 @@ cell (sram_2_16_1_freepdk45){
|
|||
timing_type : setup_rising;
|
||||
related_pin : "clk";
|
||||
rise_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027");
|
||||
values("0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021");
|
||||
}
|
||||
fall_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.009, 0.015",\
|
||||
|
|
@ -241,9 +241,9 @@ cell (sram_2_16_1_freepdk45){
|
|||
timing_type : setup_rising;
|
||||
related_pin : "clk";
|
||||
rise_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027");
|
||||
values("0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021");
|
||||
}
|
||||
fall_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.009, 0.015",\
|
||||
|
|
@ -274,9 +274,9 @@ cell (sram_2_16_1_freepdk45){
|
|||
timing_type : setup_rising;
|
||||
related_pin : "clk";
|
||||
rise_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027",\
|
||||
"0.009, 0.015, 0.027");
|
||||
values("0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021",\
|
||||
"0.009, 0.015, 0.021");
|
||||
}
|
||||
fall_constraint(CONSTRAINT_TABLE) {
|
||||
values("0.009, 0.009, 0.015",\
|
||||
|
|
@ -308,20 +308,20 @@ cell (sram_2_16_1_freepdk45){
|
|||
timing_type :"min_pulse_width";
|
||||
related_pin : clk;
|
||||
rise_constraint(scalar) {
|
||||
values("0.1955");
|
||||
values("0.449");
|
||||
}
|
||||
fall_constraint(scalar) {
|
||||
values("0.1955");
|
||||
values("0.449");
|
||||
}
|
||||
}
|
||||
timing(){
|
||||
timing_type :"minimum_period";
|
||||
related_pin : clk;
|
||||
rise_constraint(scalar) {
|
||||
values("0.391");
|
||||
values("0.898");
|
||||
}
|
||||
fall_constraint(scalar) {
|
||||
values("0.391");
|
||||
values("0.898");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue