mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev'
This commit is contained in:
commit
e476df1218
|
|
@ -5,3 +5,4 @@
|
|||
*.out
|
||||
*.toc
|
||||
*.synctex.gz
|
||||
**/model_data
|
||||
17
README.md
17
README.md
|
|
@ -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:
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 = ""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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('<','<').replace('"','"').replace('>',">")
|
||||
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('<','<').replace('"','"').replace('>',">")
|
||||
self.html += self.dlv_table.to_html()
|
||||
|
|
|
|||
|
|
@ -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 (µm<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)):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -1 +0,0 @@
|
|||
468eb9a4a038201c2b0004fe6e4ae9b2d37fdd57
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
import pstats
|
||||
p = pstats.Stats('profile.dat')
|
||||
p.strip_dirs()
|
||||
p.sort_stats('cumulative')
|
||||
p.print_stats(50)
|
||||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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. """
|
||||
|
||||
|
|
|
|||
|
|
@ -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.")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
\section{Internal Control Signals}
|
||||
\label{sec:control}
|
||||
|
||||
This section not needed... All information is in Section~\ref{sec:timing} (Timing).
|
||||
|
|
@ -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.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 47 KiB |
|
|
@ -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
Loading…
Reference in New Issue