Merge branch 'dev'

This commit is contained in:
Matt Guthaus 2019-03-06 06:40:17 -08:00
commit e476df1218
170 changed files with 3442 additions and 24640 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@
*.out
*.toc
*.synctex.gz
**/model_data

View File

@ -26,19 +26,34 @@ predictive and fabricable technologies.
# Basic Setup
## Docker Image
We have a pre-configured Ubuntu [Docker](https://www.docker.com/) image
available that has all tools installed for the [SCMOS] process. It is
available at [docker hub](https://hub.docker.com/r/vlsida/openram-ubuntu).
Please see
[our README.md](https://github.com/VLSIDA/openram-docker-images/blob/master/README.md)
for information on how to use this docker image.
## Dependencies
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 3.5 or higher
+ Python numpy (pip3 install numpy to install)
+ Python scipy (pip3 install scipy to install)
If you want to perform DRC and LVS, you will need either:
+ Calibre (for [FreePDK45])
+ [Magic] + [Netgen] (for [SCMOS])
+ [Magic] 8.2.79 or higher (for [SCMOS])
+ [Netgen] 1.5 (for [SCMOS])
You must set two environment variables:
+ OPENRAM\_HOME should point to the compiler source directory.
+ OPENERAM\_TECH should point to a root technology directory.
## Environment
For example add this to your .bashrc:
```

View File

@ -162,7 +162,7 @@ class contact(hierarchy_design.hierarchy_design):
width=well_width,
height=well_height)
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
""" Get total power of a module """
return self.return_power()

View File

@ -88,11 +88,11 @@ class design(hierarchy_design):
self.readonly_ports.append(port_number)
port_number += 1
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
""" Get total power of a module """
total_module_power = self.return_power()
for inst in self.insts:
total_module_power += inst.mod.analytical_power(proc, vdd, temp, load)
total_module_power += inst.mod.analytical_power(corner, load)
return total_module_power
def __str__(self):

View File

@ -73,8 +73,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
global total_drc_errors
global total_lvs_errors
tempspice = OPTS.openram_temp + "/temp.sp"
tempgds = OPTS.openram_temp + "/temp.gds"
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
self.sp_write(tempspice)
self.gds_write(tempgds)
@ -95,7 +95,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
global total_drc_errors
tempgds = OPTS.openram_temp + "/temp.gds"
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
self.gds_write(tempgds)
num_errors = verify.run_drc(self.name, tempgds, final_verification)
total_drc_errors += num_errors
@ -110,8 +110,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
global total_lvs_errors
tempspice = OPTS.openram_temp + "/temp.sp"
tempgds = OPTS.openram_temp + "/temp.gds"
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
self.sp_write(tempspice)
self.gds_write(tempgds)
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification)

View File

@ -2,6 +2,7 @@ import debug
import re
import os
import math
import tech
class spice():
"""
@ -218,7 +219,7 @@ class spice():
del usedMODS
spfile.close()
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
"""Inform users undefined delay module while building new modules"""
debug.warning("Design Class {0} delay function needs to be defined"
.format(self.__class__.__name__))
@ -228,15 +229,21 @@ class spice():
# return 0 to keep code running while building
return delay_data(0.0, 0.0)
def cal_delay_with_rc(self, r, c ,slew, swing = 0.5):
def cal_delay_with_rc(self, corner, r, c ,slew, swing = 0.5):
"""
Calculate the delay of a mosfet by
modeling it as a resistance driving a capacitance
"""
swing_factor = abs(math.log(1-swing)) # time constant based on swing
proc,vdd,temp = corner
#FIXME: type of delay is needed to know which process to use.
proc_mult = max(self.get_process_delay_factor(proc))
volt_mult = self.get_voltage_delay_factor(vdd)
temp_mult = self.get_temp_delay_factor(temp)
delay = swing_factor * r * c #c is in ff and delay is in fs
delay = delay * proc_mult * volt_mult * temp_mult
delay = delay * 0.001 #make the unit to ps
# Output slew should be linear to input slew which is described
# as 0.005* slew.
@ -247,6 +254,36 @@ class spice():
slew = delay * 0.6 * 2 + 0.005 * slew
return delay_data(delay = delay, slew = slew)
def get_process_delay_factor(self, proc):
"""Returns delay increase estimate based off process
Currently does +/-10 for fast/slow corners."""
proc_factors = []
for mos_proc in proc:
if mos_proc == 'T':
proc_factors.append(1.0)
elif mos_proc == 'F':
proc_factors.append(0.9)
elif mos_proc == 'S':
proc_factors.append(1.1)
return proc_factors
def get_voltage_delay_factor(self, voltage):
"""Returns delay increase due to voltage.
Implemented as linear factor based off nominal voltage.
"""
return tech.spice['vdd_nominal']/voltage
def get_temp_delay_factor(self, temp):
"""Returns delay increase due to temperature (in C).
Determines effect on threshold voltage and then linear factor is estimated.
"""
#Some portions of equation condensed (phi_t = k*T/q for T in Kelvin) in mV
#(k/q)/100 = .008625, The division 100 simplifies the conversion from C to K and mV to V
thermal_voltage_nom = .008625*tech.spice["temp_nominal"]
thermal_voltage = .008625*temp
vthresh = (tech.spice["v_threshold_typical"]+2*(thermal_voltage-thermal_voltage_nom))
#Calculate effect on Vdd-Vth. The current vdd is not used here. A separate vdd factor is calculated.
return (tech.spice['vdd_nominal'] - tech.spice["v_threshold_typical"])/(tech.spice['vdd_nominal']-vthresh)
def return_delay(self, delay, slew):
return delay_data(delay, slew)
@ -254,6 +291,22 @@ class spice():
def generate_rc_net(self,lump_num, wire_length, wire_width):
return wire_spice_model(lump_num, wire_length, wire_width)
def calc_dynamic_power(self, corner, c, freq, swing=1.0):
"""
Calculate dynamic power using effective capacitance, frequency, and corner (PVT)
"""
proc,vdd,temp = corner
net_vswing = vdd*swing
power_dyn = c*vdd*net_vswing*freq
#Apply process and temperature factors. Roughly, process and Vdd affect the delay which affects the power.
#No other estimations are currently used. Increased delay->slower freq.->less power
proc_div = max(self.get_process_delay_factor(proc))
temp_div = self.get_temp_delay_factor(temp)
power_dyn = power_dyn/(proc_div*temp_div)
return power_dyn
def return_power(self, dynamic=0.0, leakage=0.0):
return power_data(dynamic, leakage)

View File

@ -24,7 +24,7 @@ class bitcell(design.design):
self.height = bitcell.height
self.pin_map = bitcell.pin_map
def analytical_delay(self, slew, load=0, swing = 0.5):
def analytical_delay(self, corner, slew, load=0, swing = 0.5):
# delay of bit cell is not like a driver(from WL)
# so the slew used should be 0
# it should not be slew dependent?
@ -33,7 +33,7 @@ class bitcell(design.design):
from tech import spice
r = spice["min_tx_r"]*3
c_para = spice["min_tx_drain_c"]
result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = swing)
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = swing)
return result
@ -66,7 +66,7 @@ class bitcell(design.design):
column_pins = ["br"]
return column_pins
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]

View File

@ -24,7 +24,7 @@ class bitcell_1rw_1r(design.design):
self.height = bitcell_1rw_1r.height
self.pin_map = bitcell_1rw_1r.pin_map
def analytical_delay(self, slew, load=0, swing = 0.5):
def analytical_delay(self, corner, slew, load=0, swing = 0.5):
# delay of bit cell is not like a driver(from WL)
# so the slew used should be 0
# it should not be slew dependent?
@ -33,7 +33,7 @@ class bitcell_1rw_1r(design.design):
from tech import spice
r = spice["min_tx_r"]*3
c_para = spice["min_tx_drain_c"]
result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = swing)
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = swing)
return result
@ -89,7 +89,7 @@ class bitcell_1rw_1r(design.design):
column_pins = ["br0"]
return column_pins
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]

View File

@ -0,0 +1,106 @@
import design
import debug
import utils
from tech import GDS,layer,parameter,drc
class bitcell_1w_1r(design.design):
"""
A single bit cell (6T, 8T, etc.) This module implements the
single memory cell used in the design. It is a hand-made cell, so
the layout and netlist should be available in the technology
library.
"""
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("cell_1w_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "cell_1w_1r")
debug.info(2, "Create bitcell with 1W and 1R Port")
self.width = bitcell_1w_1r.width
self.height = bitcell_1w_1r.height
self.pin_map = bitcell_1w_1r.pin_map
def analytical_delay(self, corner, slew, load=0, swing = 0.5):
# delay of bit cell is not like a driver(from WL)
# so the slew used should be 0
# it should not be slew dependent?
# because the value is there
# the delay is only over half transsmission gate
from tech import spice
r = spice["min_tx_r"]*3
c_para = spice["min_tx_drain_c"]
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = swing)
return result
def list_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
bitcell_pins = ["bl0_{0}".format(col),
"br0_{0}".format(col),
"bl1_{0}".format(col),
"br1_{0}".format(col),
"wl0_{0}".format(row),
"wl1_{0}".format(row),
"vdd",
"gnd"]
return bitcell_pins
def list_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = ["wl0", "wl1"]
return row_pins
def list_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """
column_pins = ["bl0", "br0", "bl1", "br1"]
return column_pins
def list_all_bl_names(self):
""" Creates a list of all bl pins names """
column_pins = ["bl0", "bl1"]
return column_pins
def list_all_br_names(self):
""" Creates a list of all br pins names """
column_pins = ["br0", "br1"]
return column_pins
def list_read_bl_names(self):
""" Creates a list of bl pin names associated with read ports """
column_pins = ["bl0", "bl1"]
return column_pins
def list_read_br_names(self):
""" Creates a list of br pin names associated with read ports """
column_pins = ["br0", "br1"]
return column_pins
def list_write_bl_names(self):
""" Creates a list of bl pin names associated with write ports """
column_pins = ["bl0"]
return column_pins
def list_write_br_names(self):
""" Creates a list of br pin names asscociated with write ports"""
column_pins = ["br0"]
return column_pins
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
dynamic = 0 #temporary
total_power = self.return_power(dynamic, leakage)
return total_power
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width.
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin

View File

@ -866,15 +866,15 @@ class pbitcell(design.design):
vdd_pos = self.inverter_pmos_right.get_pin("D").center()
self.add_path("metal1", [Q_bar_pos, vdd_pos])
def analytical_delay(self, slew, load=0, swing = 0.5):
def analytical_delay(self, corner, slew, load=0, swing = 0.5):
#FIXME: Delay copied exactly over from bitcell
from tech import spice
r = spice["min_tx_r"]*3
c_para = spice["min_tx_drain_c"]
result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = swing)
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = swing)
return result
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]

View File

@ -0,0 +1,32 @@
import design
import debug
import utils
from tech import GDS,layer,drc,parameter
class replica_bitcell_1w_1r(design.design):
"""
A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It
is a hand-made cell, so the layout and netlist should be available in
the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "replica_cell_1w_1r")
debug.info(2, "Create replica bitcell 1w+1r object")
self.width = replica_bitcell_1w_1r.width
self.height = replica_bitcell_1w_1r.height
self.pin_map = replica_bitcell_1w_1r.pin_map
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width.
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin

View File

@ -9,6 +9,8 @@ from .functional import *
from .worst_case import *
from .simulation import *
from .bitline_delay import *
from .measurements import *
from .model_check import *
debug.info(1,"Initializing characterizer...")
OPTS.spice_exe = ""

View File

@ -22,11 +22,24 @@ class bitline_delay(delay):
self.period = tech.spice["feasible_period"]
self.is_bitline_measure = True
def create_signal_names(self):
delay.create_signal_names(self)
self.bl_signal_names = ["Xsram.Xbank0.bl", "Xsram.Xbank0.br"]
self.sen_name = "Xsram.s_en"
def create_measurement_names(self):
"""Create measurement names. The names themselves currently define the type of measurement"""
#Altering the names will crash the characterizer. TODO: object orientated approach to the measurements.
self.bitline_meas_names = ["bl_volt", "br_volt"]
self.bl_volt_meas_names = ["volt_bl", "volt_br"]
self.bl_delay_meas_names = ["delay_bl", "delay_br"] #only used in SPICE simulation
self.bl_delay_result_name = "delay_bl_vth" #Used in the return value
def set_probe(self,probe_address, probe_data):
""" Probe address and data can be set separately to utilize other
functions in this characterizer besides analyze."""
delay.set_probe(self,probe_address, probe_data)
self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data)
def write_delay_measures(self):
"""
Write the measure statements to quantify the bitline voltage at sense amp enable 50%.
@ -38,26 +51,52 @@ class bitline_delay(delay):
self.sf.write("* {}\n".format(comment))
for read_port in self.targ_read_ports:
self.write_bitline_measures_read_port(read_port)
self.write_bitline_voltage_measures(read_port)
self.write_bitline_delay_measures(read_port)
def write_bitline_measures_read_port(self, port):
def write_bitline_voltage_measures(self, port):
"""
Add measurments to capture the bitline voltages at 50% Sense amp enable
"""
debug.info(2, "Measuring bitline column={}, port={}".format(self.bitline_column,port))
if len(self.all_ports) == 1: #special naming case for single port sram bitlines
bitline_port = ""
else:
bitline_port = str(port)
sen_port_name = "{}{}".format(self.sen_name,port)
for (measure_name, bl_signal_name) in zip(self.bl_volt_meas_names, self.bl_signal_names):
bl_port_name = "{}{}_{}".format(bl_signal_name, bitline_port, self.bitline_column)
measure_port_name = "{}{}".format(measure_name,port)
self.stim.gen_meas_find_voltage(measure_port_name, sen_port_name, bl_port_name, .5, "RISE", self.cycle_times[self.measure_cycles[port]["read0"]])
def write_bitline_delay_measures(self, port):
"""
Write the measure statements to quantify the delay and power results for a read port.
"""
# add measure statements for delays/slews
measure_bitline = self.get_data_bit_column_number(self.probe_address, self.probe_data)
debug.info(2, "Measuring bitline column={}".format(measure_bitline))
for port in self.targ_read_ports:
if len(self.all_ports) == 1: #special naming case for single port sram bitlines
bitline_port = ""
else:
bitline_port = str(port)
sen_name = "Xsram.s_en{}".format(port)
bl_name = "Xsram.Xbank0.bl{}_{}".format(bitline_port, measure_bitline)
br_name = "Xsram.Xbank0.br{}_{}".format(bitline_port, measure_bitline)
self.stim.gen_meas_find_voltage("bl_volt", sen_name, bl_name, .5, "RISE", self.cycle_times[self.measure_cycles[port]["read0"]])
self.stim.gen_meas_find_voltage("br_volt", sen_name, br_name, .5, "RISE", self.cycle_times[self.measure_cycles[port]["read0"]])
for (measure_name, bl_signal_name) in zip(self.bl_delay_meas_names, self.bl_signal_names):
meas_values = self.get_delay_meas_values(measure_name, bl_signal_name, port)
self.stim.gen_meas_delay(*meas_values)
def get_delay_meas_values(self, delay_name, bitline_name, port):
"""Get the values needed to generate a Spice measurement statement based on the name of the measurement."""
if len(self.all_ports) == 1: #special naming case for single port sram bitlines
bitline_port = ""
else:
bitline_port = str(port)
meas_name="{0}{1}".format(delay_name, port)
targ_name = "{0}{1}_{2}".format(bitline_name,bitline_port,self.bitline_column)
half_vdd = 0.5 * self.vdd_voltage
trig_val = half_vdd
targ_val = self.vdd_voltage-tech.spice["v_threshold_typical"]
trig_name = "clk{0}".format(port)
trig_dir="FALL"
targ_dir="FALL"
#Half period added to delay measurement to negative clock edge
trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period/2
return (meas_name,trig_name,targ_name,trig_val,targ_val,trig_dir,targ_dir,trig_td,targ_td)
def gen_test_cycles_one_port(self, read_port, write_port):
"""Sets a list of key time-points [ns] of the waveform (each rising edge)
@ -89,6 +128,7 @@ class bitline_delay(delay):
self.add_read("R data 0 address {} to check W0 worked".format(self.probe_address),
self.probe_address,data_zeros,read_port)
self.measure_cycles[read_port]["read0"] = len(self.cycle_times)-1
def get_data_bit_column_number(self, probe_address, probe_data):
"""Calculates bitline column number of data bit under test using bit position and mux size"""
if self.sram.col_addr_size>0:
@ -115,19 +155,70 @@ class bitline_delay(delay):
self.stim.run_sim() #running sim prodoces spice output file.
for port in self.targ_read_ports:
bitlines_meas_vals = {}
for mname in self.bitline_meas_names:
bitlines_meas_vals[mname] = parse_spice_list("timing", mname)
#Check that power parsing worked.
for name, val in bitlines_meas_vals.items():
if type(val)!=float:
debug.error("Failed to Parse Bitline Values:\n\t\t{0}".format(bitlines_meas_vals),1) #Printing the entire dict looks bad.
result[port].update(bitlines_meas_vals)
#Parse and check the voltage measurements
bl_volt_meas_dict = {}
for mname in self.bl_volt_meas_names:
mname_port = "{}{}".format(mname,port)
volt_meas_val = parse_spice_list("timing", mname_port)
if type(volt_meas_val)!=float:
debug.error("Failed to Parse Bitline Voltage:\n\t\t{0}={1}".format(mname,volt_meas_val),1)
bl_volt_meas_dict[mname] = volt_meas_val
result[port].update(bl_volt_meas_dict)
#Parse and check the delay measurements. Intended that one measurement will fail, save the delay that did not fail.
bl_delay_meas_dict = {}
values_added = 0 #For error checking
for mname in self.bl_delay_meas_names: #Parse
mname_port = "{}{}".format(mname,port)
delay_meas_val = parse_spice_list("timing", mname_port)
if type(delay_meas_val)==float: #Only add if value is float, do not error.
bl_delay_meas_dict[self.bl_delay_result_name] = delay_meas_val * 1e9 #convert to ns
values_added+=1
debug.check(values_added>0, "Bitline delay measurements failed in SPICE simulation.")
debug.check(values_added<2, "Both bitlines experienced a Vth drop, check simulation results.")
result[port].update(bl_delay_meas_dict)
# The delay is from the negative edge for our SRAM
return (True,result)
def check_bitline_all_results(self, results):
"""Checks the bitline values measured for each tested port"""
for port in self.targ_read_ports:
self.check_bitline_port_results(results[port])
def check_bitline_port_results(self, port_results):
"""Performs three different checks for the bitline values: functionality, bitline swing from vdd, and differential bit swing"""
bl_volt, br_volt = port_results["volt_bl"], port_results["volt_br"]
self.check_functionality(bl_volt,br_volt)
self.check_swing_from_vdd(bl_volt,br_volt)
self.check_differential_swing(bl_volt,br_volt)
def check_functionality(self, bl_volt, br_volt):
"""Checks whether the read failed or not. Measured values are hardcoded with the intention of reading a 0."""
if bl_volt > br_volt:
debug.error("Read failure. Value 1 was read instead of 0.",1)
def check_swing_from_vdd(self, bl_volt, br_volt):
"""Checks difference on discharging bitline from VDD to see if it is within margin of the RBL height parameter."""
if bl_volt < br_volt:
discharge_volt = bl_volt
else:
discharge_volt = br_volt
desired_bl_volt = tech.parameter["rbl_height_percentage"]*self.vdd_voltage
debug.info(1, "Active bitline={:.3f}v, Desired bitline={:.3f}v".format(discharge_volt,desired_bl_volt))
vdd_error_margin = .2 #20% of vdd margin for bitline, a little high for now.
if abs(discharge_volt - desired_bl_volt) > vdd_error_margin*self.vdd_voltage:
debug.warning("Bitline voltage is not within {}% Vdd margin. Delay chain/RBL could need resizing.".format(vdd_error_margin*100))
def check_differential_swing(self, bl_volt, br_volt):
"""This check looks at the difference between the bitline voltages. This needs to be large enough to prevent
sensing errors."""
bitline_swing = abs(bl_volt-br_volt)
debug.info(1,"Bitline swing={:.3f}v".format(bitline_swing))
vdd_error_margin = .2 #20% of vdd margin for bitline, a little high for now.
if bitline_swing < vdd_error_margin*self.vdd_voltage:
debug.warning("Bitline swing less than {}% Vdd margin. Sensing errors more likely to occur.".format(vdd_error_margin))
def analyze(self, probe_address, probe_data, slews, loads):
"""Measures the bitline swing of the differential bitlines (bl/br) at 50% s_en """
self.set_probe(probe_address, probe_data)
@ -141,10 +232,10 @@ class bitline_delay(delay):
debug.info(1,"Bitline swing test: corner {}".format(self.corner))
(success, results)=self.run_delay_simulation()
debug.check(success, "Bitline Failed: period {}".format(self.period))
for mname in self.bitline_meas_names:
bitline_swings[mname] = results[read_port][mname]
debug.info(1,"Bitline values (bl/br): {}".format(bitline_swings))
return bitline_swings
debug.info(1,"Bitline values (voltages/delays):\n\t {}".format(results[read_port]))
self.check_bitline_all_results(results)
return results

View File

@ -80,3 +80,10 @@ def convert_to_float(number):
debug.error("Invalid number: {0}".format(number),1)
return float_value
def check_dict_values_is_float(dict):
"""Checks if all the values are floats. Useful for checking failed Spice measurements."""
for key, value in dict.items():
if type(value)!=float:
return False
return True

View File

@ -8,6 +8,7 @@ from .charutils import *
import utils
from globals import OPTS
from .simulation import simulation
from .measurements import *
class delay(simulation):
"""Functions to measure the delay and power of an SRAM at a given address and
@ -35,17 +36,92 @@ class delay(simulation):
self.period = 0
self.set_load_slew(0,0)
self.set_corner(corner)
self.create_signal_names()
#Create global measure names. Should maybe be an input at some point.
self.create_measurement_names()
def create_measurement_names(self):
"""Create measurement names. The names themselves currently define the type of measurement"""
#Altering the names will crash the characterizer. TODO: object orientated approach to the measurements.
self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"]
self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"]
self.voltage_when_names = ["volt_bl", "volt_br"]
self.bitline_delay_names = ["delay_bl", "delay_br"]
def create_measurement_objects(self):
"""Create the measurements used for read and write ports"""
self.create_read_port_measurement_objects()
self.create_write_port_measurement_objects()
def create_read_port_measurement_objects(self):
"""Create the measurements used for read ports: delays, slews, powers"""
self.read_meas_objs = []
trig_delay_name = "clk{0}"
targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit
self.read_meas_objs.append(delay_measure("delay_lh", trig_delay_name, targ_name, "RISE", "RISE", measure_scale=1e9))
self.read_meas_objs[-1].meta_str = "read1" #Used to index time delay values when measurements written to spice file.
self.read_meas_objs.append(delay_measure("delay_hl", trig_delay_name, targ_name, "FALL", "FALL", measure_scale=1e9))
self.read_meas_objs[-1].meta_str = "read0"
self.read_meas_objs.append(slew_measure("slew_lh", targ_name, "RISE", measure_scale=1e9))
self.read_meas_objs[-1].meta_str = "read1"
self.read_meas_objs.append(slew_measure("slew_hl", targ_name, "FALL", measure_scale=1e9))
self.read_meas_objs[-1].meta_str = "read0"
self.read_meas_objs.append(power_measure("read1_power", "RISE", measure_scale=1e3))
self.read_meas_objs[-1].meta_str = "read1"
self.read_meas_objs.append(power_measure("read0_power", "FALL", measure_scale=1e3))
self.read_meas_objs[-1].meta_str = "read0"
#This will later add a half-period to the spice time delay. Only for reading 0.
for obj in self.read_meas_objs:
if obj.meta_str is "read0":
obj.meta_add_delay = True
trig_name = "Xsram.s_en{}" #Sense amp enable
if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name
port_format = ""
else:
port_format = "{}"
bl_name = "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column)
br_name = "Xsram.Xbank0.br{}_{}".format(port_format, self.bitline_column)
self.read_meas_objs.append(voltage_when_measure(self.voltage_when_names[0], trig_name, bl_name, "RISE", .5))
self.read_meas_objs.append(voltage_when_measure(self.voltage_when_names[1], trig_name, br_name, "RISE", .5))
#These are read values but need to be separated for unique error checking.
self.create_bitline_delay_measurement_objects()
def create_bitline_delay_measurement_objects(self):
"""Create the measurements used for bitline delay values. Due to unique error checking, these are separated from other measurements.
These measurements are only associated with read values
"""
self.bitline_delay_objs = []
trig_name = "clk{0}"
if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name
port_format = ""
else:
port_format = "{}"
bl_name = "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column)
br_name = "Xsram.Xbank0.br{}_{}".format(port_format, self.bitline_column)
targ_val = (self.vdd_voltage - tech.spice["v_threshold_typical"])/self.vdd_voltage #Calculate as a percentage of vdd
targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit
self.bitline_delay_objs.append(delay_measure(self.bitline_delay_names[0], trig_name, bl_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9))
self.bitline_delay_objs[-1].meta_str = "read0"
self.bitline_delay_objs.append(delay_measure(self.bitline_delay_names[1], trig_name, br_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9))
self.bitline_delay_objs[-1].meta_str = "read1"
#Enforces the time delay on the bitline measurements for read0 or read1
for obj in self.bitline_delay_objs:
obj.meta_add_delay = True
def create_write_port_measurement_objects(self):
"""Create the measurements used for read ports: delays, slews, powers"""
self.write_meas_objs = []
self.write_meas_objs.append(power_measure("write1_power", "RISE", measure_scale=1e3))
self.write_meas_objs[-1].meta_str = "write1"
self.write_meas_objs.append(power_measure("write0_power", "FALL", measure_scale=1e3))
self.write_meas_objs[-1].meta_str = "write0"
def create_signal_names(self):
self.addr_name = "A"
self.din_name = "DIN"
@ -198,86 +274,75 @@ class delay(simulation):
self.sf.close()
def get_delay_meas_values(self, delay_name, port):
"""Get the values needed to generate a Spice measurement statement based on the name of the measurement."""
debug.check('lh' in delay_name or 'hl' in delay_name, "Measure command {0} does not contain direction (lh/hl)")
trig_clk_name = "clk{0}".format(port)
meas_name="{0}{1}".format(delay_name, port)
targ_name = "{0}".format("{0}{1}_{2}".format(self.dout_name,port,self.probe_data))
half_vdd = 0.5 * self.vdd_voltage
trig_slew_low = 0.1 * self.vdd_voltage
targ_slew_high = 0.9 * self.vdd_voltage
if 'delay' in delay_name:
trig_val = half_vdd
targ_val = half_vdd
trig_name = trig_clk_name
if 'lh' in delay_name:
trig_dir="RISE"
targ_dir="RISE"
trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read1"]]
else:
trig_dir="FALL"
targ_dir="FALL"
trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read0"]]
elif 'slew' in delay_name:
trig_name = targ_name
if 'lh' in delay_name:
trig_val = trig_slew_low
targ_val = targ_slew_high
targ_dir = trig_dir = "RISE"
trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read1"]]
else:
trig_val = targ_slew_high
targ_val = trig_slew_low
targ_dir = trig_dir = "FALL"
trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read0"]]
def get_read_measure_variants(self, port, measure_obj):
"""Checks the measurement object and calls respective function for related measurement inputs."""
meas_type = type(measure_obj)
if meas_type is delay_measure or meas_type is slew_measure:
return self.get_delay_measure_variants(port, measure_obj)
elif meas_type is power_measure:
return self.get_power_measure_variants(port, measure_obj, "read")
elif meas_type is voltage_when_measure:
return self.get_volt_when_measure_variants(port, measure_obj)
else:
debug.error(1, "Measure command {0} not recognized".format(delay_name))
return (meas_name,trig_name,targ_name,trig_val,targ_val,trig_dir,targ_dir,trig_td,targ_td)
debug.error("Input function not defined for measurement type={}".format(meas_type))
def get_delay_measure_variants(self, port, delay_obj):
"""Get the measurement values that can either vary from simulation to simulation (vdd, address) or port to port (time delays)"""
#Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port
#vdd is arguably constant as that is true for a single lib file.
if delay_obj.meta_str == "read0":
#Falling delay are measured starting from neg. clk edge. Delay adjusted to that.
meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]]
elif delay_obj.meta_str == "read1":
meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]]
else:
debug.error("Unrecognised delay Index={}".format(delay_obj.meta_str),1)
if delay_obj.meta_add_delay:
meas_cycle_delay += self.period/2
return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port)
def get_power_measure_variants(self, port, power_obj, operation):
"""Get the measurement values that can either vary port to port (time delays)"""
#Return value is intended to match the power measure format: t_initial, t_final, port
t_initial = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]]
t_final = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]+1]
return (t_initial, t_final, port)
def get_volt_when_measure_variants(self, port, power_obj):
"""Get the measurement values that can either vary port to port (time delays)"""
#Only checking 0 value reads for now.
t_trig = meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]]
return (t_trig, self.vdd_voltage, port)
def write_delay_measures_read_port(self, port):
"""
Write the measure statements to quantify the delay and power results for a read port.
"""
# add measure statements for delays/slews
for dname in self.delay_meas_names:
meas_values = self.get_delay_meas_values(dname, port)
self.stim.gen_meas_delay(*meas_values)
for measure in self.read_meas_objs+self.bitline_delay_objs:
measure_variant_inp_tuple = self.get_read_measure_variants(port, measure)
measure.write_measure(self.stim, measure_variant_inp_tuple)
def get_write_measure_variants(self, port, measure_obj):
"""Checks the measurement object and calls respective function for related measurement inputs."""
meas_type = type(measure_obj)
if meas_type is power_measure:
return self.get_power_measure_variants(port, measure_obj, "write")
else:
debug.error("Input function not defined for measurement type={}".format(meas_type))
# add measure statements for power
for pname in self.power_meas_names:
if "read" not in pname:
continue
#Different naming schemes are used for the measure cycle dict and measurement names.
#TODO: make them the same so they can be indexed the same.
if '1' in pname:
t_initial = self.cycle_times[self.measure_cycles[port]["read1"]]
t_final = self.cycle_times[self.measure_cycles[port]["read1"]+1]
elif '0' in pname:
t_initial = self.cycle_times[self.measure_cycles[port]["read0"]]
t_final = self.cycle_times[self.measure_cycles[port]["read0"]+1]
self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port),
t_initial=t_initial,
t_final=t_final)
def write_delay_measures_write_port(self, port):
"""
Write the measure statements to quantify the power results for a write port.
"""
# add measure statements for power
for pname in self.power_meas_names:
if "write" not in pname:
continue
t_initial = self.cycle_times[self.measure_cycles[port]["write0"]]
t_final = self.cycle_times[self.measure_cycles[port]["write0"]+1]
if '1' in pname:
t_initial = self.cycle_times[self.measure_cycles[port]["write1"]]
t_final = self.cycle_times[self.measure_cycles[port]["write1"]+1]
self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port),
t_initial=t_initial,
t_final=t_final)
for measure in self.write_meas_objs:
measure_variant_inp_tuple = self.get_write_measure_variants(port, measure)
measure.write_measure(self.stim, measure_variant_inp_tuple)
def write_delay_measures(self):
"""
@ -385,28 +450,7 @@ class delay(simulation):
previous_period = self.period
debug.info(1, "Found feasible_period: {0}ns".format(self.period))
return feasible_delays
def parse_values(self, values_names, port, mult = 1.0):
"""Parse multiple values in the timing output file. Optional multiplier.
Return a dict of the input names and values. Port used for parsing file.
"""
values = []
all_values_floats = True
for vname in values_names:
#ngspice converts all measure characters to lowercase, not tested on other sims
value = parse_spice_list("timing", "{0}{1}".format(vname.lower(), port))
#Check if any of the values fail to parse
if type(value)!=float:
all_values_floats = False
values.append(value)
#Apply Multiplier only if all values are floats. Let other check functions handle this error.
if all_values_floats:
return {values_names[i]:values[i]*mult for i in range(len(values))}
else:
return {values_names[i]:values[i] for i in range(len(values))}
def run_delay_simulation(self):
"""
This tries to simulate a period and checks if the result works. If
@ -427,33 +471,45 @@ class delay(simulation):
#Too much duplicate code here. Try reducing
for port in self.targ_read_ports:
debug.info(2, "Check delay values for port {}".format(port))
delay_names = [mname for mname in self.delay_meas_names]
delays = self.parse_values(delay_names, port, 1e9) # scale delays to ns
if not self.check_valid_delays(delays):
return (False,{})
result[port].update(delays)
read_port_dict = {}
#Get measurements from output file
for measure in self.read_meas_objs:
read_port_dict[measure.name] = measure.retrieve_measure(port=port)
#Check timing for read ports. Power is only checked if it was read correctly
if not self.check_valid_delays(read_port_dict):
return (False,{})
if not check_dict_values_is_float(read_port_dict):
debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict),1) #Printing the entire dict looks bad.
result[port].update(read_port_dict)
bitline_delay_dict = self.evaluate_bitline_delay(port)
result[port].update(bitline_delay_dict)
power_names = [mname for mname in self.power_meas_names if 'read' in mname]
powers = self.parse_values(power_names, port, 1e3) # scale power to mw
#Check that power parsing worked.
for name, power in powers.items():
if type(power)!=float:
debug.error("Failed to Parse Power Values:\n\t\t{0}".format(powers),1) #Printing the entire dict looks bad.
result[port].update(powers)
for port in self.targ_write_ports:
power_names = [mname for mname in self.power_meas_names if 'write' in mname]
powers = self.parse_values(power_names, port, 1e3) # scale power to mw
#Check that power parsing worked.
for name, power in powers.items():
if type(power)!=float:
debug.error("Failed to Parse Power Values:\n\t\t{0}".format(powers),1) #Printing the entire dict looks bad.
result[port].update(powers)
write_port_dict = {}
for measure in self.write_meas_objs:
write_port_dict[measure.name] = measure.retrieve_measure(port=port)
if not check_dict_values_is_float(write_port_dict):
debug.error("Failed to Measure Write Port Values:\n\t\t{0}".format(write_port_dict),1) #Printing the entire dict looks bad.
result[port].update(write_port_dict)
# The delay is from the negative edge for our SRAM
return (True,result)
def evaluate_bitline_delay(self, port):
"""Parse and check the bitline delay. One of the measurements is expected to fail which warrants its own function."""
bl_delay_meas_dict = {}
values_added = 0 #For error checking
for measure in self.bitline_delay_objs:
bl_delay_val = measure.retrieve_measure(port=port)
if type(bl_delay_val) != float or 0 > bl_delay_val or bl_delay_val > self.period/2: #Only add if value is valid, do not error.
debug.error("Bitline delay measurement failed: half-period={}, {}={}".format(self.period/2, measure.name, bl_delay_val),1)
bl_delay_meas_dict[measure.name] = bl_delay_val
return bl_delay_meas_dict
def run_power_simulation(self):
"""
This simulates a disabled SRAM to get the leakage power when it is off.
@ -478,13 +534,13 @@ class delay(simulation):
#key=raw_input("press return to continue")
return (leakage_power*1e3, trim_leakage_power*1e3)
def check_valid_delays(self, delay_dict):
def check_valid_delays(self, result_dict):
""" Check if the measurements are defined and if they are valid. """
#Hard coded names currently
delay_hl = delay_dict["delay_hl"]
delay_lh = delay_dict["delay_lh"]
slew_hl = delay_dict["slew_hl"]
slew_lh = delay_dict["slew_lh"]
delay_hl = result_dict["delay_hl"]
delay_lh = result_dict["delay_lh"]
slew_hl = result_dict["slew_hl"]
slew_lh = result_dict["slew_lh"]
period_load_slew_str = "period {0} load {1} slew {2}".format(self.period,self.load, self.slew)
# if it failed or the read was longer than a period
@ -610,9 +666,22 @@ class delay(simulation):
functions in this characterizer besides analyze."""
self.probe_address = probe_address
self.probe_data = probe_data
self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data)
self.wordline_row = self.get_address_row_number(probe_address)
self.prepare_netlist()
def get_data_bit_column_number(self, probe_address, probe_data):
"""Calculates bitline column number of data bit under test using bit position and mux size"""
if self.sram.col_addr_size>0:
col_address = int(probe_address[0:self.sram.col_addr_size],2)
else:
col_address = 0
bl_column = int(self.sram.words_per_row*probe_data + col_address)
return bl_column
def get_address_row_number(self, probe_address):
"""Calculates wordline row number of data bit under test using address and column mux size"""
return int(probe_address[self.sram.col_addr_size:],2)
def prepare_netlist(self):
""" Prepare a trimmed netlist and regular netlist. """
@ -645,6 +714,9 @@ class delay(simulation):
char_sram_data = {}
self.set_probe(probe_address, probe_data)
self.create_signal_names()
self.create_measurement_names()
self.create_measurement_objects()
self.load=max(loads)
self.slew=max(slews)
@ -828,13 +900,14 @@ class delay(simulation):
"""
if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0:
debug.warning("Analytical characterization results are not supported for multiport.")
self.create_signal_names()
self.create_measurement_names()
power = self.analytical_power(slews, loads)
port_data = self.get_empty_measure_data_dict()
for slew in slews:
for load in loads:
self.set_load_slew(load,slew)
bank_delay = self.sram.analytical_delay(self.vdd_voltage, self.slew,self.load)
bank_delay = self.sram.analytical_delay(self.corner, self.slew,self.load)
for port in self.all_ports:
for mname in self.delay_meas_names+self.power_meas_names:
if "power" in mname:
@ -845,9 +918,12 @@ class delay(simulation):
port_data[port][mname].append(bank_delay[port].slew/1e3)
else:
debug.error("Measurement name not recognized: {}".format(mname),1)
sram_data = { "min_period": 0,
"leakage_power": power.leakage}
period_margin = 0.1
risefall_delay = bank_delay[self.read_ports[0]].delay/1e3
sram_data = { "min_period":risefall_delay*2*period_margin,
"leakage_power": power.leakage}
debug.info(2,"SRAM Data:\n{}".format(sram_data))
debug.info(2,"Port Data:\n{}".format(port_data))
return (sram_data,port_data)
@ -855,7 +931,7 @@ class delay(simulation):
"""Get the dynamic and leakage power from the SRAM"""
#slews unused, only last load is used
load = loads[-1]
power = self.sram.analytical_power(self.process, self.vdd_voltage, self.temperature, load)
power = self.sram.analytical_power(self.corner, load)
#convert from nW to mW
power.dynamic /= 1e6
power.leakage /= 1e6
@ -890,7 +966,7 @@ class delay(simulation):
def get_empty_measure_data_dict(self):
"""Make a dict of lists for each type of delay and power measurement to append results to"""
measure_names = self.delay_meas_names + self.power_meas_names
measure_names = self.delay_meas_names + self.power_meas_names + self.voltage_when_names + self.bitline_delay_names
#Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists.
measure_data = [{mname:[] for mname in measure_names} for i in self.all_ports]
return measure_data

View File

@ -83,9 +83,11 @@ class lib:
(self.process, self.voltage, self.temperature) = self.corner
self.lib = open(lib_name, "w")
debug.info(1,"Writing to {0}".format(lib_name))
self.corner_name = lib_name.replace(self.out_dir,"").replace(".lib","")
self.characterize()
self.lib.close()
self.parse_info(self.corner,lib_name)
def characterize(self):
""" Characterize the current corner. """
@ -325,7 +327,7 @@ class lib:
self.lib.write(" }\n")
self.lib.write(" pin(DOUT{}){{\n".format(read_port))
self.lib.write(" pin(DOUT{0}[{1}:0]){{\n".format(read_port,self.sram.word_size-1))
self.lib.write(" timing(){ \n")
self.lib.write(" timing_sense : non_unate; \n")
self.lib.write(" related_pin : \"clk{0}\"; \n".format(read_port))
@ -358,7 +360,7 @@ class lib:
self.lib.write(" address : ADDR{0}; \n".format(write_port))
self.lib.write(" clocked_on : clk{0}; \n".format(write_port))
self.lib.write(" }\n")
self.lib.write(" pin(DIN{}){{\n".format(write_port))
self.lib.write(" pin(DIN{0}[{1}:0]){{\n".format(write_port,self.sram.word_size-1))
self.write_FF_setuphold(write_port)
self.lib.write(" }\n") # pin
self.lib.write(" }\n") #bus
@ -378,7 +380,7 @@ class lib:
self.lib.write(" direction : input; \n")
self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"]))
self.lib.write(" max_transition : {0};\n".format(self.slews[-1]))
self.lib.write(" pin(ADDR{})".format(port))
self.lib.write(" pin(ADDR{0}[{1}:0])".format(port,self.sram.addr_size-1))
self.lib.write("{\n")
self.write_FF_setuphold(port)
@ -481,17 +483,16 @@ class lib:
self.lib.write(" }\n")
def compute_delay(self):
""" Do the analysis if we haven't characterized the SRAM yet """
if not hasattr(self,"d"):
self.d = delay(self.sram, self.sp_file, self.corner)
if self.use_model:
char_results = self.d.analytical_delay(self.slews,self.loads)
self.char_sram_results, self.char_port_results = char_results
else:
probe_address = "1" * self.sram.addr_size
probe_data = self.sram.word_size - 1
char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads)
self.char_sram_results, self.char_port_results = char_results
"""Compute SRAM delays for current corner"""
self.d = delay(self.sram, self.sp_file, self.corner)
if self.use_model:
char_results = self.d.analytical_delay(self.slews,self.loads)
self.char_sram_results, self.char_port_results = char_results
else:
probe_address = "1" * self.sram.addr_size
probe_data = self.sram.word_size - 1
char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads)
self.char_sram_results, self.char_port_results = char_results
def compute_setup_hold(self):
""" Do the analysis if we haven't characterized a FF yet """
@ -507,9 +508,11 @@ class lib:
def parse_info(self,corner,lib_name):
""" Copies important characterization data to datasheet.info to be added to datasheet """
if OPTS.is_unit_test:
git_id = 'AAAAAAAAAAAAAAAAAAAA'
git_id = 'FFFFFFFFFFFFFFFFFFFF'
else:
with open(os.devnull, 'wb') as devnull:
# parses the mose recent git commit id - requres git is installed
proc = subprocess.Popen(['git','rev-parse','HEAD'], cwd=os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/', stdout=subprocess.PIPE)
git_id = str(proc.stdout.read())
@ -518,16 +521,17 @@ class lib:
git_id = git_id[2:-3]
except:
pass
# check if git id is valid
if len(git_id) != 40:
debug.warning("Failed to retrieve git id")
git_id = 'Failed to retruieve'
datasheet = open(OPTS.openram_temp +'/datasheet.info', 'a+')
current_time = datetime.datetime.now()
datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},".format(
"sram_{0}_{1}_{2}".format(OPTS.word_size, OPTS.num_words, OPTS.tech_name),
current_time = datetime.date.today()
# write static information to be parser later
datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},".format(
OPTS.output_name,
OPTS.num_words,
OPTS.num_banks,
OPTS.num_rw_ports,
@ -542,7 +546,8 @@ class lib:
lib_name,
OPTS.word_size,
git_id,
current_time
current_time,
OPTS.analytical_delay
))
# information of checks
@ -555,7 +560,9 @@ class lib:
LVS = str(total_lvs_errors)
datasheet.write("{0},{1},".format(DRC, LVS))
# write area
datasheet.write(str(self.sram.width * self.sram.height)+',')
# write timing information for all ports
for port in self.all_ports:
#DIN timings
if port in self.write_ports:
@ -652,9 +659,45 @@ class lib:
))
# write power information
for port in self.all_ports:
name = ''
read_write = ''
# write dynamic power usage
if port in self.read_ports:
web_name = " & !WEb{0}".format(port)
name = "!CSb{0} & clk{0}{1}".format(port, web_name)
read_write = 'Read'
datasheet.write("{0},{1},{2},{3},".format(
"power",
name,
read_write,
np.mean(self.char_port_results[port]["read1_power"] + self.char_port_results[port]["read0_power"])/2
))
if port in self.write_ports:
web_name = " & WEb{0}".format(port)
name = "!CSb{0} & !clk{0}{1}".format(port, web_name)
read_write = 'Write'
datasheet.write("{0},{1},{2},{3},".format(
'power',
name,
read_write,
np.mean(self.char_port_results[port]["write1_power"] + self.char_port_results[port]["write0_power"])/2
))
# write leakage power
control_str = 'CSb0'
for i in range(1, self.total_port_num):
control_str += ' & CSb{0}'.format(i)
datasheet.write("{0},{1},{2},".format('leak', control_str, self.char_sram_results["leakage_power"]))
datasheet.write("END\n")
datasheet.close()

View File

@ -10,7 +10,8 @@ class logical_effort():
min_inv_cin = 1+beta
pinv=parameter["min_inv_para_delay"]
def __init__(self, size, cin, cout, parasitic, out_is_rise=True):
def __init__(self, name, size, cin, cout, parasitic, out_is_rise=True):
self.name = name
self.cin = cin
self.cout = cout
self.logical_effort = (self.cin/size)/logical_effort.min_inv_cin
@ -19,8 +20,13 @@ class logical_effort():
self.is_rise = out_is_rise
def __str__(self):
return "g=" + str(self.logical_effort) + ", h=" + str(self.eletrical_effort) + ", p=" + str(self.parasitic_scale)+"*pinv, rise_delay="+str(self.is_rise)
return "Name={}, g={}, h={}, p={}*pinv, rise_delay={}".format(self.name,
self.logical_effort,
self.eletrical_effort,
self.parasitic_scale,
self.is_rise
)
def get_stage_effort(self):
return self.logical_effort*self.eletrical_effort
@ -29,6 +35,10 @@ class logical_effort():
def get_stage_delay(self, pinv):
return self.get_stage_effort()+self.get_parasitic_delay(pinv)
def calculate_delays(stage_effort_list, pinv):
"""Convert stage effort objects to list of delay values"""
return [stage.get_stage_delay(pinv) for stage in stage_effort_list]
def calculate_relative_delay(stage_effort_list, pinv=parameter["min_inv_para_delay"]):
"""Calculates the total delay of a given delay path made of a list of logical effort objects."""
@ -40,7 +50,7 @@ def calculate_relative_rise_fall_delays(stage_effort_list, pinv=parameter["min_i
debug.info(2, "Calculating rise/fall relative delays")
total_rise_delay, total_fall_delay = 0,0
for stage in stage_effort_list:
debug.info(3, stage)
debug.info(2, stage)
if stage.is_rise:
total_rise_delay += stage.get_stage_delay(pinv)
else:

View File

@ -0,0 +1,159 @@
import debug
from tech import drc, parameter, spice
from abc import ABC, abstractmethod
from .stimuli import *
from .charutils import *
class spice_measurement(ABC):
"""Base class for spice stimulus measurements."""
def __init__(self, measure_name, measure_scale=None):
#Names must be unique for correct spice simulation, but not enforced here.
self.name = measure_name
self.measure_scale = measure_scale
#Some meta values used externally. variables are added here for consistency accross the objects
self.meta_str = None
self.meta_add_delay = False
@abstractmethod
def get_measure_function(self):
return None
@abstractmethod
def get_measure_values(self):
return None
def write_measure(self, stim_obj, input_tuple):
measure_func = self.get_measure_function()
if measure_func == None:
debug.error("Did not set measure function",1)
measure_vals = self.get_measure_values(*input_tuple)
measure_func(stim_obj, *measure_vals)
def retrieve_measure(self, port=""):
value = parse_spice_list("timing", "{0}{1}".format(self.name.lower(), port))
if type(value)!=float or self.measure_scale == None:
return value
else:
return value*self.measure_scale
class delay_measure(spice_measurement):
"""Generates a spice measurement for the delay of 50%-to-50% points of two signals."""
def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd=0.5, targ_vdd=0.5, measure_scale=None):
spice_measurement.__init__(self, measure_name, measure_scale)
self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd)
def get_measure_function(self):
return stimuli.gen_meas_delay
def set_meas_constants(self, trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd):
"""Set the constants for this measurement: signal names, directions, and trigger scales"""
self.trig_dir_str = trig_dir_str
self.targ_dir_str = targ_dir_str
self.trig_val_of_vdd = trig_vdd
self.targ_val_of_vdd = targ_vdd
self.trig_name_no_port = trig_name
self.targ_name_no_port = targ_name
#Time delays and ports are variant and needed as inputs when writing the measurement
def get_measure_values(self, trig_td, targ_td, vdd_voltage, port=None):
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
trig_val = self.trig_val_of_vdd * vdd_voltage
targ_val = self.targ_val_of_vdd * vdd_voltage
if port != None:
#For dictionary indexing reasons, the name is formatted differently than the signals
meas_name = "{}{}".format(self.name, port)
trig_name = self.trig_name_no_port.format(port)
targ_name = self.targ_name_no_port.format(port)
else:
meas_name = self.name
trig_name = self.trig_name_no_port
targ_name = self.targ_name_no_port
return (meas_name,trig_name,targ_name,trig_val,targ_val,self.trig_dir_str,self.targ_dir_str,trig_td,targ_td)
class slew_measure(delay_measure):
def __init__(self, measure_name, signal_name, slew_dir_str, measure_scale=None):
spice_measurement.__init__(self, measure_name, measure_scale)
self.set_meas_constants(signal_name, slew_dir_str)
def set_meas_constants(self, signal_name, slew_dir_str):
"""Set the values needed to generate a Spice measurement statement based on the name of the measurement."""
self.trig_dir_str = slew_dir_str
self.targ_dir_str = slew_dir_str
if slew_dir_str == "RISE":
self.trig_val_of_vdd = 0.1
self.targ_val_of_vdd = 0.9
elif slew_dir_str == "FALL":
self.trig_val_of_vdd = 0.9
self.targ_val_of_vdd = 0.1
else:
debug.error("Unrecognised slew measurement direction={}".format(slew_dir_str),1)
self.trig_name_no_port = signal_name
self.targ_name_no_port = signal_name
#Time delays and ports are variant and needed as inputs when writing the measurement
class power_measure(spice_measurement):
"""Generates a spice measurement for the average power between two time points."""
def __init__(self, measure_name, power_type="", measure_scale=None):
spice_measurement.__init__(self, measure_name, measure_scale)
self.set_meas_constants(power_type)
def get_measure_function(self):
return stimuli.gen_meas_power
def set_meas_constants(self, power_type):
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
#Not needed for power simulation
self.power_type = power_type #Expected to be "RISE"/"FALL"
def get_measure_values(self, t_initial, t_final, port=None):
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
if port != None:
meas_name = "{}{}".format(self.name, port)
else:
meas_name = self.name
return (meas_name,t_initial,t_final)
class voltage_when_measure(spice_measurement):
"""Generates a spice measurement to measure the voltage of a signal based on the voltage of another."""
def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, trig_vdd, measure_scale=None):
spice_measurement.__init__(self, measure_name, measure_scale)
self.set_meas_constants(trig_name, targ_name, trig_dir_str, trig_vdd)
def get_measure_function(self):
return stimuli.gen_meas_find_voltage
def set_meas_constants(self, trig_name, targ_name, trig_dir_str, trig_vdd):
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
self.trig_dir_str = trig_dir_str
self.trig_val_of_vdd = trig_vdd
self.trig_name_no_port = trig_name
self.targ_name_no_port = targ_name
def get_measure_values(self, trig_td, vdd_voltage, port=None):
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
if port != None:
#For dictionary indexing reasons, the name is formatted differently than the signals
meas_name = "{}{}".format(self.name, port)
trig_name = self.trig_name_no_port.format(port)
targ_name = self.targ_name_no_port.format(port)
else:
meas_name = self.name
trig_name = self.trig_name_no_port
targ_name = self.targ_name_no_port
trig_voltage = self.trig_val_of_vdd*vdd_voltage
return (meas_name,trig_name,targ_name,trig_voltage,self.trig_dir_str,trig_td)

View File

@ -0,0 +1,348 @@
import sys,re,shutil
import debug
import tech
import math
from .stimuli import *
from .trim_spice import *
from .charutils import *
import utils
from globals import OPTS
from .delay import delay
from .measurements import *
class model_check(delay):
"""Functions to test for the worst case delay in a target SRAM
The current worst case determines a feasible period for the SRAM then tests
several bits and record the delay and differences between the bits.
"""
def __init__(self, sram, spfile, corner):
delay.__init__(self,sram,spfile,corner)
self.period = tech.spice["feasible_period"]
self.create_data_names()
def create_data_names(self):
self.wl_meas_name, self.wl_model_name = "wl_measures", "wl_model"
self.sae_meas_name, self.sae_model_name = "sae_measures", "sae_model"
self.wl_slew_name, self.sae_slew_name = "wl_slews", "sae_slews"
def create_measurement_names(self):
"""Create measurement names. The names themselves currently define the type of measurement"""
#Create delay measurement names
wl_en_driver_delay_names = ["delay_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())]
wl_driver_delay_names = ["delay_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())]
sen_driver_delay_names = ["delay_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())]
dc_delay_names = ["delay_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)]
self.wl_delay_meas_names = wl_en_driver_delay_names+["delay_wl_en", "delay_wl_bar"]+wl_driver_delay_names+["delay_wl"]
self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names
self.sae_delay_meas_names = ["delay_pre_sen"]+sen_driver_delay_names+["delay_sen"]
self.delay_chain_indices = (len(self.rbl_delay_meas_names)-len(dc_delay_names), len(self.rbl_delay_meas_names))
#Create slew measurement names
wl_en_driver_slew_names = ["slew_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())]
wl_driver_slew_names = ["slew_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())]
sen_driver_slew_names = ["slew_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())]
dc_slew_names = ["slew_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)]
self.wl_slew_meas_names = ["slew_wl_gated_clk_bar"]+wl_en_driver_slew_names+["slew_wl_en", "slew_wl_bar"]+wl_driver_slew_names+["slew_wl"]
self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names
self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen"]+sen_driver_slew_names+["slew_sen"]
def create_signal_names(self):
"""Creates list of the signal names used in the spice file along the wl and sen paths.
Names are re-harded coded here; i.e. the names are hardcoded in most of OpenRAM and are
replicated here.
"""
delay.create_signal_names(self)
#Signal names are all hardcoded, need to update to make it work for probe address and different configurations.
wl_en_driver_signals = ["Xsram.Xcontrol0.Xbuf_wl_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())]
wl_driver_signals = ["Xsram.Xbank0.Xwordline_driver0.Xwl_driver_inv{}.Zb{}_int".format(self.wordline_row, stage) for stage in range(1,self.get_num_wl_driver_stages())]
sen_driver_signals = ["Xsram.Xcontrol0.Xbuf_s_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_sen_driver_stages())]
delay_chain_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_{}".format(stage) for stage in range(1,self.get_num_delay_stages())]
self.wl_signal_names = ["Xsram.Xcontrol0.gated_clk_bar"]+\
wl_en_driver_signals+\
["Xsram.wl_en0", "Xsram.Xbank0.Xwordline_driver0.wl_bar_{}".format(self.wordline_row)]+\
wl_driver_signals+\
["Xsram.Xbank0.wl_{}".format(self.wordline_row)]
pre_delay_chain_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in"]
self.rbl_en_signal_names = pre_delay_chain_names+\
delay_chain_signal_names+\
["Xsram.Xcontrol0.Xreplica_bitline.delayed_en"]
self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en"]+\
sen_driver_signals+\
["Xsram.s_en0"]
def create_measurement_objects(self):
"""Create the measurements used for read and write ports"""
self.create_wordline_measurement_objects()
self.create_sae_measurement_objects()
self.all_measures = self.wl_meas_objs+self.sae_meas_objs
def create_wordline_measurement_objects(self):
"""Create the measurements to measure the wordline path from the gated_clk_bar signal"""
self.wl_meas_objs = []
trig_dir = "RISE"
targ_dir = "FALL"
for i in range(1, len(self.wl_signal_names)):
self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1],
self.wl_signal_names[i-1],
self.wl_signal_names[i],
trig_dir,
targ_dir,
measure_scale=1e9))
self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i-1],
self.wl_signal_names[i-1],
trig_dir,
measure_scale=1e9))
temp_dir = trig_dir
trig_dir = targ_dir
targ_dir = temp_dir
self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[-1], self.wl_signal_names[-1], trig_dir, measure_scale=1e9))
def create_sae_measurement_objects(self):
"""Create the measurements to measure the sense amp enable path from the gated_clk_bar signal. The RBL splits this path into two."""
self.sae_meas_objs = []
trig_dir = "RISE"
targ_dir = "FALL"
#Add measurements from gated_clk_bar to RBL
for i in range(1, len(self.rbl_en_signal_names)):
self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1],
self.rbl_en_signal_names[i-1],
self.rbl_en_signal_names[i],
trig_dir,
targ_dir,
measure_scale=1e9))
self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i-1],
self.rbl_en_signal_names[i-1],
trig_dir,
measure_scale=1e9))
temp_dir = trig_dir
trig_dir = targ_dir
targ_dir = temp_dir
self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[-1],
self.rbl_en_signal_names[-1],
trig_dir,
measure_scale=1e9))
#Add measurements from rbl_out to sae. Trigger directions do not invert from previous stage due to RBL.
trig_dir = "FALL"
targ_dir = "RISE"
#Add measurements from gated_clk_bar to RBL
for i in range(1, len(self.sae_signal_names)):
self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1],
self.sae_signal_names[i-1],
self.sae_signal_names[i],
trig_dir,
targ_dir,
measure_scale=1e9))
self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i-1],
self.sae_signal_names[i-1],
trig_dir,
measure_scale=1e9))
temp_dir = trig_dir
trig_dir = targ_dir
targ_dir = temp_dir
self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[-1],
self.sae_signal_names[-1],
trig_dir,
measure_scale=1e9))
def write_delay_measures(self):
"""
Write the measure statements to quantify the delay and power results for all targeted ports.
"""
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))
for read_port in self.targ_read_ports:
self.write_measures_read_port(read_port)
def get_delay_measure_variants(self, port, measure_obj):
"""Get the measurement values that can either vary from simulation to simulation (vdd, address)
or port to port (time delays)"""
#Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port
#Assuming only read 0 for now
if not (type(measure_obj) is delay_measure or type(measure_obj) is slew_measure):
debug.error("Measurement not recognized by the model checker.",1)
meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period/2
return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port)
def write_measures_read_port(self, port):
"""
Write the measure statements for all nodes along the wordline path.
"""
# add measure statements for delays/slews
for measure in self.all_measures:
measure_variant_inp_tuple = self.get_delay_measure_variants(port, measure)
measure.write_measure(self.stim, measure_variant_inp_tuple)
def get_measurement_values(self, meas_objs, port):
"""Gets the delays and slews from a specified port from the spice output file and returns them as lists."""
delay_meas_list = []
slew_meas_list = []
for measure in meas_objs:
measure_value = measure.retrieve_measure(port=port)
if type(measure_value) != float:
debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, measure_value),1)
if type(measure) is delay_measure:
delay_meas_list.append(measure_value)
elif type(measure)is slew_measure:
slew_meas_list.append(measure_value)
else:
debug.error("Measurement object not recognized.",1)
return delay_meas_list, slew_meas_list
def run_delay_simulation(self):
"""
This tries to simulate a period and checks if the result works. If
so, it returns True and the delays, slews, and powers. It
works on the trimmed netlist by default, so powers do not
include leakage of all cells.
"""
#Sanity Check
debug.check(self.period > 0, "Target simulation period non-positive")
wl_delay_result = [[] for i in self.all_ports]
wl_slew_result = [[] for i in self.all_ports]
sae_delay_result = [[] for i in self.all_ports]
sae_slew_result = [[] for i in self.all_ports]
# Checking from not data_value to data_value
self.write_delay_stimulus()
self.stim.run_sim() #running sim prodoces spice output file.
#Retrieve the results from the output file
for port in self.targ_read_ports:
#Parse and check the voltage measurements
wl_delay_result[port], wl_slew_result[port] = self.get_measurement_values(self.wl_meas_objs, port)
sae_delay_result[port], sae_slew_result[port] = self.get_measurement_values(self.sae_meas_objs, port)
return (True,wl_delay_result, sae_delay_result, wl_slew_result, sae_slew_result)
def get_model_delays(self, port):
"""Get model delays based on port. Currently assumes single RW port."""
return self.sram.control_logic_rw.get_wl_sen_delays()
def get_num_delay_stages(self):
"""Gets the number of stages in the delay chain from the control logic"""
return len(self.sram.control_logic_rw.replica_bitline.delay_fanout_list)
def get_num_delay_fanout_list(self):
"""Gets the number of stages in the delay chain from the control logic"""
return self.sram.control_logic_rw.replica_bitline.delay_fanout_list
def get_num_delay_stage_fanout(self):
"""Gets fanout in each stage in the delay chain. Assumes each stage is the same"""
return self.sram.control_logic_rw.replica_bitline.delay_fanout_list[0]
def get_num_wl_en_driver_stages(self):
"""Gets the number of stages in the wl_en driver from the control logic"""
return self.sram.control_logic_rw.wl_en_driver.num_stages
def get_num_sen_driver_stages(self):
"""Gets the number of stages in the sen driver from the control logic"""
return self.sram.control_logic_rw.s_en_driver.num_stages
def get_num_wl_driver_stages(self):
"""Gets the number of stages in the wordline driver from the control logic"""
return self.sram.bank.wordline_driver.inv.num_stages
def scale_delays(self, delay_list):
"""Takes in a list of measured delays and convert it to simple units to easily compare to model values."""
converted_values = []
#Calculate average
total = 0
for meas_value in delay_list:
total+=meas_value
average = total/len(delay_list)
#Convert values
for meas_value in delay_list:
converted_values.append(meas_value/average)
return converted_values
def min_max_normalization(self, value_list):
"""Re-scales input values on a range from 0-1 where min(list)=0, max(list)=1"""
scaled_values = []
min_max_diff = max(value_list) - min(value_list)
average = sum(value_list)/len(value_list)
for value in value_list:
scaled_values.append((value-average)/(min_max_diff))
return scaled_values
def calculate_error_l2_norm(self, list_a, list_b):
"""Calculates error between two lists using the l2 norm"""
error_list = []
for val_a, val_b in zip(list_a, list_b):
error_list.append((val_a-val_b)**2)
return error_list
def compare_measured_and_model(self, measured_vals, model_vals):
"""First scales both inputs into similar ranges and then compares the error between both."""
scaled_meas = self.min_max_normalization(measured_vals)
debug.info(1, "Scaled measurements:\n{}".format(scaled_meas))
scaled_model = self.min_max_normalization(model_vals)
debug.info(1, "Scaled model:\n{}".format(scaled_model))
errors = self.calculate_error_l2_norm(scaled_meas, scaled_model)
debug.info(1, "Errors:\n{}\n".format(errors))
def analyze(self, probe_address, probe_data, slews, loads):
"""Measures entire delay path along the wordline and sense amp enable and compare it to the model delays."""
self.load=max(loads)
self.slew=max(slews)
self.set_probe(probe_address, probe_data)
self.create_signal_names()
self.create_measurement_names()
self.create_measurement_objects()
data_dict = {}
read_port = self.read_ports[0] #only test the first read port
self.targ_read_ports = [read_port]
self.targ_write_ports = [self.write_ports[0]]
debug.info(1,"Model test: corner {}".format(self.corner))
(success, wl_delays, sae_delays, wl_slews, sae_slews)=self.run_delay_simulation()
debug.check(success, "Model measurements Failed: period={}".format(self.period))
wl_model_delays, sae_model_delays = self.get_model_delays(read_port)
debug.info(1,"Measured Wordline delays (ns):\n\t {}".format(wl_delays[read_port]))
debug.info(1,"Wordline model delays:\n\t {}".format(wl_model_delays))
debug.info(1,"Measured Wordline slews:\n\t {}".format(wl_slews[read_port]))
debug.info(1,"Measured SAE delays (ns):\n\t {}".format(sae_delays[read_port]))
debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays))
debug.info(1,"Measured SAE slews:\n\t {}".format(sae_slews[read_port]))
data_dict[self.wl_meas_name] = wl_delays[read_port]
data_dict[self.wl_model_name] = wl_model_delays
data_dict[self.sae_meas_name] = sae_delays[read_port]
data_dict[self.sae_model_name] = sae_model_delays
data_dict[self.wl_slew_name] = wl_slews[read_port]
data_dict[self.sae_slew_name] = sae_slews[read_port]
#Some evaluations of the model and measured values
# debug.info(1, "Comparing wordline measurements and model.")
# self.compare_measured_and_model(wl_delays[read_port], wl_model_delays)
# debug.info(1, "Comparing SAE measurements and model")
# self.compare_measured_and_model(sae_delays[read_port], sae_model_delays)
return data_dict
def get_all_signal_names(self):
"""Returns all signals names as a dict indexed by hardcoded names. Useful for writing the head of the CSV."""
name_dict = {}
#Signal names are more descriptive than the measurement names, first value trimmed to match size of measurements names.
name_dict[self.wl_meas_name] = self.wl_signal_names[1:]
name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured.
name_dict[self.sae_meas_name] = self.rbl_en_signal_names[1:]+self.sae_signal_names[1:]
name_dict[self.sae_model_name] = name_dict["sae_measures"]
name_dict[self.wl_slew_name] = self.wl_slew_meas_names
name_dict[self.sae_slew_name] = self.rbl_slew_meas_names+self.sae_slew_meas_names
return name_dict

View File

@ -258,14 +258,15 @@ class stimuli():
# UIC is needed for ngspice to converge
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep,end_time))
self.sf.write(".TEMP {}\n".format(self.temperature))
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.
self.sf.write(".OPTIONS POST=1 RELTOL={0} PROBE method=gear TEMP={1}\n".format(reltol,self.temperature))
self.sf.write(".OPTIONS POST=1 RELTOL={0} PROBE method=gear\n".format(reltol))
else:
self.sf.write(".OPTIONS POST=1 RUNLVL={0} PROBE TEMP={1}\n".format(runlvl,self.temperature))
self.sf.write(".OPTIONS POST=1 RUNLVL={0} PROBE\n".format(runlvl))
# create plots for all signals
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -19,8 +19,8 @@
padding-top: 11px;
padding-bottom: 11px;
text-align: left;
background-color: #004184;
color: #F1B521;
background-color: #003C6C;
color: #FDC700;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -28,17 +28,17 @@ class datasheet():
# for item in self.description:
# self.html += item + ','
self.html += '-->'
# Add vlsida logo
vlsi_logo = 0
with open(os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/datasheet/assets/vlsi_logo.png', "rb") as image_file:
vlsi_logo = base64.b64encode(image_file.read())
# Add openram logo
openram_logo = 0
with open(os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/datasheet/assets/openram_logo_placeholder.png', "rb") as image_file:
with open(os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/datasheet/assets/OpenRAM_logo.png', "rb") as image_file:
openram_logo = base64.b64encode(image_file.read())
self.html += '<a href="https://vlsida.soe.ucsc.edu/"><img src="data:image/png;base64,{0}" alt="VLSIDA"></a>'.format(str(vlsi_logo)[
2:-1])
self.html += '<a href="https://vlsida.soe.ucsc.edu/"><img src="data:image/png;base64,{0}" alt="VLSIDA"></a><a href ="https://github.com/VLSIDA/OpenRAM"><img src ="data:image/png;base64,{1}" alt = "OpenRAM"></a>'.format(str(vlsi_logo)[2:-1], str(openram_logo)[2:-1])
self.html += '<p style="font-size: 18px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">' + \
self.name + '.html' + '</p>'
@ -49,23 +49,30 @@ class datasheet():
'LVS errors: ' + str(self.LVS) + '</p>'
self.html += '<p style="font-size: 18px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">' + \
'Git commit id: ' + str(self.git_id) + '</p>'
# print port table
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Ports and Configuration</p>'
# self.html += in_out(self.io,table_id='data').__html__().replace('&lt;','<').replace('&#34;','"').replace('&gt;',">")
self.html += self.io_table.to_html()
# print operating condidition information
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Operating Conditions</p>'
# self.html += operating_conditions(self.operating,table_id='data').__html__()
self.html += self.operating_table.to_html()
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Timing and Current Data</p>'
# self.html += timing_and_current_data(self.timing,table_id='data').__html__()
# check if analytical model is being used
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Timing Data</p>'
model = ''
if self.ANALYTICAL_MODEL == 'True':
model = "analytical model: results may not be precise"
else:
model = "spice characterizer"
# display timing data
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Using '+model+'</p>'
self.html += self.timing_table.to_html()
# display power data
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Power Data</p>'
self.html += self.power_table.to_html()
# display corner information
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Characterization Corners</p>'
# self.html += characterization_corners(self.corners,table_id='data').__html__()
self.html += self.corners_table.to_html()
# display deliverables table
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Deliverables</p>'
# self.html += deliverables(self.dlv,table_id='data').__html__().replace('&lt;','<').replace('&#34;','"').replace('&gt;',">")
self.html += self.dlv_table.to_html()

View File

@ -4,7 +4,6 @@ This is a script to load data from the characterization and layout processes int
a web friendly html datasheet.
"""
# TODO:
# include power
# Diagram generation
# Improve css
@ -16,19 +15,34 @@ import csv
import datasheet
import table_gen
def process_name(corner):
"""
Expands the names of the characterization corner types into something human friendly
"""
if corner == "TT":
return "Typical - Typical"
if corner == "SS":
return "Slow - Slow"
if corner == "FF":
return "Fast - Fast"
else:
return "custom"
# def process_name(corner):
# """
# Expands the names of the characterization corner types into something human friendly
# """
# if corner == "TS":
# return "Typical - Slow"
# if corner == "TT":
# return "Typical - Typical"
# if corner == "TF":
# return "Typical - Fast"
#
# if corner == "SS":
# return "Slow - Slow"
# if corner == "ST":
# return "Slow - Typical"
# if corner == "SF":
# return "Slow - Fast"
#
# if corner == "FS":
# return "Fast - Slow"
# if corner == "FT":
# return "Fast - Typical"
# if corner == "FF":
# return "Fast - Fast"
#
# else:
# return "custom"
#
def parse_characterizer_csv(f, pages):
@ -91,15 +105,19 @@ def parse_characterizer_csv(f, pages):
DATETIME = row[col]
col += 1
ANALYTICAL_MODEL = row[col]
col += 1
DRC = row[col]
col += 1
LVS = row[col]
col += 1
AREA = row[col]
col += 1
for sheet in pages:
if sheet.name == NAME:
@ -133,7 +151,7 @@ def parse_characterizer_csv(f, pages):
1000/float(MIN_PERIOD)))
except Exception:
pass
# check current .lib file produces the slowest timing results
while(True):
col_start = col
if(row[col].startswith('DIN')):
@ -147,31 +165,31 @@ def parse_characterizer_csv(f, pages):
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('setup falling'):
if item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold rising'):
if item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold falling'):
if item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
col += 1
@ -186,31 +204,31 @@ def parse_characterizer_csv(f, pages):
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('cell fall'):
if item[0].endswith('cell fall'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('rise transition'):
if item[0].endswith('rise transition'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('fall transition'):
if item[0].endswith('fall transition'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
col += 1
@ -225,31 +243,31 @@ def parse_characterizer_csv(f, pages):
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('setup falling'):
if item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold rising'):
if item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold falling'):
if item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
col += 1
@ -264,31 +282,31 @@ def parse_characterizer_csv(f, pages):
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('setup falling'):
if item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold rising'):
if item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold falling'):
if item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
col += 1
@ -303,31 +321,31 @@ def parse_characterizer_csv(f, pages):
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('setup falling'):
if item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold rising'):
if item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold falling'):
if item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
col += 1
@ -336,8 +354,36 @@ def parse_characterizer_csv(f, pages):
sheet.description.append(str(element))
break
new_sheet.corners_table.add_row([PROC, process_name(
PROC), VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
#check if new power is worse the previous
while(True):
col_start = col
if row[col] == 'power':
for item in sheet.power_table.rows:
if item[0].startswith(row[col+1]):
if item[2].startswith('{0} Rising'.format(row[col+2])):
if float(item[2]) < float(row[col+3]):
item[2] = row[col+3]
if item[2].startswith('{0} Falling'.format(row[col+2])):
if float(item[2]) < float(row[col+3]):
item[2] = row[col+3]
col += 4
else:
break
# check if new leakge is worse the previous
while(True):
col_start = col
if row[col] == 'leak':
for item in sheet.power_table.rows:
if item[0].startswith(row[col+1]):
if float(item[2]) < float(row[col+2]):
item[2] = row[col+2]
col += 3
else:
break
# add new corner information
new_sheet.corners_table.add_row(
[PROC, VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
new_sheet.dlv_table.add_row(
['.lib', 'Synthesis models', '<a href="file://{0}">{1}</a>'.format(LIB_NAME, LIB_NAME.replace(OUT_DIR, ''))])
@ -351,14 +397,15 @@ def parse_characterizer_csv(f, pages):
new_sheet.time = DATETIME
new_sheet.DRC = DRC
new_sheet.LVS = LVS
new_sheet.ANALYTICAL_MODEL = ANALYTICAL_MODEL
new_sheet.description = [NAME, NUM_WORDS, NUM_BANKS, NUM_RW_PORTS, NUM_W_PORTS,
NUM_R_PORTS, TECH_NAME, MIN_PERIOD, WORD_SIZE, ORIGIN_ID, DATETIME]
new_sheet.corners_table = table_gen.table_gen("corners")
new_sheet.corners_table.add_row(
['Corner Name', 'Process', 'Power Supply', 'Temperature', 'Library Name Suffix'])
new_sheet.corners_table.add_row([PROC, process_name(
PROC), VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
['Transistor Type', 'Power Supply', 'Temperature', 'Corner Name'])
new_sheet.corners_table.add_row(
[PROC, VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
new_sheet.operating_table = table_gen.table_gen(
"operating_table")
new_sheet.operating_table.add_row(
@ -375,9 +422,13 @@ def parse_characterizer_csv(f, pages):
# failed to provide non-zero MIN_PERIOD
new_sheet.operating_table.add_row(
['Operating Frequency (F)', '', '', "not available in netlist only", 'MHz'])
new_sheet.power_table = table_gen.table_gen("power")
new_sheet.power_table.add_row(
['Pins', 'Mode', 'Power', 'Units'])
new_sheet.timing_table = table_gen.table_gen("timing")
new_sheet.timing_table.add_row(
['Parameter', 'Min', 'Max', 'Units'])
# parse initial timing information
while(True):
col_start = col
if(row[col].startswith('DIN')):
@ -504,6 +555,32 @@ def parse_characterizer_csv(f, pages):
for element in row[col_start:col-1]:
sheet.description.append(str(element))
break
# parse initial power and leakage information
while(True):
start = col
if(row[col].startswith('power')):
new_sheet.power_table.add_row([row[col+1],
'{0} Rising'.format(
row[col+2]),
row[col+3][0:6],
'mW']
)
new_sheet.power_table.add_row([row[col+1],
'{0} Falling'.format(
row[col+2]),
row[col+3][0:6],
'mW']
)
col += 4
elif(row[col].startswith('leak')):
new_sheet.power_table.add_row(
[row[col+1], 'leakage', row[col+2], 'mW'])
col += 3
else:
break
new_sheet.dlv_table = table_gen.table_gen("dlv")
new_sheet.dlv_table.add_row(['Type', 'Description', 'Link'])
@ -537,12 +614,13 @@ def parse_characterizer_csv(f, pages):
new_sheet.io_table.add_row(['NUM_RW_PORTS', NUM_RW_PORTS])
new_sheet.io_table.add_row(['NUM_R_PORTS', NUM_R_PORTS])
new_sheet.io_table.add_row(['NUM_W_PORTS', NUM_W_PORTS])
new_sheet.io_table.add_row(['Area', AREA])
new_sheet.io_table.add_row(
['Area (&microm<sup>2</sup>)', str(round(float(AREA)))])
class datasheet_gen():
def datasheet_write(name):
"""writes the datasheet to a file"""
in_dir = OPTS.openram_temp
if not (os.path.isdir(in_dir)):

View File

@ -1,24 +1,29 @@
class table_gen:
def __init__(self,name):
"""small library of functions to generate the html tables"""
def __init__(self, name):
self.name = name
self.rows = []
self.table_id = 'data'
def add_row(self,row):
def add_row(self, row):
"""add a row to table_gen object"""
self.rows.append(row)
def gen_table_head(self):
"""generate html table header"""
html = ''
html += '<thead>'
html += '<tr>'
for col in self.rows[0]:
html += '<th>' + str(col) + '</th>'
html += '<th>' + str(col) + '</th>'
html += '</tr>'
html += '</thead>'
return html
def gen_table_body(self):
"""generate html body (used after gen_table_head)"""
html = ''
html += '<tbody>'
@ -31,13 +36,13 @@ class table_gen:
html += '</tr>'
html += '</tbody>'
return html
def to_html(self):
"""writes table_gen object to inline html"""
html = ''
html += '<table id= \"'+self.table_id+'\">'
html += self.gen_table_head()
html += self.gen_table_body()
html += '</table>'
return html

View File

@ -1,827 +0,0 @@
#!/usr/bin/python
# The MIT License (MIT)
#
# Copyright (c) 2011-2016 Aliaksei Chapyzhenka
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Translated to Python from original file:
# https://github.com/drom/wavedrom/blob/master/src/WaveDrom.js
#
import sys
import json
import math
import waveskin
font_width = 7
lane = {
"xs" : 20, # tmpgraphlane0.width
"ys" : 20, # tmpgraphlane0.height
"xg" : 120, # tmpgraphlane0.x
"yg" : 0, # head gap
"yh0" : 0, # head gap title
"yh1" : 0, # head gap
"yf0" : 0, # foot gap
"yf1" : 0, # foot gap
"y0" : 5, # tmpgraphlane0.y
"yo" : 30, # tmpgraphlane1.y - y0
"tgo" : -10, # tmptextlane0.x - xg
"ym" : 15, # tmptextlane0.y - y0
"xlabel" : 6, # tmptextlabel.x - xg
"xmax" : 1,
"scale" : 1,
"head" : {},
"foot" : {}
}
def genBrick (texts, extra, times) :
R = []
if len( texts ) == 4 :
for j in range( times ):
R.append(texts[0])
for i in range ( extra ):
R.append(texts[1])
R.append(texts[2])
for i in range ( extra ):
R.append(texts[3])
return R
if len( texts ) == 1 :
texts.append(texts[0])
R.append(texts[0])
for i in range (times * (2 * (extra + 1)) - 1) :
R.append(texts[1])
return R
def genFirstWaveBrick (text, extra, times) :
pattern = {
'p': ['pclk', '111', 'nclk', '000'],
'n': ['nclk', '000', 'pclk', '111'],
'P': ['Pclk', '111', 'nclk', '000'],
'N': ['Nclk', '000', 'pclk', '111'],
'l': ['000'],
'L': ['000'],
'0': ['000'],
'h': ['111'],
'H': ['111'],
'1': ['111'],
'=': ['vvv-2'],
'2': ['vvv-2'],
'3': ['vvv-3'],
'4': ['vvv-4'],
'5': ['vvv-5'],
'd': ['ddd'],
'u': ['uuu'],
'z': ['zzz']
}
return genBrick( pattern.get( text, ['xxx'] ) , extra, times );
def genWaveBrick (text, extra, times) :
x1 = {'p':'pclk', 'n':'nclk', 'P':'Pclk', 'N':'Nclk', 'h':'pclk', 'l':'nclk', 'H':'Pclk', 'L':'Nclk'}
x2 = {'0':'0', '1':'1', 'x':'x', 'd':'d', 'u':'u', 'z':'z', '=':'v', '2':'v', '3':'v', '4':'v', '5':'v' }
x3 = {'0': '', '1': '', 'x': '', 'd': '', 'u': '', 'z': '', '=':'-2', '2':'-2', '3':'-3', '4':'-4', '5':'-5'}
y1 = {
'p':'0', 'n':'1',
'P':'0', 'N':'1',
'h':'1', 'l':'0',
'H':'1', 'L':'0',
'0':'0', '1':'1', 'x':'x', 'd':'d', 'u':'u', 'z':'z', '=':'v', '2':'v', '3':'v', '4':'v', '5':'v'}
y2 = {
'p': '', 'n': '',
'P': '', 'N': '',
'h': '', 'l': '',
'H': '', 'L': '',
'0': '', '1': '', 'x': '', 'd': '', 'u': '', 'z': '', '=':'-2', '2':'-2', '3':'-3', '4':'-4', '5':'-5'}
x4 = {
'p': '111', 'n': '000',
'P': '111', 'N': '000',
'h': '111', 'l': '000',
'H': '111', 'L': '000',
'0': '000', '1': '111', 'x': 'xxx', 'd': 'ddd', 'u': 'uuu', 'z': 'zzz',
'=': 'vvv-2', '2': 'vvv-2', '3': 'vvv-3', '4': 'vvv-4', '5': 'vvv-5'}
x5 = {'p':'nclk', 'n':'pclk', 'P':'nclk', 'N':'pclk'}
x6 = {'p': '000', 'n': '111', 'P': '000', 'N': '111'}
xclude = {'hp':'111', 'Hp':'111', 'ln': '000', 'Ln': '000', 'nh':'111', 'Nh':'111', 'pl': '000', 'Pl':'000'}
#atext = text.split()
atext = text
tmp0 = x4.get(atext[1])
tmp1 = x1.get(atext[1])
if tmp1 == None :
tmp2 = x2.get(atext[1])
if tmp2 == None :
# unknown
return genBrick(['xxx'], extra, times)
else :
tmp3 = y1.get(atext[0])
if tmp3 == None :
# unknown
return genBrick(['xxx'], extra, times)
# soft curves
return genBrick([tmp3 + 'm' + tmp2 + y2[atext[0]] + x3[atext[1]], tmp0], extra, times)
else :
tmp4 = xclude.get(text)
if tmp4 != None :
tmp1 = tmp4
# sharp curves
tmp2 = x5.get(atext[1])
if tmp2 == None :
# hlHL
return genBrick([tmp1, tmp0], extra, times)
else :
# pnPN
return genBrick([tmp1, tmp0, tmp2, x6[atext[1]]], extra, times)
def parseWaveLane (text, extra) :
R = []
Stack = text
Next = Stack[0]
Stack = Stack[1:]
Repeats = 1
while len(Stack) and ( Stack[0] == '.' or Stack[0] == '|' ): # repeaters parser
Stack=Stack[1:]
Repeats += 1
R.extend(genFirstWaveBrick(Next, extra, Repeats))
while len(Stack) :
Top = Next
Next = Stack[0]
Stack = Stack[1:]
Repeats = 1
while len(Stack) and ( Stack[0] == '.' or Stack[0] == '|' ) : # repeaters parser
Stack=Stack[1:]
Repeats += 1
R.extend(genWaveBrick((Top + Next), extra, Repeats))
for i in range( lane['phase'] ):
R = R[1:]
return R
def parseWaveLanes (sig) :
def data_extract (e) :
tmp = e.get('data')
if tmp == None : return None
if is_type_str (tmp) : tmp=tmp.split()
return tmp
content = []
for sigx in sig :
lane['period'] = sigx.get('period',1)
lane['phase'] = int( sigx.get('phase',0 ) * 2 )
sub_content=[]
sub_content.append( [sigx.get('name',' '), sigx.get('phase',0 ) ] )
sub_content.append( parseWaveLane( sigx['wave'], int(lane['period'] * lane['hscale'] - 1 ) ) if sigx.get('wave') else None )
sub_content.append( data_extract(sigx) )
content.append(sub_content)
return content
def findLaneMarkers (lanetext) :
lcount = 0
gcount = 0
ret = []
for i in range( len( lanetext ) ) :
if lanetext[i] == 'vvv-2' or lanetext[i] == 'vvv-3' or lanetext[i] == 'vvv-4' or lanetext[i] == 'vvv-5' :
lcount += 1
else :
if lcount !=0 :
ret.append(gcount - ((lcount + 1) / 2))
lcount = 0
gcount += 1
if lcount != 0 :
ret.append(gcount - ((lcount + 1) / 2))
return ret
def renderWaveLane (root, content, index) :
xmax = 0
xgmax = 0
glengths = []
svgns = 'http://www.w3.org/2000/svg'
xlinkns = 'http://www.w3.org/1999/xlink'
xmlns = 'http://www.w3.org/XML/1998/namespace'
for j in range( len(content) ):
name = content[j][0][0]
if name : # check name
g = [
'g',
{
'id': 'wavelane_' + str(j) + '_' + str(index),
'transform': 'translate(0,' + str(lane['y0'] + j * lane['yo']) + ')'
}
]
root.append(g)
title = [
'text',
{
'x': lane['tgo'],
'y': lane['ym'],
'class': 'info',
'text-anchor': 'end',
'xml:space': 'preserve'
},
['tspan', name]
]
g.append(title)
glengths.append( len(name) * font_width + font_width )
xoffset = content[j][0][1]
xoffset = math.ceil(2 * xoffset) - 2 * xoffset if xoffset > 0 else -2 * xoffset
gg = [
'g',
{
'id': 'wavelane_draw_' + str(j) + '_' + str(index),
'transform': 'translate(' + str( xoffset * lane['xs'] ) + ', 0)'
}
]
g.append(gg)
if content[j][1] :
for i in range( len(content[j][1]) ) :
b = [
'use',
{
#'id': 'use_' + str(i) + '_' + str(j) + '_' + str(index),
'xmlns:xlink':xlinkns,
'xlink:href': '#' + str( content[j][1][i] ),
'transform': 'translate(' + str(i * lane['xs']) + ')'
}
]
gg.append(b)
if content[j][2] and len(content[j][2]) :
labels = findLaneMarkers(content[j][1])
if len(labels) != 0 :
for k in range( len(labels) ) :
if content[j][2] and k < len(content[j][2]) :
title = [
'text',
{
'x': int(labels[k]) * lane['xs'] + lane['xlabel'],
'y': lane['ym'],
'text-anchor': 'middle',
'xml:space': 'preserve'
},
['tspan',content[j][2][k]]
]
gg.append(title)
if len(content[j][1]) > xmax :
xmax = len(content[j][1])
lane['xmax'] = xmax
lane['xg'] = xgmax + 20
return glengths
def renderMarks (root, content, index) :
def captext ( g, cxt, anchor, y ) :
if cxt.get(anchor) and cxt[anchor].get('text') :
tmark = [
'text',
{
'x': float( cxt['xmax'] ) * float( cxt['xs'] ) / 2,
'y': y,
'text-anchor': 'middle',
'fill': '#000',
'xml:space': 'preserve'
}, cxt[anchor]['text']
]
g.append(tmark)
def ticktock ( g, cxt, ref1, ref2, x, dx, y, length ) :
L = []
if cxt.get(ref1) == None or cxt[ref1].get(ref2) == None :
return
val = cxt[ref1][ref2]
if is_type_str( val ) :
val = val.split()
elif type( val ) is int :
offset = val
val = []
for i in range ( length ) :
val.append(i + offset)
if type( val ) is list :
if len( val ) == 0 :
return
elif len( val ) == 1 :
offset = val[0]
if is_type_str(offset) :
L = val
else :
for i in range ( length ) :
L[i] = i + offset
elif len( val ) == 2:
offset = int(val[0])
step = int(val[1])
tmp = val[1].split('.')
if len( tmp ) == 2 :
dp = len( tmp[1] )
if is_type_str(offset) or is_type_str(step) :
L = val
else :
offset = step * offset
for i in range( length ) :
L[i] = "{0:.",dp,"f}".format(step * i + offset)
else :
L = val
else :
return
for i in range( length ) :
tmp = L[i]
tmark = [
'text',
{
'x': i * dx + x,
'y': y,
'text-anchor': 'middle',
'class': 'muted',
'xml:space': 'preserve'
}, str(tmp)
]
g.append(tmark)
mstep = 2 * int(lane['hscale'])
mmstep = mstep * lane['xs']
marks = int( lane['xmax'] / mstep )
gy = len( content ) * int(lane['yo'])
g = ['g', {'id': 'gmarks_' + str(index)}]
root.insert(0,g)
for i in range( marks + 1):
gg = [
'path',
{
'id': 'gmark_' + str(i) + '_' + str(index),
'd': 'm ' + str(i * mmstep) + ',' + '0' + ' 0,' + str(gy),
'style': 'stroke:#888;stroke-width:0.5;stroke-dasharray:1,3'
}
]
g.append( gg )
captext(g, lane, 'head', -33 if lane['yh0'] else -13 )
captext(g, lane, 'foot', gy + ( 45 if lane['yf0'] else 25 ) )
ticktock( g, lane, 'head', 'tick', 0, mmstep, -5, marks + 1)
ticktock( g, lane, 'head', 'tock', mmstep / 2, mmstep, -5, marks)
ticktock( g, lane, 'foot', 'tick', 0, mmstep, gy + 15, marks + 1)
ticktock( g, lane, 'foot', 'tock', mmstep / 2, mmstep, gy + 15, marks)
def renderArcs (root, source, index, top) :
Stack = []
Edge = {'words': [], 'frm': 0, 'shape': '', 'to': 0, 'label': ''}
Events = {}
svgns = 'http://www.w3.org/2000/svg'
xmlns = 'http://www.w3.org/XML/1998/namespace'
if source :
for i in range (len (source) ) :
lane['period'] = source[i].get('period',1)
lane['phase'] = int( source[i].get('phase',0 ) * 2 )
text = source[i].get('node')
if text:
Stack = text
pos = 0
while len( Stack ) :
eventname = Stack[0]
Stack=Stack[1:]
if eventname != '.' :
Events[eventname] = {
'x' : str( int( float( lane['xs'] ) * (2 * pos * lane['period'] * lane['hscale'] - lane['phase'] ) + float( lane['xlabel'] ) ) ),
'y' : str( int( i * lane['yo'] + lane['y0'] + float( lane['ys'] ) * 0.5 ) )
}
pos += 1
gg = [ 'g', { 'id' : 'wavearcs_' + str( index ) } ]
root.append(gg)
if top.get('edge') :
for i in range( len ( top['edge'] ) ) :
Edge['words'] = top['edge'][i].split()
Edge['label'] = top['edge'][i][len(Edge['words'][0]):]
Edge['label'] = Edge['label'][1:]
Edge['frm'] = Edge['words'][0][0]
Edge['to'] = Edge['words'][0][-1]
Edge['shape'] = Edge['words'][0][1:-1]
frm = Events[Edge['frm']]
to = Events[Edge['to']]
gmark = [
'path',
{
'id': 'gmark_' + Edge['frm'] + '_' + Edge['to'],
'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + to['x'] + ',' + to['y'],
'style': 'fill:none;stroke:#00F;stroke-width:1'
}
]
gg.append(gmark)
dx = float( to['x'] ) - float( frm['x'] )
dy = float( to['y'] ) - float( frm['y'] )
lx = (float(frm['x']) + float(to['x'])) / 2
ly = (float(frm['y']) + float(to['y'])) / 2
pattern = {
'~' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) },
'-~' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) },
'~-' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + '0' + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) },
'-|' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)},
'|-' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' 0,' + str(dy) + ' ' + str(dx) + ',0'},
'-|-' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'},
'->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none'},
'~>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'-~>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'~->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + '0' + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'-|>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)},
'|->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' 0,' + str(dy) + ' ' + str(dx) + ',0'},
'-|->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'},
'<->' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none'},
'<~>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'<-~>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'<-|>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)},
'<-|->': {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'}
}
gmark[1].update( pattern.get( Edge['shape'], { 'style': 'fill:none;stroke:#00F;stroke-width:1' } ) )
if Edge['label']:
if Edge['shape'] == '-~' :
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75
if Edge['shape'] == '~-' :
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.25
if Edge['shape'] == '-|' :
lx = float(to['x'])
if Edge['shape'] == '|-' :
lx = float(frm['x'])
if Edge['shape'] == '-~>':
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75
if Edge['shape'] == '~->':
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.25
if Edge['shape'] == '-|>' :
lx = float(to['x'])
if Edge['shape'] == '|->' :
lx = float(frm['x'])
if Edge['shape'] == '<-~>':
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75
if Edge['shape'] =='<-|>' :
lx = float(to['x'])
lwidth = len( Edge['label'] ) * font_width
label = [
'text',
{
'style': 'font-size:10px;',
'text-anchor': 'middle',
'xml:space': 'preserve',
'x': int( lx ),
'y': int( ly + 3 )
},
[ 'tspan', Edge['label'] ]
]
underlabel = [
'rect',
{
'height': 9,
'style': 'fill:#FFF;',
'width': lwidth,
'x': int( lx - lwidth / 2 ),
'y': int( ly - 5 )
}
]
gg.append(underlabel)
gg.append(label)
for k in Events:
if k.islower() :
if int( Events[k]['x'] ) > 0 :
lwidth = len( k ) * font_width
underlabel = [
'rect',
{
'x': float( Events[k]['x'] ) - float(lwidth) / 2,
'y': int( Events[k]['y'] ) - 4,
'height': 8,
'width': lwidth,
'style': 'fill:#FFF;'
}
]
gg.append(underlabel)
label = [
'text',
{
'style': 'font-size:8px;',
'x': int( Events[k]['x'] ),
'y': int( Events[k]['y'] ) + 2,
'width': lwidth,
'text-anchor': 'middle'
},
k
]
gg.append(label)
def parseConfig (source) :
lane['hscale'] = 1
if lane.get('hscale0') :
lane['hscale'] = lane['hscale0']
if source and source.get('config') and source.get('config').get('hscale'):
hscale = round(source.get('config').get('hscale'))
if hscale > 0 :
if hscale > 100 : hscale = 100
lane['hscale'] = hscale
lane['yh0'] = 0
lane['yh1'] = 0
if source and source.get('head') :
lane['head'] = source['head']
if source.get('head').get('tick',0) == 0 : lane['yh0'] = 20
if source.get('head').get('tock',0) == 0 : lane['yh0'] = 20
if source.get('head').get('text') : lane['yh1'] = 46; lane['head']['text'] = source['head']['text']
lane['yf0'] = 0
lane['yf1'] = 0
if source and source.get('foot') :
lane['foot'] = source['foot']
if source.get('foot').get('tick',0) == 0 : lane['yf0'] = 20
if source.get('foot').get('tock',0) == 0 : lane['yf0'] = 20
if source.get('foot').get('text') : lane['yf1'] = 46; lane['foot']['text'] = source['foot']['text']
def rec (tmp, state) :
name = str( tmp[0] )
delta_x = 25
state['x'] += delta_x
for i in range( len( tmp ) ) :
if type( tmp[i] ) is list :
old_y = state['y']
rec( tmp[i], state )
state['groups'].append( {'x':state['xx'], 'y':old_y, 'height':state['y'] - old_y, 'name': state['name'] } )
elif type( tmp[i] ) is dict :
state['lanes'].append(tmp[i])
state['width'].append(state['x'])
state['y'] += 1
state['xx'] = state['x']
state['x'] -= delta_x
state['name'] = name
def insertSVGTemplate (index, parent, source) :
e = waveskin.WaveSkin['default']
if source.get('config') and source.get('config').get('skin') :
if waveskin.WaveSkin.get( source.get('config').get('skin') ) :
e = waveskin.WaveSkin[ source.get('config').get('skin') ]
if index == 0 :
lane['xs'] = int( e[3][1][2][1]['width'] )
lane['ys'] = int( e[3][1][2][1]['height'] )
lane['xlabel'] = int( e[3][1][2][1]['x'] )
lane['ym'] = int( e[3][1][2][1]['y'] )
else :
e = ['svg', {'id': 'svg', 'xmlns': 'http://www.w3.org/2000/svg', 'xmlns:xlink': 'http://www.w3.org/1999/xlink', 'height': '0'},
['g', {'id': 'waves'},
['g', {'id': 'lanes'}],
['g', {'id': 'groups'}]
]
]
e[-1][1]['id'] = 'waves_' + str(index)
e[-1][2][1]['id'] = 'lanes_' + str(index)
e[-1][3][1]['id'] = 'groups_' + str(index)
e[1]['id'] = 'svgcontent_' + str(index)
e[1]['height'] = 0
parent.extend(e)
def renderWaveForm (index, source, output) :
xmax = 0
root = []
groups = []
if source.get('signal'):
insertSVGTemplate(index, output, source)
parseConfig( source )
ret = {'x':0, 'y':0, 'xmax':0, 'width':[], 'lanes':[], 'groups':[] }
rec( source['signal'], ret )
content = parseWaveLanes(ret['lanes'])
glengths = renderWaveLane(root, content, index)
for i in range( len( glengths ) ):
xmax = max( xmax, ( glengths[i] + ret['width'][i] ) )
renderMarks(root, content, index)
renderArcs(root, ret['lanes'], index, source)
renderGaps(root, ret['lanes'], index)
renderGroups(groups, ret['groups'], index)
lane['xg'] = int( math.ceil( float( xmax - lane['tgo'] ) / float(lane['xs'] ) ) ) * lane['xs']
width = (lane['xg'] + lane['xs'] * (lane['xmax'] + 1) )
height = len(content) * lane['yo'] + lane['yh0'] + lane['yh1'] + lane['yf0'] + lane['yf1']
output[1]={
'id' :'svgcontent_' + str(index),
'xmlns' :"http://www.w3.org/2000/svg",
'xmlns:xlink':"http://www.w3.org/1999/xlink",
'width' :str(width),
'height' :str(height),
'viewBox' :'0 0 ' + str(width) + ' ' + str(height),
'overflow' :"hidden"
}
output[-1][2][1]['transform']='translate(' + str(lane['xg'] + 0.5) + ', ' + str((float(lane['yh0']) + float(lane['yh1'])) + 0.5) + ')'
output[-1][2].extend(root)
output[-1][3].extend(groups)
def renderGroups (root, groups, index) :
svgns = 'http://www.w3.org/2000/svg',
xmlns = 'http://www.w3.org/XML/1998/namespace'
for i in range( len( groups ) ) :
group = [
'path',
{
'id': 'group_' + str(i) + '_' + str(index),
'd': 'm ' + str( groups[i]['x'] + 0.5 ) + ',' + str( groups[i]['y']* lane['yo'] + 3.5 + lane['yh0'] + lane['yh1'] ) + ' c -3,0 -5,2 -5,5 l 0,' + str( int( groups[i]['height'] * lane['yo'] - 16 ) ) + ' c 0,3 2,5 5,5',
'style': 'stroke:#0041c4;stroke-width:1;fill:none'
}
]
root.append(group)
name = groups[i]['name']
x = str( int( groups[i]['x'] - 10 ) )
y = str( int( lane['yo'] * (groups[i]['y'] + (float(groups[i]['height']) / 2)) + lane['yh0'] + lane['yh1'] ) )
label = [
['g',
{'transform': 'translate(' + x + ',' + y + ')'},
['g', {'transform': 'rotate(270)'},
'text',
{
'text-anchor': 'middle',
'class': 'info',
'xml:space' : 'preserve'
},
['tspan',name]
]
]
]
root.append(label)
def renderGaps (root, source, index) :
Stack = []
svgns = 'http://www.w3.org/2000/svg',
xlinkns = 'http://www.w3.org/1999/xlink'
if source:
gg = [
'g',
{ 'id': 'wavegaps_' + str(index) }
]
for i in range( len( source )):
lane['period'] = source[i].get('period',1)
lane['phase'] = int( source[i].get('phase',0 ) * 2 )
g = [
'g',
{
'id': 'wavegap_' + str(i) + '_' + str(index),
'transform': 'translate(0,' + str(lane['y0'] + i * lane['yo']) + ')'
}
]
gg.append(g)
if source[i].get('wave'):
text = source[i]['wave']
Stack = text
pos = 0
while len( Stack ) :
c = Stack [0]
Stack = Stack[1:]
if c == '|' :
b = [
'use',
{
'xmlns:xlink':xlinkns,
'xlink:href':'#gap',
'transform': 'translate(' + str(int(float(lane['xs']) * ((2 * pos + 1) * float(lane['period']) * float(lane['hscale']) - float(lane['phase'])))) + ')'
}
]
g.append(b)
pos += 1
root.append( gg )
def is_type_str( var ) :
if sys.version_info[0] < 3:
return type( var ) is str or type( var ) is unicode
else:
return type( var ) is str
def convert_to_svg( root ) :
svg_output = ''
if type( root ) is list:
if len(root) >= 2 and type( root[1] ) is dict:
if len( root ) == 2 :
svg_output += '<' + root[0] + convert_to_svg( root[1] ) + '/>\n'
elif len( root ) >= 3 :
svg_output += '<' + root[0] + convert_to_svg( root[1] ) + '>\n'
if len( root ) == 3:
svg_output += convert_to_svg( root[2] )
else:
svg_output += convert_to_svg( root[2:] )
svg_output += '</' + root[0] + '>\n'
elif type( root[0] ) is list:
for eleml in root:
svg_output += convert_to_svg( eleml )
else:
svg_output += '<' + root[0] + '>\n'
for eleml in root[1:]:
svg_output += convert_to_svg( eleml )
svg_output += '</' + root[0] + '>\n'
elif type( root ) is dict:
for elemd in root :
svg_output += ' ' + elemd + '="' + str(root[elemd]) + '"'
else:
svg_output += root
return svg_output
if __name__ == '__main__':
if len( sys.argv ) != 5:
print ( 'Usage : ' + sys.argv[0] + ' source <input.json> svg <output.svg>' )
exit(1)
if sys.argv[3] != 'svg' :
print ( 'Error: only SVG format supported.' )
exit(1)
output=[]
inputfile = sys.argv[2]
outputfile = sys.argv[4]
with open(inputfile,'r') as f:
jinput = json.load(f)
renderWaveForm(0,jinput,output)
svg_output = convert_to_svg(output)
with open(outputfile,'w') as f:
f.write( svg_output )

View File

@ -48,17 +48,35 @@ def print_raw(str):
def log(str):
if log.create_file:
compile_log = open(globals.OPTS.output_path +
globals.OPTS.output_name + '.log', "w")
log.create_file = 0
if globals.OPTS.output_name != '':
if log.create_file:
# We may have not yet read the config, so we need to ensure
# it ends with a /
# This is also done in read_config if we change the path
# FIXME: There's actually a bug here. The first few lines
# could be in one log file and after read_config it could be
# in another log file if the path or name changes.
if not globals.OPTS.output_path.endswith('/'):
globals.OPTS.output_path += "/"
compile_log = open(globals.OPTS.output_path +
globals.OPTS.output_name + '.log', "w+")
log.create_file = 0
else:
compile_log = open(globals.OPTS.output_path +
globals.OPTS.output_name + '.log', "a")
if len(log.setup_output) != 0:
for line in log.setup_output:
compile_log.write(line)
log.setup_output = []
compile_log.write(str + '\n')
else:
compile_log = open(globals.OPTS.output_path +
globals.OPTS.output_name + '.log', "a")
compile_log.write(str + '\n')
log.create_file = 1
log.setup_output.append(str + "\n")
# use a static list of strings to store messages until the global paths are set up
log.setup_output = []
log.create_file = 1
def info(lev, str):
@ -71,5 +89,5 @@ def info(lev, str):
class_name = ""
else:
class_name = mod.__name__
print_raw("[{0}/{1}]: {2}".format(class_name, frm[0].f_code.co_name, str))
print_raw("[{0}/{1}]: {2}".format(class_name,
frm[0].f_code.co_name, str))

View File

@ -1,8 +1,6 @@
word_size = 2
num_words = 16
bitcell = "bitcell_1rw_1r"
replica_bitcell = "replica_bitcell_1rw_1r"
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0

View File

@ -0,0 +1,18 @@
word_size = 2
num_words = 16
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
output_path = "temp"
output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size,num_words,tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -1 +0,0 @@
468eb9a4a038201c2b0004fe6e4ae9b2d37fdd57

View File

@ -132,6 +132,8 @@ def init_openram(config_file, is_unit_test=True):
from sram_factory import factory
factory.reset()
setup_bitcell()
# Reset the static duplicate name checker for unit tests.
import hierarchy_design
@ -157,6 +159,46 @@ def init_openram(config_file, is_unit_test=True):
if not CHECKPOINT_OPTS:
CHECKPOINT_OPTS = copy.copy(OPTS)
def setup_bitcell():
"""
Determine the correct custom or parameterized bitcell for the design.
"""
global OPTS
# If we have non-1rw ports,
# and the user didn't over-ride the bitcell manually,
# figure out the right bitcell to use
if (OPTS.bitcell=="bitcell" and OPTS.replica_bitcell=="replica_bitcell"):
if (OPTS.num_rw_ports==1 and OPTS.num_w_ports==0 and OPTS.num_r_ports==0):
OPTS.bitcell = "bitcell"
OPTS.replica_bitcell = "replica_bitcell"
else:
ports = ""
if OPTS.num_rw_ports>0:
ports += "{}rw_".format(OPTS.num_rw_ports)
if OPTS.num_w_ports>0:
ports += "{}w_".format(OPTS.num_w_ports)
if OPTS.num_r_ports>0:
ports += "{}r".format(OPTS.num_r_ports)
OPTS.bitcell = "bitcell_"+ports
OPTS.replica_bitcell = "replica_bitcell_"+ports
# See if a custom bitcell exists
from importlib import find_loader
bitcell_loader = find_loader(OPTS.bitcell)
replica_bitcell_loader = find_loader(OPTS.replica_bitcell)
# Use the pbitcell if we couldn't find a custom bitcell
# or its custom replica bitcell
if bitcell_loader==None or replica_bitcell_loader==None:
# Use the pbitcell (and give a warning if not in unit test mode)
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
if not OPTS.is_unit_test:
debug.warning("Using the parameterized bitcell which may have suboptimal density.")
debug.info(1,"Using bitcell: {}".format(OPTS.bitcell))
def get_tool(tool_type, preferences, default_name=None):
@ -250,7 +292,8 @@ def read_config(config_file, is_unit_test=True):
OPTS.num_words,
ports,
OPTS.tech_name)
def end_openram():
@ -417,7 +460,11 @@ def report_status():
debug.error("Tech name must be specified in config file.")
debug.print_raw("Technology: {0}".format(OPTS.tech_name))
debug.print_raw("Total size: {} bits".format(OPTS.word_size*OPTS.num_words*OPTS.num_banks))
total_size = OPTS.word_size*OPTS.num_words*OPTS.num_banks
debug.print_raw("Total size: {} bits".format(total_size))
if total_size>=2**14:
debug.warning("Requesting such a large memory size ({0}) will have a large run-time. ".format(total_size) +
"Consider using multiple smaller banks.")
debug.print_raw("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
OPTS.num_words,
OPTS.num_banks))

View File

@ -1216,26 +1216,30 @@ class bank(design.design):
rotate=90)
def analytical_delay(self, vdd, slew, load):
def analytical_delay(self, corner, slew, load):
""" return analytical delay of the bank"""
results = []
decoder_delay = self.row_decoder.analytical_delay(slew, self.wordline_driver.input_load())
decoder_delay = self.row_decoder.analytical_delay(corner, slew, self.wordline_driver.input_load())
word_driver_delay = self.wordline_driver.analytical_delay(decoder_delay.slew, self.bitcell_array.input_load())
word_driver_delay = self.wordline_driver.analytical_delay(corner,
decoder_delay.slew,
self.bitcell_array.input_load())
#FIXME: Array delay is the same for every port.
bitcell_array_delay = self.bitcell_array.analytical_delay(word_driver_delay.slew)
bitcell_array_delay = self.bitcell_array.analytical_delay(corner, word_driver_delay.slew)
#This also essentially creates the same delay for each port. Good structure, no substance
for port in self.all_ports:
if self.words_per_row > 1:
column_mux_delay = self.column_mux_array[port].analytical_delay(vdd, bitcell_array_delay.slew,
self.sense_amp_array.input_load())
column_mux_delay = self.column_mux_array[port].analytical_delay(corner,
bitcell_array_delay.slew,
self.sense_amp_array.input_load())
else:
column_mux_delay = self.return_delay(delay = 0.0, slew=word_driver_delay.slew)
bl_t_data_out_delay = self.sense_amp_array.analytical_delay(column_mux_delay.slew,
bl_t_data_out_delay = self.sense_amp_array.analytical_delay(corner,
column_mux_delay.slew,
self.bitcell_array.output_load())
# output load of bitcell_array is set to be only small part of bl for sense amp.
results.append(decoder_delay + word_driver_delay + bitcell_array_delay + column_mux_delay + bl_t_data_out_delay)

View File

@ -130,7 +130,7 @@ class bitcell_array(design.design):
self.add_power_pin(pin_name, pin.center(), 0, pin.layer)
def analytical_delay(self, slew, load=0):
def analytical_delay(self, corner, slew, load=0):
from tech import drc
wl_wire = self.gen_wl_wire()
wl_wire.return_delay_over_wire(slew)
@ -141,25 +141,25 @@ class bitcell_array(design.design):
cell_load = 2 * bl_wire.return_input_cap() # we ingore the wire r
# hence just use the whole c
bl_swing = 0.1
cell_delay = self.cell.analytical_delay(wl_to_cell_delay.slew, cell_load, swing = bl_swing)
cell_delay = self.cell.analytical_delay(corner, wl_to_cell_delay.slew, cell_load, swing = bl_swing)
#we do not consider the delay over the wire for now
return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay,
wl_to_cell_delay.slew)
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW."""
from tech import drc
from tech import drc, parameter
# Dynamic Power from Bitline
bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap()
bl_swing = 0.1 #This should probably be defined in the tech file or input
bl_swing = parameter["rbl_height_percentage"]
freq = spice["default_event_rate"]
bitline_dynamic = bl_swing*cell_load*vdd*vdd*freq #not sure if calculation is correct
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
#Calculate the bitcell power which currently only includes leakage
cell_power = self.cell.analytical_power(proc, vdd, temp, load)
cell_power = self.cell.analytical_power(corner, load)
#Leakage power grows with entire array and bitlines.
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,

View File

@ -30,13 +30,15 @@ class control_logic(design.design):
self.port_type = port_type
self.num_cols = word_size*words_per_row
self.num_words = num_rows * words_per_row
self.num_words = num_rows*words_per_row
self.enable_delay_chain_resizing = False
self.enable_delay_chain_resizing = True
#self.sram=None #disable re-sizing for debugging, FIXME: resizing is not working, needs to be adjusted for new control logic.
self.wl_timing_tolerance = 1 #Determines how much larger the sen delay should be. Accounts for possible error in model.
self.parasitic_inv_delay = parameter["min_inv_para_delay"] #Keeping 0 for now until further testing.
#Determines how much larger the sen delay should be. Accounts for possible error in model.
self.wl_timing_tolerance = 1
self.parasitic_inv_delay = parameter["min_inv_para_delay"]
self.wl_stage_efforts = None
self.sen_stage_efforts = None
if self.port_type == "rw":
self.num_control_signals = 2
@ -130,43 +132,61 @@ class control_logic(design.design):
self.add_mod(self.p_en_bar_driver)
if (self.port_type == "rw") or (self.port_type == "r"):
delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size()
bitcell_loads = int(math.ceil(self.num_rows / 2.0))
self.replica_bitline = factory.create(module_type="replica_bitline",
delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic,
bitcell_loads=bitcell_loads)
if self.sram != None:
self.set_sen_wl_delays()
if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_total_timing_match(): #check condition based on resizing method
#This resizes to match fall and rise delays, can make the delay chain weird sizes.
# stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic)
# self.replica_bitline = replica_bitline(stage_list, bitcell_loads, name="replica_bitline_resized_"+self.port_type)
#This resizes based on total delay.
delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic)
from importlib import reload
self.delay_chain_resized = False
c = reload(__import__(OPTS.replica_bitline))
replica_bitline = getattr(c, OPTS.replica_bitline)
bitcell_loads = int(math.ceil(self.num_rows * parameter["rbl_height_percentage"]))
#Use a model to determine the delays with that heuristic
if OPTS.use_tech_delay_chain_size: #Use tech parameters if set.
fanout_list = parameter["static_fanout_list"]
debug.info(1, "Using tech parameters to size delay chain: fanout_list={}".format(fanout_list))
self.replica_bitline = factory.create(module_type="replica_bitline",
delay_fanout_list=[delay_fanout]*delay_stages,
delay_fanout_list=fanout_list,
bitcell_loads=bitcell_loads)
self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing
if self.sram != None: #Calculate model value even for specified sizes
self.set_sen_wl_delays()
else: #Otherwise, use a heuristic and/or model based sizing.
#First use a heuristic
delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size()
self.replica_bitline = factory.create(module_type="replica_bitline",
delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic,
bitcell_loads=bitcell_loads)
#Resize if necessary, condition depends on resizing method
if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match():
#This resizes to match fall and rise delays, can make the delay chain weird sizes.
stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic)
self.replica_bitline = factory.create(module_type="replica_bitline",
delay_fanout_list=stage_list,
bitcell_loads=bitcell_loads)
#This resizes based on total delay.
# delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic)
# self.replica_bitline = factory.create(module_type="replica_bitline",
# delay_fanout_list=[delay_fanout]*delay_stages,
# bitcell_loads=bitcell_loads)
self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing
self.delay_chain_resized = True
self.add_mod(self.replica_bitline)
def get_heuristic_delay_chain_size(self):
"""Use a basic heuristic to determine the size of the delay chain used for the Sense Amp Enable """
# FIXME: These should be tuned according to the additional size parameters
delay_fanout = 3 # This can be anything >=2
# Delay stages Must be non-inverting
if self.words_per_row >= 4:
#FIXME: The minimum was 2 fanout, now it will not pass DRC unless it is 3. Why?
delay_fanout = 3 # This can be anything >=3
# Model poorly captures delay of the column mux. Be pessismistic for column mux
if self.words_per_row >= 2:
delay_stages = 8
elif self.words_per_row == 2:
delay_stages = 6
else:
delay_stages = 4
delay_stages = 2
#Read ports have a shorter s_en delay. The model is not accurate enough to catch this difference
#on certain sram configs.
if self.port_type == "r":
delay_stages+=2
return (delay_stages, delay_fanout)
def set_sen_wl_delays(self):
@ -227,14 +247,25 @@ class control_logic(design.design):
required_delay_rise = self.wl_delay_rise*self.wl_timing_tolerance - (self.sen_delay_rise-previous_delay_chain_delay/2)
debug.info(2,"Required delays from chain: fall={}, rise={}".format(required_delay_fall,required_delay_rise))
#If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
WARNING_FANOUT_DIFF = 5
stages_close = False
#The stages need to be equal (or at least a even number of stages with matching rise/fall delays)
while True:
stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,fanout_fall)
stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,fanout_rise)
debug.info(1,"Fall stages={}, rise stages={}".format(stages_fall,stages_rise))
if abs(stages_fall-stages_rise) == 1 and not stages_close:
stages_close = True
safe_fanout_rise = fanout_rise
safe_fanout_fall = fanout_fall
if stages_fall == stages_rise:
break
elif abs(stages_fall-stages_rise) == 1:
elif abs(stages_fall-stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall-fanout_rise):
debug.info(1, "Delay chain fanouts between stages are large. Making chain size larger for safety.")
fanout_rise = safe_fanout_rise
fanout_fall = safe_fanout_fall
break
#There should also be a condition to make sure the fanout does not get too large.
#Otherwise, increase the fanout of delay with the most stages, calculate new stages
@ -819,8 +850,8 @@ class control_logic(design.design):
def get_delays_to_wl(self):
"""Get the delay (in delay units) of the clk to a wordline in the bitcell array"""
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
stage_efforts = self.determine_wordline_stage_efforts()
clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(stage_efforts, self.parasitic_inv_delay)
self.wl_stage_efforts = self.determine_wordline_stage_efforts()
clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts, self.parasitic_inv_delay)
total_delay = clk_to_wl_rise + clk_to_wl_fall
debug.info(1, "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise, clk_to_wl_fall,total_delay))
return clk_to_wl_rise,clk_to_wl_fall
@ -849,8 +880,8 @@ class control_logic(design.design):
This does not incorporate the delay of the replica bitline.
"""
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
stage_efforts = self.determine_sa_enable_stage_efforts()
clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(stage_efforts, self.parasitic_inv_delay)
self.sen_stage_efforts = self.determine_sa_enable_stage_efforts()
clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts, self.parasitic_inv_delay)
total_delay = clk_to_sen_rise + clk_to_sen_fall
debug.info(1, "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise, clk_to_sen_fall,total_delay))
return clk_to_sen_rise, clk_to_sen_fall
@ -881,4 +912,12 @@ class control_logic(design.design):
last_stage_rise = stage_effort_list[-1].is_rise
return stage_effort_list
def get_wl_sen_delays(self):
"""Gets a list of the stages and delays in order of their path."""
if self.sen_stage_efforts == None or self.wl_stage_efforts == None:
debug.error("Model delays not calculated for SRAM.", 1)
wl_delays = logical_effort.calculate_delays(self.wl_stage_efforts, self.parasitic_inv_delay)
sen_delays = logical_effort.calculate_delays(self.sen_stage_efforts, self.parasitic_inv_delay)
return wl_delays, sen_delays

View File

@ -21,11 +21,11 @@ class dff(design.design):
self.height = dff.height
self.pin_map = dff.pin_map
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load)
f = spice["default_event_rate"]
power_dyn = c_eff*vdd*vdd*f
freq = spice["default_event_rate"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["msflop_leakage"]
total_power = self.return_power(power_dyn, power_leak)
@ -39,7 +39,7 @@ class dff(design.design):
transition_prob = spice["flop_transition_prob"]
return transition_prob*(c_load + c_para)
def analytical_delay(self, slew, load = 0.0):
def analytical_delay(self, corner, slew, load = 0.0):
# dont know how to calculate this now, use constant in tech file
result = self.return_delay(spice["dff_delay"], spice["dff_slew"])
return result

View File

@ -154,8 +154,8 @@ class dff_array(design.design):
def analytical_delay(self, slew, load=0.0):
return self.dff.analytical_delay(slew=slew, load=load)
def analytical_delay(self, corner, slew, load=0.0):
return self.dff.analytical_delay(corner, slew=slew, load=load)
def get_clk_cin(self):
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""

View File

@ -172,11 +172,11 @@ class dff_buf(design.design):
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
dff_delay=self.dff.analytical_delay(slew=slew, load=self.inv1.input_load())
inv1_delay = self.inv1.analytical_delay(slew=dff_delay.slew, load=self.inv2.input_load())
inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load)
dff_delay=self.dff.analytical_delay(corner, slew=slew, load=self.inv1.input_load())
inv1_delay = self.inv1.analytical_delay(corner, slew=dff_delay.slew, load=self.inv2.input_load())
inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load)
return dff_delay + inv1_delay + inv2_delay
def get_clk_cin(self):

View File

@ -187,7 +187,7 @@ class dff_buf_array(design.design):
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
return self.dff.analytical_delay(slew=slew, load=load)
def get_clk_cin(self):

View File

@ -145,10 +145,10 @@ class dff_inv(design.design):
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
dff_delay=self.dff.analytical_delay(slew=slew, load=self.inv1.input_load())
inv1_delay = self.inv1.analytical_delay(slew=dff_delay.slew, load=load)
dff_delay=self.dff.analytical_delay(corner, slew=slew, load=self.inv1.input_load())
inv1_delay = self.inv1.analytical_delay(corner, slew=dff_delay.slew, load=load)
return dff_delay + inv1_delay
def get_clk_cin(self):

View File

@ -185,8 +185,8 @@ class dff_inv_array(design.design):
def analytical_delay(self, slew, load=0.0):
return self.dff.analytical_delay(slew=slew, load=load)
def analytical_delay(self, corner, slew, load=0.0):
return self.dff.analytical_delay(corner, slew=slew, load=load)
def get_clk_cin(self):
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""

View File

@ -594,7 +594,7 @@ class hierarchical_decoder(design.design):
rotate=90)
def analytical_delay(self, slew, load = 0.0):
def analytical_delay(self, corner, slew, load = 0.0):
# A -> out
if self.determine_predecodes(self.num_inputs)[1]==0:
pre = self.pre2_4
@ -602,15 +602,15 @@ class hierarchical_decoder(design.design):
else:
pre = self.pre3_8
nand = self.nand3
a_t_out_delay = pre.analytical_delay(slew=slew,load = nand.input_load())
a_t_out_delay = pre.analytical_delay(corner, slew=slew,load = nand.input_load())
# out -> z
out_t_z_delay = nand.analytical_delay(slew= a_t_out_delay.slew,
out_t_z_delay = nand.analytical_delay(corner, slew= a_t_out_delay.slew,
load = self.inv.input_load())
result = a_t_out_delay + out_t_z_delay
# Z -> decode_out
z_t_decodeout_delay = self.inv.analytical_delay(slew = out_t_z_delay.slew , load = load)
z_t_decodeout_delay = self.inv.analytical_delay(corner, slew = out_t_z_delay.slew , load = load)
result = result + z_t_decodeout_delay
return result

View File

@ -51,15 +51,15 @@ class hierarchical_predecode2x4(hierarchical_predecode):
return combination
def analytical_delay(self, slew, load = 0.0 ):
def analytical_delay(self, corner, slew, load = 0.0 ):
# in -> inbar
a_t_b_delay = self.inv.analytical_delay(slew=slew, load=self.nand.input_load())
a_t_b_delay = self.inv.analytical_delay(corner, slew=slew, load=self.nand.input_load())
# inbar -> z
b_t_z_delay = self.nand.analytical_delay(slew=a_t_b_delay.slew, load=self.inv.input_load())
b_t_z_delay = self.nand.analytical_delay(corner, slew=a_t_b_delay.slew, load=self.inv.input_load())
# Z -> out
a_t_out_delay = self.inv.analytical_delay(slew=b_t_z_delay.slew, load=load)
a_t_out_delay = self.inv.analytical_delay(corner, slew=b_t_z_delay.slew, load=load)
return a_t_b_delay + b_t_z_delay + a_t_out_delay

View File

@ -60,15 +60,15 @@ class hierarchical_predecode3x8(hierarchical_predecode):
return combination
def analytical_delay(self, slew, load = 0.0 ):
def analytical_delay(self, corner, slew, load = 0.0 ):
# A -> Abar
a_t_b_delay = self.inv.analytical_delay(slew=slew, load=self.nand.input_load())
a_t_b_delay = self.inv.analytical_delay(corner, slew=slew, load=self.nand.input_load())
# Abar -> z
b_t_z_delay = self.nand.analytical_delay(slew=a_t_b_delay.slew, load=self.inv.input_load())
b_t_z_delay = self.nand.analytical_delay(corner, slew=a_t_b_delay.slew, load=self.inv.input_load())
# Z -> out
a_t_out_delay = self.inv.analytical_delay(slew=b_t_z_delay.slew, load=load)
a_t_out_delay = self.inv.analytical_delay(corner, slew=b_t_z_delay.slew, load=load)
return a_t_b_delay + b_t_z_delay + a_t_out_delay

View File

@ -816,19 +816,19 @@ class multibank(design.design):
offset=in_pin + self.m2m3_via_offset,
rotate=90)
def analytical_delay(self, slew, load):
def analytical_delay(self, corner, slew, load):
""" return analytical delay of the bank"""
decoder_delay = self.row_decoder.analytical_delay(slew, self.wordline_driver.input_load())
decoder_delay = self.row_decoder.analytical_delay(corner, slew, self.wordline_driver.input_load())
word_driver_delay = self.wordline_driver.analytical_delay(decoder_delay.slew, self.bitcell_array.input_load())
word_driver_delay = self.wordline_driver.analytical_delay(corner, decoder_delay.slew, self.bitcell_array.input_load())
bitcell_array_delay = self.bitcell_array.analytical_delay(word_driver_delay.slew)
bitcell_array_delay = self.bitcell_array.analytical_delay(corner, word_driver_delay.slew)
bl_t_data_out_delay = self.sense_amp_array.analytical_delay(bitcell_array_delay.slew,
bl_t_data_out_delay = self.sense_amp_array.analytical_delay(corner, bitcell_array_delay.slew,
self.bitcell_array.output_load())
# output load of bitcell_array is set to be only small part of bl for sense amp.
data_t_DATA_delay = self.tri_gate_array.analytical_delay(bl_t_data_out_delay.slew, load)
data_t_DATA_delay = self.tri_gate_array.analytical_delay(corner, bl_t_data_out_delay.slew, load)
result = decoder_delay + word_driver_delay + bitcell_array_delay + bl_t_data_out_delay + data_t_DATA_delay
return result

View File

@ -31,14 +31,14 @@ class sense_amp(design.design):
bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file.
return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
from tech import spice
r = spice["min_tx_r"]/(10)
c_para = spice["min_tx_drain_c"]
result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew)
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
return self.return_delay(result.delay, result.slew)
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
@ -48,4 +48,5 @@ class sense_amp(design.design):
"""Get the relative capacitance of sense amp enable gate cin"""
pmos_cin = parameter["sa_en_pmos_size"]/drc("minwidth_tx")
nmos_cin = parameter["sa_en_nmos_size"]/drc("minwidth_tx")
#sen is connected to 2 pmos isolation TX and 1 nmos per sense amp.
return 2*pmos_cin + nmos_cin

View File

@ -136,10 +136,10 @@ class sense_amp_array(design.design):
def input_load(self):
return self.amp.input_load()
def analytical_delay(self, slew, load=0.0):
return self.amp.analytical_delay(slew=slew, load=load)
def analytical_delay(self, corner, slew, load=0.0):
return self.amp.analytical_delay(corner, slew=slew, load=load)
def get_en_cin(self):
"""Get the relative capacitance of all the sense amp enable connections in the array"""
sense_amp_en_cin = self.amp.get_en_cin()
return sense_amp_en_cin * self.words_per_row
return sense_amp_en_cin * self.word_size

View File

@ -216,13 +216,14 @@ class single_level_column_mux_array(design.design):
offset= br_out_offset,
rotate=90)
def analytical_delay(self, vdd, slew, load=0.0):
def analytical_delay(self, corner, vdd, slew, load=0.0):
from tech import spice, parameter
proc,vdd,temp = corner
r = spice["min_tx_r"]/(self.mux.ptx_width/parameter["min_tx_size"])
#Drains of mux transistors make up capacitance.
c_para = spice["min_tx_drain_c"]*(self.mux.ptx_width/parameter["min_tx_size"])*self.words_per_row#ff
volt_swing = spice["v_threshold_typical"]/vdd
result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = volt_swing)
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = volt_swing)
return self.return_delay(result.delay, result.slew)

View File

@ -27,13 +27,13 @@ class tri_gate(design.design):
self.height = tri_gate.height
self.pin_map = tri_gate.pin_map
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
from tech import spice
r = spice["min_tx_r"]
c_para = spice["min_tx_drain_c"]
return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew)
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
total_power = self.return_power()

View File

@ -116,6 +116,6 @@ class tri_gate_array(design.design):
def analytical_delay(self, slew, load=0.0):
return self.tri.analytical_delay(slew = slew, load = load)
def analytical_delay(self, corner, slew, load=0.0):
return self.tri.analytical_delay(corner, slew = slew, load = load)

View File

@ -210,12 +210,12 @@ class wordline_driver(design.design):
end=wl_offset-vector(self.m1_width,0))
def analytical_delay(self, slew, load=0):
def analytical_delay(self, corner, slew, load=0):
# decode -> net
decode_t_net = self.nand2.analytical_delay(slew, self.inv.input_load())
decode_t_net = self.nand2.analytical_delay(corner, slew, self.inv.input_load())
# net -> wl
net_t_wl = self.inv.analytical_delay(decode_t_net.slew, load)
net_t_wl = self.inv.analytical_delay(corner, decode_t_net.slew, load)
return decode_t_net + net_t_wl

View File

@ -71,6 +71,9 @@ class options(optparse.Values):
# You can manually specify banks, but it is better to auto-detect it.
num_banks = 1
#Uses the delay chain size in the tech.py file rather automatic sizing.
use_tech_delay_chain_size = False
# These are the default modules that can be over-riden
bank_select = "bank_select"
bitcell_array = "bitcell_array"

View File

@ -107,10 +107,10 @@ class pand2(pgate.pgate):
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
nand_delay = selfnand.analytical_delay(slew=slew, load=self.inv.input_load())
inv_delay = self.inv.analytical_delay(slew=nand_delay.slew, load=load)
nand_delay = self.nand.analytical_delay(corner, slew=slew, load=self.inv.input_load())
inv_delay = self.inv.analytical_delay(corner, slew=nand_delay.slew, load=load)
return nand_delay + inv_delay
def get_stage_efforts(self, external_cout, inp_is_rise=False):

View File

@ -110,10 +110,10 @@ class pbuf(pgate.pgate):
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
inv1_delay = self.inv1.analytical_delay(slew=slew, load=self.inv2.input_load())
inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load)
inv1_delay = self.inv1.analytical_delay(corner, slew=slew, load=self.inv2.input_load())
inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load)
return inv1_delay + inv2_delay
def get_stage_efforts(self, external_cout, inp_is_rise=False):

View File

@ -172,7 +172,7 @@ class pdriver(pgate.pgate):
def input_load(self):
return self.inv_list[0].input_load()
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
"""Calculate the analytical delay of INV1 -> ... -> INVn"""
cout_list = []
@ -184,7 +184,7 @@ class pdriver(pgate.pgate):
delays = []
for inv,cout in zip(self.inv_list,cout_list):
delays.append(inv.analytical_delay(slew=input_slew, load=cout))
delays.append(inv.analytical_delay(corner, slew=input_slew, load=cout))
input_slew = delays[-1].slew
delay = delays[0]
@ -196,17 +196,16 @@ class pdriver(pgate.pgate):
def get_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A -> Z path"""
cout_list = {}
cout_list = []
for prev_inv,inv in zip(self.inv_list, self.inv_list[1:]):
cout_list[prev_inv]=inv.get_cin()
cout_list[self.inv_list[-1]]=external_cout
cout_list.append(inv.get_cin())
cout_list.append(external_cout)
stage_effort_list = []
last_inp_is_rise = inp_is_rise
for inv in self.inv_list:
stage = inv.get_stage_effort(cout_list[inv], last_inp_is_rise)
for inv,cout in zip(self.inv_list,cout_list):
stage = inv.get_stage_effort(cout, last_inp_is_rise)
stage_effort_list.append(stage)
last_inp_is_rise = stage.is_rise

View File

@ -261,16 +261,16 @@ class pinv(pgate.pgate):
def input_load(self):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"])
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew)
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_rate"]
power_dyn = c_eff*vdd*vdd*freq
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["inv_leakage"]
total_power = self.return_power(power_dyn, power_leak)
@ -292,4 +292,4 @@ class pinv(pgate.pgate):
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
parasitic_delay = 1
return logical_effort.logical_effort(self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise)
return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise)

View File

@ -177,10 +177,10 @@ class pinvbuf(design.design):
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
inv1_delay = self.inv1.analytical_delay(slew=slew, load=self.inv2.input_load())
inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load)
inv1_delay = self.inv1.analytical_delay(corner, slew=slew, load=self.inv2.input_load())
inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load)
return inv1_delay + inv2_delay
def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False):

View File

@ -227,16 +227,16 @@ class pnand2(pgate.pgate):
def input_load(self):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"])
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew)
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_rate"]
power_dyn = c_eff*vdd*vdd*freq
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["nand2_leakage"]
total_power = self.return_power(power_dyn, power_leak)
@ -258,4 +258,4 @@ class pnand2(pgate.pgate):
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
parasitic_delay = 2
return logical_effort.logical_effort(self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise)
return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise)

View File

@ -4,6 +4,7 @@ import debug
from tech import drc, parameter, spice
from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory
class pnand3(pgate.pgate):
@ -239,16 +240,16 @@ class pnand3(pgate.pgate):
def input_load(self):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"])
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew)
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_rate"]
power_dyn = c_eff*vdd*vdd*freq
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["nand3_leakage"]
total_power = self.return_power(power_dyn, power_leak)
@ -270,4 +271,4 @@ class pnand3(pgate.pgate):
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
parasitic_delay = 3
return logical_effort.logical_effort(self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise)
return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise)

View File

@ -212,16 +212,16 @@ class pnor2(pgate.pgate):
def input_load(self):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
def analytical_delay(self, slew, load=0.0):
def analytical_delay(self, corner, slew, load=0.0):
r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"])
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew)
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
def analytical_power(self, proc, vdd, temp, load):
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_rate"]
power_dyn = c_eff*vdd*vdd*freq
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["nor2_leakage"]
total_power = self.return_power(power_dyn, power_leak)

View File

@ -1,5 +0,0 @@
import pstats
p = pstats.Stats('profile.dat')
p.strip_dirs()
p.sort_stats('cumulative')
p.print_stats(50)

View File

@ -499,9 +499,9 @@ class sram_base(design, verilog, lef):
sp.close()
def analytical_delay(self, vdd, slew,load):
def analytical_delay(self, corner, slew,load):
""" LH and HL are the same in analytical model. """
return self.bank.analytical_delay(vdd,slew,load)
return self.bank.analytical_delay(corner,slew,load)
def determine_wordline_stage_efforts(self, inp_is_rise=True):
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""

View File

@ -7,13 +7,13 @@ from sram_factory import factory
class sram_config:
""" This is a structure that is used to hold the SRAM configuration options. """
def __init__(self, word_size, num_words, num_banks=1):
def __init__(self, word_size, num_words, num_banks=1, words_per_row=None):
self.word_size = word_size
self.num_words = num_words
self.num_banks = num_banks
# This will get over-written when we determine the organization
self.words_per_row = None
self.words_per_row = words_per_row
self.compute_sizes()

View File

@ -26,7 +26,7 @@ class pbitcell_test(openram_test):
debug.info(2, "Bitcell with 1 of each port: read/write, write, and read")
tx = pbitcell(name="pbc")
self.local_check(tx)
OPTS.num_rw_ports=0
OPTS.num_w_ports=1
OPTS.num_r_ports=1

View File

@ -36,17 +36,21 @@ class control_logic_test(openram_test):
# Check port specific control logic
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
debug.info(1, "Testing sample for control_logic for multiport, only write control logic")
a = control_logic.control_logic(num_rows=128, words_per_row=1, word_size=8, port_type="rw")
self.local_check(a)
OPTS.num_rw_ports = 0
OPTS.num_w_ports = 1
debug.info(1, "Testing sample for control_logic for multiport, only write control logic")
a = control_logic.control_logic(num_rows=128, words_per_row=1, word_size=8, port_type="w")
self.local_check(a)
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1
debug.info(1, "Testing sample for control_logic for multiport, only read control logic")
a = control_logic.control_logic(num_rows=128, words_per_row=1, word_size=8, port_type="r")
self.local_check(a)

View File

@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
#@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error")
class psram_1bank_2mux_1w_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "bitcell_1w_1r"
OPTS.replica_bitcell="replica_bitcell_1w_1r"
OPTS.num_rw_ports = 0
OPTS.num_w_ports = 1
OPTS.num_r_ports = 1
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.num_words=32
c.words_per_row=2
c.recompute_sizes()
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -23,7 +23,7 @@ class timing_sram_test(openram_test):
from importlib import reload
import characterizer
reload(characterizer)
from characterizer import delay, bitline_delay
from characterizer import delay
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=1,
@ -43,37 +43,44 @@ class timing_sram_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay(s.s, tempspice, corner)
bl = bitline_delay(s.s, tempspice, corner)
import tech
loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2]
data, port_data = d.analyze(probe_address, probe_data, slews, loads)
#bitline_swing = bl.analyze(probe_address, probe_data, slews, loads)
#Combine info about port into all data
data.update(port_data[0])
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [0.2152017],
'delay_lh': [0.2152017],
'leakage_power': 0.0022907,
'min_period': 0.488,
'read0_power': [0.47437749999999995],
'read1_power': [0.45026109999999997],
'slew_hl': [0.0846786],
'slew_lh': [0.0846786],
'write0_power': [0.40809259999999997],
'write1_power': [0.4078904]}
golden_data = {'delay_bl': [0.1980959],
'delay_br': [0.1946091],
'delay_hl': [0.2121267],
'delay_lh': [0.2121267],
'leakage_power': 0.0023761999999999998,
'min_period': 0.43,
'read0_power': [0.5139368],
'read1_power': [0.48940979999999995],
'slew_hl': [0.0516745],
'slew_lh': [0.0516745],
'volt_bl': [0.5374525],
'volt_br': [1.1058],
'write0_power': [0.46267169999999996],
'write1_power': [0.4670826]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [1.4333000000000002],
'delay_lh': [1.4333000000000002],
'leakage_power': 0.0271847,
'min_period': 2.891,
'read0_power': [15.714200000000002],
'read1_power': [14.9848],
'slew_hl': [0.6819276999999999],
'slew_lh': [0.6819276999999999],
'write0_power': [13.9658],
'write1_power': [14.8422]}
golden_data = {'delay_bl': [1.1029],
'delay_br': [0.9656455999999999],
'delay_hl': [1.288],
'delay_lh': [1.288],
'leakage_power': 0.0273896,
'min_period': 2.578,
'read0_power': [16.9996],
'read1_power': [16.2616],
'slew_hl': [0.47891700000000004],
'slew_lh': [0.47891700000000004],
'volt_bl': [4.2155],
'volt_br': [5.8142],
'write0_power': [16.0656],
'write1_power': [16.2616]}
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results

View File

@ -51,27 +51,36 @@ class timing_sram_test(openram_test):
data.update(port_data[0])
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [0.221699],
'delay_lh': [0.221699],
'leakage_power': 0.001467648,
'min_period': 0.605,
'read0_power': [0.3879335],
'read1_power': [0.3662724],
'slew_hl': [0.08562444999999999],
'slew_lh': [0.08562444999999999],
'write0_power': [0.3362456],
'write1_power': [0.3372035]}
golden_data = {'delay_bl': [0.2003652],
'delay_br': [0.198698],
'delay_hl': [0.2108836],
'delay_lh': [0.2108836],
'leakage_power': 0.001564799,
'min_period': 0.508,
'read0_power': [0.43916689999999997],
'read1_power': [0.4198608],
'slew_hl': [0.0455126],
'slew_lh': [0.0455126],
'volt_bl': [0.6472883],
'volt_br': [1.114024],
'write0_power': [0.40681890000000004],
'write1_power': [0.4198608]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [1.7951730000000001],
'delay_lh': [1.7951730000000001],
'leakage_power': 0.001669513,
'min_period': 3.594,
'read0_power': [17.03022],
'read1_power': [16.55897],
'slew_hl': [0.7079951],
'slew_lh': [0.7079951],
'write0_power': [15.16726],
'write1_power': [16.13527]}
golden_data = {'delay_bl': [1.3937359999999999],
'delay_br': [1.2596429999999998],
'delay_hl': [1.5747600000000002],
'delay_lh': [1.5747600000000002],
'leakage_power': 0.00195795,
'min_period': 3.281,
'read0_power': [14.92874],
'read1_power': [14.369810000000001],
'slew_hl': [0.49631959999999997],
'slew_lh': [0.49631959999999997],
'volt_bl': [4.132618],
'volt_br': [5.573099],
'write0_power': [13.79953],
'write1_power': [14.369810000000001]}
else:
self.assertTrue(False) # other techs fail

View File

@ -0,0 +1,69 @@
#!/usr/bin/env python3
"""
Check the .lib file for an SRAM
"""
import unittest
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
class model_corners_lib_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from characterizer import lib
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=2,
num_words=16,
num_banks=1)
c.words_per_row=1
c.recompute_sizes()
debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank")
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
#Set the corners. Lib will create a power set of the lists.
if OPTS.tech_name == "scn4m_subm":
OPTS.process_corners = ["TT", "SS", "FF"]
OPTS.supply_voltages = [5.0]
OPTS.temperatures = [25]
elif OPTS.tech_name == "freepdk45":
OPTS.process_corners = ["TT", "SS", "FF"]
OPTS.supply_voltages = [1.0]
OPTS.temperatures = [25]
lib(out_dir=OPTS.openram_temp, sram=s.s, sp_file=tempspice, use_model=True)
# get all of the .lib files generated
files = os.listdir(OPTS.openram_temp)
nametest = re.compile("\.lib$", re.IGNORECASE)
lib_files = filter(nametest.search, files)
# and compare them with the golden model
for filename in lib_files:
newname = filename.replace(".lib","_analytical.lib")
libname = "{0}/{1}".format(OPTS.openram_temp,filename)
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname)
self.assertTrue(self.isapproxdiff(libname,golden,0.15))
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""
Run a regression test on various srams
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class delay_model_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.spice_name="hspice"
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.trim_netlist = False
debug.info(1, "Trimming disabled for this test. Simulation could be slow.")
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
import characterizer
reload(characterizer)
from characterizer import model_check
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=16,
num_banks=1)
c.words_per_row=1
c.recompute_sizes()
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = sram(c, name="sram1")
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
probe_address = "1" * s.s.addr_size
probe_data = s.s.word_size - 1
debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data))
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
mc = model_check(s.s, tempspice, corner)
import tech
loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2]
sram_data = mc.analyze(probe_address, probe_data, slews, loads)
#Combine info about port into all data
#debug.info(1,"Data:\n{}".format(wl_data))
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -0,0 +1,321 @@
library (sram_2_16_1_freepdk45_FF_1p0V_25C_lib){
delay_model : "table_lookup";
time_unit : "1ns" ;
voltage_unit : "1v" ;
current_unit : "1mA" ;
resistance_unit : "1kohm" ;
capacitive_load_unit(1 ,fF) ;
leakage_power_unit : "1mW" ;
pulling_resistance_unit :"1kohm" ;
operating_conditions(OC){
process : 1.0 ;
voltage : 1.0 ;
temperature : 25;
}
input_threshold_pct_fall : 50.0 ;
output_threshold_pct_fall : 50.0 ;
input_threshold_pct_rise : 50.0 ;
output_threshold_pct_rise : 50.0 ;
slew_lower_threshold_pct_fall : 10.0 ;
slew_upper_threshold_pct_fall : 90.0 ;
slew_lower_threshold_pct_rise : 10.0 ;
slew_upper_threshold_pct_rise : 90.0 ;
nom_voltage : 1.0;
nom_temperature : 25;
nom_process : 1.0;
default_cell_leakage_power : 0.0 ;
default_leakage_power_density : 0.0 ;
default_input_pin_cap : 1.0 ;
default_inout_pin_cap : 1.0 ;
default_output_pin_cap : 0.0 ;
default_max_transition : 0.5 ;
default_fanout_load : 1.0 ;
default_max_fanout : 4.0 ;
default_connection_class : universal ;
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.00125, 0.005, 0.04");
index_2("0.052275, 0.2091, 1.6728");
}
lu_table_template(CONSTRAINT_TABLE){
variable_1 : related_pin_transition;
variable_2 : constrained_pin_transition;
index_1("0.00125, 0.005, 0.04");
index_2("0.00125, 0.005, 0.04");
}
default_operating_conditions : OC;
type (DATA){
base_type : array;
data_type : bit;
bit_width : 2;
bit_from : 0;
bit_to : 1;
}
type (ADDR){
base_type : array;
data_type : bit;
bit_width : 4;
bit_from : 0;
bit_to : 3;
}
cell (sram_2_16_1_freepdk45){
memory(){
type : ram;
address_width : 4;
word_width : 2;
}
interface_timing : true;
dont_use : true;
map_only : true;
dont_touch : true;
area : 1124.88;
leakage_power () {
when : "CSb0";
value : 0.000167;
}
cell_leakage_power : 0;
bus(DIN0){
bus_type : DATA;
direction : input;
capacitance : 0.2091;
memory_write(){
address : ADDR0;
clocked_on : clk0;
}
pin(DIN0[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
}
bus(DOUT0){
bus_type : DATA;
direction : output;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
memory_read(){
address : ADDR0;
}
pin(DOUT0[1:0]){
timing(){
timing_sense : non_unate;
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.088, 0.088, 0.088",\
"0.088, 0.088, 0.088",\
"0.088, 0.088, 0.088");
}
cell_fall(CELL_TABLE) {
values("0.088, 0.088, 0.088",\
"0.088, 0.088, 0.088",\
"0.088, 0.088, 0.088");
}
rise_transition(CELL_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_transition(CELL_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
}
bus(ADDR0){
bus_type : ADDR;
direction : input;
capacitance : 0.2091;
max_transition : 0.04;
pin(ADDR0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
}
pin(CSb0){
direction : input;
capacitance : 0.2091;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
pin(WEb0){
direction : input;
capacitance : 0.2091;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
pin(clk0){
clock : true;
direction : input;
capacitance : 0.2091;
internal_power(){
when : "!CSb0 & clk0 & !WEb0";
rise_power(scalar){
values("0.033101244168888884");
}
fall_power(scalar){
values("0.033101244168888884");
}
}
internal_power(){
when : "!CSb0 & !clk0 & WEb0";
rise_power(scalar){
values("0.033101244168888884");
}
fall_power(scalar){
values("0.033101244168888884");
}
}
internal_power(){
when : "CSb0";
rise_power(scalar){
values("0");
}
fall_power(scalar){
values("0");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("0.009");
}
fall_constraint(scalar) {
values("0.009");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("0.018");
}
fall_constraint(scalar) {
values("0.018");
}
}
}
}
}

View File

@ -0,0 +1,321 @@
library (sram_2_16_1_freepdk45_SS_1p0V_25C_lib){
delay_model : "table_lookup";
time_unit : "1ns" ;
voltage_unit : "1v" ;
current_unit : "1mA" ;
resistance_unit : "1kohm" ;
capacitive_load_unit(1 ,fF) ;
leakage_power_unit : "1mW" ;
pulling_resistance_unit :"1kohm" ;
operating_conditions(OC){
process : 1.0 ;
voltage : 1.0 ;
temperature : 25;
}
input_threshold_pct_fall : 50.0 ;
output_threshold_pct_fall : 50.0 ;
input_threshold_pct_rise : 50.0 ;
output_threshold_pct_rise : 50.0 ;
slew_lower_threshold_pct_fall : 10.0 ;
slew_upper_threshold_pct_fall : 90.0 ;
slew_lower_threshold_pct_rise : 10.0 ;
slew_upper_threshold_pct_rise : 90.0 ;
nom_voltage : 1.0;
nom_temperature : 25;
nom_process : 1.0;
default_cell_leakage_power : 0.0 ;
default_leakage_power_density : 0.0 ;
default_input_pin_cap : 1.0 ;
default_inout_pin_cap : 1.0 ;
default_output_pin_cap : 0.0 ;
default_max_transition : 0.5 ;
default_fanout_load : 1.0 ;
default_max_fanout : 4.0 ;
default_connection_class : universal ;
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.00125, 0.005, 0.04");
index_2("0.052275, 0.2091, 1.6728");
}
lu_table_template(CONSTRAINT_TABLE){
variable_1 : related_pin_transition;
variable_2 : constrained_pin_transition;
index_1("0.00125, 0.005, 0.04");
index_2("0.00125, 0.005, 0.04");
}
default_operating_conditions : OC;
type (DATA){
base_type : array;
data_type : bit;
bit_width : 2;
bit_from : 0;
bit_to : 1;
}
type (ADDR){
base_type : array;
data_type : bit;
bit_width : 4;
bit_from : 0;
bit_to : 3;
}
cell (sram_2_16_1_freepdk45){
memory(){
type : ram;
address_width : 4;
word_width : 2;
}
interface_timing : true;
dont_use : true;
map_only : true;
dont_touch : true;
area : 1124.88;
leakage_power () {
when : "CSb0";
value : 0.000167;
}
cell_leakage_power : 0;
bus(DIN0){
bus_type : DATA;
direction : input;
capacitance : 0.2091;
memory_write(){
address : ADDR0;
clocked_on : clk0;
}
pin(DIN0[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
}
bus(DOUT0){
bus_type : DATA;
direction : output;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
memory_read(){
address : ADDR0;
}
pin(DOUT0[1:0]){
timing(){
timing_sense : non_unate;
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.107, 0.107, 0.107",\
"0.107, 0.107, 0.107",\
"0.107, 0.107, 0.107");
}
cell_fall(CELL_TABLE) {
values("0.107, 0.107, 0.107",\
"0.107, 0.107, 0.107",\
"0.107, 0.107, 0.107");
}
rise_transition(CELL_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_transition(CELL_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
}
bus(ADDR0){
bus_type : ADDR;
direction : input;
capacitance : 0.2091;
max_transition : 0.04;
pin(ADDR0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
}
pin(CSb0){
direction : input;
capacitance : 0.2091;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
pin(WEb0){
direction : input;
capacitance : 0.2091;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
pin(clk0){
clock : true;
direction : input;
capacitance : 0.2091;
internal_power(){
when : "!CSb0 & clk0 & !WEb0";
rise_power(scalar){
values("0.033101244168888884");
}
fall_power(scalar){
values("0.033101244168888884");
}
}
internal_power(){
when : "!CSb0 & !clk0 & WEb0";
rise_power(scalar){
values("0.033101244168888884");
}
fall_power(scalar){
values("0.033101244168888884");
}
}
internal_power(){
when : "CSb0";
rise_power(scalar){
values("0");
}
fall_power(scalar){
values("0");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("0.0105");
}
fall_constraint(scalar) {
values("0.0105");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("0.021");
}
fall_constraint(scalar) {
values("0.021");
}
}
}
}
}

View File

@ -93,7 +93,7 @@ cell (sram_2_16_1_freepdk45){
address : ADDR0;
clocked_on : clk0;
}
pin(DIN0){
pin(DIN0[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -132,7 +132,7 @@ cell (sram_2_16_1_freepdk45){
memory_read(){
address : ADDR0;
}
pin(DOUT0){
pin(DOUT0[1:0]){
timing(){
timing_sense : non_unate;
related_pin : "clk0";
@ -166,7 +166,7 @@ cell (sram_2_16_1_freepdk45){
direction : input;
capacitance : 0.2091;
max_transition : 0.04;
pin(ADDR0){
pin(ADDR0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";

View File

@ -93,7 +93,7 @@ cell (sram_2_16_1_freepdk45){
address : ADDR0;
clocked_on : clk0;
}
pin(DIN0){
pin(DIN0[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -132,7 +132,7 @@ cell (sram_2_16_1_freepdk45){
memory_read(){
address : ADDR0;
}
pin(DOUT0){
pin(DOUT0[1:0]){
timing(){
timing_sense : non_unate;
related_pin : "clk0";
@ -166,7 +166,7 @@ cell (sram_2_16_1_freepdk45){
direction : input;
capacitance : 0.2091;
max_transition : 0.04;
pin(ADDR0){
pin(ADDR0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";

View File

@ -93,7 +93,7 @@ cell (sram_2_16_1_freepdk45){
address : ADDR0;
clocked_on : clk0;
}
pin(DIN0){
pin(DIN0[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -132,7 +132,7 @@ cell (sram_2_16_1_freepdk45){
memory_read(){
address : ADDR0;
}
pin(DOUT0){
pin(DOUT0[1:0]){
timing(){
timing_sense : non_unate;
related_pin : "clk0";
@ -166,7 +166,7 @@ cell (sram_2_16_1_freepdk45){
direction : input;
capacitance : 0.2091;
max_transition : 0.04;
pin(ADDR0){
pin(ADDR0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";

View File

@ -0,0 +1,321 @@
library (sram_2_16_1_scn4m_subm_FF_5p0V_25C_lib){
delay_model : "table_lookup";
time_unit : "1ns" ;
voltage_unit : "1v" ;
current_unit : "1mA" ;
resistance_unit : "1kohm" ;
capacitive_load_unit(1 ,fF) ;
leakage_power_unit : "1mW" ;
pulling_resistance_unit :"1kohm" ;
operating_conditions(OC){
process : 1.0 ;
voltage : 5.0 ;
temperature : 25;
}
input_threshold_pct_fall : 50.0 ;
output_threshold_pct_fall : 50.0 ;
input_threshold_pct_rise : 50.0 ;
output_threshold_pct_rise : 50.0 ;
slew_lower_threshold_pct_fall : 10.0 ;
slew_upper_threshold_pct_fall : 90.0 ;
slew_lower_threshold_pct_rise : 10.0 ;
slew_upper_threshold_pct_rise : 90.0 ;
nom_voltage : 5.0;
nom_temperature : 25;
nom_process : 1.0;
default_cell_leakage_power : 0.0 ;
default_leakage_power_density : 0.0 ;
default_input_pin_cap : 1.0 ;
default_inout_pin_cap : 1.0 ;
default_output_pin_cap : 0.0 ;
default_max_transition : 0.5 ;
default_fanout_load : 1.0 ;
default_max_fanout : 4.0 ;
default_connection_class : universal ;
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.0125, 0.05, 0.4");
index_2("2.45605, 9.8242, 78.5936");
}
lu_table_template(CONSTRAINT_TABLE){
variable_1 : related_pin_transition;
variable_2 : constrained_pin_transition;
index_1("0.0125, 0.05, 0.4");
index_2("0.0125, 0.05, 0.4");
}
default_operating_conditions : OC;
type (DATA){
base_type : array;
data_type : bit;
bit_width : 2;
bit_from : 0;
bit_to : 1;
}
type (ADDR){
base_type : array;
data_type : bit;
bit_width : 4;
bit_from : 0;
bit_to : 3;
}
cell (sram_2_16_1_scn4m_subm){
memory(){
type : ram;
address_width : 4;
word_width : 2;
}
interface_timing : true;
dont_use : true;
map_only : true;
dont_touch : true;
area : 73068.14000000001;
leakage_power () {
when : "CSb0";
value : 0.000167;
}
cell_leakage_power : 0;
bus(DIN0){
bus_type : DATA;
direction : input;
capacitance : 9.8242;
memory_write(){
address : ADDR0;
clocked_on : clk0;
}
pin(DIN0[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
}
bus(DOUT0){
bus_type : DATA;
direction : output;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
memory_read(){
address : ADDR0;
}
pin(DOUT0[1:0]){
timing(){
timing_sense : non_unate;
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.241, 0.241, 0.241",\
"0.241, 0.241, 0.241",\
"0.241, 0.241, 0.241");
}
cell_fall(CELL_TABLE) {
values("0.241, 0.241, 0.241",\
"0.241, 0.241, 0.241",\
"0.241, 0.241, 0.241");
}
rise_transition(CELL_TABLE) {
values("0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004");
}
fall_transition(CELL_TABLE) {
values("0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004");
}
}
}
}
bus(ADDR0){
bus_type : ADDR;
direction : input;
capacitance : 9.8242;
max_transition : 0.4;
pin(ADDR0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
}
pin(CSb0){
direction : input;
capacitance : 9.8242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
pin(WEb0){
direction : input;
capacitance : 9.8242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
pin(clk0){
clock : true;
direction : input;
capacitance : 9.8242;
internal_power(){
when : "!CSb0 & clk0 & !WEb0";
rise_power(scalar){
values("4.99880645");
}
fall_power(scalar){
values("4.99880645");
}
}
internal_power(){
when : "!CSb0 & !clk0 & WEb0";
rise_power(scalar){
values("4.99880645");
}
fall_power(scalar){
values("4.99880645");
}
}
internal_power(){
when : "CSb0";
rise_power(scalar){
values("0");
}
fall_power(scalar){
values("0");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("0.024");
}
fall_constraint(scalar) {
values("0.024");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("0.048");
}
fall_constraint(scalar) {
values("0.048");
}
}
}
}
}

View File

@ -0,0 +1,321 @@
library (sram_2_16_1_scn4m_subm_SS_5p0V_25C_lib){
delay_model : "table_lookup";
time_unit : "1ns" ;
voltage_unit : "1v" ;
current_unit : "1mA" ;
resistance_unit : "1kohm" ;
capacitive_load_unit(1 ,fF) ;
leakage_power_unit : "1mW" ;
pulling_resistance_unit :"1kohm" ;
operating_conditions(OC){
process : 1.0 ;
voltage : 5.0 ;
temperature : 25;
}
input_threshold_pct_fall : 50.0 ;
output_threshold_pct_fall : 50.0 ;
input_threshold_pct_rise : 50.0 ;
output_threshold_pct_rise : 50.0 ;
slew_lower_threshold_pct_fall : 10.0 ;
slew_upper_threshold_pct_fall : 90.0 ;
slew_lower_threshold_pct_rise : 10.0 ;
slew_upper_threshold_pct_rise : 90.0 ;
nom_voltage : 5.0;
nom_temperature : 25;
nom_process : 1.0;
default_cell_leakage_power : 0.0 ;
default_leakage_power_density : 0.0 ;
default_input_pin_cap : 1.0 ;
default_inout_pin_cap : 1.0 ;
default_output_pin_cap : 0.0 ;
default_max_transition : 0.5 ;
default_fanout_load : 1.0 ;
default_max_fanout : 4.0 ;
default_connection_class : universal ;
lu_table_template(CELL_TABLE){
variable_1 : input_net_transition;
variable_2 : total_output_net_capacitance;
index_1("0.0125, 0.05, 0.4");
index_2("2.45605, 9.8242, 78.5936");
}
lu_table_template(CONSTRAINT_TABLE){
variable_1 : related_pin_transition;
variable_2 : constrained_pin_transition;
index_1("0.0125, 0.05, 0.4");
index_2("0.0125, 0.05, 0.4");
}
default_operating_conditions : OC;
type (DATA){
base_type : array;
data_type : bit;
bit_width : 2;
bit_from : 0;
bit_to : 1;
}
type (ADDR){
base_type : array;
data_type : bit;
bit_width : 4;
bit_from : 0;
bit_to : 3;
}
cell (sram_2_16_1_scn4m_subm){
memory(){
type : ram;
address_width : 4;
word_width : 2;
}
interface_timing : true;
dont_use : true;
map_only : true;
dont_touch : true;
area : 73068.14000000001;
leakage_power () {
when : "CSb0";
value : 0.000167;
}
cell_leakage_power : 0;
bus(DIN0){
bus_type : DATA;
direction : input;
capacitance : 9.8242;
memory_write(){
address : ADDR0;
clocked_on : clk0;
}
pin(DIN0[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
}
bus(DOUT0){
bus_type : DATA;
direction : output;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
memory_read(){
address : ADDR0;
}
pin(DOUT0[1:0]){
timing(){
timing_sense : non_unate;
related_pin : "clk0";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.294, 0.294, 0.294",\
"0.294, 0.294, 0.294",\
"0.294, 0.294, 0.294");
}
cell_fall(CELL_TABLE) {
values("0.294, 0.294, 0.294",\
"0.294, 0.294, 0.294",\
"0.294, 0.294, 0.294");
}
rise_transition(CELL_TABLE) {
values("0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004");
}
fall_transition(CELL_TABLE) {
values("0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004",\
"0.004, 0.004, 0.004");
}
}
}
}
bus(ADDR0){
bus_type : ADDR;
direction : input;
capacitance : 9.8242;
max_transition : 0.4;
pin(ADDR0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
}
pin(CSb0){
direction : input;
capacitance : 9.8242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
pin(WEb0){
direction : input;
capacitance : 9.8242;
timing(){
timing_type : setup_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009",\
"0.009, 0.009, 0.009");
}
}
timing(){
timing_type : hold_rising;
related_pin : "clk0";
rise_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
fall_constraint(CONSTRAINT_TABLE) {
values("0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001",\
"0.001, 0.001, 0.001");
}
}
}
pin(clk0){
clock : true;
direction : input;
capacitance : 9.8242;
internal_power(){
when : "!CSb0 & clk0 & !WEb0";
rise_power(scalar){
values("4.99880645");
}
fall_power(scalar){
values("4.99880645");
}
}
internal_power(){
when : "!CSb0 & !clk0 & WEb0";
rise_power(scalar){
values("4.99880645");
}
fall_power(scalar){
values("4.99880645");
}
}
internal_power(){
when : "CSb0";
rise_power(scalar){
values("0");
}
fall_power(scalar){
values("0");
}
}
timing(){
timing_type :"min_pulse_width";
related_pin : clk0;
rise_constraint(scalar) {
values("0.0295");
}
fall_constraint(scalar) {
values("0.0295");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk0;
rise_constraint(scalar) {
values("0.059");
}
fall_constraint(scalar) {
values("0.059");
}
}
}
}
}

View File

@ -93,7 +93,7 @@ cell (sram_2_16_1_scn4m_subm){
address : ADDR0;
clocked_on : clk0;
}
pin(DIN0){
pin(DIN0[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -132,7 +132,7 @@ cell (sram_2_16_1_scn4m_subm){
memory_read(){
address : ADDR0;
}
pin(DOUT0){
pin(DOUT0[1:0]){
timing(){
timing_sense : non_unate;
related_pin : "clk0";
@ -166,7 +166,7 @@ cell (sram_2_16_1_scn4m_subm){
direction : input;
capacitance : 9.8242;
max_transition : 0.4;
pin(ADDR0){
pin(ADDR0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";

View File

@ -93,7 +93,7 @@ cell (sram_2_16_1_scn4m_subm){
address : ADDR0;
clocked_on : clk0;
}
pin(DIN0){
pin(DIN0[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -132,7 +132,7 @@ cell (sram_2_16_1_scn4m_subm){
memory_read(){
address : ADDR0;
}
pin(DOUT0){
pin(DOUT0[1:0]){
timing(){
timing_sense : non_unate;
related_pin : "clk0";
@ -166,7 +166,7 @@ cell (sram_2_16_1_scn4m_subm){
direction : input;
capacitance : 9.8242;
max_transition : 0.4;
pin(ADDR0){
pin(ADDR0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";

View File

@ -93,7 +93,7 @@ cell (sram_2_16_1_scn4m_subm){
address : ADDR0;
clocked_on : clk0;
}
pin(DIN0){
pin(DIN0[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";
@ -132,7 +132,7 @@ cell (sram_2_16_1_scn4m_subm){
memory_read(){
address : ADDR0;
}
pin(DOUT0){
pin(DOUT0[1:0]){
timing(){
timing_sense : non_unate;
related_pin : "clk0";
@ -166,7 +166,7 @@ cell (sram_2_16_1_scn4m_subm){
direction : input;
capacitance : 9.8242;
max_transition : 0.4;
pin(ADDR0){
pin(ADDR0[3:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk0";

View File

@ -13,7 +13,7 @@ class openram_test(unittest.TestCase):
self.reset()
tempgds = OPTS.openram_temp + "temp.gds"
tempgds = "{0}{1}.gds".format(OPTS.openram_temp,w.name)
w.gds_write(tempgds)
import verify
@ -21,14 +21,15 @@ class openram_test(unittest.TestCase):
if result != 0:
self.fail("DRC failed: {}".format(w.name))
self.cleanup()
if OPTS.purge_temp:
self.cleanup()
def local_check(self, a, final_verification=False):
self.reset()
tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds"
tempspice = "{0}{1}.sp".format(OPTS.openram_temp,a.name)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp,a.name)
a.sp_write(tempspice)
# cannot write gds in netlist_only mode
@ -36,7 +37,7 @@ class openram_test(unittest.TestCase):
a.gds_write(tempgds)
import verify
result=verify.run_drc(a.name, tempgds)
result=verify.run_drc(a.name, tempgds, extract=True, final_verification=final_verification)
if result != 0:
#zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid())
#debug.info(0,"Archiving failed files to {}.zip".format(zip_file))
@ -44,7 +45,7 @@ class openram_test(unittest.TestCase):
self.fail("DRC failed: {}".format(a.name))
result=verify.run_lvs(a.name, tempgds, tempspice, final_verification)
result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification)
if result != 0:
#zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid())
#debug.info(0,"Archiving failed files to {}.zip".format(zip_file))
@ -53,6 +54,7 @@ class openram_test(unittest.TestCase):
if OPTS.purge_temp:
self.cleanup()
def find_feasible_test_period(self, delay_obj, sram, load, slew):
"""Creates a delay simulation to determine a feasible period for the functional tests to run.
@ -62,6 +64,9 @@ class openram_test(unittest.TestCase):
delay_obj.set_load_slew(load, slew)
delay_obj.set_probe(probe_address="1"*sram.addr_size, probe_data=(sram.word_size-1))
test_port = delay_obj.read_ports[0] #Only test one port, assumes other ports have similar period.
delay_obj.create_signal_names()
delay_obj.create_measurement_names()
delay_obj.create_measurement_objects()
delay_obj.find_feasible_period_one_port(test_port)
return delay_obj.period

View File

@ -30,7 +30,7 @@ num_drc_runs = 0
num_lvs_runs = 0
num_pex_runs = 0
def run_drc(name, gds_name):
def run_drc(name, gds_name, final_verification=False):
"""Run DRC check on a given top-level name which is
implemented in gds_name."""
@ -93,7 +93,7 @@ def run_drc(name, gds_name):
return errors
def run_lvs(name, gds_name, sp_name):
def run_lvs(name, gds_name, sp_name, final_verification=False):
"""Run LVS check on a given top-level name which is
implemented in gds_name and sp_name. """
@ -178,7 +178,7 @@ def run_lvs(name, gds_name, sp_name):
return errors
def run_pex(name, gds_name, sp_name, output=None):
def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
"""Run pex on a given top-level name which is
implemented in gds_name and sp_name. """
debug.error("PEX extraction not implemented with Assura.",-1)

View File

@ -126,9 +126,9 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False):
f.close()
# those lines should be the last 3
results = results[-3:]
geometries = int(re.split("\W+", results[0])[5])
rulechecks = int(re.split("\W+", results[1])[4])
errors = int(re.split("\W+", results[2])[5])
geometries = int(re.split(r'\W+', results[0])[5])
rulechecks = int(re.split(r'\W+', results[1])[4])
errors = int(re.split(r'\W+', results[2])[5])
# always display this summary
if errors > 0:
@ -227,7 +227,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
incorrect = list(filter(test.search, results))
# Errors begin with "Error:"
test = re.compile("\s+Error:")
test = re.compile(r'\s+Error:')
errors = list(filter(test.search, results))
for e in errors:
debug.error(e.strip("\n"))
@ -282,7 +282,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
return total_errors
def run_pex(cell_name, gds_name, sp_name, output=None):
def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False):
"""Run pex on a given top-level name which is
implemented in gds_name and sp_name. """
@ -363,7 +363,7 @@ def correct_port(name, output_file_name, ref_file_name):
pex_file.seek(match_index_start)
rest_text = pex_file.read()
# locate the end of circuit definition line
match = re.search("\* \n", rest_text)
match = re.search(r'\* \n', rest_text)
match_index_end = match.start()
# store the unchanged part of pex file in memory
pex_file.seek(0)

View File

@ -45,7 +45,10 @@ def write_magic_script(cell_name, gds_name, extract=False, final_verification=Fa
#f.write("load {}_new\n".format(cell_name))
#f.write("cellname rename {0}_new {0}\n".format(cell_name))
#f.write("load {}\n".format(cell_name))
f.write("cellname delete \\(UNNAMED\\)\n")
f.write("writeall force\n")
f.write("select top cell\n")
f.write("expand\n")
f.write("drc check\n")
f.write("drc catchup\n")
f.write("drc count total\n")
@ -55,14 +58,26 @@ def write_magic_script(cell_name, gds_name, extract=False, final_verification=Fa
else:
pre = ""
if final_verification:
f.write(pre+"extract unique\n")
f.write(pre+"extract\n")
f.write(pre+"ext2spice hierarchy on\n")
f.write(pre+"extract unique all\n".format(cell_name))
f.write(pre+"extract\n".format(cell_name))
#f.write(pre+"ext2spice hierarchy on\n")
#f.write(pre+"ext2spice scale off\n")
# lvs exists in 8.2.79, but be backword compatible for now
#f.write(pre+"ext2spice lvs\n")
f.write(pre+"ext2spice hierarchy on\n")
f.write(pre+"ext2spice format ngspice\n")
f.write(pre+"ext2spice cthresh infinite\n")
f.write(pre+"ext2spice rthresh infinite\n")
f.write(pre+"ext2spice renumber off\n")
f.write(pre+"ext2spice scale off\n")
f.write(pre+"ext2spice blackbox on\n")
f.write(pre+"ext2spice subcircuit top auto\n")
f.write(pre+"ext2spice global off\n")
# Can choose hspice, ngspice, or spice3,
# but they all seem compatible enough.
#f.write(pre+"ext2spice format ngspice\n")
f.write(pre+"ext2spice\n")
f.write(pre+"ext2spice {}\n".format(cell_name))
f.write("quit -noprompt\n")
f.write("EOF\n")
@ -136,15 +151,20 @@ def run_drc(cell_name, gds_name, extract=True, final_verification=False):
# etc.
try:
f = open(outfile, "r")
except:
debug.error("Unable to retrieve DRC results file. Is magic set up?",1)
except FileNotFoundError:
debug.error("Unable to load DRC results file from {}. Is magic set up?".format(outfile),1)
results = f.readlines()
f.close()
errors=1
# those lines should be the last 3
for line in results:
if "Total DRC errors found:" in line:
errors = int(re.split(": ", line)[1])
break
else:
debug.error("Unable to find the total error line in Magic output.",1)
# always display this summary
if errors > 0:
@ -185,7 +205,11 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
total_errors = 0
# check the result for these lines in the summary:
f = open(resultsfile, "r")
try:
f = open(resultsfile, "r")
except FileNotFoundError:
debug.error("Unable to load LVS results from {}".format(resultsfile),1)
results = f.readlines()
f.close()
# Look for the results after the final "Subcircuit summary:"
@ -235,7 +259,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
return total_errors
def run_pex(name, gds_name, sp_name, output=None):
def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
"""Run pex on a given top-level name which is
implemented in gds_name and sp_name. """

View File

@ -9,7 +9,7 @@ drc_warned = False
lvs_warned = False
pex_warned = False
def run_drc(cell_name, gds_name, extract=False):
def run_drc(cell_name, gds_name, extract=False, final_verification=False):
global drc_warned
if not drc_warned:
debug.warning("DRC unable to run.")
@ -25,7 +25,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
# Since we warned, return a failing test.
return 1
def run_pex(name, gds_name, sp_name, output=None):
def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
global pex_warned
if not pex_warned:
debug.warning("PEX unable to run.")

View File

@ -1,6 +0,0 @@
all: openram_manual.tex
pdflatex openram_manual
bib:
bibtex openram_manual
clean:
rm -f openram_manual.pdf *.aux *.bbl *.blq *.dvi *.log *.lot *.toc *.lof *.blg

View File

@ -1,4 +0,0 @@
\section{Internal Control Signals}
\label{sec:control}
This section not needed... All information is in Section~\ref{sec:timing} (Timing).

View File

@ -1,49 +0,0 @@
\section{Debug Framework}
\label{sec:debug}
All output in OpenRAM should use the shared debug framework. This is
still under development but is in a usable state. It is going to be
replaced with the Python Logging framework which is quite simple.
All of the debug framework is contained in debug.py and is based
around the concept of a ``debug level'' which is a single global
variable in this file. This level is, by default, 0 which will output
normal minimal output. The general guidelines for debug output are:
\begin{itemize}
\item 0 Normal output
\item 1 Verbose output
\item 2 Detailed output
\item 3+ Excessively detailed output
\end{itemize}
The debug level can be adjusted on the command line when arguments are parsed using the ``-v'' flag. Adding more ``-v'' flags will increase the debug level as in the following examples:
\begin{verbatim}
python tests/01_library_drc_test.py -vv
python openram.py 4 16 -v -v
\end{verbatim}
which each put the program in debug level 2 (detailed output).
Since every module may output a lot of information in the higher debug
levels, the output format is standardized to allow easy searching via
grep or other command-line tools. The standard output formatting is
used through three interface functions:
\begin{itemize}
\item debug.info(int, msg)
\item debug.warning(msg)
\item debug.error(msg)
\end{itemize}
The msg string in each case can be any string format including data or
other useful debug information. The string should also contain
information to make it human understandable. {\bf It should not just be
a number!} The warning and error messages are independent of debug
levels while the info message will only print the message if the
current debug level is above the parameter value.
The output format of the debug info messages are:
\begin{verbatim}
[ module ]: msg
\end{verbatim}
where module is the calling module name and msg is the string
provided. This enables a grep command to get the relevant lines. The
warning and error messages include the file name and line number of
the warning/error.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 47 KiB

View File

@ -1,747 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="Logic Diagram.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient4867">
<stop
style="stop-color:#cccccc;stop-opacity:1;"
offset="0"
id="stop4869" />
<stop
style="stop-color:#cccccc;stop-opacity:0;"
offset="1"
id="stop4871" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3807">
<stop
style="stop-color:#cccccc;stop-opacity:1;"
offset="0"
id="stop3809" />
<stop
style="stop-color:#cccccc;stop-opacity:0;"
offset="1"
id="stop3811" />
</linearGradient>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Lstart"
style="overflow:visible">
<path
id="path3831"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
transform="scale(0.8) translate(12.5,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Lend"
style="overflow:visible;">
<path
id="path3834"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
transform="scale(0.8) rotate(180) translate(12.5,0)" />
</marker>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3807"
id="linearGradient3813"
x1="199.69901"
y1="218.7489"
x2="199.43983"
y2="217.65218"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,116)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3807"
id="linearGradient3819"
gradientUnits="userSpaceOnUse"
x1="199.69901"
y1="218.7489"
x2="199.43983"
y2="217.65218"
gradientTransform="translate(0,-691.72632)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4867"
id="linearGradient4873"
x1="394.20999"
y1="489.94479"
x2="393.62537"
y2="490.3624"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,100)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4867"
id="linearGradient4879"
gradientUnits="userSpaceOnUse"
x1="394.20999"
y1="489.94479"
x2="393.62537"
y2="490.3624"
gradientTransform="translate(-1019.8693,100)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4867"
id="linearGradient4885"
gradientUnits="userSpaceOnUse"
x1="394.20999"
y1="489.94479"
x2="393.62537"
y2="490.3624"
gradientTransform="translate(0,100)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
gridtolerance="10000"
guidetolerance="10"
objecttolerance="10"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="16.932823"
inkscape:cx="504.04531"
inkscape:cy="492.15828"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1600"
inkscape:window-height="1132"
inkscape:window-x="0"
inkscape:window-y="0"
showguides="true"
inkscape:guide-bbox="true" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:url(#linearGradient3813);fill-opacity:1"
id="rect2383"
width="165.71428"
height="94.646927"
x="133.76231"
y="309.66827" />
<rect
style="fill:#cccccc"
id="rect2385"
width="208.57143"
height="188.57143"
x="406.00479"
y="309.50024" />
<rect
style="fill:#cccccc"
id="rect2387"
width="208.38266"
height="62.857143"
x="405.96155"
y="70.725639" />
<rect
style="fill:url(#linearGradient4873);fill-opacity:1"
id="rect2389"
width="138.24103"
height="54.285713"
x="371.81412"
y="558.85974" />
<text
xml:space="preserve"
style="font-size:23.82649612px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="452.81046"
y="105.8875"
id="text2399"
transform="scale(0.9927707,1.0072819)"><tspan
sodipodi:role="line"
id="tspan2401"
x="452.81046"
y="105.8875">Precharge</tspan></text>
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="478.94641"
y="410.42963"
id="text2403"><tspan
sodipodi:role="line"
id="tspan2405"
x="478.94641"
y="410.42963">Array</tspan></text>
<rect
style="fill:#cccccc"
id="rect2429"
width="277.14285"
height="54.285713"
x="371.49484"
y="672.11481" />
<rect
style="fill:#cccccc"
id="rect2431"
width="277.14285"
height="54.285713"
x="372.53488"
y="794.2793" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="439.30377"
y="708.78662"
id="text2437"><tspan
sodipodi:role="line"
id="tspan2439"
x="439.30377"
y="708.78662">Sense Amp</tspan></text>
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="428.77615"
y="828.11359"
id="text2441"><tspan
sodipodi:role="line"
id="tspan2443"
x="428.77615"
y="828.11359">Output Latch</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:none;stroke-opacity:1"
d="M 510.87523,794.2793 L 510.29735,726.40052"
id="path2453"
inkscape:connector-type="polyline"
inkscape:connection-start="#rect2431"
inkscape:connection-end="#rect2429" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.00533342px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
d="M 510.37148,613.14813 L 510.36003,672.35929"
id="path2455"
inkscape:connector-type="polyline" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.00327015px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
d="M 297.90384,403.80303 L 406.00315,403.80054"
id="path2469"
inkscape:connector-type="polyline" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="237.36064"
y="462.23129"
id="text2497"><tspan
sodipodi:role="line"
id="tspan2499"
x="237.36064"
y="462.23129" /></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
d="M 511.49401,848.56501 L 512.51267,919.88151"
id="path2521"
inkscape:connector-type="polyline"
inkscape:connection-start="#rect2431" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="432.52811"
y="954.8941"
id="text2527"><tspan
sodipodi:role="line"
id="tspan2529"
x="432.52811"
y="954.8941">Out to System</tspan></text>
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="322.32162"
y="-51.962601"
id="text2531"
transform="matrix(9.034996e-4,0.9999996,-0.9999996,9.034996e-4,0,0)"><tspan
sodipodi:role="line"
id="tspan2533"
x="322.32162"
y="-51.962601">In From System</tspan><tspan
sodipodi:role="line"
x="322.32162"
y="-21.962603"
id="tspan2535" /></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="538.99377"
y="516.83362"
id="text2832"><tspan
sodipodi:role="line"
id="tspan2834"
x="538.99377"
y="516.83362"> Bit Lines</tspan><tspan
sodipodi:role="line"
x="538.99377"
y="531.83362"
id="tspan2938">BL<tspan
style="font-size:6px"
id="tspan2950">0</tspan>,BR<tspan
style="font-size:6px"
id="tspan2948">0</tspan>, BL<tspan
style="font-size:6px"
id="tspan2946">1</tspan>, BR<tspan
style="font-size:6px"
id="tspan2944">1</tspan>, ... BL<tspan
style="font-size:6px"
id="tspan2942">j</tspan> BR<tspan
style="font-size:6px"
id="tspan2940">j </tspan></tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 364.52511,382.19547 L 336.74637,425.84777"
id="path2836" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="280.00189"
y="-339.76331"
id="text2838"
transform="matrix(3.852356e-3,0.9999926,-0.9999926,3.852356e-3,0,0)"><tspan
sodipodi:role="line"
id="tspan2840"
x="280.00189"
y="-339.76331"> Word Lines</tspan><tspan
sodipodi:role="line"
x="280.00189"
y="-324.76331"
id="tspan2930"> W<tspan
style="font-size:6px"
id="tspan2936">0</tspan>, W<tspan
style="font-size:6px"
id="tspan2934">1</tspan>, ..., W<tspan
style="font-size:6px"
id="tspan2932">log(n)</tspan></tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1"
d="M 371.888,589.5005 L 309.17124,589.84843"
id="path2842"
inkscape:connector-type="polyline" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1"
d="M 370.94208,702.41208 L 308.22531,702.76001"
id="path2854"
inkscape:connector-type="polyline" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1"
d="M 371.88799,824.43518 L 309.17123,824.78311"
id="path2860"
inkscape:connector-type="polyline" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 491.10498,654.25031 L 526.70018,628.88523"
id="path2870" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="320.09518"
y="-99.119331"
id="text2888"
transform="matrix(-1.094382e-2,0.9999401,-0.9999401,-1.094382e-2,0,0)"><tspan
sodipodi:role="line"
id="tspan2890"
x="320.09518"
y="-99.119331">A<tspan
style="font-size:6px"
id="tspan2892">0</tspan>, A<tspan
style="font-size:6px"
id="tspan2896">1</tspan>, ... A<tspan
style="font-size:6px"
id="tspan2898">n</tspan></tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="552.88428"
y="-299.25662"
id="text2902"
transform="matrix(-1.094382e-2,0.9999401,-0.9999401,-1.094382e-2,0,0)"><tspan
sodipodi:role="line"
id="tspan2904"
x="552.88428"
y="-299.25662">A<tspan
style="font-size:6px"
id="tspan2906">0</tspan>, A<tspan
style="font-size:6px"
id="tspan2908">1</tspan>, ... Ak<tspan
style="font-size:6px"
id="tspan2910" /></tspan></text>
<text
xml:space="preserve"
style="font-size:37.14432144px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="784.93219"
y="-237.72998"
id="text2912"
transform="matrix(2.6675397e-2,0.8871754,-1.1264084,2.54215e-2,0,0)"><tspan
sodipodi:role="line"
id="tspan2914"
x="784.93219"
y="-237.72998"
style="font-size:18.57216072px">S<tspan
style="font-size:9.28608036px"
id="tspan2916">clk</tspan></tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 491.47553,766.55443 L 530.65923,741.92468"
id="path2922" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="784.55219"
y="-372.34665"
id="text2924"
transform="matrix(-9.6248034e-2,0.9953574,-0.9953574,-9.6248034e-2,0,0)"><tspan
sodipodi:role="line"
id="tspan2926"
x="784.55219"
y="-372.34665">clk</tspan><tspan
sodipodi:role="line"
x="784.55219"
y="-357.34665"
id="tspan2928" /></text>
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="727.75165"
y="543.27771"
id="text2970"><tspan
sodipodi:role="line"
id="tspan2972"
x="727.75165"
y="543.27771" /></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="537.27209"
y="156.72511"
id="text2992"><tspan
sodipodi:role="line"
id="tspan2994"
x="537.27209"
y="156.72511"> Bit Lines</tspan><tspan
sodipodi:role="line"
x="537.27209"
y="171.72511"
id="tspan2996">BL<tspan
style="font-size:6px"
id="tspan2998">0</tspan>,BR<tspan
style="font-size:6px"
id="tspan3000">0</tspan>, BL<tspan
style="font-size:6px"
id="tspan3002">1</tspan>, BR<tspan
style="font-size:6px"
id="tspan3004">1</tspan>, ... BL<tspan
style="font-size:6px"
id="tspan3006">j</tspan> BR<tspan
style="font-size:6px"
id="tspan3008">j </tspan></tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="523.96411"
y="638.21588"
id="text3010"><tspan
sodipodi:role="line"
id="tspan3012"
x="523.96411"
y="638.21588"> Bit Lines</tspan><tspan
sodipodi:role="line"
x="523.96411"
y="653.21588"
id="tspan3014">BL<tspan
style="font-size:6px"
id="tspan3016">0</tspan>,BR<tspan
style="font-size:6px"
id="tspan3018">0</tspan>, BL<tspan
style="font-size:6px"
id="tspan3020">1</tspan>, BR<tspan
style="font-size:6px"
id="tspan3022">1</tspan>, ... BL<tspan
style="font-size:6px"
id="tspan3024">j/2^k</tspan> BR<tspan
style="font-size:6px"
id="tspan3026">j/2^k </tspan></tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:none;marker-start:url(#Arrow1Lstart)"
d="M 404.99504,103.56693 L 342.27828,103.91486"
id="path3046"
inkscape:connector-type="polyline" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="86.832726"
y="-327.60617"
id="text3052"
transform="matrix(-1.094382e-2,0.9999401,-0.9999401,-1.094382e-2,0,0)"><tspan
sodipodi:role="line"
id="tspan3054"
x="86.832726"
y="-327.60617">Pclk</tspan><tspan
sodipodi:role="line"
x="86.832726"
y="-312.60617"
id="tspan3062" /></text>
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="551.03613"
y="753.10077"
id="text3064"><tspan
sodipodi:role="line"
id="tspan3066"
x="551.03613"
y="753.10077"
style="font-size:12px"> Data</tspan><tspan
sodipodi:role="line"
x="551.03613"
y="768.10077"
id="tspan3068"><tspan
style="font-size:12px"
id="tspan3111">D</tspan><tspan
style="font-size:8px"
id="tspan3074">0</tspan><tspan
style="font-size:12px"
id="tspan3115">, </tspan><tspan
style="font-size:12px"
id="tspan3109">D</tspan><tspan
style="font-size:8px"
id="tspan3072">1</tspan><tspan
style="font-size:12px"
id="tspan3113">, ...,</tspan><tspan
style="font-size:12px"
id="tspan3107"> D</tspan><tspan
style="font-size:8px"
id="tspan3070">j/2^k</tspan></tspan></text>
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="543.46613"
y="865.92303"
id="text3117"><tspan
sodipodi:role="line"
id="tspan3119"
x="543.46613"
y="865.92303"
style="font-size:12px"> Data Out</tspan><tspan
sodipodi:role="line"
x="543.46613"
y="880.92303"
id="tspan3121"><tspan
style="font-size:12px"
id="tspan3123">Do</tspan><tspan
style="font-size:8px"
id="tspan3125">0</tspan><tspan
style="font-size:12px"
id="tspan3127">, </tspan><tspan
style="font-size:12px"
id="tspan3129">Do</tspan><tspan
style="font-size:8px"
id="tspan3131">1</tspan><tspan
style="font-size:12px"
id="tspan3133">, ...,</tspan><tspan
style="font-size:12px"
id="tspan3135"> Do</tspan><tspan
style="font-size:8px"
id="tspan3137">j/2^k</tspan></tspan></text>
<path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke-width:0.40000001000000002;stroke-miterlimit:18.10000038000000089;stroke-dasharray:none"
id="path3309"
d="M 75.196328,158.6717 C 80.758538,157.28869 86.44734,156.6028 92.133726,155.94035 C 107.05435,154.05138 121.8677,151.43215 136.75528,149.30749 C 147.64996,147.53122 158.58499,146.01225 169.47598,144.21603 C 171.5715,143.74706 173.73538,143.65255 175.84432,143.27807 C 176.90658,143.08945 177.95684,142.73177 179.00742,142.48204 C 180.58421,142.34805 181.98047,141.66528 183.53245,141.39396 L 172.37378,149.88252 C 170.88347,150.29297 169.41362,150.72728 167.88051,150.96244 C 164.844,151.6568 161.73546,151.85357 158.66031,152.32711 C 148.00248,153.81685 137.31827,155.11752 126.69325,156.8396 C 111.66369,159.01206 96.704443,161.62748 81.668664,163.7554 C 75.768122,164.62195 69.820463,165.47786 64.116915,167.27925 L 75.196328,158.6717 z" />
<rect
y="-498.05804"
x="133.76231"
height="94.646927"
width="165.71428"
id="rect3815"
style="fill:url(#linearGradient3819);fill-opacity:1"
transform="scale(1,-1)" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="170.85649"
y="394.76114"
id="text2393"><tspan
sodipodi:role="line"
id="tspan2395"
x="170.85649"
y="394.76114">Address </tspan><tspan
sodipodi:role="line"
x="170.85649"
y="424.76114"
id="tspan2397">Decoder</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1"
d="M 133.5,403.36218 L 70.75,402.86218"
id="path3823" />
<rect
y="558.85974"
x="371.81412"
height="54.285713"
width="138.24103"
id="rect4875"
style="fill:url(#linearGradient4885);fill-opacity:1" />
<rect
style="fill:url(#linearGradient4879);fill-opacity:1"
id="rect4877"
width="138.24103"
height="54.285713"
x="-648.05518"
y="558.85974"
transform="scale(-1,1)" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="439.2803"
y="595.7511"
id="text2407"><tspan
sodipodi:role="line"
id="tspan2409"
x="439.2803"
y="595.7511">Column Mux</tspan></text>
<path
inkscape:connector-type="polyline"
id="path4881"
d="M 510.37147,497.86869 L 510.36004,557.47571"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.00782228px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1" />
<path
id="path4883"
d="M 491.10498,538.25031 L 526.70018,512.88523"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="fill:#cccccc"
id="rect2427"
width="277.14285"
height="54.285713"
x="372.23978"
y="190.47002" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="432.57791"
y="225.61827"
id="text2433"><tspan
sodipodi:role="line"
id="tspan2435"
x="432.57791"
y="225.61827">Write Driver</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1"
d="M 371.40729,219.41404 L 308.69052,219.76197"
id="path2848"
inkscape:connector-type="polyline" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:none;stroke-opacity:1"
d="M 340.99482,208.10497 C 327.752,230.80694 327.752,230.80694 327.752,230.80694"
id="path2882" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="167.72096"
y="-293.01819"
id="text2884"
transform="matrix(-2.8547241e-3,0.9999959,-0.9999959,-2.8547241e-3,0,0)"><tspan
sodipodi:role="line"
id="tspan2886"
x="167.72096"
y="-293.01819">Enable, Data in</tspan></text>
<text
xml:space="preserve"
style="font-size:8px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="519.95123"
y="268.63846"
id="text3028"><tspan
sodipodi:role="line"
id="tspan3030"
x="519.95123"
y="268.63846"
style="font-size:12px"> Bit Lines</tspan><tspan
sodipodi:role="line"
x="519.95123"
y="283.63846"
id="tspan3084"><tspan
style="font-size:12px"
id="tspan3103">BL</tspan>0 ,<tspan
style="font-size:12px"
id="tspan3101">BR</tspan><tspan
style="font-size:8px"
id="tspan3036">0</tspan>, <tspan
style="font-size:12px"
id="tspan3099">BL</tspan><tspan
style="font-size:8px"
id="tspan3038">1</tspan>, <tspan
style="font-size:12px"
id="tspan3097">BR</tspan><tspan
style="font-size:8px"
id="tspan3078">1</tspan>, ... <tspan
style="font-size:12px"
id="tspan3095">BL</tspan><tspan
style="font-size:8px"
id="tspan3042">j/2^k</tspan><tspan
style="font-size:12px"
id="tspan3093"> BR</tspan><tspan
style="font-size:8px"
id="tspan3044">j /2^k</tspan></tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:none;marker-end:url(#Arrow1Lend)"
d="M 510.33208,133.58278 L 510.65644,190.47002"
id="path4910"
inkscape:connector-type="polyline"
inkscape:connection-start="#rect2387"
inkscape:connection-end="#rect2427" />
<path
inkscape:connector-type="polyline"
id="path6475"
d="M 508.33374,244.83566 L 508.65589,308.42732"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.0536716px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:none;marker-end:url(#Arrow1Lend);stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 491.10498,280.25031 L 526.70018,254.88523"
id="path6477" />
<path
id="path6479"
d="M 491.10498,168.25031 L 526.70018,142.88523"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More