mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-24 22:07:43 -04:00
Update code from upstream repository https://github.com/lowRISC/opentitan to revision 5cae0cf1fac783e0d0df8c8597bf65322a696a56 * Allow different assertion "backends" in prim_assert.sv (Rupert Swarbrick) * [prim_prince/doc] Update documentation (Michael Schaffner) * [prim_prince] Add option to instantiate a registers half-way (Michael Schaffner) * [prim_cipher_pkg] Reuse sbox4_8bit to build wider sbox layers (Michael Schaffner) * [dv/prim] add PRESENT testbench (Udi Jonnalagadda) * [uvmdvgen] Scoreboard update. (Srikrishna Iyer) * [flash_ctrl dv] Fix V1 tests (Srikrishna Iyer) * [prim_cipher_pkg] Replicate common subfunctions for other widths (Michael Schaffner) * [prim/present] fix PRESENT decryption bugs (Udi Jonnalagadda) * [prim/present] fix some PRESENT encryption bugs (Udi Jonnalagadda) * [dv] Add get_mem DPI function to Verilator simutil (Stefan Wallentowitz) * [lint/entropy_src] Add the entropy source to the lint regression (Michael Schaffner) * [style-lint] Fix some common style lint warnings (Michael Schaffner) * first set of security checks added to D2 checklist (Scott Johnson) * [fpv/tooling] add FPV class extension in dvsim (Cindy Chen) * [dvsim/lint] Minor fixes for printout issues and result parser status (Michael Schaffner) * [syn] Print detailed messages to .md if publication is disabled (Michael Schaffner) * [prim_util] Do not use $clog2() in Xcelium (Philipp Wagner) * [prim] Update ResetValue parameter in prim_flop_2sync (Timothy Chen) * Modified some command-line arguments for DSim (Aimee Sutton) * [prim_util] Make prim_util a package (Philipp Wagner) * [dv] Move mem checking to scb (Weicai Yang) * [lint] Make PINCONNECTEMPTY Verilator waiver common (Philipp Wagner) * [prim] - Fix generic flash enum reference (Timothy Chen) * [prim_ram_*adv] Mark cfg port as unused (Philipp Wagner) * [prim_fifo_sync] Use vbits() for simpler code (Philipp Wagner) * [prim_flash] Add reset to held_part (Eunchan Kim) * [lint] Add more lint waivers (Philipp Wagner) * [dv] Add random backdoor for csr_hw_reset (Weicai Yang) * [dv] Add set_freq_khz in clk_rst_if (Weicai Yang) * [prim] Close GAPI file handle in primgen (Philipp Wagner) * [fpv/prim_packer] fix CI failure due to index out of bound (Cindy Chen) * [prim_arbiter_*] Propagate parameter changes (Michael Schaffner) * [prim_arbiter_tree] Fix incorrect arbitration behavior (Michael Schaffner) * [prim_arbiter_ppc] Add more FPV fairness checks (Michael Schaffner) * [prim_ram*] Add an assertion that checks wmask consistency (Michael Schaffner) * [memutil] Increase max memory width to 256bit (Tom Roberts) * [flash] - Add flash info page support (Timothy Chen) Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
379 lines
15 KiB
Python
379 lines
15 KiB
Python
# Copyright lowRISC contributors.
|
|
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
r"""
|
|
Utility functions common across dvsim.
|
|
"""
|
|
|
|
import logging as log
|
|
import os
|
|
import re
|
|
import shlex
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
from collections import OrderedDict
|
|
|
|
import hjson
|
|
import mistletoe
|
|
from premailer import transform
|
|
|
|
# For verbose logging
|
|
VERBOSE = 15
|
|
|
|
|
|
# Run a command and get the result. Exit with error if the command did not
|
|
# succeed. This is a simpler version of the run_cmd function below.
|
|
def run_cmd(cmd):
|
|
(status, output) = subprocess.getstatusoutput(cmd)
|
|
if status:
|
|
sys.stderr.write("cmd " + cmd + " returned with status " + str(status))
|
|
sys.exit(status)
|
|
return output
|
|
|
|
|
|
# Run a command with a specified timeout. If the command does not finish before
|
|
# the timeout, then it returns -1. Else it returns the command output. If the
|
|
# command fails, it throws an exception and returns the stderr.
|
|
def run_cmd_with_timeout(cmd, timeout=-1, exit_on_failure=1):
|
|
args = shlex.split(cmd)
|
|
p = subprocess.Popen(args,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT)
|
|
|
|
# If timeout is set, poll for the process to finish until timeout
|
|
result = ""
|
|
status = -1
|
|
if timeout == -1:
|
|
p.wait()
|
|
else:
|
|
start = time.time()
|
|
while time.time() - start < timeout:
|
|
if p.poll() is not None:
|
|
break
|
|
time.sleep(.01)
|
|
|
|
# Capture output and status if cmd exited, else kill it
|
|
if p.poll() is not None:
|
|
result = p.communicate()[0]
|
|
status = p.returncode
|
|
else:
|
|
log.error("cmd \"%s\" timed out!", cmd)
|
|
p.kill()
|
|
|
|
if status != 0:
|
|
log.error("cmd \"%s\" exited with status %d", cmd, status)
|
|
if exit_on_failure == 1:
|
|
sys.exit(status)
|
|
|
|
return (result, status)
|
|
|
|
|
|
# Parse hjson and return a dict
|
|
def parse_hjson(hjson_file):
|
|
hjson_cfg_dict = None
|
|
try:
|
|
log.debug("Parsing %s", hjson_file)
|
|
f = open(hjson_file, 'rU')
|
|
text = f.read()
|
|
hjson_cfg_dict = hjson.loads(text, use_decimal=True)
|
|
f.close()
|
|
except Exception as e:
|
|
log.fatal(
|
|
"Failed to parse \"%s\" possibly due to bad path or syntax error.\n%s",
|
|
hjson_file, e)
|
|
sys.exit(1)
|
|
return hjson_cfg_dict
|
|
|
|
|
|
def subst_wildcards(var, mdict, ignored_wildcards=[], ignore_error=False):
|
|
'''
|
|
If var has wildcards specified within {..}, find and substitute them.
|
|
'''
|
|
def subst(wildcard, mdict):
|
|
if wildcard in mdict.keys():
|
|
return mdict[wildcard]
|
|
else:
|
|
return None
|
|
|
|
if "{eval_cmd}" in var:
|
|
idx = var.find("{eval_cmd}") + 11
|
|
subst_var = subst_wildcards(var[idx:], mdict, ignored_wildcards,
|
|
ignore_error)
|
|
# If var has wildcards that were ignored, then skip running the command
|
|
# for now, assume that it will be handled later.
|
|
match = re.findall(r"{([A-Za-z0-9\_]+)}", subst_var)
|
|
if len(match) == 0:
|
|
var = run_cmd(subst_var)
|
|
else:
|
|
match = re.findall(r"{([A-Za-z0-9\_]+)}", var)
|
|
if len(match) > 0:
|
|
subst_list = {}
|
|
for item in match:
|
|
if item not in ignored_wildcards:
|
|
log.debug("Found wildcard \"%s\" in \"%s\"", item, var)
|
|
found = subst(item, mdict)
|
|
if found is not None:
|
|
if type(found) is list:
|
|
subst_found = []
|
|
for element in found:
|
|
element = subst_wildcards(
|
|
element, mdict, ignored_wildcards,
|
|
ignore_error)
|
|
subst_found.append(element)
|
|
# Expand list into a str since list within list is
|
|
# not supported.
|
|
found = " ".join(subst_found)
|
|
|
|
elif type(found) is str:
|
|
found = subst_wildcards(found, mdict,
|
|
ignored_wildcards,
|
|
ignore_error)
|
|
|
|
elif type(found) is bool:
|
|
found = int(found)
|
|
subst_list[item] = found
|
|
else:
|
|
# Check if the wildcard exists as an environment variable
|
|
env_var = os.environ.get(item)
|
|
if env_var is not None:
|
|
subst_list[item] = env_var
|
|
elif not ignore_error:
|
|
log.error(
|
|
"Substitution for the wildcard \"%s\" not found",
|
|
item)
|
|
sys.exit(1)
|
|
for item in subst_list:
|
|
var = var.replace("{" + item + "}", str(subst_list[item]))
|
|
return var
|
|
|
|
|
|
def find_and_substitute_wildcards(sub_dict,
|
|
full_dict,
|
|
ignored_wildcards=[],
|
|
ignore_error=False):
|
|
'''
|
|
Recursively find key values containing wildcards in sub_dict in full_dict
|
|
and return resolved sub_dict.
|
|
'''
|
|
for key in sub_dict.keys():
|
|
if type(sub_dict[key]) in [dict, OrderedDict]:
|
|
# Recursively call this funciton in sub-dicts
|
|
sub_dict[key] = find_and_substitute_wildcards(
|
|
sub_dict[key], full_dict, ignored_wildcards, ignore_error)
|
|
|
|
elif type(sub_dict[key]) is list:
|
|
sub_dict_key_values = list(sub_dict[key])
|
|
# Loop through the list of key's values and substitute each var
|
|
# in case it contains a wildcard
|
|
for i in range(len(sub_dict_key_values)):
|
|
if type(sub_dict_key_values[i]) in [dict, OrderedDict]:
|
|
# Recursively call this funciton in sub-dicts
|
|
sub_dict_key_values[i] = \
|
|
find_and_substitute_wildcards(sub_dict_key_values[i],
|
|
full_dict, ignored_wildcards, ignore_error)
|
|
|
|
elif type(sub_dict_key_values[i]) is str:
|
|
sub_dict_key_values[i] = subst_wildcards(
|
|
sub_dict_key_values[i], full_dict, ignored_wildcards,
|
|
ignore_error)
|
|
|
|
# Set the substituted key values back
|
|
sub_dict[key] = sub_dict_key_values
|
|
|
|
elif type(sub_dict[key]) is str:
|
|
sub_dict[key] = subst_wildcards(sub_dict[key], full_dict,
|
|
ignored_wildcards, ignore_error)
|
|
return sub_dict
|
|
|
|
|
|
def md_results_to_html(title, css_file, md_text):
|
|
'''Convert results in md format to html. Add a little bit of styling.
|
|
'''
|
|
html_text = "<!DOCTYPE html>\n"
|
|
html_text += "<html lang=\"en\">\n"
|
|
html_text += "<head>\n"
|
|
if title != "":
|
|
html_text += " <title>{}</title>\n".format(title)
|
|
html_text += "</head>\n"
|
|
html_text += "<body>\n"
|
|
html_text += "<div class=\"results\">\n"
|
|
html_text += mistletoe.markdown(md_text)
|
|
html_text += "</div>\n"
|
|
html_text += "</body>\n"
|
|
html_text += "</html>\n"
|
|
html_text = htmc_color_pc_cells(html_text)
|
|
# this function converts css style to inline html style
|
|
html_text = transform(html_text,
|
|
external_styles=css_file,
|
|
cssutils_logging_level=log.ERROR)
|
|
return html_text
|
|
|
|
|
|
def htmc_color_pc_cells(text):
|
|
'''This function finds cells in a html table that contain numerical values
|
|
(and a few known strings) followed by a single space and an identifier.
|
|
Depending on the identifier, it shades the cell in a specific way. A set of
|
|
12 color palettes for setting those shades are encoded in ./style.css.
|
|
These are 'cna' (grey), 'c0' (red), 'c1' ... 'c10' (green). The shade 'cna'
|
|
is used for items that are maked as 'not applicable'. The shades 'c1' to
|
|
'c9' form a gradient from red to lime-green to indicate 'levels of
|
|
completeness'. 'cna' is used for greying out a box for 'not applicable'
|
|
items, 'c0' is for items that are considered risky (or not yet started) and
|
|
'c10' for items that have completed successfully, or that are
|
|
'in good standing'.
|
|
|
|
These are the supported identifiers: %, %u, G, B, E, W, EN, WN.
|
|
The shading behavior for these is described below.
|
|
|
|
%: Coloured percentage, where the number in front of the '%' sign is mapped
|
|
to a color for the cell ranging from red ('c0') to green ('c10').
|
|
%u: Uncoloured percentage, where no markup is applied and '%u' is replaced
|
|
with '%' in the output.
|
|
G: This stands for 'Good' and results in a green cell.
|
|
B: This stands for 'Bad' and results in a red cell.
|
|
E: This stands for 'Errors' and the cell is colored with red if the number
|
|
in front of the indicator is larger than 0. Otherwise the cell is
|
|
colored with green.
|
|
W: This stands for 'Warnings' and the cell is colored with yellow ('c6')
|
|
if the number in front of the indicator is larger than 0. Otherwise
|
|
the cell is colored with green.
|
|
EN: This stands for 'Errors Negative', which behaves the same as 'E' except
|
|
that the cell is colored red if the number in front of the indicator is
|
|
negative.
|
|
WN: This stands for 'Warnings Negative', which behaves the same as 'W'
|
|
except that the cell is colored yellow if the number in front of the
|
|
indicator is negative.
|
|
|
|
N/A items can have any of the following indicators and need not be
|
|
preceeded with a numerical value:
|
|
|
|
'--', 'NA', 'N.A.', 'N.A', 'N/A', 'na', 'n.a.', 'n.a', 'n/a'
|
|
|
|
'''
|
|
|
|
# Replace <td> with <td class="color-class"> based on the fp
|
|
# value. "color-classes" are listed in ./style.css as follows: "cna"
|
|
# for NA value, "c0" to "c10" for fp value falling between 0.00-9.99,
|
|
# 10.00-19.99 ... 90.00-99.99, 100.0 respetively.
|
|
def color_cell(cell, cclass, indicator="%"):
|
|
op = cell.replace("<td", "<td class=\"" + cclass + "\"")
|
|
# Remove the indicator.
|
|
op = re.sub(r"\s*" + indicator + r"\s*", "", op)
|
|
return op
|
|
|
|
# List of 'not applicable' identifiers.
|
|
na_list = ['--', 'NA', 'N.A.', 'N.A', 'N/A', 'na', 'n.a.', 'n.a', 'n/a']
|
|
na_list_patterns = '|'.join(na_list)
|
|
|
|
# List of floating point patterns: '0', '0.0' & '.0'
|
|
fp_patterns = r"[\+\-]?\d+\.?\d*"
|
|
|
|
patterns = fp_patterns + '|' + na_list_patterns
|
|
indicators = "%|%u|G|B|E|W|EN|WN"
|
|
match = re.findall(r"(<td.*>\s*(" + patterns + r")\s+(" + indicators + r")\s*</td>)", text)
|
|
if len(match) > 0:
|
|
subst_list = {}
|
|
fp_nums = []
|
|
for item in match:
|
|
# item is a tuple - first is the full string indicating the table
|
|
# cell which we want to replace, second is the floating point value.
|
|
cell = item[0]
|
|
fp_num = item[1]
|
|
indicator = item[2]
|
|
# Skip if fp_num is already processed.
|
|
if (fp_num, indicator) in fp_nums:
|
|
continue
|
|
fp_nums.append((fp_num, indicator))
|
|
if fp_num in na_list:
|
|
subst = color_cell(cell, "cna", indicator)
|
|
else:
|
|
# Item is a fp num.
|
|
try:
|
|
fp = float(fp_num)
|
|
except ValueError:
|
|
log.error("Percentage item \"%s\" in cell \"%s\" is not an "
|
|
"integer or a floating point number", fp_num, cell)
|
|
continue
|
|
# Percentage, colored.
|
|
if indicator == "%":
|
|
if fp >= 0.0 and fp < 10.0:
|
|
subst = color_cell(cell, "c0")
|
|
elif fp >= 10.0 and fp < 20.0:
|
|
subst = color_cell(cell, "c1")
|
|
elif fp >= 20.0 and fp < 30.0:
|
|
subst = color_cell(cell, "c2")
|
|
elif fp >= 30.0 and fp < 40.0:
|
|
subst = color_cell(cell, "c3")
|
|
elif fp >= 40.0 and fp < 50.0:
|
|
subst = color_cell(cell, "c4")
|
|
elif fp >= 50.0 and fp < 60.0:
|
|
subst = color_cell(cell, "c5")
|
|
elif fp >= 60.0 and fp < 70.0:
|
|
subst = color_cell(cell, "c6")
|
|
elif fp >= 70.0 and fp < 80.0:
|
|
subst = color_cell(cell, "c7")
|
|
elif fp >= 80.0 and fp < 90.0:
|
|
subst = color_cell(cell, "c8")
|
|
elif fp >= 90.0 and fp < 100.0:
|
|
subst = color_cell(cell, "c9")
|
|
elif fp >= 100.0:
|
|
subst = color_cell(cell, "c10")
|
|
# Percentage, uncolored.
|
|
elif indicator == "%u":
|
|
subst = cell.replace("%u", "%")
|
|
# Good: green
|
|
elif indicator == "G":
|
|
subst = color_cell(cell, "c10", indicator)
|
|
# Bad: red
|
|
elif indicator == "B":
|
|
subst = color_cell(cell, "c0", indicator)
|
|
# Bad if positive: red for errors, yellow for warnings,
|
|
# otherwise green.
|
|
elif indicator in ["E", "W"]:
|
|
if fp <= 0:
|
|
subst = color_cell(cell, "c10", indicator)
|
|
elif indicator == "W":
|
|
subst = color_cell(cell, "c6", indicator)
|
|
elif indicator == "E":
|
|
subst = color_cell(cell, "c0", indicator)
|
|
# Bad if negative: red for errors, yellow for warnings,
|
|
# otherwise green.
|
|
elif indicator in ["EN", "WN"]:
|
|
if fp >= 0:
|
|
subst = color_cell(cell, "c10", indicator)
|
|
elif indicator == "WN":
|
|
subst = color_cell(cell, "c6", indicator)
|
|
elif indicator == "EN":
|
|
subst = color_cell(cell, "c0", indicator)
|
|
subst_list[cell] = subst
|
|
for item in subst_list:
|
|
text = text.replace(item, subst_list[item])
|
|
return text
|
|
|
|
|
|
def print_msg_list(msg_list_title, msg_list, max_msg_count=-1):
|
|
'''This function prints a list of messages to Markdown.
|
|
|
|
The argument msg_list_title contains a string for the list title, whereas
|
|
the msg_list argument contains the actual list of message strings.
|
|
max_msg_count limits the number of messages to be printed (set to negative
|
|
number to print all messages).
|
|
|
|
Example:
|
|
|
|
print_msg_list("### Tool Warnings", ["Message A", "Message B"], 10)
|
|
'''
|
|
md_results = ""
|
|
if msg_list:
|
|
md_results += msg_list_title + "\n"
|
|
md_results += "```\n"
|
|
for k, msg in enumerate(msg_list):
|
|
if k <= max_msg_count or max_msg_count < 0:
|
|
md_results += msg + "\n\n"
|
|
else:
|
|
md_results += "Note: %d more messages have been suppressed (max_msg_count = %d) \n\n" % (
|
|
len(msg_list) - max_msg_count, max_msg_count)
|
|
break
|
|
md_results += "```\n"
|
|
return md_results
|