mirror of
https://github.com/VLSIDA/OpenRAM.git
synced 2026-08-29 09:30:38 +02:00
Merge branch 'dev' into multiport_characterization
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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, 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(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, proc, vdd, temp, 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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -507,9 +509,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 +522,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 +547,8 @@ class lib:
|
||||
lib_name,
|
||||
OPTS.word_size,
|
||||
git_id,
|
||||
current_time
|
||||
current_time,
|
||||
OPTS.analytical_delay
|
||||
))
|
||||
|
||||
# information of checks
|
||||
@@ -555,7 +561,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 +660,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()
|
||||
|
||||
|
||||
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 )
|
||||
+28
-10
@@ -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
|
||||
+45
-2
@@ -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,42 @@ 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 (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"
|
||||
# If we have non-1rw ports, figure out the right bitcell to use
|
||||
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.")
|
||||
else:
|
||||
debug.info(1,"Using custom bitcell: {}".format(OPTS.bitcell))
|
||||
|
||||
|
||||
def get_tool(tool_type, preferences, default_name=None):
|
||||
@@ -250,7 +288,8 @@ def read_config(config_file, is_unit_test=True):
|
||||
OPTS.num_words,
|
||||
ports,
|
||||
OPTS.tech_name)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def end_openram():
|
||||
@@ -417,7 +456,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))
|
||||
|
||||
@@ -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
|
||||
|
||||
+51
@@ -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()
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -28,8 +28,8 @@ class openram_test(unittest.TestCase):
|
||||
|
||||
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
|
||||
@@ -37,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))
|
||||
@@ -45,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))
|
||||
@@ -54,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.
|
||||
|
||||
@@ -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.")
|
||||
|
||||
Reference in New Issue
Block a user