2015-09-27 10:54:19 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
|
|
import getopt, sys, re
|
|
|
|
|
|
2015-09-28 11:54:13 +02:00
|
|
|
ignore_cells = set([
|
2016-02-01 23:32:03 +01:00
|
|
|
"ADTTRIBUF", "DL", "GIOBUG", "LUT_MUX", "MUX4",
|
2015-09-28 11:54:13 +02:00
|
|
|
"PLL40_2_FEEDBACK_PATH_DELAY", "PLL40_2_FEEDBACK_PATH_EXTERNAL",
|
|
|
|
|
"PLL40_2_FEEDBACK_PATH_PHASE_AND_DELAY", "PLL40_2_FEEDBACK_PATH_SIMPLE",
|
|
|
|
|
"PLL40_2F_FEEDBACK_PATH_DELAY", "PLL40_2F_FEEDBACK_PATH_EXTERNAL",
|
|
|
|
|
"PLL40_2F_FEEDBACK_PATH_PHASE_AND_DELAY", "PLL40_2F_FEEDBACK_PATH_SIMPLE",
|
|
|
|
|
"PLL40_FEEDBACK_PATH_DELAY", "PLL40_FEEDBACK_PATH_EXTERNAL",
|
|
|
|
|
"PLL40_FEEDBACK_PATH_PHASE_AND_DELAY", "PLL40_FEEDBACK_PATH_SIMPLE",
|
|
|
|
|
"PRE_IO_PIN_TYPE", "sync_clk_enable", "TRIBUF"
|
|
|
|
|
])
|
|
|
|
|
|
2015-09-27 10:54:19 +02:00
|
|
|
database = dict()
|
|
|
|
|
sdf_inputs = list()
|
|
|
|
|
txt_inputs = list()
|
2015-09-27 13:15:52 +02:00
|
|
|
output_mode = "txt"
|
|
|
|
|
label = "unknown"
|
|
|
|
|
edgefile = None
|
2015-09-27 10:54:19 +02:00
|
|
|
|
|
|
|
|
def usage():
|
|
|
|
|
print("""
|
|
|
|
|
Usage: python3 timings.py [options] [sdf_file..]
|
|
|
|
|
|
|
|
|
|
-t filename
|
|
|
|
|
read TXT file
|
|
|
|
|
|
2015-09-27 13:15:52 +02:00
|
|
|
-l label
|
|
|
|
|
label for HTML file title
|
|
|
|
|
|
|
|
|
|
-h edgefile
|
|
|
|
|
output HTML, use specified edge file
|
|
|
|
|
|
2015-09-27 10:54:19 +02:00
|
|
|
-s
|
|
|
|
|
output SDF (not TXT) format
|
|
|
|
|
""")
|
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
2015-09-27 13:15:52 +02:00
|
|
|
opts, args = getopt.getopt(sys.argv[1:], "t:l:h:s")
|
2015-09-27 10:54:19 +02:00
|
|
|
except:
|
|
|
|
|
usage()
|
|
|
|
|
|
|
|
|
|
for o, a in opts:
|
|
|
|
|
if o == "-t":
|
|
|
|
|
txt_inputs.append(a)
|
2015-09-27 13:15:52 +02:00
|
|
|
elif o == "-l":
|
|
|
|
|
label = a
|
|
|
|
|
elif o == "-h":
|
|
|
|
|
output_mode = "html"
|
|
|
|
|
edgefile = a
|
2015-09-27 10:54:19 +02:00
|
|
|
elif o == "-s":
|
2015-09-27 13:15:52 +02:00
|
|
|
output_mode = "sdf"
|
2015-09-27 10:54:19 +02:00
|
|
|
else:
|
|
|
|
|
usage()
|
|
|
|
|
|
|
|
|
|
sdf_inputs += args
|
|
|
|
|
|
|
|
|
|
|
2015-09-27 13:15:52 +02:00
|
|
|
convert = lambda text: int(text) if text.isdigit() else text.lower()
|
|
|
|
|
alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
|
|
|
|
|
alphanum_key_list = lambda l: [len(l)] + [ alphanum_key(s) for s in l ]
|
|
|
|
|
|
|
|
|
|
|
2015-09-27 10:54:19 +02:00
|
|
|
def skip_whitespace(text, cursor):
|
|
|
|
|
while cursor < len(text) and text[cursor] in [" ", "\t", "\r", "\n"]:
|
|
|
|
|
cursor += 1
|
|
|
|
|
return cursor
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_sdf(text, cursor):
|
|
|
|
|
cursor = skip_whitespace(text, cursor)
|
|
|
|
|
|
|
|
|
|
if cursor < len(text) and text[cursor] == "(":
|
|
|
|
|
expr = []
|
|
|
|
|
cursor += 1
|
|
|
|
|
while cursor < len(text) and text[cursor] != ")":
|
|
|
|
|
child, cursor = parse_sdf(text, cursor)
|
|
|
|
|
expr.append(child)
|
|
|
|
|
cursor = skip_whitespace(text, cursor)
|
|
|
|
|
return expr, cursor+1
|
|
|
|
|
|
|
|
|
|
if cursor < len(text) and text[cursor] == '"':
|
|
|
|
|
expr = '"'
|
|
|
|
|
cursor += 1
|
|
|
|
|
while cursor < len(text) and text[cursor] != '"':
|
|
|
|
|
expr += text[cursor]
|
|
|
|
|
cursor += 1
|
|
|
|
|
return expr + '"', cursor+1
|
|
|
|
|
|
|
|
|
|
expr = ""
|
|
|
|
|
while cursor < len(text) and text[cursor] not in [" ", "\t", "\r", "\n", "(", ")"]:
|
|
|
|
|
expr += text[cursor]
|
|
|
|
|
cursor += 1
|
|
|
|
|
return expr, cursor
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def sdf_to_string(expr):
|
|
|
|
|
if type(expr) is list:
|
|
|
|
|
tokens = []
|
|
|
|
|
tokens.append("(")
|
|
|
|
|
first_child = True
|
|
|
|
|
for child in expr:
|
|
|
|
|
if not first_child:
|
|
|
|
|
tokens.append(" ")
|
|
|
|
|
tokens.append(sdf_to_string(child))
|
|
|
|
|
first_child = False
|
|
|
|
|
tokens.append(")")
|
|
|
|
|
return "".join(tokens)
|
|
|
|
|
else:
|
|
|
|
|
return expr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def dump_sdf(expr, indent=""):
|
|
|
|
|
if type(expr) is list:
|
|
|
|
|
if len(expr) > 0 and expr[0] in ["IOPATH", "SETUP", "HOLD", "CELLTYPE", "INSTANCE", "SDFVERSION",
|
|
|
|
|
"DESIGN", "DATE", "VENDOR", "DIVIDER", "TIMESCALE", "RECOVERY", "REMOVAL"]:
|
|
|
|
|
print(indent + sdf_to_string(expr))
|
|
|
|
|
else:
|
|
|
|
|
print("%s(%s" % (indent, expr[0] if len(expr) > 0 else ""))
|
|
|
|
|
for child in expr[1:]:
|
|
|
|
|
dump_sdf(child, indent + " ")
|
|
|
|
|
print("%s)" % indent)
|
|
|
|
|
else:
|
|
|
|
|
print("%s%s" % (indent, expr))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generalize_instances(expr):
|
|
|
|
|
if type(expr) is list:
|
|
|
|
|
if len(expr) == 2 and expr[0] == "INSTANCE":
|
|
|
|
|
expr[1] = "*"
|
|
|
|
|
for child in expr:
|
|
|
|
|
generalize_instances(child)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def list_to_tuple(expr):
|
|
|
|
|
if type(expr) is list:
|
|
|
|
|
tup = []
|
|
|
|
|
for child in expr:
|
|
|
|
|
tup.append(list_to_tuple(child))
|
|
|
|
|
return tuple(tup)
|
|
|
|
|
return expr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def uniquify_cells(expr):
|
|
|
|
|
cache = set()
|
|
|
|
|
filtered_expr = []
|
|
|
|
|
|
|
|
|
|
for child in expr:
|
|
|
|
|
t = list_to_tuple(child)
|
|
|
|
|
if t not in cache:
|
|
|
|
|
filtered_expr.append(child)
|
|
|
|
|
cache.add(t)
|
|
|
|
|
|
|
|
|
|
return filtered_expr
|
|
|
|
|
|
|
|
|
|
|
2015-09-27 13:15:52 +02:00
|
|
|
def rewrite_celltype(celltype):
|
|
|
|
|
if celltype.startswith("PRE_IO_PIN_TYPE_"):
|
|
|
|
|
celltype = "PRE_IO_PIN_TYPE"
|
|
|
|
|
|
|
|
|
|
if celltype.startswith("Span4Mux"):
|
|
|
|
|
if celltype == "Span4Mux":
|
|
|
|
|
celltype = "Span4Mux_v4"
|
|
|
|
|
elif celltype == "Span4Mux_v":
|
|
|
|
|
celltype = "Span4Mux_v4"
|
|
|
|
|
elif celltype == "Span4Mux_h":
|
|
|
|
|
celltype = "Span4Mux_h4"
|
|
|
|
|
else:
|
|
|
|
|
match = re.match("Span4Mux_s(.*)_(h|v)", celltype)
|
|
|
|
|
if match:
|
|
|
|
|
celltype = "Span4Mux_%c%d" % (match.group(2), int(match.group(1)))
|
|
|
|
|
|
|
|
|
|
if celltype.startswith("Span12Mux"):
|
|
|
|
|
if celltype == "Span12Mux":
|
|
|
|
|
celltype = "Span12Mux_v12"
|
|
|
|
|
elif celltype == "Span12Mux_v":
|
|
|
|
|
celltype = "Span12Mux_v12"
|
|
|
|
|
elif celltype == "Span12Mux_h":
|
|
|
|
|
celltype = "Span12Mux_h12"
|
|
|
|
|
else:
|
|
|
|
|
match = re.match("Span12Mux_s(.*)_(h|v)", celltype)
|
|
|
|
|
if match:
|
|
|
|
|
celltype = "Span12Mux_%c%d" % (match.group(2), int(match.group(1)))
|
|
|
|
|
|
|
|
|
|
return celltype
|
|
|
|
|
|
|
|
|
|
|
2015-09-27 14:54:57 +02:00
|
|
|
def add_entry(celltype, entry):
|
|
|
|
|
entry = sdf_to_string(entry)
|
|
|
|
|
entry = entry.replace("(posedge ", "posedge:")
|
|
|
|
|
entry = entry.replace("(negedge ", "negedge:")
|
|
|
|
|
entry = entry.replace("(", "")
|
|
|
|
|
entry = entry.replace(")", "")
|
|
|
|
|
entry = entry.split()
|
|
|
|
|
if celltype.count("FEEDBACK") == 0 and entry[0] == "IOPATH" and entry[2].startswith("PLLOUT"):
|
|
|
|
|
entry[3] = "*:*:*"
|
|
|
|
|
entry[4] = "*:*:*"
|
|
|
|
|
database[celltype].add(tuple(entry))
|
|
|
|
|
|
|
|
|
|
|
2015-09-27 10:54:19 +02:00
|
|
|
###########################################
|
|
|
|
|
# Parse SDF input files
|
|
|
|
|
|
|
|
|
|
for filename in sdf_inputs:
|
|
|
|
|
print("### reading SDF file %s" % filename, file=sys.stderr)
|
|
|
|
|
|
|
|
|
|
intext = []
|
|
|
|
|
with open(filename, "r") as f:
|
|
|
|
|
for line in f:
|
|
|
|
|
line = re.sub("//.*", "", line)
|
|
|
|
|
intext.append(line)
|
|
|
|
|
|
|
|
|
|
sdfdata, _ = parse_sdf("".join(intext), 0)
|
|
|
|
|
generalize_instances(sdfdata)
|
|
|
|
|
sdfdata = uniquify_cells(sdfdata)
|
|
|
|
|
|
|
|
|
|
for cell in sdfdata:
|
|
|
|
|
if cell[0] != "CELL":
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
celltype = None
|
|
|
|
|
|
|
|
|
|
for stmt in cell:
|
|
|
|
|
if stmt[0] == "CELLTYPE":
|
2015-09-27 13:15:52 +02:00
|
|
|
celltype = rewrite_celltype(stmt[1][1:-1])
|
2018-01-22 17:34:51 +01:00
|
|
|
if celltype == "SB_MAC16":
|
|
|
|
|
try:
|
|
|
|
|
with open(filename.replace(".sdf", ".dsp"), "r") as dspf:
|
|
|
|
|
celltype = dspf.readline().strip()
|
|
|
|
|
except:
|
|
|
|
|
break
|
2015-09-27 10:54:19 +02:00
|
|
|
database.setdefault(celltype, set())
|
|
|
|
|
|
|
|
|
|
if stmt[0] == "DELAY":
|
|
|
|
|
assert stmt[1][0] == "ABSOLUTE"
|
|
|
|
|
for entry in stmt[1][1:]:
|
|
|
|
|
assert entry[0] == "IOPATH"
|
2015-09-27 14:54:57 +02:00
|
|
|
add_entry(celltype, entry)
|
2015-09-27 10:54:19 +02:00
|
|
|
|
|
|
|
|
if stmt[0] == "TIMINGCHECK":
|
|
|
|
|
for entry in stmt[1:]:
|
2015-09-27 14:54:57 +02:00
|
|
|
add_entry(celltype, entry)
|
2015-09-27 10:54:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
###########################################
|
|
|
|
|
# Parse TXT input files
|
|
|
|
|
|
|
|
|
|
for filename in txt_inputs:
|
|
|
|
|
print("### reading TXT file %s" % filename, file=sys.stderr)
|
|
|
|
|
with open(filename, "r") as f:
|
|
|
|
|
celltype = None
|
|
|
|
|
for line in f:
|
|
|
|
|
line = line.split()
|
2015-09-27 11:54:43 +02:00
|
|
|
if len(line) > 1:
|
2015-09-27 10:54:19 +02:00
|
|
|
if line[0] == "CELL":
|
2015-09-27 13:15:52 +02:00
|
|
|
celltype = rewrite_celltype(line[1])
|
2015-09-27 11:54:43 +02:00
|
|
|
database.setdefault(celltype, set())
|
2015-09-27 10:54:19 +02:00
|
|
|
else:
|
2015-09-27 14:54:57 +02:00
|
|
|
add_entry(celltype, line)
|
2015-09-27 10:54:19 +02:00
|
|
|
|
|
|
|
|
|
2015-09-28 11:54:13 +02:00
|
|
|
###########################################
|
|
|
|
|
# Filter database
|
|
|
|
|
|
|
|
|
|
for celltype in ignore_cells:
|
2016-01-17 11:51:04 +01:00
|
|
|
if celltype in database:
|
|
|
|
|
del database[celltype]
|
2015-09-28 11:54:13 +02:00
|
|
|
|
|
|
|
|
|
2015-09-27 10:54:19 +02:00
|
|
|
###########################################
|
|
|
|
|
# Create SDF output
|
|
|
|
|
|
2015-09-27 13:15:52 +02:00
|
|
|
if output_mode == "sdf":
|
2015-09-27 10:54:19 +02:00
|
|
|
print("(DELAYFILE")
|
|
|
|
|
print(" (SDFVERSION \"3.0\")")
|
|
|
|
|
print(" (TIMESCALE 1ps)")
|
|
|
|
|
|
|
|
|
|
def format_entry(entry):
|
|
|
|
|
text = []
|
|
|
|
|
for i in range(len(entry)):
|
|
|
|
|
if i > 2:
|
|
|
|
|
text.append("(%s)" % entry[i])
|
|
|
|
|
elif entry[i].startswith("posedge:"):
|
|
|
|
|
text.append("(posedge %s)" % entry[i].replace("posedge:", ""))
|
|
|
|
|
elif entry[i].startswith("negedge:"):
|
|
|
|
|
text.append("(negedge %s)" % entry[i].replace("negedge:", ""))
|
|
|
|
|
else:
|
|
|
|
|
text.append(entry[i])
|
|
|
|
|
return " ".join(text)
|
|
|
|
|
|
|
|
|
|
for celltype in sorted(database, key=alphanum_key):
|
|
|
|
|
print(" (CELL")
|
|
|
|
|
print(" (CELLTYPE \"%s\")" % celltype)
|
|
|
|
|
print(" (INSTANCE *)")
|
|
|
|
|
|
|
|
|
|
delay_abs_entries = list()
|
|
|
|
|
timingcheck_entries = list()
|
|
|
|
|
for entry in sorted(database[celltype], key=alphanum_key_list):
|
|
|
|
|
if entry[0] == "IOPATH":
|
|
|
|
|
delay_abs_entries.append(entry)
|
|
|
|
|
else:
|
|
|
|
|
timingcheck_entries.append(entry)
|
|
|
|
|
|
|
|
|
|
if len(delay_abs_entries) != 0:
|
|
|
|
|
print(" (DELAY")
|
|
|
|
|
print(" (ABSOLUTE")
|
|
|
|
|
for entry in delay_abs_entries:
|
|
|
|
|
print(" (%s)" % format_entry(entry))
|
|
|
|
|
print(" )")
|
|
|
|
|
print(" )")
|
|
|
|
|
|
|
|
|
|
if len(timingcheck_entries) != 0:
|
|
|
|
|
print(" (TIMINGCHECK")
|
|
|
|
|
for entry in timingcheck_entries:
|
|
|
|
|
print(" (%s)" % format_entry(entry))
|
|
|
|
|
print(" )")
|
|
|
|
|
|
|
|
|
|
print(" )")
|
|
|
|
|
|
|
|
|
|
print(")")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
###########################################
|
|
|
|
|
# Create TXT output
|
|
|
|
|
|
2015-09-27 13:15:52 +02:00
|
|
|
if output_mode == "txt":
|
2015-09-27 10:54:19 +02:00
|
|
|
for celltype in sorted(database, key=alphanum_key):
|
|
|
|
|
print("CELL %s" % celltype)
|
|
|
|
|
entries_lens = list()
|
|
|
|
|
for entry in database[celltype]:
|
|
|
|
|
for i in range(len(entry)):
|
|
|
|
|
if i < len(entries_lens):
|
|
|
|
|
entries_lens[i] = max(entries_lens[i], len(entry[i]))
|
|
|
|
|
else:
|
|
|
|
|
entries_lens.append(len(entry[i]))
|
|
|
|
|
for entry in sorted(database[celltype], key=alphanum_key_list):
|
|
|
|
|
for i in range(len(entry)):
|
|
|
|
|
print("%s%-*s" % (" " if i != 0 else "", entries_lens[i] if i != len(entry)-1 else 0, entry[i]), end="")
|
|
|
|
|
print()
|
|
|
|
|
print()
|
|
|
|
|
|
2015-09-27 13:15:52 +02:00
|
|
|
|
|
|
|
|
###########################################
|
|
|
|
|
# Create HTML output
|
|
|
|
|
|
|
|
|
|
if output_mode == "html":
|
|
|
|
|
print("<h1>IceStorm Timing Model: %s</h1>" % label)
|
|
|
|
|
|
2015-09-27 14:54:57 +02:00
|
|
|
edge_celltypes = set()
|
2015-09-27 13:15:52 +02:00
|
|
|
source_by_sink_desc = dict()
|
|
|
|
|
sink_by_source_desc = dict()
|
|
|
|
|
|
|
|
|
|
with open(edgefile, "r") as f:
|
|
|
|
|
for line in f:
|
|
|
|
|
source, sink = line.split()
|
|
|
|
|
source_cell, source_port = source.split(".")
|
|
|
|
|
sink_cell, sink_port = sink.split(".")
|
2015-09-27 14:54:57 +02:00
|
|
|
|
2015-09-27 13:15:52 +02:00
|
|
|
source_cell = rewrite_celltype(source_cell)
|
|
|
|
|
sink_cell = rewrite_celltype(sink_cell)
|
2015-09-27 14:54:57 +02:00
|
|
|
|
2015-09-28 11:54:13 +02:00
|
|
|
assert source_cell not in ignore_cells
|
|
|
|
|
assert sink_cell not in ignore_cells
|
|
|
|
|
|
2015-09-27 14:54:57 +02:00
|
|
|
if source_cell in ["GND", "VCC"]:
|
|
|
|
|
continue
|
|
|
|
|
|
2015-09-27 13:15:52 +02:00
|
|
|
source_by_sink_desc.setdefault(sink_cell, set())
|
|
|
|
|
sink_by_source_desc.setdefault(source_cell, set())
|
2015-09-27 14:54:57 +02:00
|
|
|
|
2015-09-27 13:15:52 +02:00
|
|
|
source_by_sink_desc[sink_cell].add((sink_port, source_cell, source_port))
|
|
|
|
|
sink_by_source_desc[source_cell].add((source_port, sink_cell, sink_port))
|
|
|
|
|
|
2015-09-27 14:54:57 +02:00
|
|
|
edge_celltypes.add(source_cell)
|
|
|
|
|
edge_celltypes.add(sink_cell)
|
|
|
|
|
|
|
|
|
|
print("<div style=\"-webkit-column-count: 3; -moz-column-count: 3; column-count: 3;\"><ul style=\"margin:0\">")
|
2015-09-27 13:15:52 +02:00
|
|
|
for celltype in sorted(database, key=alphanum_key):
|
2015-09-27 14:54:57 +02:00
|
|
|
if celltype not in edge_celltypes:
|
|
|
|
|
print("### ignoring unused cell type %s" % celltype, file=sys.stderr)
|
|
|
|
|
else:
|
|
|
|
|
print("<li><a href=\"#%s\">%s</a></li>" % (celltype, celltype))
|
|
|
|
|
print("</ul></div>")
|
|
|
|
|
|
|
|
|
|
for celltype in sorted(database, key=alphanum_key):
|
|
|
|
|
if celltype not in edge_celltypes:
|
|
|
|
|
continue
|
|
|
|
|
|
2015-09-27 13:15:52 +02:00
|
|
|
print("<p><hr></p>")
|
|
|
|
|
print("<h2><a name=\"%s\">%s</a></h2>" % (celltype, celltype))
|
|
|
|
|
|
|
|
|
|
if celltype in source_by_sink_desc:
|
|
|
|
|
print("<h3>Sources driving this cell type:</h3>")
|
|
|
|
|
print("<table width=\"600\" border>")
|
|
|
|
|
print("<tr><th>Input Port</th><th>Source Cell</th><th>Source Port</th></tr>")
|
|
|
|
|
for entry in sorted(source_by_sink_desc[celltype], key=alphanum_key_list):
|
|
|
|
|
print("<tr><td>%s</td><td><a href=\"#%s\">%s</a></td><td>%s</td></tr>" % (entry[0], entry[1], entry[1], entry[2]))
|
|
|
|
|
print("</table>")
|
|
|
|
|
|
|
|
|
|
if celltype in sink_by_source_desc:
|
|
|
|
|
print("<h3>Sinks driven by this cell type:</h3>")
|
|
|
|
|
print("<table width=\"600\" border>")
|
|
|
|
|
print("<tr><th>Output Port</th><th>Sink Cell</th><th>Sink Port</th></tr>")
|
|
|
|
|
for entry in sorted(sink_by_source_desc[celltype], key=alphanum_key_list):
|
|
|
|
|
print("<tr><td>%s</td><td><a href=\"#%s\">%s</a></td><td>%s</td></tr>" % (entry[0], entry[1], entry[1], entry[2]))
|
|
|
|
|
print("</table>")
|
|
|
|
|
|
|
|
|
|
delay_abs_entries = list()
|
|
|
|
|
timingcheck_entries = list()
|
|
|
|
|
for entry in sorted(database[celltype], key=alphanum_key_list):
|
|
|
|
|
if entry[0] == "IOPATH":
|
|
|
|
|
delay_abs_entries.append(entry)
|
|
|
|
|
else:
|
|
|
|
|
timingcheck_entries.append(entry)
|
|
|
|
|
|
|
|
|
|
if len(delay_abs_entries) > 0:
|
|
|
|
|
print("<h3>Propagation Delays:</h3>")
|
|
|
|
|
print("<table width=\"800\" border>")
|
|
|
|
|
print("<tr><th rowspan=\"2\">Input Port</th><th rowspan=\"2\">Output Port</th>")
|
|
|
|
|
print("<th colspan=\"3\">Low-High Transition</th><th colspan=\"3\">High-Low Transition</th></tr>")
|
|
|
|
|
print("<tr><th>Min</th><th>Typ</th><th>Max</th><th>Min</th><th>Typ</th><th>Max</th></tr>")
|
|
|
|
|
for entry in delay_abs_entries:
|
|
|
|
|
print("<tr><td>%s</td><td>%s</td>" % (entry[1].replace(":", " "), entry[2].replace(":", " ")), end="")
|
|
|
|
|
print("<td>%s</td><td>%s</td><td>%s</td>" % tuple(entry[3].split(":")), end="")
|
|
|
|
|
print("<td>%s</td><td>%s</td><td>%s</td>" % tuple(entry[4].split(":")), end="")
|
|
|
|
|
print("</tr>")
|
|
|
|
|
print("</table>")
|
|
|
|
|
|
|
|
|
|
if len(timingcheck_entries) > 0:
|
|
|
|
|
print("<h3>Timing Checks:</h3>")
|
|
|
|
|
print("<table width=\"800\" border>")
|
|
|
|
|
print("<tr><th rowspan=\"2\">Check Type</th><th rowspan=\"2\">Input Port</th>")
|
|
|
|
|
print("<th rowspan=\"2\">Output Port</th><th colspan=\"3\">Timing</th></tr>")
|
|
|
|
|
print("<tr><th>Min</th><th>Typ</th><th>Max</th></tr>")
|
|
|
|
|
for entry in timingcheck_entries:
|
|
|
|
|
print("<tr><td>%s</td><td>%s</td><td>%s</td>" % (entry[0], entry[1].replace(":", " "), entry[2].replace(":", " ")), end="")
|
|
|
|
|
print("<td>%s</td><td>%s</td><td>%s</td>" % tuple(entry[3].split(":")), end="")
|
|
|
|
|
print("</tr>")
|
|
|
|
|
print("</table>")
|