Merge branch 'dev' into multiport_characterization

This commit is contained in:
Hunter Nichols
2019-02-26 22:48:39 -08:00
112 changed files with 794 additions and 24251 deletions
+5 -5
View File
@@ -73,8 +73,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
global total_drc_errors
global total_lvs_errors
tempspice = OPTS.openram_temp + "/temp.sp"
tempgds = OPTS.openram_temp + "/temp.gds"
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
self.sp_write(tempspice)
self.gds_write(tempgds)
@@ -95,7 +95,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
global total_drc_errors
tempgds = OPTS.openram_temp + "/temp.gds"
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
self.gds_write(tempgds)
num_errors = verify.run_drc(self.name, tempgds, final_verification)
total_drc_errors += num_errors
@@ -110,8 +110,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
global total_lvs_errors
tempspice = OPTS.openram_temp + "/temp.sp"
tempgds = OPTS.openram_temp + "/temp.gds"
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
self.sp_write(tempspice)
self.gds_write(tempgds)
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification)
+106
View File
@@ -0,0 +1,106 @@
import design
import debug
import utils
from tech import GDS,layer,parameter,drc
class bitcell_1w_1r(design.design):
"""
A single bit cell (6T, 8T, etc.) This module implements the
single memory cell used in the design. It is a hand-made cell, so
the layout and netlist should be available in the technology
library.
"""
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("cell_1w_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "cell_1w_1r")
debug.info(2, "Create bitcell with 1W and 1R Port")
self.width = bitcell_1w_1r.width
self.height = bitcell_1w_1r.height
self.pin_map = bitcell_1w_1r.pin_map
def analytical_delay(self, 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
+55 -11
View File
@@ -83,9 +83,11 @@ class lib:
(self.process, self.voltage, self.temperature) = self.corner
self.lib = open(lib_name, "w")
debug.info(1,"Writing to {0}".format(lib_name))
self.corner_name = lib_name.replace(self.out_dir,"").replace(".lib","")
self.characterize()
self.lib.close()
self.parse_info(self.corner,lib_name)
def characterize(self):
""" Characterize the current corner. """
@@ -325,7 +327,7 @@ class lib:
self.lib.write(" }\n")
self.lib.write(" pin(DOUT{}){{\n".format(read_port))
self.lib.write(" pin(DOUT{0}[{1}:0]){{\n".format(read_port,self.sram.word_size-1))
self.lib.write(" timing(){ \n")
self.lib.write(" timing_sense : non_unate; \n")
self.lib.write(" related_pin : \"clk{0}\"; \n".format(read_port))
@@ -358,7 +360,7 @@ class lib:
self.lib.write(" address : ADDR{0}; \n".format(write_port))
self.lib.write(" clocked_on : clk{0}; \n".format(write_port))
self.lib.write(" }\n")
self.lib.write(" pin(DIN{}){{\n".format(write_port))
self.lib.write(" pin(DIN{0}[{1}:0]){{\n".format(write_port,self.sram.word_size-1))
self.write_FF_setuphold(write_port)
self.lib.write(" }\n") # pin
self.lib.write(" }\n") #bus
@@ -378,7 +380,7 @@ class lib:
self.lib.write(" direction : input; \n")
self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"]))
self.lib.write(" max_transition : {0};\n".format(self.slews[-1]))
self.lib.write(" pin(ADDR{})".format(port))
self.lib.write(" pin(ADDR{0}[{1}:0])".format(port,self.sram.addr_size-1))
self.lib.write("{\n")
self.write_FF_setuphold(port)
@@ -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

+2 -2
View File
@@ -19,8 +19,8 @@
padding-top: 11px;
padding-bottom: 11px;
text-align: left;
background-color: #004184;
color: #F1B521;
background-color: #003C6C;
color: #FDC700;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 42 KiB

+20 -13
View File
@@ -28,17 +28,17 @@ class datasheet():
# for item in self.description:
# self.html += item + ','
self.html += '-->'
# Add vlsida logo
vlsi_logo = 0
with open(os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/datasheet/assets/vlsi_logo.png', "rb") as image_file:
vlsi_logo = base64.b64encode(image_file.read())
# Add openram logo
openram_logo = 0
with open(os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/datasheet/assets/openram_logo_placeholder.png', "rb") as image_file:
with open(os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/datasheet/assets/OpenRAM_logo.png', "rb") as image_file:
openram_logo = base64.b64encode(image_file.read())
self.html += '<a href="https://vlsida.soe.ucsc.edu/"><img src="data:image/png;base64,{0}" alt="VLSIDA"></a>'.format(str(vlsi_logo)[
2:-1])
self.html += '<a href="https://vlsida.soe.ucsc.edu/"><img src="data:image/png;base64,{0}" alt="VLSIDA"></a><a href ="https://github.com/VLSIDA/OpenRAM"><img src ="data:image/png;base64,{1}" alt = "OpenRAM"></a>'.format(str(vlsi_logo)[2:-1], str(openram_logo)[2:-1])
self.html += '<p style="font-size: 18px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">' + \
self.name + '.html' + '</p>'
@@ -49,23 +49,30 @@ class datasheet():
'LVS errors: ' + str(self.LVS) + '</p>'
self.html += '<p style="font-size: 18px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">' + \
'Git commit id: ' + str(self.git_id) + '</p>'
# print port table
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Ports and Configuration</p>'
# self.html += in_out(self.io,table_id='data').__html__().replace('&lt;','<').replace('&#34;','"').replace('&gt;',">")
self.html += self.io_table.to_html()
# print operating condidition information
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Operating Conditions</p>'
# self.html += operating_conditions(self.operating,table_id='data').__html__()
self.html += self.operating_table.to_html()
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Timing and Current Data</p>'
# self.html += timing_and_current_data(self.timing,table_id='data').__html__()
# check if analytical model is being used
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Timing Data</p>'
model = ''
if self.ANALYTICAL_MODEL == 'True':
model = "analytical model: results may not be precise"
else:
model = "spice characterizer"
# display timing data
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Using '+model+'</p>'
self.html += self.timing_table.to_html()
# display power data
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Power Data</p>'
self.html += self.power_table.to_html()
# display corner information
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Characterization Corners</p>'
# self.html += characterization_corners(self.corners,table_id='data').__html__()
self.html += self.corners_table.to_html()
# display deliverables table
self.html += '<p style="font-size: 26px;font-family: Trebuchet MS, Arial, Helvetica, sans-serif;">Deliverables</p>'
# self.html += deliverables(self.dlv,table_id='data').__html__().replace('&lt;','<').replace('&#34;','"').replace('&gt;',">")
self.html += self.dlv_table.to_html()
+136 -58
View File
@@ -4,7 +4,6 @@ This is a script to load data from the characterization and layout processes int
a web friendly html datasheet.
"""
# TODO:
# include power
# Diagram generation
# Improve css
@@ -16,19 +15,34 @@ import csv
import datasheet
import table_gen
def process_name(corner):
"""
Expands the names of the characterization corner types into something human friendly
"""
if corner == "TT":
return "Typical - Typical"
if corner == "SS":
return "Slow - Slow"
if corner == "FF":
return "Fast - Fast"
else:
return "custom"
# def process_name(corner):
# """
# Expands the names of the characterization corner types into something human friendly
# """
# if corner == "TS":
# return "Typical - Slow"
# if corner == "TT":
# return "Typical - Typical"
# if corner == "TF":
# return "Typical - Fast"
#
# if corner == "SS":
# return "Slow - Slow"
# if corner == "ST":
# return "Slow - Typical"
# if corner == "SF":
# return "Slow - Fast"
#
# if corner == "FS":
# return "Fast - Slow"
# if corner == "FT":
# return "Fast - Typical"
# if corner == "FF":
# return "Fast - Fast"
#
# else:
# return "custom"
#
def parse_characterizer_csv(f, pages):
@@ -91,15 +105,19 @@ def parse_characterizer_csv(f, pages):
DATETIME = row[col]
col += 1
ANALYTICAL_MODEL = row[col]
col += 1
DRC = row[col]
col += 1
LVS = row[col]
col += 1
AREA = row[col]
col += 1
for sheet in pages:
if sheet.name == NAME:
@@ -133,7 +151,7 @@ def parse_characterizer_csv(f, pages):
1000/float(MIN_PERIOD)))
except Exception:
pass
# check current .lib file produces the slowest timing results
while(True):
col_start = col
if(row[col].startswith('DIN')):
@@ -147,31 +165,31 @@ def parse_characterizer_csv(f, pages):
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('setup falling'):
if item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold rising'):
if item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold falling'):
if item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
col += 1
@@ -186,31 +204,31 @@ def parse_characterizer_csv(f, pages):
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('cell fall'):
if item[0].endswith('cell fall'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('rise transition'):
if item[0].endswith('rise transition'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('fall transition'):
if item[0].endswith('fall transition'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
col += 1
@@ -225,31 +243,31 @@ def parse_characterizer_csv(f, pages):
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('setup falling'):
if item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold rising'):
if item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold falling'):
if item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
col += 1
@@ -264,31 +282,31 @@ def parse_characterizer_csv(f, pages):
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('setup falling'):
if item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold rising'):
if item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold falling'):
if item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
col += 1
@@ -303,31 +321,31 @@ def parse_characterizer_csv(f, pages):
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('setup falling'):
if item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold rising'):
if item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
elif item[0].endswith('hold falling'):
if item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 2
col += 1
@@ -336,8 +354,36 @@ def parse_characterizer_csv(f, pages):
sheet.description.append(str(element))
break
new_sheet.corners_table.add_row([PROC, process_name(
PROC), VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
#check if new power is worse the previous
while(True):
col_start = col
if row[col] == 'power':
for item in sheet.power_table.rows:
if item[0].startswith(row[col+1]):
if item[2].startswith('{0} Rising'.format(row[col+2])):
if float(item[2]) < float(row[col+3]):
item[2] = row[col+3]
if item[2].startswith('{0} Falling'.format(row[col+2])):
if float(item[2]) < float(row[col+3]):
item[2] = row[col+3]
col += 4
else:
break
# check if new leakge is worse the previous
while(True):
col_start = col
if row[col] == 'leak':
for item in sheet.power_table.rows:
if item[0].startswith(row[col+1]):
if float(item[2]) < float(row[col+2]):
item[2] = row[col+2]
col += 3
else:
break
# add new corner information
new_sheet.corners_table.add_row(
[PROC, VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
new_sheet.dlv_table.add_row(
['.lib', 'Synthesis models', '<a href="file://{0}">{1}</a>'.format(LIB_NAME, LIB_NAME.replace(OUT_DIR, ''))])
@@ -351,14 +397,15 @@ def parse_characterizer_csv(f, pages):
new_sheet.time = DATETIME
new_sheet.DRC = DRC
new_sheet.LVS = LVS
new_sheet.ANALYTICAL_MODEL = ANALYTICAL_MODEL
new_sheet.description = [NAME, NUM_WORDS, NUM_BANKS, NUM_RW_PORTS, NUM_W_PORTS,
NUM_R_PORTS, TECH_NAME, MIN_PERIOD, WORD_SIZE, ORIGIN_ID, DATETIME]
new_sheet.corners_table = table_gen.table_gen("corners")
new_sheet.corners_table.add_row(
['Corner Name', 'Process', 'Power Supply', 'Temperature', 'Library Name Suffix'])
new_sheet.corners_table.add_row([PROC, process_name(
PROC), VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
['Transistor Type', 'Power Supply', 'Temperature', 'Corner Name'])
new_sheet.corners_table.add_row(
[PROC, VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
new_sheet.operating_table = table_gen.table_gen(
"operating_table")
new_sheet.operating_table.add_row(
@@ -375,9 +422,13 @@ def parse_characterizer_csv(f, pages):
# failed to provide non-zero MIN_PERIOD
new_sheet.operating_table.add_row(
['Operating Frequency (F)', '', '', "not available in netlist only", 'MHz'])
new_sheet.power_table = table_gen.table_gen("power")
new_sheet.power_table.add_row(
['Pins', 'Mode', 'Power', 'Units'])
new_sheet.timing_table = table_gen.table_gen("timing")
new_sheet.timing_table.add_row(
['Parameter', 'Min', 'Max', 'Units'])
# parse initial timing information
while(True):
col_start = col
if(row[col].startswith('DIN')):
@@ -504,6 +555,32 @@ def parse_characterizer_csv(f, pages):
for element in row[col_start:col-1]:
sheet.description.append(str(element))
break
# parse initial power and leakage information
while(True):
start = col
if(row[col].startswith('power')):
new_sheet.power_table.add_row([row[col+1],
'{0} Rising'.format(
row[col+2]),
row[col+3][0:6],
'mW']
)
new_sheet.power_table.add_row([row[col+1],
'{0} Falling'.format(
row[col+2]),
row[col+3][0:6],
'mW']
)
col += 4
elif(row[col].startswith('leak')):
new_sheet.power_table.add_row(
[row[col+1], 'leakage', row[col+2], 'mW'])
col += 3
else:
break
new_sheet.dlv_table = table_gen.table_gen("dlv")
new_sheet.dlv_table.add_row(['Type', 'Description', 'Link'])
@@ -537,12 +614,13 @@ def parse_characterizer_csv(f, pages):
new_sheet.io_table.add_row(['NUM_RW_PORTS', NUM_RW_PORTS])
new_sheet.io_table.add_row(['NUM_R_PORTS', NUM_R_PORTS])
new_sheet.io_table.add_row(['NUM_W_PORTS', NUM_W_PORTS])
new_sheet.io_table.add_row(['Area', AREA])
new_sheet.io_table.add_row(
['Area (&microm<sup>2</sup>)', str(round(float(AREA)))])
class datasheet_gen():
def datasheet_write(name):
"""writes the datasheet to a file"""
in_dir = OPTS.openram_temp
if not (os.path.isdir(in_dir)):
+11 -6
View File
@@ -1,24 +1,29 @@
class table_gen:
def __init__(self,name):
"""small library of functions to generate the html tables"""
def __init__(self, name):
self.name = name
self.rows = []
self.table_id = 'data'
def add_row(self,row):
def add_row(self, row):
"""add a row to table_gen object"""
self.rows.append(row)
def gen_table_head(self):
"""generate html table header"""
html = ''
html += '<thead>'
html += '<tr>'
for col in self.rows[0]:
html += '<th>' + str(col) + '</th>'
html += '<th>' + str(col) + '</th>'
html += '</tr>'
html += '</thead>'
return html
def gen_table_body(self):
"""generate html body (used after gen_table_head)"""
html = ''
html += '<tbody>'
@@ -31,13 +36,13 @@ class table_gen:
html += '</tr>'
html += '</tbody>'
return html
def to_html(self):
"""writes table_gen object to inline html"""
html = ''
html += '<table id= \"'+self.table_id+'\">'
html += self.gen_table_head()
html += self.gen_table_body()
html += '</table>'
return html
-827
View File
@@ -1,827 +0,0 @@
#!/usr/bin/python
# The MIT License (MIT)
#
# Copyright (c) 2011-2016 Aliaksei Chapyzhenka
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Translated to Python from original file:
# https://github.com/drom/wavedrom/blob/master/src/WaveDrom.js
#
import sys
import json
import math
import waveskin
font_width = 7
lane = {
"xs" : 20, # tmpgraphlane0.width
"ys" : 20, # tmpgraphlane0.height
"xg" : 120, # tmpgraphlane0.x
"yg" : 0, # head gap
"yh0" : 0, # head gap title
"yh1" : 0, # head gap
"yf0" : 0, # foot gap
"yf1" : 0, # foot gap
"y0" : 5, # tmpgraphlane0.y
"yo" : 30, # tmpgraphlane1.y - y0
"tgo" : -10, # tmptextlane0.x - xg
"ym" : 15, # tmptextlane0.y - y0
"xlabel" : 6, # tmptextlabel.x - xg
"xmax" : 1,
"scale" : 1,
"head" : {},
"foot" : {}
}
def genBrick (texts, extra, times) :
R = []
if len( texts ) == 4 :
for j in range( times ):
R.append(texts[0])
for i in range ( extra ):
R.append(texts[1])
R.append(texts[2])
for i in range ( extra ):
R.append(texts[3])
return R
if len( texts ) == 1 :
texts.append(texts[0])
R.append(texts[0])
for i in range (times * (2 * (extra + 1)) - 1) :
R.append(texts[1])
return R
def genFirstWaveBrick (text, extra, times) :
pattern = {
'p': ['pclk', '111', 'nclk', '000'],
'n': ['nclk', '000', 'pclk', '111'],
'P': ['Pclk', '111', 'nclk', '000'],
'N': ['Nclk', '000', 'pclk', '111'],
'l': ['000'],
'L': ['000'],
'0': ['000'],
'h': ['111'],
'H': ['111'],
'1': ['111'],
'=': ['vvv-2'],
'2': ['vvv-2'],
'3': ['vvv-3'],
'4': ['vvv-4'],
'5': ['vvv-5'],
'd': ['ddd'],
'u': ['uuu'],
'z': ['zzz']
}
return genBrick( pattern.get( text, ['xxx'] ) , extra, times );
def genWaveBrick (text, extra, times) :
x1 = {'p':'pclk', 'n':'nclk', 'P':'Pclk', 'N':'Nclk', 'h':'pclk', 'l':'nclk', 'H':'Pclk', 'L':'Nclk'}
x2 = {'0':'0', '1':'1', 'x':'x', 'd':'d', 'u':'u', 'z':'z', '=':'v', '2':'v', '3':'v', '4':'v', '5':'v' }
x3 = {'0': '', '1': '', 'x': '', 'd': '', 'u': '', 'z': '', '=':'-2', '2':'-2', '3':'-3', '4':'-4', '5':'-5'}
y1 = {
'p':'0', 'n':'1',
'P':'0', 'N':'1',
'h':'1', 'l':'0',
'H':'1', 'L':'0',
'0':'0', '1':'1', 'x':'x', 'd':'d', 'u':'u', 'z':'z', '=':'v', '2':'v', '3':'v', '4':'v', '5':'v'}
y2 = {
'p': '', 'n': '',
'P': '', 'N': '',
'h': '', 'l': '',
'H': '', 'L': '',
'0': '', '1': '', 'x': '', 'd': '', 'u': '', 'z': '', '=':'-2', '2':'-2', '3':'-3', '4':'-4', '5':'-5'}
x4 = {
'p': '111', 'n': '000',
'P': '111', 'N': '000',
'h': '111', 'l': '000',
'H': '111', 'L': '000',
'0': '000', '1': '111', 'x': 'xxx', 'd': 'ddd', 'u': 'uuu', 'z': 'zzz',
'=': 'vvv-2', '2': 'vvv-2', '3': 'vvv-3', '4': 'vvv-4', '5': 'vvv-5'}
x5 = {'p':'nclk', 'n':'pclk', 'P':'nclk', 'N':'pclk'}
x6 = {'p': '000', 'n': '111', 'P': '000', 'N': '111'}
xclude = {'hp':'111', 'Hp':'111', 'ln': '000', 'Ln': '000', 'nh':'111', 'Nh':'111', 'pl': '000', 'Pl':'000'}
#atext = text.split()
atext = text
tmp0 = x4.get(atext[1])
tmp1 = x1.get(atext[1])
if tmp1 == None :
tmp2 = x2.get(atext[1])
if tmp2 == None :
# unknown
return genBrick(['xxx'], extra, times)
else :
tmp3 = y1.get(atext[0])
if tmp3 == None :
# unknown
return genBrick(['xxx'], extra, times)
# soft curves
return genBrick([tmp3 + 'm' + tmp2 + y2[atext[0]] + x3[atext[1]], tmp0], extra, times)
else :
tmp4 = xclude.get(text)
if tmp4 != None :
tmp1 = tmp4
# sharp curves
tmp2 = x5.get(atext[1])
if tmp2 == None :
# hlHL
return genBrick([tmp1, tmp0], extra, times)
else :
# pnPN
return genBrick([tmp1, tmp0, tmp2, x6[atext[1]]], extra, times)
def parseWaveLane (text, extra) :
R = []
Stack = text
Next = Stack[0]
Stack = Stack[1:]
Repeats = 1
while len(Stack) and ( Stack[0] == '.' or Stack[0] == '|' ): # repeaters parser
Stack=Stack[1:]
Repeats += 1
R.extend(genFirstWaveBrick(Next, extra, Repeats))
while len(Stack) :
Top = Next
Next = Stack[0]
Stack = Stack[1:]
Repeats = 1
while len(Stack) and ( Stack[0] == '.' or Stack[0] == '|' ) : # repeaters parser
Stack=Stack[1:]
Repeats += 1
R.extend(genWaveBrick((Top + Next), extra, Repeats))
for i in range( lane['phase'] ):
R = R[1:]
return R
def parseWaveLanes (sig) :
def data_extract (e) :
tmp = e.get('data')
if tmp == None : return None
if is_type_str (tmp) : tmp=tmp.split()
return tmp
content = []
for sigx in sig :
lane['period'] = sigx.get('period',1)
lane['phase'] = int( sigx.get('phase',0 ) * 2 )
sub_content=[]
sub_content.append( [sigx.get('name',' '), sigx.get('phase',0 ) ] )
sub_content.append( parseWaveLane( sigx['wave'], int(lane['period'] * lane['hscale'] - 1 ) ) if sigx.get('wave') else None )
sub_content.append( data_extract(sigx) )
content.append(sub_content)
return content
def findLaneMarkers (lanetext) :
lcount = 0
gcount = 0
ret = []
for i in range( len( lanetext ) ) :
if lanetext[i] == 'vvv-2' or lanetext[i] == 'vvv-3' or lanetext[i] == 'vvv-4' or lanetext[i] == 'vvv-5' :
lcount += 1
else :
if lcount !=0 :
ret.append(gcount - ((lcount + 1) / 2))
lcount = 0
gcount += 1
if lcount != 0 :
ret.append(gcount - ((lcount + 1) / 2))
return ret
def renderWaveLane (root, content, index) :
xmax = 0
xgmax = 0
glengths = []
svgns = 'http://www.w3.org/2000/svg'
xlinkns = 'http://www.w3.org/1999/xlink'
xmlns = 'http://www.w3.org/XML/1998/namespace'
for j in range( len(content) ):
name = content[j][0][0]
if name : # check name
g = [
'g',
{
'id': 'wavelane_' + str(j) + '_' + str(index),
'transform': 'translate(0,' + str(lane['y0'] + j * lane['yo']) + ')'
}
]
root.append(g)
title = [
'text',
{
'x': lane['tgo'],
'y': lane['ym'],
'class': 'info',
'text-anchor': 'end',
'xml:space': 'preserve'
},
['tspan', name]
]
g.append(title)
glengths.append( len(name) * font_width + font_width )
xoffset = content[j][0][1]
xoffset = math.ceil(2 * xoffset) - 2 * xoffset if xoffset > 0 else -2 * xoffset
gg = [
'g',
{
'id': 'wavelane_draw_' + str(j) + '_' + str(index),
'transform': 'translate(' + str( xoffset * lane['xs'] ) + ', 0)'
}
]
g.append(gg)
if content[j][1] :
for i in range( len(content[j][1]) ) :
b = [
'use',
{
#'id': 'use_' + str(i) + '_' + str(j) + '_' + str(index),
'xmlns:xlink':xlinkns,
'xlink:href': '#' + str( content[j][1][i] ),
'transform': 'translate(' + str(i * lane['xs']) + ')'
}
]
gg.append(b)
if content[j][2] and len(content[j][2]) :
labels = findLaneMarkers(content[j][1])
if len(labels) != 0 :
for k in range( len(labels) ) :
if content[j][2] and k < len(content[j][2]) :
title = [
'text',
{
'x': int(labels[k]) * lane['xs'] + lane['xlabel'],
'y': lane['ym'],
'text-anchor': 'middle',
'xml:space': 'preserve'
},
['tspan',content[j][2][k]]
]
gg.append(title)
if len(content[j][1]) > xmax :
xmax = len(content[j][1])
lane['xmax'] = xmax
lane['xg'] = xgmax + 20
return glengths
def renderMarks (root, content, index) :
def captext ( g, cxt, anchor, y ) :
if cxt.get(anchor) and cxt[anchor].get('text') :
tmark = [
'text',
{
'x': float( cxt['xmax'] ) * float( cxt['xs'] ) / 2,
'y': y,
'text-anchor': 'middle',
'fill': '#000',
'xml:space': 'preserve'
}, cxt[anchor]['text']
]
g.append(tmark)
def ticktock ( g, cxt, ref1, ref2, x, dx, y, length ) :
L = []
if cxt.get(ref1) == None or cxt[ref1].get(ref2) == None :
return
val = cxt[ref1][ref2]
if is_type_str( val ) :
val = val.split()
elif type( val ) is int :
offset = val
val = []
for i in range ( length ) :
val.append(i + offset)
if type( val ) is list :
if len( val ) == 0 :
return
elif len( val ) == 1 :
offset = val[0]
if is_type_str(offset) :
L = val
else :
for i in range ( length ) :
L[i] = i + offset
elif len( val ) == 2:
offset = int(val[0])
step = int(val[1])
tmp = val[1].split('.')
if len( tmp ) == 2 :
dp = len( tmp[1] )
if is_type_str(offset) or is_type_str(step) :
L = val
else :
offset = step * offset
for i in range( length ) :
L[i] = "{0:.",dp,"f}".format(step * i + offset)
else :
L = val
else :
return
for i in range( length ) :
tmp = L[i]
tmark = [
'text',
{
'x': i * dx + x,
'y': y,
'text-anchor': 'middle',
'class': 'muted',
'xml:space': 'preserve'
}, str(tmp)
]
g.append(tmark)
mstep = 2 * int(lane['hscale'])
mmstep = mstep * lane['xs']
marks = int( lane['xmax'] / mstep )
gy = len( content ) * int(lane['yo'])
g = ['g', {'id': 'gmarks_' + str(index)}]
root.insert(0,g)
for i in range( marks + 1):
gg = [
'path',
{
'id': 'gmark_' + str(i) + '_' + str(index),
'd': 'm ' + str(i * mmstep) + ',' + '0' + ' 0,' + str(gy),
'style': 'stroke:#888;stroke-width:0.5;stroke-dasharray:1,3'
}
]
g.append( gg )
captext(g, lane, 'head', -33 if lane['yh0'] else -13 )
captext(g, lane, 'foot', gy + ( 45 if lane['yf0'] else 25 ) )
ticktock( g, lane, 'head', 'tick', 0, mmstep, -5, marks + 1)
ticktock( g, lane, 'head', 'tock', mmstep / 2, mmstep, -5, marks)
ticktock( g, lane, 'foot', 'tick', 0, mmstep, gy + 15, marks + 1)
ticktock( g, lane, 'foot', 'tock', mmstep / 2, mmstep, gy + 15, marks)
def renderArcs (root, source, index, top) :
Stack = []
Edge = {'words': [], 'frm': 0, 'shape': '', 'to': 0, 'label': ''}
Events = {}
svgns = 'http://www.w3.org/2000/svg'
xmlns = 'http://www.w3.org/XML/1998/namespace'
if source :
for i in range (len (source) ) :
lane['period'] = source[i].get('period',1)
lane['phase'] = int( source[i].get('phase',0 ) * 2 )
text = source[i].get('node')
if text:
Stack = text
pos = 0
while len( Stack ) :
eventname = Stack[0]
Stack=Stack[1:]
if eventname != '.' :
Events[eventname] = {
'x' : str( int( float( lane['xs'] ) * (2 * pos * lane['period'] * lane['hscale'] - lane['phase'] ) + float( lane['xlabel'] ) ) ),
'y' : str( int( i * lane['yo'] + lane['y0'] + float( lane['ys'] ) * 0.5 ) )
}
pos += 1
gg = [ 'g', { 'id' : 'wavearcs_' + str( index ) } ]
root.append(gg)
if top.get('edge') :
for i in range( len ( top['edge'] ) ) :
Edge['words'] = top['edge'][i].split()
Edge['label'] = top['edge'][i][len(Edge['words'][0]):]
Edge['label'] = Edge['label'][1:]
Edge['frm'] = Edge['words'][0][0]
Edge['to'] = Edge['words'][0][-1]
Edge['shape'] = Edge['words'][0][1:-1]
frm = Events[Edge['frm']]
to = Events[Edge['to']]
gmark = [
'path',
{
'id': 'gmark_' + Edge['frm'] + '_' + Edge['to'],
'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + to['x'] + ',' + to['y'],
'style': 'fill:none;stroke:#00F;stroke-width:1'
}
]
gg.append(gmark)
dx = float( to['x'] ) - float( frm['x'] )
dy = float( to['y'] ) - float( frm['y'] )
lx = (float(frm['x']) + float(to['x'])) / 2
ly = (float(frm['y']) + float(to['y'])) / 2
pattern = {
'~' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) },
'-~' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) },
'~-' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + '0' + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) },
'-|' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)},
'|-' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' 0,' + str(dy) + ' ' + str(dx) + ',0'},
'-|-' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'},
'->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none'},
'~>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'-~>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'~->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + '0' + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'-|>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)},
'|->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' 0,' + str(dy) + ' ' + str(dx) + ',0'},
'-|->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'},
'<->' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none'},
'<~>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'<-~>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'<-|>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)},
'<-|->': {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'}
}
gmark[1].update( pattern.get( Edge['shape'], { 'style': 'fill:none;stroke:#00F;stroke-width:1' } ) )
if Edge['label']:
if Edge['shape'] == '-~' :
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75
if Edge['shape'] == '~-' :
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.25
if Edge['shape'] == '-|' :
lx = float(to['x'])
if Edge['shape'] == '|-' :
lx = float(frm['x'])
if Edge['shape'] == '-~>':
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75
if Edge['shape'] == '~->':
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.25
if Edge['shape'] == '-|>' :
lx = float(to['x'])
if Edge['shape'] == '|->' :
lx = float(frm['x'])
if Edge['shape'] == '<-~>':
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75
if Edge['shape'] =='<-|>' :
lx = float(to['x'])
lwidth = len( Edge['label'] ) * font_width
label = [
'text',
{
'style': 'font-size:10px;',
'text-anchor': 'middle',
'xml:space': 'preserve',
'x': int( lx ),
'y': int( ly + 3 )
},
[ 'tspan', Edge['label'] ]
]
underlabel = [
'rect',
{
'height': 9,
'style': 'fill:#FFF;',
'width': lwidth,
'x': int( lx - lwidth / 2 ),
'y': int( ly - 5 )
}
]
gg.append(underlabel)
gg.append(label)
for k in Events:
if k.islower() :
if int( Events[k]['x'] ) > 0 :
lwidth = len( k ) * font_width
underlabel = [
'rect',
{
'x': float( Events[k]['x'] ) - float(lwidth) / 2,
'y': int( Events[k]['y'] ) - 4,
'height': 8,
'width': lwidth,
'style': 'fill:#FFF;'
}
]
gg.append(underlabel)
label = [
'text',
{
'style': 'font-size:8px;',
'x': int( Events[k]['x'] ),
'y': int( Events[k]['y'] ) + 2,
'width': lwidth,
'text-anchor': 'middle'
},
k
]
gg.append(label)
def parseConfig (source) :
lane['hscale'] = 1
if lane.get('hscale0') :
lane['hscale'] = lane['hscale0']
if source and source.get('config') and source.get('config').get('hscale'):
hscale = round(source.get('config').get('hscale'))
if hscale > 0 :
if hscale > 100 : hscale = 100
lane['hscale'] = hscale
lane['yh0'] = 0
lane['yh1'] = 0
if source and source.get('head') :
lane['head'] = source['head']
if source.get('head').get('tick',0) == 0 : lane['yh0'] = 20
if source.get('head').get('tock',0) == 0 : lane['yh0'] = 20
if source.get('head').get('text') : lane['yh1'] = 46; lane['head']['text'] = source['head']['text']
lane['yf0'] = 0
lane['yf1'] = 0
if source and source.get('foot') :
lane['foot'] = source['foot']
if source.get('foot').get('tick',0) == 0 : lane['yf0'] = 20
if source.get('foot').get('tock',0) == 0 : lane['yf0'] = 20
if source.get('foot').get('text') : lane['yf1'] = 46; lane['foot']['text'] = source['foot']['text']
def rec (tmp, state) :
name = str( tmp[0] )
delta_x = 25
state['x'] += delta_x
for i in range( len( tmp ) ) :
if type( tmp[i] ) is list :
old_y = state['y']
rec( tmp[i], state )
state['groups'].append( {'x':state['xx'], 'y':old_y, 'height':state['y'] - old_y, 'name': state['name'] } )
elif type( tmp[i] ) is dict :
state['lanes'].append(tmp[i])
state['width'].append(state['x'])
state['y'] += 1
state['xx'] = state['x']
state['x'] -= delta_x
state['name'] = name
def insertSVGTemplate (index, parent, source) :
e = waveskin.WaveSkin['default']
if source.get('config') and source.get('config').get('skin') :
if waveskin.WaveSkin.get( source.get('config').get('skin') ) :
e = waveskin.WaveSkin[ source.get('config').get('skin') ]
if index == 0 :
lane['xs'] = int( e[3][1][2][1]['width'] )
lane['ys'] = int( e[3][1][2][1]['height'] )
lane['xlabel'] = int( e[3][1][2][1]['x'] )
lane['ym'] = int( e[3][1][2][1]['y'] )
else :
e = ['svg', {'id': 'svg', 'xmlns': 'http://www.w3.org/2000/svg', 'xmlns:xlink': 'http://www.w3.org/1999/xlink', 'height': '0'},
['g', {'id': 'waves'},
['g', {'id': 'lanes'}],
['g', {'id': 'groups'}]
]
]
e[-1][1]['id'] = 'waves_' + str(index)
e[-1][2][1]['id'] = 'lanes_' + str(index)
e[-1][3][1]['id'] = 'groups_' + str(index)
e[1]['id'] = 'svgcontent_' + str(index)
e[1]['height'] = 0
parent.extend(e)
def renderWaveForm (index, source, output) :
xmax = 0
root = []
groups = []
if source.get('signal'):
insertSVGTemplate(index, output, source)
parseConfig( source )
ret = {'x':0, 'y':0, 'xmax':0, 'width':[], 'lanes':[], 'groups':[] }
rec( source['signal'], ret )
content = parseWaveLanes(ret['lanes'])
glengths = renderWaveLane(root, content, index)
for i in range( len( glengths ) ):
xmax = max( xmax, ( glengths[i] + ret['width'][i] ) )
renderMarks(root, content, index)
renderArcs(root, ret['lanes'], index, source)
renderGaps(root, ret['lanes'], index)
renderGroups(groups, ret['groups'], index)
lane['xg'] = int( math.ceil( float( xmax - lane['tgo'] ) / float(lane['xs'] ) ) ) * lane['xs']
width = (lane['xg'] + lane['xs'] * (lane['xmax'] + 1) )
height = len(content) * lane['yo'] + lane['yh0'] + lane['yh1'] + lane['yf0'] + lane['yf1']
output[1]={
'id' :'svgcontent_' + str(index),
'xmlns' :"http://www.w3.org/2000/svg",
'xmlns:xlink':"http://www.w3.org/1999/xlink",
'width' :str(width),
'height' :str(height),
'viewBox' :'0 0 ' + str(width) + ' ' + str(height),
'overflow' :"hidden"
}
output[-1][2][1]['transform']='translate(' + str(lane['xg'] + 0.5) + ', ' + str((float(lane['yh0']) + float(lane['yh1'])) + 0.5) + ')'
output[-1][2].extend(root)
output[-1][3].extend(groups)
def renderGroups (root, groups, index) :
svgns = 'http://www.w3.org/2000/svg',
xmlns = 'http://www.w3.org/XML/1998/namespace'
for i in range( len( groups ) ) :
group = [
'path',
{
'id': 'group_' + str(i) + '_' + str(index),
'd': 'm ' + str( groups[i]['x'] + 0.5 ) + ',' + str( groups[i]['y']* lane['yo'] + 3.5 + lane['yh0'] + lane['yh1'] ) + ' c -3,0 -5,2 -5,5 l 0,' + str( int( groups[i]['height'] * lane['yo'] - 16 ) ) + ' c 0,3 2,5 5,5',
'style': 'stroke:#0041c4;stroke-width:1;fill:none'
}
]
root.append(group)
name = groups[i]['name']
x = str( int( groups[i]['x'] - 10 ) )
y = str( int( lane['yo'] * (groups[i]['y'] + (float(groups[i]['height']) / 2)) + lane['yh0'] + lane['yh1'] ) )
label = [
['g',
{'transform': 'translate(' + x + ',' + y + ')'},
['g', {'transform': 'rotate(270)'},
'text',
{
'text-anchor': 'middle',
'class': 'info',
'xml:space' : 'preserve'
},
['tspan',name]
]
]
]
root.append(label)
def renderGaps (root, source, index) :
Stack = []
svgns = 'http://www.w3.org/2000/svg',
xlinkns = 'http://www.w3.org/1999/xlink'
if source:
gg = [
'g',
{ 'id': 'wavegaps_' + str(index) }
]
for i in range( len( source )):
lane['period'] = source[i].get('period',1)
lane['phase'] = int( source[i].get('phase',0 ) * 2 )
g = [
'g',
{
'id': 'wavegap_' + str(i) + '_' + str(index),
'transform': 'translate(0,' + str(lane['y0'] + i * lane['yo']) + ')'
}
]
gg.append(g)
if source[i].get('wave'):
text = source[i]['wave']
Stack = text
pos = 0
while len( Stack ) :
c = Stack [0]
Stack = Stack[1:]
if c == '|' :
b = [
'use',
{
'xmlns:xlink':xlinkns,
'xlink:href':'#gap',
'transform': 'translate(' + str(int(float(lane['xs']) * ((2 * pos + 1) * float(lane['period']) * float(lane['hscale']) - float(lane['phase'])))) + ')'
}
]
g.append(b)
pos += 1
root.append( gg )
def is_type_str( var ) :
if sys.version_info[0] < 3:
return type( var ) is str or type( var ) is unicode
else:
return type( var ) is str
def convert_to_svg( root ) :
svg_output = ''
if type( root ) is list:
if len(root) >= 2 and type( root[1] ) is dict:
if len( root ) == 2 :
svg_output += '<' + root[0] + convert_to_svg( root[1] ) + '/>\n'
elif len( root ) >= 3 :
svg_output += '<' + root[0] + convert_to_svg( root[1] ) + '>\n'
if len( root ) == 3:
svg_output += convert_to_svg( root[2] )
else:
svg_output += convert_to_svg( root[2:] )
svg_output += '</' + root[0] + '>\n'
elif type( root[0] ) is list:
for eleml in root:
svg_output += convert_to_svg( eleml )
else:
svg_output += '<' + root[0] + '>\n'
for eleml in root[1:]:
svg_output += convert_to_svg( eleml )
svg_output += '</' + root[0] + '>\n'
elif type( root ) is dict:
for elemd in root :
svg_output += ' ' + elemd + '="' + str(root[elemd]) + '"'
else:
svg_output += root
return svg_output
if __name__ == '__main__':
if len( sys.argv ) != 5:
print ( 'Usage : ' + sys.argv[0] + ' source <input.json> svg <output.svg>' )
exit(1)
if sys.argv[3] != 'svg' :
print ( 'Error: only SVG format supported.' )
exit(1)
output=[]
inputfile = sys.argv[2]
outputfile = sys.argv[4]
with open(inputfile,'r') as f:
jinput = json.load(f)
renderWaveForm(0,jinput,output)
svg_output = convert_to_svg(output)
with open(outputfile,'w') as f:
f.write( svg_output )
+28 -10
View File
@@ -48,17 +48,35 @@ def print_raw(str):
def log(str):
if log.create_file:
compile_log = open(globals.OPTS.output_path +
globals.OPTS.output_name + '.log', "w")
log.create_file = 0
if globals.OPTS.output_name != '':
if log.create_file:
# We may have not yet read the config, so we need to ensure
# it ends with a /
# This is also done in read_config if we change the path
# FIXME: There's actually a bug here. The first few lines
# could be in one log file and after read_config it could be
# in another log file if the path or name changes.
if not globals.OPTS.output_path.endswith('/'):
globals.OPTS.output_path += "/"
compile_log = open(globals.OPTS.output_path +
globals.OPTS.output_name + '.log', "w+")
log.create_file = 0
else:
compile_log = open(globals.OPTS.output_path +
globals.OPTS.output_name + '.log', "a")
if len(log.setup_output) != 0:
for line in log.setup_output:
compile_log.write(line)
log.setup_output = []
compile_log.write(str + '\n')
else:
compile_log = open(globals.OPTS.output_path +
globals.OPTS.output_name + '.log', "a")
compile_log.write(str + '\n')
log.create_file = 1
log.setup_output.append(str + "\n")
# use a static list of strings to store messages until the global paths are set up
log.setup_output = []
log.create_file = 1
def info(lev, str):
@@ -71,5 +89,5 @@ def info(lev, str):
class_name = ""
else:
class_name = mod.__name__
print_raw("[{0}/{1}]: {2}".format(class_name, frm[0].f_code.co_name, str))
print_raw("[{0}/{1}]: {2}".format(class_name,
frm[0].f_code.co_name, str))
@@ -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
View File
@@ -1 +0,0 @@
468eb9a4a038201c2b0004fe6e4ae9b2d37fdd57
+45 -2
View File
@@ -132,6 +132,8 @@ def init_openram(config_file, is_unit_test=True):
from sram_factory import factory
factory.reset()
setup_bitcell()
# Reset the static duplicate name checker for unit tests.
import hierarchy_design
@@ -157,6 +159,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))
+1 -1
View File
@@ -26,7 +26,7 @@ class pbitcell_test(openram_test):
debug.info(2, "Bitcell with 1 of each port: read/write, write, and read")
tx = pbitcell(name="pbc")
self.local_check(tx)
OPTS.num_rw_ports=0
OPTS.num_w_ports=1
OPTS.num_r_ports=1
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
#@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error")
class psram_1bank_2mux_1w_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "bitcell_1w_1r"
OPTS.replica_bitcell="replica_bitcell_1w_1r"
OPTS.num_rw_ports = 0
OPTS.num_w_ports = 1
OPTS.num_r_ports = 1
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.num_words=32
c.words_per_row=2
c.recompute_sizes()
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()
@@ -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";
+6 -5
View File
@@ -13,7 +13,7 @@ class openram_test(unittest.TestCase):
self.reset()
tempgds = OPTS.openram_temp + "temp.gds"
tempgds = "{0}{1}.gds".format(OPTS.openram_temp,w.name)
w.gds_write(tempgds)
import verify
@@ -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.
+3 -3
View File
@@ -30,7 +30,7 @@ num_drc_runs = 0
num_lvs_runs = 0
num_pex_runs = 0
def run_drc(name, gds_name):
def run_drc(name, gds_name, final_verification=False):
"""Run DRC check on a given top-level name which is
implemented in gds_name."""
@@ -93,7 +93,7 @@ def run_drc(name, gds_name):
return errors
def run_lvs(name, gds_name, sp_name):
def run_lvs(name, gds_name, sp_name, final_verification=False):
"""Run LVS check on a given top-level name which is
implemented in gds_name and sp_name. """
@@ -178,7 +178,7 @@ def run_lvs(name, gds_name, sp_name):
return errors
def run_pex(name, gds_name, sp_name, output=None):
def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
"""Run pex on a given top-level name which is
implemented in gds_name and sp_name. """
debug.error("PEX extraction not implemented with Assura.",-1)
+6 -6
View File
@@ -126,9 +126,9 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False):
f.close()
# those lines should be the last 3
results = results[-3:]
geometries = int(re.split("\W+", results[0])[5])
rulechecks = int(re.split("\W+", results[1])[4])
errors = int(re.split("\W+", results[2])[5])
geometries = int(re.split(r'\W+', results[0])[5])
rulechecks = int(re.split(r'\W+', results[1])[4])
errors = int(re.split(r'\W+', results[2])[5])
# always display this summary
if errors > 0:
@@ -227,7 +227,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
incorrect = list(filter(test.search, results))
# Errors begin with "Error:"
test = re.compile("\s+Error:")
test = re.compile(r'\s+Error:')
errors = list(filter(test.search, results))
for e in errors:
debug.error(e.strip("\n"))
@@ -282,7 +282,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
return total_errors
def run_pex(cell_name, gds_name, sp_name, output=None):
def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False):
"""Run pex on a given top-level name which is
implemented in gds_name and sp_name. """
@@ -363,7 +363,7 @@ def correct_port(name, output_file_name, ref_file_name):
pex_file.seek(match_index_start)
rest_text = pex_file.read()
# locate the end of circuit definition line
match = re.search("\* \n", rest_text)
match = re.search(r'\* \n', rest_text)
match_index_end = match.start()
# store the unchanged part of pex file in memory
pex_file.seek(0)
+32 -8
View File
@@ -45,7 +45,10 @@ def write_magic_script(cell_name, gds_name, extract=False, final_verification=Fa
#f.write("load {}_new\n".format(cell_name))
#f.write("cellname rename {0}_new {0}\n".format(cell_name))
#f.write("load {}\n".format(cell_name))
f.write("cellname delete \\(UNNAMED\\)\n")
f.write("writeall force\n")
f.write("select top cell\n")
f.write("expand\n")
f.write("drc check\n")
f.write("drc catchup\n")
f.write("drc count total\n")
@@ -55,14 +58,26 @@ def write_magic_script(cell_name, gds_name, extract=False, final_verification=Fa
else:
pre = ""
if final_verification:
f.write(pre+"extract unique\n")
f.write(pre+"extract\n")
f.write(pre+"ext2spice hierarchy on\n")
f.write(pre+"extract unique all\n".format(cell_name))
f.write(pre+"extract\n".format(cell_name))
#f.write(pre+"ext2spice hierarchy on\n")
#f.write(pre+"ext2spice scale off\n")
# lvs exists in 8.2.79, but be backword compatible for now
#f.write(pre+"ext2spice lvs\n")
f.write(pre+"ext2spice hierarchy on\n")
f.write(pre+"ext2spice format ngspice\n")
f.write(pre+"ext2spice cthresh infinite\n")
f.write(pre+"ext2spice rthresh infinite\n")
f.write(pre+"ext2spice renumber off\n")
f.write(pre+"ext2spice scale off\n")
f.write(pre+"ext2spice blackbox on\n")
f.write(pre+"ext2spice subcircuit top auto\n")
f.write(pre+"ext2spice global off\n")
# Can choose hspice, ngspice, or spice3,
# but they all seem compatible enough.
#f.write(pre+"ext2spice format ngspice\n")
f.write(pre+"ext2spice\n")
f.write(pre+"ext2spice {}\n".format(cell_name))
f.write("quit -noprompt\n")
f.write("EOF\n")
@@ -136,15 +151,20 @@ def run_drc(cell_name, gds_name, extract=True, final_verification=False):
# etc.
try:
f = open(outfile, "r")
except:
debug.error("Unable to retrieve DRC results file. Is magic set up?",1)
except FileNotFoundError:
debug.error("Unable to load DRC results file from {}. Is magic set up?".format(outfile),1)
results = f.readlines()
f.close()
errors=1
# those lines should be the last 3
for line in results:
if "Total DRC errors found:" in line:
errors = int(re.split(": ", line)[1])
break
else:
debug.error("Unable to find the total error line in Magic output.",1)
# always display this summary
if errors > 0:
@@ -185,7 +205,11 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
total_errors = 0
# check the result for these lines in the summary:
f = open(resultsfile, "r")
try:
f = open(resultsfile, "r")
except FileNotFoundError:
debug.error("Unable to load LVS results from {}".format(resultsfile),1)
results = f.readlines()
f.close()
# Look for the results after the final "Subcircuit summary:"
@@ -235,7 +259,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
return total_errors
def run_pex(name, gds_name, sp_name, output=None):
def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
"""Run pex on a given top-level name which is
implemented in gds_name and sp_name. """
+2 -2
View File
@@ -9,7 +9,7 @@ drc_warned = False
lvs_warned = False
pex_warned = False
def run_drc(cell_name, gds_name, extract=False):
def run_drc(cell_name, gds_name, extract=False, final_verification=False):
global drc_warned
if not drc_warned:
debug.warning("DRC unable to run.")
@@ -25,7 +25,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
# Since we warned, return a failing test.
return 1
def run_pex(name, gds_name, sp_name, output=None):
def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
global pex_warned
if not pex_warned:
debug.warning("PEX unable to run.")