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>
This commit is contained in:
Rupert Swarbrick 2020-07-01 10:07:57 +01:00 committed by Philipp Wagner
parent 1aa4d5a32b
commit f35a407906
76 changed files with 2617 additions and 692 deletions

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/lowRISC/opentitan
rev: c91b50f357a76dae2ada104e397f6a91f72a33da
rev: 5cae0cf1fac783e0d0df8c8597bf65322a696a56
}
}

View file

@ -69,13 +69,18 @@ interface clk_rst_if #(
if (wait_posedge && (rst_n === 1'b0)) @(posedge rst_n);
endtask
// set the clk frequency in mhz
function automatic void set_freq_mhz(real freq_mhz);
clk_freq_mhz = freq_mhz;
// set the clk frequency in khz
function automatic void set_freq_khz(int freq_khz);
clk_freq_mhz = $itor(freq_khz) / 1000;
clk_period_ps = 1000_000 / clk_freq_mhz;
recompute = 1'b1;
endfunction
// set the clk frequency in mhz
function automatic void set_freq_mhz(int freq_mhz);
set_freq_khz(freq_mhz * 1000);
endfunction
// call this function at t=0 (from tb top) to enable clk and rst_n to be driven
function automatic void set_active(bit drive_clk_val = 1'b1, bit drive_rst_n_val = 1'b1);
time t = $time;
@ -218,7 +223,7 @@ interface clk_rst_if #(
// start driving clk only after the first por reset assertion. The fork/join means that we'll
// wait a whole number of clock periods, which means it's possible for the clock to synchronise
// with the "expected" timestamps.
bit done = 1'b0;
bit done;
fork
begin
wait_for_reset(.wait_posedge(1'b0));

View file

@ -158,6 +158,8 @@ endclass
// checks. It is run as the first step of the CSR HW reset test.
//--------------------------------------------------------------------------------------------------
class csr_write_seq extends csr_base_seq;
static bit test_backdoor_path_done; // only run once
bit en_rand_backdoor_write;
`uvm_object_utils(csr_write_seq)
`uvm_object_new
@ -165,7 +167,20 @@ class csr_write_seq extends csr_base_seq;
virtual task body();
uvm_reg_data_t wdata;
// check all hdl paths are valid
if (!test_backdoor_path_done) begin
uvm_reg_mem_hdl_paths_seq hdl_check_seq;
hdl_check_seq = uvm_reg_mem_hdl_paths_seq::type_id::create("hdl_check_seq");
foreach (models[i]) begin
hdl_check_seq.model = models[i];
hdl_check_seq.start(null);
end
test_backdoor_path_done = 1;
end
foreach (test_csrs[i]) begin
dv_base_reg dv_csr;
bit backdoor;
// check if parent block or register is excluded from write
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrHwResetTest)) begin
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite exclusion",
@ -178,7 +193,14 @@ class csr_write_seq extends csr_base_seq;
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrHwResetTest, m_csr_excl_item);
csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0));
`downcast(dv_csr, test_csrs[i])
if (en_rand_backdoor_write && !dv_csr.get_is_ext_reg()) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(backdoor,
backdoor dist {0 :/ 7, 1 :/ 3};)
end
csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0), .backdoor(backdoor));
end
endtask

View file

@ -63,7 +63,6 @@ package csr_utils_pkg;
function automatic void get_csr_addrs(input uvm_reg_block ral, ref uvm_reg_addr_t csr_addrs[$]);
uvm_reg csrs[$];
ral.get_registers(csrs);
csr_addrs.delete();
foreach (csrs[i]) begin
csr_addrs.push_back(csrs[i].get_address());
end
@ -232,8 +231,7 @@ package csr_utils_pkg;
input bit predict = 0,
input uvm_reg_map map = null);
if (backdoor) begin
csr_poke(csr, value, check);
if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_DIRECT)));
csr_poke(csr, value, check, predict);
end else if (blocking) begin
csr_wr_sub(csr, value, check, path, timeout_ns, map);
if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
@ -286,13 +284,24 @@ package csr_utils_pkg;
// backdoor write csr
task automatic csr_poke(input uvm_reg csr,
input uvm_reg_data_t value,
input uvm_check_e check = UVM_CHECK);
input uvm_check_e check = UVM_CHECK,
input bit predict = 0);
uvm_status_e status;
string msg_id = {csr_utils_pkg::msg_id, "::csr_poke"};
uvm_reg_data_t old_mirrored_val = csr.get_mirrored_value();
csr.poke(.status(status), .value(value));
if (check == UVM_CHECK) begin
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
if (check == UVM_CHECK && status != UVM_IS_OK) begin
string str;
uvm_hdl_path_concat paths[$];
csr.get_full_hdl_path(paths);
foreach (paths[0].slices[i]) str = $sformatf("%0s\n%0s", str, paths[0].slices[i].path);
`uvm_fatal(msg_id, $sformatf("poke failed for %0s, check below paths %0s",
csr.get_full_name(), str))
end
// poke always updates predict value, if predict == 0, revert back to old mirrored value
if (!predict) begin
void'(csr.predict(.value(old_mirrored_val), .kind(UVM_PREDICT_DIRECT)));
end
endtask

View file

@ -11,10 +11,6 @@ class dv_base_reg_field extends uvm_reg_field;
// when use UVM_PREDICT_WRITE and the CSR access is WO, this function will return the default
// val of the register, rather than the written value
// TODO, need to handle predict value when backdoor write happens WO reg
// 1. for read, design ties the read data to default value
// 2. when backdoor write updates internal reg, backdoor read can get the written value, but
// frontdoor read always returns the default value.
virtual function uvm_reg_data_t XpredictX(uvm_reg_data_t cur_val,
uvm_reg_data_t wr_val,
uvm_reg_map map);

View file

@ -6,6 +6,8 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
bit is_active = 1;
bit en_scb = 1; // can be changed at run-time
bit en_scb_tl_err_chk = 1;
bit en_scb_mem_chk = 1;
bit en_cov = 1;
bit has_ral = 1;
bit under_reset = 0;

View file

@ -32,6 +32,8 @@ class dv_base_test #(type CFG_T = dv_base_env_cfg,
// knob to en/dis scb (enabled by default)
void'($value$plusargs("en_scb=%0b", cfg.en_scb));
void'($value$plusargs("en_scb_tl_err_chk=%0b", cfg.en_scb_tl_err_chk));
void'($value$plusargs("en_scb_mem_chk=%0b", cfg.en_scb_mem_chk));
// knob to cfg all agents with zero delays
void'($value$plusargs("zero_delays=%0b", cfg.zero_delays));
endfunction : build_phase

View file

@ -172,6 +172,7 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
m_csr_write_seq.models = cfg.ral_models;
m_csr_write_seq.set_csr_excl_item(csr_excl);
m_csr_write_seq.external_checker = cfg.en_scb;
m_csr_write_seq.en_rand_backdoor_write = 1;
if (!enable_asserts_in_hw_reset_rand_wr) $assertoff;
m_csr_write_seq.start(null);

View file

@ -45,8 +45,8 @@ bool VerilatorMemUtil::RegisterMemoryArea(const std::string name,
size_t width_bit) {
MemArea mem = {.name = name, .location = location, .width_bit = width_bit};
assert((width_bit <= 128) &&
"TODO: Memory loading only supported up to 128 bits.");
assert((width_bit <= 256) &&
"TODO: Memory loading only supported up to 256 bits.");
auto ret = mem_register_.emplace(name, mem);
if (ret.second == false) {

View file

@ -13,7 +13,7 @@ import sys
import hjson
from Deploy import Deploy
from utils import VERBOSE, md_results_to_html, parse_hjson, subst_wildcards
from utils import VERBOSE, md_results_to_html, parse_hjson, print_msg_list, subst_wildcards
# Interface class for extensions.
@ -86,6 +86,7 @@ class FlowCfg():
# Full and summary results in md text.
self.results_md = ""
self.results_summary_md = ""
self.email_summary_md = "" # if user wanted to customize email content
def __post_init__(self):
# Run some post init checks
@ -116,7 +117,7 @@ class FlowCfg():
This method takes 2 args.
flow_cfg_file: This is the flow cfg file to be parsed.
is_entry_point: the cfg file that is passed on the command line is
the entry point cfg. If the cfg file is a part of an inport_cfgs
the entry point cfg. If the cfg file is a part of an import_cfgs
or use_cfgs key, then it is not an entry point.
'''
hjson_dict = parse_hjson(flow_cfg_file)
@ -485,7 +486,9 @@ class FlowCfg():
def gen_email_html_summary(self):
if self.is_master_cfg:
gen_results = self.results_summary_md
# user can customize email content by using email_summary_md,
# otherwise default to send out results_summary_md
gen_results = self.email_summary_md or self.results_summary_md
else:
gen_results = self.results_md
results_html = md_results_to_html(self.results_title, self.css_file, gen_results)

279
vendor/lowrisc_ip/dvsim/FpvCfg.py vendored Normal file
View file

@ -0,0 +1,279 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
import logging as log
from pathlib import Path
import hjson
from tabulate import tabulate
from OneShotCfg import OneShotCfg
from utils import subst_wildcards
class FpvCfg(OneShotCfg):
"""Derivative class for FPV purposes.
"""
def __init__(self, flow_cfg_file, proj_root, args):
super().__init__(flow_cfg_file, proj_root, args)
self.header = ["name", "errors", "warnings", "proven", "cex", "undetermined",
"covered", "unreachable", "pass_rate", "cov_rate"]
self.summary_header = ["name", "pass_rate", "stimuli_cov", "coi_cov", "prove_cov"]
def __post_init__(self):
super().__post_init__()
self.results_title = self.name.upper() + " FPV Results"
def parse_dict_to_str(self, input_dict, excl_keys = []):
# This is a helper function to parse dictionary items into a string.
# This function has an optional input "excl_keys" for user to exclude
# printing out certain items according to their keys.
# Note this function did not sort the input dictionary's key value
# before printing the keys and items. If input dictionary is not an
# OrderedDictionary, print out key order is not predictable.
# This function works for Hjson lib outputs because the lib uses an
# OrderDict when it reads dictionaries.
# Example Input:
# {
# "unreachable": ["prop1, prop2, prop3"],
# "cex" : ["prop1"],
# }
# Example Output:
# string = "unreachable:
# ```
# prop1
# prop2
# prop3
# ```
# cex:
# ```
# prop1
# ```"
output_str = ""
for key, item in input_dict.items():
if (key not in excl_keys) and item:
output_str += "\n" + key + ":\n"
output_str += "```\n"
output_str += "\n".join(item)
output_str += "\n```\n"
return output_str
def get_fpv_summary_results(self, result):
summary = []
fpv_summary = result.get("fpv_summary")
if fpv_summary is None:
results_str = "No fpv_summary found\n"
summary.append("N/A")
else:
colalign = ("center", ) * len(self.header)
table = [self.header]
table.append([
self.name,
str(fpv_summary["errors"]) + " E ",
str(fpv_summary["warnings"]) + " W ",
str(fpv_summary["proven"]) + " G ",
str(fpv_summary["cex"]) + " E ",
str(fpv_summary["undetermined"]) + " W ",
str(fpv_summary["covered"]) + " G ",
str(fpv_summary["unreachable"]) + " E ",
fpv_summary["pass_rate"],
fpv_summary["cov_rate"]
])
summary.append(fpv_summary["pass_rate"])
if len(table) > 1:
results_str = tabulate(table, headers="firstrow", tablefmt="pipe",
colalign=colalign)
else:
results_str = "No content in fpv_summary\n"
summary.append("N/A")
return results_str, summary
def get_fpv_coverage_results(self, result):
summary = []
fpv_coverage = result.get("fpv_coverage")
if fpv_coverage is None:
results_str = "No fpv_coverage found\n"
summary = ["N/A", "N/A", "N/A"]
else:
cov_header = ["stimuli", "coi", "proof"]
cov_colalign = ("center", ) * len(cov_header)
cov_table = [cov_header]
cov_table.append([
fpv_coverage["stimuli"],
fpv_coverage["coi"],
fpv_coverage["proof"]
])
summary.append(fpv_coverage["stimuli"])
summary.append(fpv_coverage["coi"])
summary.append(fpv_coverage["proof"])
if len(cov_table) > 1:
results_str = tabulate(cov_table, headers="firstrow",
tablefmt="pipe", colalign=cov_colalign)
else:
results_str = "No content in fpv_coverage\n"
summary = ["N/A", "N/A", "N/A"]
return results_str, summary
def gen_results_summary(self):
# Gathers the aggregated results from all sub configs
# The results_summary will only contain the passing rate and
# percentages of the stimuli, coi, and proven coverage
# The email_summary will contain all the information from results_md
log.info("Create summary of FPV results")
results_str = "## " + self.results_title + " (Summary)\n\n"
results_str += "### " + self.timestamp_long + "\n\n"
colalign = ("center", ) * len(self.summary_header)
table = [self.summary_header]
for cfg in self.cfgs:
try:
table.append(cfg.result_summary[cfg.name])
except KeyError as e:
table.append([cfg.name, "ERROR", "N/A", "N/A", "N/A"])
log.error("cfg: %s could not find generated results_summary: %s", cfg.name, e)
if len(table) > 1:
self.results_summary_md = results_str + tabulate(
table, headers="firstrow", tablefmt="pipe", colalign=colalign)
else:
self.results_summary_md = results_str
log.info("[result summary]: %s", self.results_summary_md)
# Generate email results summary
colalign = ("left", ) + ("center", ) * (len(self.header) - 1)
email_table = [self.header]
error_message = ""
for cfg in self.cfgs:
email_result = cfg.result.get("fpv_summary")
if email_result is not None:
email_table.append([
cfg.name,
str(email_result["errors"]) + " E ",
str(email_result["warnings"]) + " W ",
str(email_result["proven"]) + " G ",
str(email_result["cex"]) + " E ",
str(email_result["undetermined"]) + " W ",
str(email_result["covered"]) + " G ",
str(email_result["unreachable"]) + " E ",
email_result["pass_rate"],
email_result["cov_rate"]
])
messages = cfg.result.get("messages")
if messages is not None:
# TODO: temp disable printing out "fpv_warnings" in results_summary
# Will clean up FPV warnings first, then display "fpv_warnings"
error = self.parse_dict_to_str(messages, ["fpv_warnings"])
if error:
error_message += "\n#### " + cfg.name + "\n"
error_message += error
if len(email_table) > 1:
self.email_summary_md = results_str + tabulate(
email_table, headers="firstrow", tablefmt="pipe", colalign=colalign)
self.email_summary_md += error_message
return self.results_summary_md
def _gen_results(self):
# This function is called after the regression and looks for
# results.hjson file with aggregated results from the FPV run.
# The hjson file is required to follow this format:
# {
# "messages": {
# "errors" : []
# "warnings" : []
# "cex" : ["property1", "property2"...],
# "undetermined": [],
# "unreachable" : [],
# },
#
# "fpv_summary": {
# "errors" : 0
# "warnings" : 2
# "proven" : 20,
# "cex" : 5,
# "covered" : 18,
# "undetermined": 7,
# "unreachable" : 2,
# "pass_rate" : "90 %",
# "cover_rate" : "90 %"
# },
# }
# The categories for property results are: proven, cex, undetermined,
# covered, and unreachable.
#
# If coverage was enabled then results.hjson will also have an item that
# shows FPV coverage. It will have the following format:
# "fpv_coverage": {
# stimuli: "90 %",
# coi : "90 %",
# proof : "80 %"
# }
results_str = "## " + self.results_title + "\n\n"
results_str += "### " + self.timestamp_long + "\n"
results_str += "### FPV Tool: " + self.tool.upper() + "\n"
results_str += "### LogFile dir: " + self.scratch_path + "/default\n\n"
summary = [self.name] # cfg summary for publish results
if len(self.build_modes) != 1:
mode_names = [mode.name for mode in self.build_modes]
log.error("FPV only supports mode 'default', but found these modes: %s", mode_names)
else:
mode = self.build_modes[0]
result_data = Path(subst_wildcards(self.build_dir, {"build_mode": mode.name}) +
'/results.hjson')
try:
with open(result_data, "r") as results_file:
self.result = hjson.load(results_file, use_decimal=True)
except IOError as err:
log.warning("%s", err)
self.result = {
"messages": {
"errors": ["IOError: %s" % err],
}
}
fpv_result_str, fpv_summary = self.get_fpv_summary_results(self.result)
results_str += fpv_result_str
summary += fpv_summary
if self.cov:
results_str += "\n\n## Coverage Results\n"
results_str += ("### Coverage html file dir: " +
self.scratch_path + "/default/formal-icarus\n\n")
cov_result_str, cov_summary = self.get_fpv_coverage_results(self.result)
results_str += cov_result_str
summary += cov_summary
messages = self.result.get("messages")
if messages is not None:
results_str += self.parse_dict_to_str(messages)
# Write results to the scratch area
self.results_md = results_str
results_file = self.scratch_path + "/results_" + self.timestamp + ".md"
with open(results_file, 'w') as f:
f.write(self.results_md)
log.info("[results page]: [%s] [%s]", self.name, results_file)
# Generate result summary
if not self.cov:
summary += ["N/A", "N/A", "N/A"]
self.result_summary[self.name] = summary
return self.results_md
def _publish_results(self):
'''This does nothing: detailed messages from FPV runs are not published
Our agreement with tool vendors allows us to publish the summary
results (as in gen_results_summary), but not anything more detailed.
'''
return

View file

@ -12,20 +12,7 @@ from pathlib import Path
from tabulate import tabulate
from OneShotCfg import OneShotCfg
from utils import subst_wildcards
# helper function for printing messages
def _print_msg_list(msg_list_name, msg_list):
md_results = ""
if msg_list:
md_results += "### %s\n" % msg_list_name
md_results += "```\n"
for msg in msg_list:
md_results += msg + "\n\n"
md_results += "```\n"
return md_results
from utils import print_msg_list, subst_wildcards
class LintCfg(OneShotCfg):
"""Derivative class for linting purposes.
@ -185,22 +172,22 @@ class LintCfg(OneShotCfg):
self.result_summary["lint_errors"] += self.result["lint_errors"]
# Append detailed messages if they exist
if sum([
len(self.result["warnings"]),
len(self.result["errors"]),
len(self.result["lint_warnings"]),
len(self.result["lint_errors"])
]):
fail_msgs += "\n## Errors and Warnings for Build Mode `'" + mode.name + "'`\n"
fail_msgs += _print_msg_list("Tool Errors",
self.result["errors"])
fail_msgs += _print_msg_list("Tool Warnings",
self.result["warnings"])
fail_msgs += _print_msg_list("Lint Errors",
self.result["lint_errors"])
fail_msgs += _print_msg_list("Lint Warnings",
self.result["lint_warnings"])
# fail_msgs += _print_msg_list("Lint Infos", results["lint_infos"])
hdr_key_pairs = [("Tool Warnings", "warnings"),
("Tool Errors", "errors"),
("Lint Warnings", "lint_warnings"),
("Lint Errors", "lint_errors")]
has_msg = False
for _, key in hdr_key_pairs:
if key in self.result:
has_msg = True
break
if has_msg:
fail_msgs += "\n### Errors and Warnings for Build Mode `'" + mode.name + "'`\n"
for hdr, key in hdr_key_pairs:
msgs = self.result.get(key)
fail_msgs += print_msg_list("#### " + hdr, msgs, self.max_msg_count)
if len(table) > 1:
self.results_md = results_str + tabulate(

View file

@ -58,6 +58,7 @@ class OneShotCfg(FlowCfg):
self.build_modes = []
self.run_modes = []
self.regressions = []
self.max_msg_count = -1
# Flow results
self.result = OrderedDict()
@ -73,7 +74,7 @@ class OneShotCfg(FlowCfg):
self.links = {}
self.build_list = []
self.deploy = []
self.cov = args.cov
# Parse the cfg_file file tree
self.parse_flow_cfg(flow_cfg_file)
self._post_parse_flow_cfg()

View file

@ -12,7 +12,7 @@ import hjson
from tabulate import tabulate
from OneShotCfg import OneShotCfg
from utils import subst_wildcards
from utils import print_msg_list, subst_wildcards
class SynCfg(OneShotCfg):
@ -343,6 +343,29 @@ class SynCfg(OneShotCfg):
else:
results_str += "No power report found\n\n"
# Append detailed messages if they exist
# Note that these messages are omitted in publication mode
hdr_key_pairs = [("Flow Warnings", "flow_warnings"),
("Flow Errors", "flow_errors"),
("Analyze Warnings", "analyze_warnings"),
("Analyze Errors", "analyze_errors"),
("Elab Warnings", "elab_warnings"),
("Elab Errors", "elab_errors"),
("Compile Warnings", "compile_warnings"),
("Compile Errors", "compile_errors")]
has_msg = False
for _, key in hdr_key_pairs:
if key in self.result['messages']:
has_msg = True
break
if has_msg and not self.args.publish:
results_str += "\n### Errors and Warnings for Build Mode `'" + mode.name + "'`\n"
for hdr, key in hdr_key_pairs:
msgs = self.result['messages'].get(key)
results_str += print_msg_list("#### " + hdr, msgs, self.max_msg_count)
# TODO: add support for pie / bar charts for area splits and
# QoR history

View file

@ -29,6 +29,7 @@ import textwrap
from signal import SIGINT, signal
import Deploy
import FpvCfg
import LintCfg
import SimCfg
import SynCfg
@ -147,7 +148,8 @@ def make_config(args, proj_root):
factories = {
'ascentlint': LintCfg.LintCfg,
'veriblelint': LintCfg.LintCfg,
'dc': SynCfg.SynCfg
'dc': SynCfg.SynCfg,
'jaspergold': FpvCfg.FpvCfg
}
factory = factories.get(args.tool, SimCfg.SimCfg)

View file

@ -350,3 +350,30 @@ def htmc_color_pc_cells(text):
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

View file

@ -17,6 +17,7 @@ REPORT_DIR ?= reports
IPS ?= ip-aes \
ip-alert_handler \
ip-clkmgr \
ip-entropy_src \
ip-flash_ctrl \
ip-gpio \
ip-hmac \

View file

@ -111,7 +111,7 @@ def main():
# lint infos do not count as failures
nr_errors = len(results["errors"]) + len(results["lint_errors"])
nr_warnings = len(results["warnings"]) + len(results["lint_warnings"])
if nr_errors > 0 and nr_warnings > 0:
if nr_errors > 0 or nr_warnings > 0:
print("Lint not successful, got %d warnings and %d errors." %
(nr_warnings, nr_errors))
sys.exit(1)

View file

@ -118,7 +118,7 @@ def main():
nr_warnings = len(results["warnings"]) + len(results["lint_warnings"])
print("Lint not successful, got %d warnings and %d errors." %
(nr_warnings, nr_errors))
if nr_errors > 0 and nr_warnings > 0:
if nr_errors > 0 or nr_warnings > 0:
sys.exit(1)
sys.exit(0)

View file

@ -4,3 +4,8 @@
//
// common waiver rules for verilator
`verilator_config
// Do not warn about unconnected pins in module instantiations, e.g.
// `.optional_output ()`.
lint_off -rule PINCONNECTEMPTY

View file

@ -5,7 +5,7 @@ title: "Primitive Component: PRINCE Scrambler"
# Overview
`prim_prince` is an (unhardened) implementation of the [64bit PRINCE block cipher](https://en.wikipedia.org/wiki/Prince_(cipher)).
It is a fully unrolled combinational implementation with a configurable number of rounds.
It is a fully unrolled combinational implementation with a configurable number of rounds (data and key state registers placed half-way in the design can optionally be enabled).
Due to the mirrored construction of this cipher, the same circuit can be used for encryption and decryption, as described below.
Further, the primitive supports a 32bit block cipher flavor which is not specified in the original paper.
@ -22,35 +22,47 @@ DataWidth | int | Block size, can be 32 or 64.
KeyWidth | int | Key size, can be 64 for block size 32, or 128 for block size 64
NumRounds | int | Half the number of the reflected PRINCE rounds. Can range from 1 to 5. The effective number of non-linear layers is 2 + 2 * NumRounds.
UseOldKeySched | bit | If set to 1, fall back to the original keyschedule (not recommended). Defaults to 0.
HalfwayDataReg | bit | If set to 1, instantiates a data register half-way in the data path
HalfwayKeyReg | bit | If set to 1, instantiates a key register half-way in the data path. This is only required if the key is not static and changes with every new data input.
## Signal Interfaces
Name | In/Out | Description
-------------|--------|---------------------------------
clk_i | input | Clock input
rst_ni | input | Reset input
valid_i | input | Data valid input
data_i | input | Plaintext input
data_i | input | Plaintext input
key_i | input | Key input
dec_i | input | Assert for decryption
valid_o | output | Data valid output
data_o | output | Output of the ciphertext
# Theory of Operations
```
/----------------\
dec_i | |
------------>| PRINCE |
key_i | |
=====/======>| DataWidth |
[KeyWidth] | KeyWidth |
| NumRounds |
data_i | UseOldKeySched | data_o
=====/======>| |=====/=======>
[DataWidth] | | [DataWidth]
| |
\----------------/
/-----------------\
clk_i / rst_ni | |
-------------->| |
dec_i | |
-------------->| PRINCE |
valid_i | | valid_o
-------------->| DataWidth |--------------->
key_i | KeyWidth |
=====/========>| NumRounds |
[KeyWidth] | UseOldKeySched | data_o
| HalfwayDataReg |=======/=======>
data_i | HalfwayKeyReg | [DataWidth]
=====/========>| |
[DataWidth] | |
| |
\-----------------/
```
The PRINCE module is fully unrolled and combinational, meaning that it does not have any clock, reset or handshaking inputs.
The only inputs are the key and the plaintext, and the only output is the ciphertext.
The PRINCE module is fully unrolled and combinational by default.
But since data and key state registers can optionally be enabled, the primitive also has a clock, reset and valid input besides the key and plaintext inputs.
On the output side it exposes the ciphertext with its corresponding valid signal.
The internal construction follows the the algorithm described in the original [paper](https://eprint.iacr.org/2012/529.pdf).
The block size is 64bit and the key size is 128bit.

View file

@ -43,4 +43,4 @@ endif
# Include the tool Makefile below #
# Dont add anything else below it! #
####################################
include ${DV_DIR}/../../../dv/tools/Makefile
include ${DV_DIR}/../../../../dv/tools/Makefile

View file

@ -0,0 +1,27 @@
/*
* S-Boxes and P-Boxes for
* Implementation of PRESENT in C
* v2.1, 10/13/2008
*
* Implementation is located at http://www.lightweightcrypto.org/implementations.php,
* under the title "Testvectors for PRESENT".
*
* Thomas Siebert, thomas.siebert@rub.de
*/
const uint8_t Sbox[16] = {12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2};
const uint8_t SboxInv[16] = {5, 14, 15, 8, 12, 1, 2, 13,
11, 4, 6, 3, 0, 7, 9, 10};
const uint8_t PboxInv[64] = {0, 16, 32, 48, 1, 17, 33, 49, 2, 18, 34, 50, 3,
19, 35, 51, 4, 20, 36, 52, 5, 21, 37, 53, 6, 22,
38, 54, 7, 23, 39, 55, 8, 24, 40, 56, 9, 25, 41,
57, 10, 26, 42, 58, 11, 27, 43, 59, 12, 28, 44, 60,
13, 29, 45, 61, 14, 30, 46, 62, 15, 31, 47, 63};
const uint8_t Pbox[64] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48,
52, 56, 60, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37,
41, 45, 49, 53, 57, 61, 2, 6, 10, 14, 18, 22, 26,
30, 34, 38, 42, 46, 50, 54, 58, 62, 3, 7, 11, 15,
19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63};

View file

@ -0,0 +1,196 @@
/*
* Commandline-Option-Fetcher for
* Implementation of PRESENT in C
* v2.1, 10/13/2008
*
* Implementation is located at http://www.lightweightcrypto.org/implementations.php,
* under the title "Testvectors for PRESENT".
*
* Thomas Siebert, thomas.siebert@rub.de
*/
#include <string.h>
#include <getopt.h>
//----------------------------------
// Function prototype
//----------------------------------
// void comline_fetch_options( struct Options * , int , char ** );
//----------------------------------
// Struct declaration
//----------------------------------
struct Options {
_Bool Error;
_Bool Mode;
_Bool KeySize80;
uint8_t Verbose;
uint64_t KeyHigh;
uint64_t KeyLow;
uint64_t Text;
uint16_t Rounds;
};
#define Encrypt_Mode 1
#define Decrypt_Mode 0
//----------------------------------
// Functions
//----------------------------------
void comline_fetch_options(struct Options *sOpt, int argc, char **const argv) {
int c;
_Bool Opt_Decrypt = 0, Opt_Encrypt = 0, Opt_File = 0, Opt_Verbose = 0;
char *Opt_Text = NULL, *Opt_Key = NULL, *Opt_Rounds = NULL;
FILE *KeyFile = NULL, *TextFile = NULL;
int keyres;
sOpt->Error = 0;
sOpt->Verbose = 1;
while ((c = getopt(argc, argv, "defv:r:k:t:")) !=
-1) // Cycle through Options
{
switch (c) // set flags
{
case 'd':
if (Opt_Encrypt || Opt_Decrypt)
sOpt->Error = 1;
else
Opt_Decrypt = 1;
break;
case 'e':
if (Opt_Encrypt || Opt_Decrypt)
sOpt->Error = 1;
else
Opt_Encrypt = 1;
break;
case 'f':
if (Opt_File)
sOpt->Error = 1;
else
Opt_File = 1;
break;
case 'v':
if (Opt_Verbose)
sOpt->Error = 1;
else if (optarg != NULL) {
if (strcmp(optarg, "0") == 0)
sOpt->Verbose = 0;
else if (strcmp(optarg, "1") == 0)
sOpt->Verbose = 1;
else if (strcmp(optarg, "2") == 0)
sOpt->Verbose = 2;
else
sOpt->Error = 1;
} else
sOpt->Error = 1;
Opt_Verbose = 1;
break;
case 'k':
if (Opt_Key != NULL)
sOpt->Error = 1;
else
Opt_Key = optarg;
break;
case 'r':
if (Opt_Rounds)
sOpt->Error = 1;
else
Opt_Rounds = optarg;
break;
case 't':
if (Opt_Text != NULL)
sOpt->Error = 1;
else
Opt_Text = optarg;
break;
case '?':
sOpt->Error = 1;
break;
}
} // End Option-Cycle
// Set Error if Parameters missing
if (Opt_Key == NULL || Opt_Text == NULL || (!(Opt_Decrypt || Opt_Encrypt))) {
sOpt->Error = 1;
}
else {
// Handle Rounds Parameter
if (Opt_Rounds != NULL) // if Round Param there...
{
if (strlen(Opt_Rounds) < 6) // check length
{
uint32_t Rounds;
sscanf(Opt_Rounds, "%5" SCNu32 "", &Rounds); // get round no.
if ((Rounds > 65534) || Rounds == 0)
sOpt->Error = 1; // check 0<Rounds<65535
else
sOpt->Rounds = Rounds; // override roundno.
} else
sOpt->Error = 1;
} else
sOpt->Rounds = 32; //...else use standard
// Check if decrypt or encrypt mode
if (Opt_Encrypt)
sOpt->Mode = Encrypt_Mode;
else
sOpt->Mode = Decrypt_Mode;
// Read key and text (file mode)
if (Opt_File) {
KeyFile = fopen(Opt_Key, "r");
TextFile = fopen(Opt_Text, "r");
if (!((KeyFile == NULL) || (TextFile) == NULL)) {
fseek(KeyFile, 0, SEEK_END);
if ((ftell(KeyFile)) >= 32) {
fseek(KeyFile, 0, SEEK_SET);
if (fscanf(KeyFile, "%016" SCNx64 "", &sOpt->KeyHigh) == 0)
sOpt->Error = 1;
if (fscanf(KeyFile, "%016" SCNx64 "", &sOpt->KeyLow) == 0)
sOpt->Error = 1;
sOpt->KeySize80 = 0;
} else if ((ftell(KeyFile)) >= 20) {
fseek(KeyFile, 0, SEEK_SET);
if (fscanf(KeyFile, "%016" SCNx64 "", &sOpt->KeyHigh) == 0)
sOpt->Error = 1;
if (fscanf(KeyFile, "%04" SCNx16 "", &sOpt->KeyLow) == 0)
sOpt->Error = 1;
sOpt->KeySize80 = 1;
} else
sOpt->Error = 1;
if (fscanf(TextFile, "%016" SCNx64 "", &sOpt->Text) == EOF)
sOpt->Error = 1;
} else
sOpt->Error = 1;
if (!(KeyFile == NULL))
fclose(KeyFile);
if (!(TextFile == NULL))
fclose(TextFile);
}
// Read key and text (commandline mode)
else {
if (((strlen(Opt_Key) != 32) && (strlen(Opt_Key) != 20)) ||
(strlen(Opt_Text) != 16)) { // if wrong length...
sOpt->Error = 1; // set error
}
if (!(sOpt->Error)) // if no error...
{
sscanf(Opt_Key, "%016" SCNx64 "", &sOpt->KeyHigh); // get values
if (strlen(Opt_Key) == 20) // set key + size
{
sOpt->KeySize80 = 1;
sscanf(Opt_Key + 16, "%016" SCNx16 "", &sOpt->KeyLow);
} else {
sOpt->KeySize80 = 0;
sscanf(Opt_Key + 16, "%016" SCNx64 "", &sOpt->KeyLow);
}
sscanf(Opt_Text, "%016" SCNx64 "", &sOpt->Text);
}
}
}
}

View file

@ -0,0 +1,64 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "present.inc"
#include "svdpi.h"
typedef unsigned long long int ull_t;
// Helper function used only by this C file.
// Returns the key schedule corresponding to the input key.
uint64_t *get_key_schedule(uint64_t key_high, uint64_t key_low,
uint8_t num_rounds, uint8_t key_size_80) {
return key_schedule(key_high, key_low, num_rounds, (_Bool)key_size_80, 0);
}
extern void c_dpi_key_schedule(uint64_t key_high, uint64_t key_low,
uint8_t num_rounds, uint8_t key_size_80,
svBitVecVal *key_array) {
uint64_t *key_schedule = (uint64_t *)malloc(num_rounds * sizeof(uint64_t));
uint64_t key;
svBitVecVal key_hi;
svBitVecVal key_lo;
// get the key schedule from the C model
key_schedule = get_key_schedule(key_high, key_low, num_rounds, key_size_80);
// write the key schedule to simulation
int i;
for (i = 0; i < num_rounds; i++) {
key = key_schedule[i];
key_hi = (svBitVecVal)(key >> 32);
key_lo = (svBitVecVal)(key & 0xFFFFFFFF);
key_array[i * 2] = key_lo;
key_array[i * 2 + 1] = key_hi;
}
// free allocated memory
free(key_schedule);
}
extern uint64_t c_dpi_encrypt(uint64_t plaintext, uint64_t key_high,
uint64_t key_low, uint8_t num_rounds,
uint8_t key_size_80) {
uint64_t encrypt_result;
uint64_t *key_schedule = (uint64_t *)malloc(num_rounds * sizeof(uint64_t));
key_schedule = get_key_schedule(key_high, key_low, num_rounds, key_size_80);
encrypt_result = (uint64_t)encrypt(plaintext, key_schedule, num_rounds, 0);
free(key_schedule);
return encrypt_result;
}
extern uint64_t c_dpi_decrypt(uint64_t ciphertext, uint64_t key_high,
uint64_t key_low, uint8_t num_rounds,
uint8_t key_size_80) {
uint64_t decrypt_result;
uint64_t *key_schedule = (uint64_t *)malloc(sizeof(uint64_t));
key_schedule = get_key_schedule(key_high, key_low, num_rounds, key_size_80);
decrypt_result = (uint64_t)decrypt(ciphertext, key_schedule, num_rounds, 0);
free(key_schedule);
return decrypt_result;
}

View file

@ -0,0 +1,21 @@
CAPI=2:
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:dv:crypto_dpi_present:0.1"
description: "PRESENT block cipher reference implementation in C from Ruhr-University Bochum"
filesets:
files_dv:
files:
- boxes.inc: {file_type: cSource, is_include_file: true}
- comline.inc: {file_type: cSource, is_include_file: true}
- verbose.inc: {file_type: cSource, is_include_file: true}
- present.inc: {file_type: cSource, is_include_file: true}
- crypto_dpi_present.c: {file_type: cSource}
- crypto_dpi_present_pkg.sv: {file_type: systemVerilogSource}
file_type: cSource
targets:
default:
filesets:
- files_dv

View file

@ -0,0 +1,120 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
package crypto_dpi_present_pkg;
// dep packages
import uvm_pkg::*;
// parameters
// This is defined here so we can size all arrays properly.
parameter int unsigned NumRounds = 31;
// DPI-C imports
import "DPI-C" context function void c_dpi_key_schedule(
input longint unsigned key_high,
input longint unsigned key_low,
input int unsigned num_rounds,
input int unsigned key_size_80,
output bit [NumRounds:0][63:0] key_schedule
);
import "DPI-C" context function longint c_dpi_encrypt(
input longint unsigned plaintext,
input longint unsigned key_high,
input longint unsigned key_low,
input int unsigned num_rounds,
input int unsigned key_size_80
);
import "DPI-C" context function longint c_dpi_decrypt(
input longint unsigned ciphertext,
input longint unsigned key_high,
input longint unsigned key_low,
input int unsigned num_rounds,
input int unsigned key_size_80
);
// Helper Functions
function automatic void get_keys(input bit [127:0] key,
input bit key_size_80,
output bit [63:0] key_high,
output bit [63:0] key_low);
key_high = (key_size_80) ? key[79:16] : key[127:64];
key_low = (key_size_80) ? key[15:0] : key[63:0];
endfunction
//////////////////////////////////////////////////////
// SV wrapper functions to be used by the testbench //
//////////////////////////////////////////////////////
// This function takes in a 128 bit key by default, it determines how to
// split this key for the DPI calls based on the value of key_size_80.
//
// This returns the list of round keys used during the course of the algorithm.
function automatic void sv_dpi_present_get_key_schedule(
input bit [127:0] key,
input bit key_size_80,
output bit [NumRounds:0][63:0] key_schedule
);
bit [63:0] key_high, key_low;
bit [(NumRounds*2)+1:0][31:0] compressed_key_schedule;
get_keys(key, key_size_80, key_high, key_low);
c_dpi_key_schedule(key_high, key_low, NumRounds+1, key_size_80, compressed_key_schedule);
for (int i = 0; i < NumRounds+1; i++) begin
key_schedule[i][31:0] = compressed_key_schedule[i*2];
key_schedule[i][63:32] = compressed_key_schedule[i*2+1];
end
endfunction
// This function encrypts the input plaintext with the PRESENT encryption
// algorithm using the specified number of rounds.
//
// This produces a list of all intermediate values produced after each round
// of the algorithm, including the final encrypted ciphertext value.
function automatic void sv_dpi_present_encrypt(
input bit [63:0] plaintext,
input bit [127:0] key,
input bit key_size_80,
output bit [NumRounds-1:0][63:0] encrypted_values
);
bit [63:0] key_high, key_low;
get_keys(key, key_size_80, key_high, key_low);
for (int i = 1; i <= NumRounds; i++) begin
encrypted_values[i-1] = c_dpi_encrypt(plaintext, key_high, key_low, i+1, key_size_80);
end
endfunction
// This function decrypts the input ciphertext with the PRESENT decryption
// algorithm using the specified number of rounds.
//
// This produces a list of all intermediate values produced after each round
// of the algorithm, including the final decrypted plaintext value.
function automatic void sv_dpi_present_decrypt(
input bit [NumRounds-1:0][63:0] ciphertext,
input bit [127:0] key,
input bit key_size_80,
output bit [NumRounds-1:0][63:0] decrypted_values
);
bit [63:0] key_high, key_low;
get_keys(key, key_size_80, key_high, key_low);
for (int i = 1; i <= NumRounds; i++) begin
decrypted_values[i-1] = c_dpi_decrypt(ciphertext[i-1], key_high, key_low, i+1, key_size_80);
end
endfunction
endpackage

View file

@ -0,0 +1,396 @@
/*
* Implementation of PRESENT in C
* v2.1, 10/13/2008
*
* Implementation is located at http://www.lightweightcrypto.org/implementations.php,
* under the title "Testvectors for PRESENT".
*
* Thomas Siebert, thomas.siebert@rub.de
*
*
* Your Compiler currently should support
* the ANSI-C99-standard.
*
* Tested with gcc (with Option -std=c99)
*/
//----------------------------------
// Includes
//----------------------------------
#include "verbose.inc" //For verbose output
#include "comline.inc" //Command Line
#include "boxes.inc" //S-Boxes and P-Boxes
#include <stdlib.h>
//----------------------------------
// Macros for bit manipulation
//----------------------------------
//returns...
#define high45_64(h45in) ((uint64_t)h45in >> 9) // 45 msb as lsb
#define high61_64(h4in) ((uint64_t)h4in >> 3) // 61 msb as lsb
#define high4_64(h4in) ((uint64_t)h4in >> 60) // 4 msb as lsb
#define high8to4_64(h8in) (((uint64_t)h8in >> 56) & 0x0F) // 4 msb as 2. lsb
#define high16_64(h16in) ((uint64_t)h16in >> 48) // 16 msb as lsb
#define high1_64(h1in) ((uint64_t)h1in >> 63) // msb as lsb
#define low4_64(l4in) ((uint64_t)l4in << 60) // 4 lsb as msb
#define low8to4_64(l4in) ((uint64_t)l4in << 56) // 4 lsb as 2. msb
#define low16_64(l4in) ((uint64_t)l4in << 48) // 4 lsb as msb
#define rotate1l_64(r1lin) \
(high1_64(r1lin) | (r1lin << 1)) // input rotated left (1x)
#define rotate1r_64(r1rin) \
(high1_64(r1rin) | (r1rin >> 1)) // input rotated right (1x)
#define rotate4l_64(r4lin) \
(high4_64(r4lin) | (r4lin << 4)) // input rotated left (4x)
#define rotate4r_64(r4rin) \
(high4_64(r4rin) | (r4rin >> 4)) // input rotated right (4x)
//----------------------------------
// Function prototypes
//----------------------------------
uint64_t encrypt(uint64_t, uint64_t *, uint16_t, _Bool);
uint64_t decrypt(uint64_t, uint64_t *, uint16_t, _Bool);
uint64_t *key_schedule(uint64_t, uint64_t, uint16_t, _Bool, _Bool);
// We have commented out the main(...) function of the C model as the testbench is directly
// calling encrypt(), decrypt(), and key_schedule() functions.
// It also contains unnecessary command line parsing functionality that is not needed for
// the testbench.
//
//----------------------------------
// Start of code
//----------------------------------
// int main( int argc, char ** const argv )
//{
// // Initialize variables
// uint64_t result;
// struct Options Opt;
//
// // Get Commandline Options
// comline_fetch_options( &Opt, argc, argv );
//
// // Banner
// if ( Opt.Verbose != 0 )
// {
// printf( "---------------------------------------\n" );
// printf( "PRESENT Commandline Tool v2.1\n" );
// printf( "Thomas Siebert, thomas.siebert@rub.de\n" );
// printf( "---------------------------------------\n\n" );
// }
//
// if ( !Opt.Error )
// {
// uint64_t *subkey;
//
// if ( Opt.Mode == Encrypt_Mode )
// {
// // Put out Values
// if ( Opt.Verbose != 0 )
// {
// printf( "Starting values\n" );
// printf( "Plaintext: %016"PRIx64" \n", Opt.Text);
// if (Opt.KeySize80) printf( "Given Key (80bit):
//%016"PRIx64" %04"PRIx64"\n\n", Opt.KeyHigh, (Opt.KeyLow&0xFFFF) ); else
//printf( "Given Key (128bit): %016"PRIx64" %016"PRIx64"\n\n", Opt.KeyHigh,
//Opt.KeyLow );
// }
//
// // Generate Subkeys
// subkey=key_schedule( Opt.KeyHigh, Opt.KeyLow, Opt.Rounds,
//Opt.KeySize80, (Opt.Verbose>1) );
//
// // Start Encryption
// if ( Opt.Verbose != 0 ) printf( "Starting
//encryption...\n" ); result=encrypt(Opt.Text, subkey, Opt.Rounds,
//(Opt.Verbose>1) ); if ( Opt.Verbose != 0 ) printf( "Resulting Cipher:
//%016"PRIx64" \n\n", result); else printf( "%016"PRIx64"\n", result);
// }
//
// else if ( Opt.Mode == Decrypt_Mode )
// {
// // Put out Values
// if ( Opt.Verbose != 0 )
// {
// printf( "Starting values\n" );
// printf( "Ciphertext: %016"PRIx64" \n",
//Opt.Text); if (Opt.KeySize80) printf( "Given Key (80bit): %016"PRIx64"
//%04"PRIx64"\n\n", Opt.KeyHigh, (Opt.KeyLow&0xFFFF) ); else printf( "Given Key
//(128bit): %016"PRIx64" %016"PRIx64"\n\n", Opt.KeyHigh, Opt.KeyLow );
// }
//
// // Generate Subkeys
// subkey=key_schedule( Opt.KeyHigh, Opt.KeyLow, Opt.Rounds,
//Opt.KeySize80, (Opt.Verbose>1) );
//
// // Start Decryption
// if ( Opt.Verbose != 0 ) printf( "Starting
//decryption...\n" ); result=decrypt(Opt.Text, subkey, Opt.Rounds,
//(Opt.Verbose>1) ); if ( Opt.Verbose != 0 ) printf( "Resulting Plaintext:
//%016"PRIx64" \n", result); else printf( "%016"PRIx64"\n", result);
// }
//
// free(subkey);
//
// }
//
// else
// {
// // Put out Syntax
// printf( "Syntax:\n");
// printf( "PRESENT -d|e [-f] [-r rounds] [-v level] -k key -t
//text\n\n"); printf( "Choose -d to decrypt, or -e to encrypt one block\n\n");
// printf( "-f (optional): File input, see below\n");
// printf( "-r rounds (optional): Change number of rounds (up
//to 65534, standard is 32)\n"); printf( "-v level (optional): Specify verbose
//level:\n"); printf( " 0 for result-output only\n"); printf( " 1 for output
//of mode, input, result (standard)\n"); printf( " 2 for roundwise
//output\n\n"); printf( "-k key: Key in hexadecimal (length: *EXACTLY* 20
//chars(80bit)/32 chars(128bit))\n"); printf( "-t text: Text in hexadecimal
//(length: *EXACTLY* 16 chars)\n"); printf( "If -f is set, key and text
//represent files containing the values,\n"); printf( "otherwise they must be
//passed directly via commandline.\n\n"); printf( "Returned Errorlevel: 0 if
//successful, 1 if non-successful\n");
// }
// return Opt.Error;
//}
//----------------------------------
// Key Scheduling
//----------------------------------
uint64_t *key_schedule(uint64_t key_high, uint64_t key_low, uint16_t Rounds,
_Bool KeySize80, _Bool Output) {
uint64_t temp64;
uint64_t i;
uint64_t *subkey = (uint64_t *)malloc(Rounds * sizeof(uint64_t));
if (subkey != NULL) {
if (Output)
v_key_start();
if (KeySize80) {
key_low &= 0xFFFF;
if (Output)
v_k80_init(key_high, key_low);
for (i = 0; i < Rounds; i++) {
subkey[i] = key_high;
//----------------------------------
// Shift
//----------------------------------
temp64 = key_high;
key_high <<= 61;
key_high |= (key_low << 45);
key_high |= (temp64 >> 19);
key_low = (temp64 >> 3) & 0xFFFF;
if (Output && (i + 2 <= Rounds))
v_k80_shift(key_high, key_low);
//----------------------------------
// S-Box
//----------------------------------
temp64 = high4_64(key_high); // get highest nibble
key_high &= 0x0FFFFFFFFFFFFFFF; // kill highest nibble
temp64 = Sbox[temp64];
key_high |= low4_64(temp64); // put new value to highest nibble (sbox)
if (Output && (i + 2 <= Rounds))
v_k80_sbox(key_high, key_low);
//----------------------------------
// Round Salt
//----------------------------------
key_low ^= (((i + 1) & 0x01) << 15);
key_high ^= ((i + 1) >> 1);
if (Output && (i + 2 <= Rounds))
v_k80_round(key_high, key_low, i);
}
} else // 128 Bit
{
if (Output)
v_k128_init(key_high, key_low);
for (i = 0; i < Rounds; i++) {
subkey[i] = key_high;
//----------------------------------
// Shift
//----------------------------------
temp64 = high61_64(key_high);
key_high <<= 61;
key_high |= high61_64(key_low);
key_low <<= 61;
key_low |= temp64;
if (Output && (i + 2 <= Rounds))
v_k128_shift(key_high, key_low);
//----------------------------------
// S-Box
//----------------------------------
temp64 = high4_64(key_high); // get highest nibble
key_high &= 0x0FFFFFFFFFFFFFFF; // kill highest nibble
temp64 = Sbox[temp64];
key_high |= low4_64(temp64); // put new value to highest nibble (sbox)
temp64 = high8to4_64(key_high); // get 2. highest nibble
key_high &= 0xF0FFFFFFFFFFFFFF; // kill 2. highest nibble
temp64 = Sbox[temp64];
key_high |=
low8to4_64(temp64); // put new value to 2. highest nibble (sbox)
if (Output && (i + 2 <= Rounds))
v_k128_sbox(key_high, key_low);
//----------------------------------
// Round Salt
//----------------------------------
key_low ^= (((i + 1) & 0x03) << 62); // add counter to lower key part
key_high ^= ((i + 1) >> 2); // add counter to higher key part
if (Output && (i + 2 <= Rounds))
v_k128_round(key_high, key_low, i);
}
}
if (Output)
v_final();
} else {
printf("RAM problem!\n");
exit(0);
}
return subkey;
}
//----------------------------------
// Encryption
//----------------------------------
uint64_t encrypt(uint64_t in, uint64_t *subkey, uint16_t Rounds,
_Bool Roundwise) { // Start encryption
#define out in
uint16_t RoundNr;
uint64_t text;
if (Roundwise)
v_enc_start(in);
for (RoundNr = 1; RoundNr < Rounds; RoundNr++) { // Start "for"
uint16_t temp;
#define SboxNr temp
#define PBit temp
if (Roundwise)
v_roundstart(RoundNr, subkey[RoundNr - 1]);
//----------------------------------
// Xor with roundkey
//----------------------------------
text = in ^ subkey[RoundNr - 1];
if (Roundwise)
v_after_xor(text);
//----------------------------------
// S-Boxes
//----------------------------------
for (SboxNr = 0; SboxNr < 16; SboxNr++) {
uint16_t SboxVal;
SboxVal = text & 0x0F; // get lowest nibble
text &= 0xFFFFFFFFFFFFFFF0; // kill lowest nibble
text |= Sbox[SboxVal]; // put new value to lowest nibble (sbox)
text = rotate4l_64(text); // next(rotate by one nibble)
}
if (Roundwise)
v_after_s(text);
//----------------------------------
// P-Box
//----------------------------------
for (PBit = 0, out = 0; PBit < 64; PBit++) {
out = rotate1l_64(out); // next(rotate by one bit)
out |= ((text >> 63 - Pbox[PBit]) &
1); // put new value to lowest bit (pbox)
}
if (Roundwise)
v_after_p(in);
} // End "for"
text = in ^ subkey[RoundNr - 1];
if (Roundwise)
v_enc_final(text, subkey[RoundNr - 1]);
return text;
} // End encryption
//----------------------------------
// Decryption
//----------------------------------
uint64_t decrypt(uint64_t in, uint64_t *subkey, uint16_t Rounds,
_Bool Roundwise) { // Start decryption
#define out in
uint16_t RoundNr;
uint64_t text;
if (Roundwise)
v_dec_start(in);
for (RoundNr = 1; RoundNr <= Rounds; RoundNr++) { // Start "for"
uint64_t key_temp;
uint16_t temp;
#define SboxNr temp
#define PBit temp
if (Roundwise)
v_roundstart(RoundNr, subkey[Rounds - RoundNr]);
//----------------------------------
// Xor with roundkey
//----------------------------------
text = in ^ subkey[Rounds - RoundNr];
if (Roundwise)
v_after_xor(text);
//----------------------------------
// P-Box
//----------------------------------
for (PBit = 0, out = 0; PBit < 64; PBit++) {
out = rotate1l_64(out); // next(rotate by one bit)
out |= ((text >> 63 - PboxInv[PBit]) &
1); // put new value to lowest bit (pbox)
}
if (Roundwise)
v_after_p(out);
//----------------------------------
// S-Boxes
//----------------------------------
for (SboxNr = 0; SboxNr < 16; SboxNr++) {
uint16_t SboxVal;
SboxVal = out & 0x0F;
out &= 0xFFFFFFFFFFFFFFF0;
out |= SboxInv[SboxVal];
out = rotate4l_64(out);
}
if (Roundwise)
v_after_s(out);
} // End "for"
if (Roundwise)
v_final();
return text;
} // End decryption

View file

@ -0,0 +1,125 @@
/*
* Verbose-Functions for
* Implementation of PRESENT in C
* v2.1, 10/13/2008
*
* Implementation is located at http://www.lightweightcrypto.org/implementations.php,
* under the title "Testvectors for PRESENT".
*
* Thomas Siebert, thomas.siebert@rub.de
*/
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
//----------------------------------
// Function prototypes
//----------------------------------
void v_enc_start(uint64_t);
void v_enc_final(uint64_t, uint64_t);
void v_dec_start(uint64_t);
void v_roundstart(uint16_t, uint64_t);
void v_after_xor(uint64_t);
void v_after_s(uint64_t);
void v_after_p(uint64_t);
void v_final(void);
void v_k80_init(uint64_t, uint64_t);
void v_k80_shift(uint64_t, uint64_t);
void v_k80_sbox(uint64_t, uint64_t);
void v_k80_round(uint64_t, uint64_t, uint16_t);
void v_k128_init(uint64_t, uint64_t);
void v_k128_shift(uint64_t, uint64_t);
void v_k128_sbox(uint64_t, uint64_t);
void v_k128_round(uint64_t, uint64_t, uint16_t);
void v_key_start(void);
//----------------------------------
// Functions
//----------------------------------
void v_enc_start(uint64_t v_plain) {
printf("************************************\n");
printf("Verbose output of PRESENT-encryption\n");
printf("************************************\n\n");
printf("Given Plaintext: %016" PRIx64 " \n\n", v_plain);
}
void v_dec_start(uint64_t v_plain) {
printf("************************************\n");
printf("Verbose output of PRESENT-decryption\n");
printf("************************************\n\n");
printf("Given Ciphertext: %016" PRIx64 " \n", v_plain);
}
void v_roundstart(uint16_t v_round, uint64_t v_key) {
printf("--------------------------------------\n");
printf("Round %" PRIu16 "\n", v_round);
printf("Subkey: %016" PRIx64 "\n\n", v_key);
printf("Text after...\n");
}
void v_enc_final(uint64_t v_final_text, uint64_t v_key) {
printf("--------------------------------------\n");
printf("Final Round\n\n");
printf("Subkey: %016" PRIx64 "\n", v_key);
printf("Text: %016" PRIx64 " \n\n", v_final_text);
v_final();
}
void v_final(void) {
printf("************************************\n");
printf("End of verbose output\n");
printf("************************************\n");
}
void v_after_xor(uint64_t v_xor) {
printf("...Key-Xor: %016" PRIx64 " \n", v_xor);
}
void v_after_s(uint64_t v_s) { printf(".....S-Box: %016" PRIx64 " \n", v_s); }
void v_after_p(uint64_t v_p) { printf(".....P-Box: %016" PRIx64 " \n", v_p); }
void v_k128_init(uint64_t key_high, uint64_t key_low) {
printf("Input: %016" PRIx64 " %016" PRIx64 "\n\n", key_high, key_low);
printf("Subkey Round 1: >>%016" PRIx64 "<<\n\n", key_high);
}
void v_k128_shift(uint64_t key_high, uint64_t key_low) {
printf("...after Shift: %016" PRIx64 " %016" PRIx64 "\n", key_high, key_low);
}
void v_k128_sbox(uint64_t key_high, uint64_t key_low) {
printf("...after S-Box: %016" PRIx64 " %016" PRIx64 "\n", key_high, key_low);
}
void v_k128_round(uint64_t key_high, uint64_t key_low, uint16_t i) {
printf("Subkey Round %" PRIu16 " (after Salt): >>%016" PRIx64 "<< %016" PRIx64
"\n\n",
i + 2, key_high, key_low);
}
void v_k80_init(uint64_t key_high, uint64_t key_low) {
printf("Input: %016" PRIx64 " %04" PRIx64 "\n\n", key_high,
(key_low & 0xFFFF));
printf("Subkey Round 1: >>%016" PRIx64 "<<\n\n", key_high);
}
void v_k80_shift(uint64_t key_high, uint64_t key_low) {
printf("...after Shift: %016" PRIx64 " %04" PRIx64 "\n", key_high, key_low);
}
void v_k80_sbox(uint64_t key_high, uint64_t key_low) {
printf("...after S-Box: %016" PRIx64 " %04" PRIx64 "\n", key_high, key_low);
}
void v_k80_round(uint64_t key_high, uint64_t key_low, uint16_t i) {
printf("Subkey Round %" PRIu16 " (after Salt): >>%016" PRIx64 "<< %04" PRIx64
"\n\n",
i + 2, key_high, key_low);
}
void v_key_start(void) {
printf("**************************************\n");
printf("Verbose output of PRESENT-Key-Schedule\n");
printf("**************************************\n\n");
}

View file

@ -0,0 +1,26 @@
CAPI=2:
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:dv:prim_present_sim:0.1"
description: "PRESENT block cipher DV sim target"
filesets:
files_rtl:
depend:
- lowrisc:prim:all
file_type: systemVerilogSource
files_dv:
depend:
- lowrisc:dv:crypto_dpi_present:0.1
files:
- tb/prim_present_tb.sv
file_type: systemVerilogSource
targets:
sim:
toplevel: prim_present_tb
filesets:
- files_rtl
- files_dv
default_tool: vcs

View file

@ -0,0 +1,61 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
// Name of the sim cfg - typically same as the name of the DUT.
name: prim_present
// Top level dut name (sv module).
dut: prim_present
// Top level testbench name (sv module).
tb: prim_present_tb
// Simulator used to sign off this block
tool: vcs
// Fusesoc core file used for building the file list.
fusesoc_core: lowrisc:dv:prim_present_sim:0.1
// Testplan hjson file.
testplan: "{proj_root}/hw/ip/prim/dv/prim_present/prim_present_testplan.hjson"
// Import additional common sim cfg files.
import_cfgs: ["{proj_root}/hw/dv/data/common_sim_cfg.hjson"]
// Add additional tops for simulation.
//sim_tops: ["-top prim_present_bind"]
sim_tops: []
// Default iterations for all tests - each test entry can override this.
reseed: 50
// Add excl files to tool_srcs so that it gets copied over to the scratch area.
//tool_srcs: ["{proj_root}/hw/ip/prim/dv/prim_present/prim_present_cov_excl.el"]
tool_srcs: []
// Refer to the excl file with vcs_cov_excl_files var with the rel path from tool_srcs_dir
// so the VCS can find it.
//vcs_cov_excl_files: ["{tool_srcs_dir}/prim_present_cov_excl.el"]
vcs_cov_excl_files: []
// Default UVM test and seq class name.
uvm_test: ""
uvm_test_seq: ""
// List of test specifications.
tests: [
{
name: prim_present_sanity
}
]
// List of regressions.
regressions: [
{
name: sanity
tests: ["prim_present_sanity"]
}
]
}

View file

@ -0,0 +1,14 @@
{
name: "prim_present"
import_testplans: []
entries: [
{
name: sanity
desc: '''Simple PRESENT test - feed golden and random testvectors into
both the encryption and decryption algorithms and verify that all
outputs match those from the reference model.'''
milestone: V2
tests: ["prim_present_sanity"]
}
]
}

View file

@ -0,0 +1,243 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_present, drives various test vectors into all
// implementations and compares intermediate and final output against
// C-reference model, for both encryption and decryption.
//
// This testbench only tests the PRESENT block cipher in its 64-bit data
// and 128-bit key configuration, other configurations with different sets of
// widths remain untested.
module prim_present_tb;
//////////////////////////////////////////////////////
// config
//////////////////////////////////////////////////////
// Default to {data_width:64, key_width:128} configuration.
// Data width and key width can be overriden from command-line if desired.
`ifdef DATA_WIDTH
localparam int unsigned DataWidth = `DATA_WIDTH;
`else
localparam int unsigned DataWidth = 64;
`endif
`ifdef KEY_WIDTH
localparam int unsigned KeyWidth = `KEY_WIDTH;
`else
localparam int unsigned KeyWidth = 128;
`endif
// Max number of rounds according to spec.
// We redefine this parameter here to avoid clutter from the long package identifier.
localparam int unsigned NumRounds = crypto_dpi_present_pkg::NumRounds;
// used to index the data arrays
localparam bit Encrypt = 1'b0;
localparam bit Decrypt = 1'b1;
// this parameter is required for the DPI model.
localparam KeySize80 = (KeyWidth == 80);
//////////////////////////////////////////////////////
// DUTs for both encryption and decryption
//////////////////////////////////////////////////////
// data_in[0]: encryption, data_in[1]: decryption.
// Same scheme used for key_in, data_out, key_out.
logic [1:0][NumRounds-1:0][DataWidth-1:0] data_in;
logic [1:0][NumRounds-1:0][KeyWidth-1 :0] key_in;
logic [1:0][NumRounds-1:0][DataWidth-1:0] data_out;
logic [1:0][NumRounds-1:0][KeyWidth-1 :0] key_out;
for (genvar j = 0; j < 2; j++) begin : gen_encrypt_decrypt
for (genvar k = 0; k < NumRounds; k++) begin : gen_duts
prim_present #(
.DataWidth ( DataWidth ),
.KeyWidth ( KeyWidth ),
.NumRounds ( k+1 ),
.Decrypt ( j )
) dut (
.data_i ( data_in[j][k] ),
.key_i ( key_in[j][k] ),
.data_o ( data_out[j][k] ),
.key_o ( key_out[j][k] )
);
end
end
//////////////////////////////////////////////////////
// API called by the testbench to drive/check stimulus
//////////////////////////////////////////////////////
// Top level API task that should be called to run a full pass
// of encryption and decryption on some input data and key.
task test_present(bit [DataWidth-1:0] plaintext,
bit [KeyWidth-1:0] key);
bit [NumRounds:0][63:0] key_schedule;
bit [NumRounds-1:0][DataWidth-1:0] encrypted_text;
crypto_dpi_present_pkg::sv_dpi_present_get_key_schedule(key, KeySize80, key_schedule);
$display("Starting encryption pass with data[0x%0x] and key[0x%0x]!", plaintext, key);
check_encryption(plaintext, key, key_schedule, encrypted_text);
$display("Starting decryption pass!");
check_decryption(encrypted_text, key, key_out[Encrypt]);
endtask
// Helper task to drive plaintext and key into each encryption instance.
// Calls a subroutine to perform checks on the outputs (once they are available).
task check_encryption(input bit [DataWidth-1:0] plaintext,
input bit [KeyWidth-1:0] key,
input bit [NumRounds:0][63:0] key_schedule,
output bit [NumRounds-1:0][DataWidth-1:0] expected_ciphertext);
// Drive input into encryption instances.
for (int unsigned i = 0; i < NumRounds; i++) begin
data_in[Encrypt][i] = plaintext;
key_in[Encrypt][i] = key;
end
// Wait a bit for the DUTs to finish calculations.
#100ns;
// query DPI model for expected encrypted output.
crypto_dpi_present_pkg::sv_dpi_present_encrypt(plaintext, key,
KeySize80, expected_ciphertext);
check_output(key_schedule[NumRounds:1], expected_ciphertext,
key_out[Encrypt], data_out[Encrypt], "Encryption");
endtask
// Helper task to drive ciphertext and key into each decryption instance.
// Calls a subroutine to perform checks on the outputs (once they are available).
task check_decryption(input bit [NumRounds-1:0][DataWidth-1:0] ciphertext,
input bit [KeyWidth-1:0] key,
input bit [NumRounds-1:0][KeyWidth-1:0] decryption_keys);
// the expected plaintext after decryption will be provided by the C model.
bit [NumRounds-1:0][DataWidth-1:0] expected_plaintext;
// the expected key after decryption will simply be the original key.
// the C model only provides a key schedule, which is not useful here.
bit [NumRounds-1:0][63:0] expected_key;
for (int i = 0; i < NumRounds; i++) begin
expected_key[i] = key[KeyWidth-1:KeyWidth-64];
end
// Drive input into decryption instances.
data_in[Decrypt] = ciphertext;
key_in[Decrypt] = decryption_keys;
// Wait a bit for the DUTs to finish calculations.
#100ns;
// query DPI model for expected decrypted output.
crypto_dpi_present_pkg::sv_dpi_present_decrypt(ciphertext, key, KeySize80, expected_plaintext);
check_output(expected_key, expected_plaintext,
key_out[Decrypt], data_out[Decrypt], "Decryption");
endtask
// Helper subroutine to compare key and data output values from
// the C-reference model and the DUTs.
//
// For each instance of PRESENT (whether encryption or decryption),
// we need to perform two checks:
// 1) Check that the output key matches the corresponding key in the schedule.
// 2) Check that the output data matches the output of the reference model.
//
// If any comparison error is seen, this task short-circuits immediately,
// printing out some debug information and the correct failure signature.
task check_output(input bit [NumRounds-1:0][63:0] expected_key,
input bit [NumRounds-1:0][DataWidth-1:0] expected_text,
input bit [NumRounds-1:0][KeyWidth-1:0] actual_key,
input bit [NumRounds-1:0][DataWidth-1:0] actual_data,
input string msg);
bit error = 1'b0;
for (int unsigned i = 0; i < NumRounds; i++) begin
// compare the output key to the corresponding key in the schedule.
if (expected_key[i] != actual_key[i][KeyWidth-1:KeyWidth-64]) begin
error = 1'b1;
$error("%s output key mismatch at round %0d! Expected[0x%0x] - Actual[0x%0x]",
msg, i, expected_key[i], actual_key[i][KeyWidth-1:KeyWidth-64]);
break;
end
// compare encrypted output text to reference model
if (expected_text[i] != actual_data[i]) begin
error = 1'b1;
$error("%s output text mismatch at round %0d! Expected[0x%0x] - Actual[0x%0x]",
msg, i, expected_text[i], actual_data[i]);
break;
end
end
if (error) $fatal("TEST FAILED CHECKS");
endtask
//////////////////////////////////////////////////////
// main testbench body
//////////////////////////////////////////////////////
initial begin : p_stimuli
// The key and plaintext/ciphertext to be fed into PRESENT instances.
bit [KeyWidth-1:0] key;
bit [DataWidth-1:0] plaintext;
$timeformat(-12, 0, " ps", 12);
/////////////////////////////
// Test the 4 golden vectors.
/////////////////////////////
plaintext = '0;
key = '0;
test_present(plaintext, key);
plaintext = '0;
key = '1;
test_present(plaintext, key);
plaintext = '1;
key = '0;
test_present(plaintext, key);
plaintext = '1;
key = '1;
test_present(plaintext, key);
// Test random vectors
for (int i = 0; i < 5000; i++) begin
if (!std::randomize(plaintext)) begin
$fatal("Randomization of plaintext failed!");
end
if (!std::randomize(key)) begin
$fatal("Randomization of key failed!");
end
test_present(plaintext, key);
end
// Final error checking and print out the test signature (expected by simulation flow).
$display("All encryption and decryption passes were successful!");
$display("TEST PASSED CHECKS");
$finish();
end
endmodule : prim_present_tb

View file

@ -8,10 +8,7 @@ filesets:
files_formal:
depend:
- lowrisc:prim:all
# TODO: add more dependencies here if needed
files:
- vip/prim_arbiter_ppc_assert_fpv.sv
- tb/prim_arbiter_ppc_bind_fpv.sv
- tb/prim_arbiter_ppc_fpv.sv
file_type: systemVerilogSource

View file

@ -8,10 +8,7 @@ filesets:
files_formal:
depend:
- lowrisc:prim:all
# TODO: add more dependencies here if needed
files:
- vip/prim_arbiter_tree_assert_fpv.sv
- tb/prim_arbiter_tree_bind_fpv.sv
- tb/prim_arbiter_tree_fpv.sv
file_type: systemVerilogSource

View file

@ -1,25 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
module prim_arbiter_ppc_bind_fpv;
bind prim_arbiter_ppc prim_arbiter_ppc_assert_fpv #(
.N(N),
.DW(DW)
) i_prim_arbiter_ppc_assert_fpv (
.clk_i,
.rst_ni,
.req_i,
.data_i,
.gnt_o,
.idx_o,
.valid_o,
.data_o,
.ready_i
);
endmodule : prim_arbiter_ppc_bind_fpv

View file

@ -6,24 +6,31 @@
// Intended to be used with a formal tool.
module prim_arbiter_ppc_fpv #(
parameter int unsigned N = 4,
parameter int unsigned DW = 32
parameter int unsigned N = 8,
parameter int unsigned DW = 32,
parameter bit EnDataPort = 1,
parameter bit EnReqStabA = 1,
localparam int IdxW = $clog2(N)
) (
input clk_i,
input rst_ni,
input [N-1:0] req_i,
input [DW-1:0]data_i [N],
output logic[N-1:0] gnt_o,
output logic[$clog2(N)-1:0] idx_o,
output logic valid_o,
output logic[DW-1:0] data_o,
input ready_i
input clk_i,
input rst_ni,
input [ N-1:0] req_i,
input [DW-1:0] data_i [N],
output logic [ N-1:0] gnt_o,
output logic [IdxW-1:0] idx_o,
output logic valid_o,
output logic [DW-1:0] data_o,
input ready_i
);
prim_arbiter_ppc #(
.N(N),
.DW(DW)
.DW(DW),
.EnDataPort(EnDataPort),
.EnReqStabA(EnReqStabA)
) i_prim_arbiter_ppc (
.clk_i,
.rst_ni,

View file

@ -1,26 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
module prim_arbiter_tree_bind_fpv;
bind prim_arbiter_tree prim_arbiter_tree_assert_fpv #(
.N(N),
.DW(DW),
.Lock(Lock)
) i_prim_arbiter_tree_assert_fpv (
.clk_i,
.rst_ni,
.req_i,
.data_i,
.gnt_o,
.idx_o,
.valid_o,
.data_o,
.ready_i
);
endmodule : prim_arbiter_tree_bind_fpv

View file

@ -6,25 +6,31 @@
// Intended to be used with a formal tool.
module prim_arbiter_tree_fpv #(
parameter int unsigned N = 4,
parameter int unsigned DW = 32,
parameter bit Lock = 1'b1
parameter int N = 8,
parameter int DW = 32,
parameter bit EnDataPort = 1,
parameter bit EnReqStabA = 1,
localparam int IdxW = $clog2(N)
) (
input clk_i,
input rst_ni,
input [N-1:0] req_i,
input [DW-1:0]data_i [N],
output logic[N-1:0] gnt_o,
output logic[$clog2(N)-1:0] idx_o,
output logic valid_o,
output logic[DW-1:0] data_o,
input ready_i
input clk_i,
input rst_ni,
input [ N-1:0] req_i,
input [DW-1:0] data_i [N],
output logic [ N-1:0] gnt_o,
output logic [IdxW-1:0] idx_o,
output logic valid_o,
output logic [DW-1:0] data_o,
input ready_i
);
prim_arbiter_tree #(
.N(N),
.DW(DW),
.Lock(Lock)
.EnDataPort(EnDataPort),
.EnReqStabA(EnReqStabA)
) i_prim_arbiter_tree (
.clk_i,
.rst_ni,
@ -37,4 +43,5 @@ module prim_arbiter_tree_fpv #(
.ready_i
);
endmodule : prim_arbiter_tree_fpv

View file

@ -1,47 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Assertions for prim_arbiter_ppc.
// Intended to be used with a formal tool.
`include "prim_assert.sv"
module prim_arbiter_ppc_assert_fpv #(
parameter int unsigned N = 4,
parameter int unsigned DW = 32
) (
input clk_i,
input rst_ni,
input [N-1:0] req_i,
input [DW-1:0]data_i [N],
input logic[N-1:0] gnt_o,
input logic[$clog2(N)-1:0] idx_o,
input logic valid_o,
input logic[DW-1:0] data_o,
input ready_i
);
///////////////////////////////
// Declarations & Parameters //
///////////////////////////////
/////////////////
// Assumptions //
/////////////////
// `ASSUME(MyAssumption_M, ..., clk_i, !rst_ni)
////////////////////////
// Forward Assertions //
////////////////////////
// `ASSERT(MyFwdAssertion_A, ..., clk_i, !rst_ni)
/////////////////////////
// Backward Assertions //
/////////////////////////
// `ASSERT(MyBkwdAssertion_A, ..., clk_i, !rst_ni)
endmodule : prim_arbiter_ppc_assert_fpv

View file

@ -1,48 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Assertions for prim_arbiter_tree.
// Intended to be used with a formal tool.
`include "prim_assert.sv"
module prim_arbiter_tree_assert_fpv #(
parameter int unsigned N = 4,
parameter int unsigned DW = 32,
parameter bit Lock = 1'b1
) (
input clk_i,
input rst_ni,
input [N-1:0] req_i,
input [DW-1:0]data_i [N],
input logic[N-1:0] gnt_o,
input logic[$clog2(N)-1:0] idx_o,
input logic valid_o,
input logic[DW-1:0] data_o,
input ready_i
);
///////////////////////////////
// Declarations & Parameters //
///////////////////////////////
/////////////////
// Assumptions //
/////////////////
// `ASSUME(MyAssumption_M, ..., clk_i, !rst_ni)
////////////////////////
// Forward Assertions //
////////////////////////
// `ASSERT(MyFwdAssertion_A, ..., clk_i, !rst_ni)
/////////////////////////
// Backward Assertions //
/////////////////////////
// `ASSERT(MyBkwdAssertion_A, ..., clk_i, !rst_ni)
endmodule : prim_arbiter_tree_assert_fpv

View file

@ -1,4 +1,14 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
`verilator_config
// prim_subreg
// for RO wd is not used
lint_off -rule UNUSED -file "*/rtl/prim_subreg.sv" -match "Signal is not used: 'wd'"
// prim_fifo_sync
// In passthrough mode, clk and reset are not read form within this module
lint_off -rule UNUSED -file "*/rtl/prim_fifo_sync.sv" -match "*clk_i*"
lint_off -rule UNUSED -file "*/rtl/prim_fifo_sync.sv" -match "*rst_ni*"

View file

@ -57,7 +57,5 @@ waive -rules {HIER_BRANCH_NOT_READ} -location {tlul_fifo_sync.sv} -regexp {Conne
# primitivies: prim_ram_2p_wrapper
#
#waive -rules INPUT_NOT_READ -location {prim_ram_*_wrapper*} -regexp {cfg_i} \
# -comment "Register model doesn't need config port"
#waive -rules NOT_READ -location {prim_ram_*_wrapper*} -regexp {(a|b)_rdata_(q|d)\[38} \
# -comment "Syndrome is not going out to the interface"

View file

@ -46,7 +46,6 @@ targets:
- '-CFLAGS "-std=c++11 -Wall -DVM_TRACE_FMT_FST -DTOPLEVEL_NAME=prim_sync_reqack_tb -g -O0"'
- '-LDFLAGS "-pthread -lutil -lelf"'
- "-Wall"
- "-Wno-PINCONNECTEMPTY"
# XXX: Cleanup all warnings and remove this option
# (or make it more fine-grained at least)
- "-Wno-fatal"

View file

@ -10,6 +10,7 @@ filesets:
files_rtl:
depend:
- lowrisc:prim:assert
- lowrisc:prim:util
- lowrisc:prim:diff_decode # for prim_alert_sender/receiver
- lowrisc:prim:pad_wrapper
- lowrisc:prim:prim_pkg

View file

@ -9,6 +9,9 @@ filesets:
files_rtl:
files:
- rtl/prim_assert.sv : {is_include_file : true}
- rtl/prim_assert_dummy_macros.svh : {is_include_file : true}
- rtl/prim_assert_yosys_macros.svh : {is_include_file : true}
- rtl/prim_assert_standard_macros.svh : {is_include_file : true}
file_type: systemVerilogSource
targets:

View file

@ -8,7 +8,7 @@ description: "Utilities"
filesets:
files_rtl:
files:
- rtl/prim_util.svh : {is_include_file : true}
- rtl/prim_util_pkg.sv
file_type: systemVerilogSource
targets:

View file

@ -5,45 +5,51 @@
// N:1 arbiter module
//
// Verilog parameter
// N: Number of request ports
// DW: Data width
// N: Number of request ports
// DW: Data width
// DataPort: Set to 1 to enable the data port. Otherwise that port will be ignored.
// EnReqStabA: Checks whether requests remain asserted until granted
//
// This is the original implementation of the arbiter which relies on parallel prefix
// computing optimization to optimize the request / arbiter tree. Not all synthesis tools
// may support this.
// This is the original implementation of the arbiter which relies on parallel prefix computing
// optimization to optimize the request / arbiter tree. Not all synthesis tools may support this.
//
// Note that the currently winning request is held if the data sink is not ready.
// This behavior is required by some interconnect protocols (AXI, TL). Note that
// this implies that an asserted request must stay asserted
// until it has been granted. Note that for PPC, this option cannot
// be disabled.
// Note that the currently winning request is held if the data sink is not ready. This behavior is
// required by some interconnect protocols (AXI, TL). The module contains an assertion that checks
// this behavior.
//
// Also, this module contains a request stability assertion that checks that requests stay asserted
// until they have been served. This assertion can be optionally disabled by setting EnReqStabA to
// zero. This is a non-functional parameter and does not affect the designs behavior.
//
// See also: prim_arbiter_tree
`include "prim_assert.sv"
module prim_arbiter_ppc #(
parameter int unsigned N = 4,
parameter int unsigned N = 8,
parameter int unsigned DW = 32,
// Configurations
// EnDataPort: {0, 1}, if 0, input data will be ignored
parameter int EnDataPort = 1,
parameter bit EnDataPort = 1,
// Non-functional parameter to switch on the request stability assertion
parameter bit EnReqStabA = 1,
// Derived parameters
localparam int unsigned IdxW = $clog2(N)
localparam int IdxW = $clog2(N)
) (
input clk_i,
input rst_ni,
input [ N-1:0] req_i,
input [DW-1:0] data_i [N],
output logic [ N-1:0] gnt_o,
output logic [IdxW-1:0] idx_o,
input [ N-1:0] req_i,
input [DW-1:0] data_i [N],
output logic [ N-1:0] gnt_o,
output logic [IdxW-1:0] idx_o,
output logic valid_o,
output logic [DW-1:0] data_o,
input ready_i
output logic valid_o,
output logic [DW-1:0] data_o,
input ready_i
);
`ASSERT_INIT(CheckNGreaterZero_A, N > 0)
@ -118,20 +124,23 @@ module prim_arbiter_ppc #(
end
end
end
////////////////
// assertions //
////////////////
// grant shall be higher index than prev. unless no higher requests exist
`ASSERT(RoundRobin_A, valid_o && ready_i && $past(ready_i) && $past(valid_o) &&
|(masked_req) |-> idx_o > $past(idx_o))
end
////////////////
// assertions //
////////////////
// KNOWN assertions on outputs, except for data as that may be partially X in simulation
// e.g. when used on a BUS
`ASSERT_KNOWN(ValidKnown_A, valid_o)
`ASSERT_KNOWN(GrantKnown_A, gnt_o)
`ASSERT_KNOWN(IdxKnown_A, idx_o)
// grant index shall be higher index than previous index, unless no higher requests exist.
`ASSERT(RoundRobin_A,
##1 valid_o && ready_i && $past(ready_i) && $past(valid_o) &&
|(req_i & ~((N'(1) << $past(idx_o)+1) - 1)) |->
idx_o > $past(idx_o))
// we can only grant one requestor at a time
`ASSERT(CheckHotOne_A, $onehot0(gnt_o))
// A grant implies that the sink is ready
@ -148,27 +157,69 @@ module prim_arbiter_ppc #(
`ASSERT(NoReadyValidNoGrant_A, !(ready_i || valid_o) |-> gnt_o == 0)
// check index / grant correspond
`ASSERT(IndexIsCorrect_A, ready_i && valid_o |-> gnt_o[idx_o] && req_i[idx_o])
if (EnDataPort) begin: gen_data_port_assertion
// data flow
`ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o])
// KNOWN assertions on outputs, except for data as that may be partially X in simulation
// e.g. when used on a BUS
`ASSERT_KNOWN(ValidKnown_A, valid_o)
`ASSERT_KNOWN(GrantKnown_A, gnt_o)
`ASSERT_KNOWN(IdxKnown_A, idx_o)
`ifndef SYNTHESIS
// A grant implies a request
int unsigned k; // this is a symbolic variable
`ASSUME(KStable_M, ##1 $stable(k), clk_i, !rst_ni)
`ASSUME(KRange_M, k < N, clk_i, !rst_ni)
`ASSERT(GntImpliesReq_A, gnt_o[k] |-> req_i[k])
end
if (EnReqStabA) begin : gen_lock_assertion
// requests must stay asserted until they have been granted
`ASSUME(ReqStaysHighUntilGranted_M, (|req_i) && !ready_i |=>
`ASSUME(ReqStaysHighUntilGranted0_M, (|req_i) && !ready_i |=>
(req_i & $past(req_i)) == $past(req_i))
// check that the arbitration decision is held if the sink is not ready
`ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o))
end
// FPV-only assertions with symbolic variables
`ifdef FPV_ON
// symbolic variables
int unsigned k;
bit ReadyIsStable;
bit ReqsAreStable;
// constraints for symbolic variables
`ASSUME(KStable_M, ##1 $stable(k))
`ASSUME(KRange_M, k < N)
// this is used enable checking for stable and unstable ready_i and req_i signals in the same run.
// the symbolic variables act like a switch that the solver can trun on and off.
`ASSUME(ReadyIsStable_M, ##1 $stable(ReadyIsStable))
`ASSUME(ReqsAreStable_M, ##1 $stable(ReqsAreStable))
`ASSUME(ReadyStable_M, ##1 !ReadyIsStable || $stable(ready_i))
`ASSUME(ReqsStable_M, ##1 !ReqsAreStable || $stable(req_i))
// A grant implies a request
`ASSERT(GntImpliesReq_A, gnt_o[k] |-> req_i[k])
// if request and ready are constantly held at 1, we should eventually get a grant
`ASSERT(NoStarvation_A,
ReqsAreStable && ReadyIsStable && ready_i && req_i[k] |->
strong(##[0:$] gnt_o[k]))
// if N requests are constantly asserted and ready is constant 1, each request must
// be granted exactly once over a time window of N cycles for the arbiter to be fair.
for (genvar n = 1; n <= N; n++) begin : gen_fairness
integer gnt_cnt;
`ASSERT(Fairness_A,
ReqsAreStable && ReadyIsStable && ready_i && req_i[k] &&
$countones(req_i) == n |->
##n gnt_cnt == $past(gnt_cnt, n) + 1)
always_ff @(posedge clk_i or negedge rst_ni) begin : p_cnt
if (!rst_ni) begin
gnt_cnt <= 0;
end else begin
gnt_cnt <= gnt_cnt + gnt_o[k];
end
end
end
if (EnReqStabA) begin : gen_lock_assertion_fpv
// requests must stay asserted until they have been granted
`ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] & !gnt_o[k] |=>
req_i[k], clk_i, !rst_ni)
end
`endif
endmodule
endmodule : prim_arbiter_ppc

View file

@ -5,43 +5,58 @@
// N:1 arbiter module
//
// Verilog parameter
// N: Number of request ports
// DW: Data width
// Lock: Lock arbiter decision when destination is not ready
// N: Number of request ports
// DW: Data width
// DataPort: Set to 1 to enable the data port. Otherwise that port will be ignored.
// EnReqStabA: Checks whether requests remain asserted until granted
//
// Hand optimized version which implements a binary tree to optimize
// timing. In particular, arbitration decisions and data mux steering happen
// simultaneously on the corresponding tree level, which leads to improved propagation
// delay compared to a solution that arbitrates first, followed by a data mux selection.
// This is a tree implementation of a round robin arbiter. It has the same behavior as the PPC
// implementation in prim_arbiter_ppc, and also uses a prefix summing approach to determine the next
// request to be granted. The main difference with respect to the PPC arbiter is that the leading 1
// detection and the prefix summation are performed with a binary tree instead of a sequential loop.
// Also, if the data port is enabled, the data is muxed based on the local arbitration decisions at
// each node of the arbiter tree. This means that the data can propagate through the tree
// simultaneously with the requests, instead of waiting for the arbitration to determine the winner
// index first. As a result, this design has a shorter critical path than other implementations,
// leading to better ovberall timing.
//
// If Lock is turned on, the currently winning request is held if the
// data sink is not ready. This behavior is required by some interconnect
// protocols (AXI, TL), and hence it is turned on by default.
// Note that this implies that an asserted request must stay asserted
// until it has been granted.
// Note that the currently winning request is held if the data sink is not ready. This behavior is
// required by some interconnect protocols (AXI, TL). The module contains an assertion that checks
// this behavior.
//
// Also, this module contains a request stability assertion that checks that requests stay asserted
// until they have been served. This assertion can be optionally disabled by setting EnReqStabA to
// zero. This is a non-functional parameter and does not affect the designs behavior.
//
// See also: prim_arbiter_ppc
`include "prim_assert.sv"
module prim_arbiter_tree #(
parameter int unsigned N = 4,
parameter int unsigned DW = 32,
// holds the last arbiter decision in case the sink is not ready
// this should be enabled when used in AXI or TL protocols.
parameter bit Lock = 1'b1
parameter int N = 8,
parameter int DW = 32,
// Configurations
// EnDataPort: {0, 1}, if 0, input data will be ignored
parameter bit EnDataPort = 1,
// Non-functional parameter to switch on the request stability assertion
parameter bit EnReqStabA = 1,
// Derived parameters
localparam int IdxW = $clog2(N)
) (
input clk_i,
input rst_ni,
input [ N-1:0] req_i,
input [DW-1:0] data_i [N],
output logic [ N-1:0] gnt_o,
output logic [$clog2(N)-1:0] idx_o,
input [ N-1:0] req_i,
input [DW-1:0] data_i [N],
output logic [ N-1:0] gnt_o,
output logic [IdxW-1:0] idx_o,
output logic valid_o,
output logic [DW-1:0] data_o,
input ready_i
output logic valid_o,
output logic [DW-1:0] data_o,
input ready_i
);
`ASSERT_INIT(CheckNGreaterZero_A, N > 0)
@ -58,34 +73,16 @@ module prim_arbiter_tree #(
// align to powers of 2 for simplicity
// a full binary tree with N levels has 2**N + 2**N-1 nodes
localparam int unsigned NumLevels = $clog2(N);
logic [N-1:0] req;
logic [2**(NumLevels+1)-2:0] req_tree;
logic [2**(NumLevels+1)-2:0] gnt_tree;
logic [2**(NumLevels+1)-2:0][NumLevels-1:0] idx_tree;
logic [2**(NumLevels+1)-2:0][DW-1:0] data_tree;
logic [NumLevels-1:0] rr_q;
logic [2**(IdxW+1)-2:0] req_tree;
logic [2**(IdxW+1)-2:0] prio_tree;
logic [2**(IdxW+1)-2:0] rdy_tree;
logic [2**(IdxW+1)-2:0] sel_tree;
logic [2**(IdxW+1)-2:0] mask_tree;
logic [2**(IdxW+1)-2:0][IdxW-1:0] idx_tree;
logic [2**(IdxW+1)-2:0][DW-1:0] data_tree;
logic [2**IdxW-1:0] prio_mask_d, prio_mask_q;
// req_locked
if (Lock) begin : gen_lock
logic [N-1:0] mask_d, mask_q;
// if the request cannot be served, we store the current request bits
// and apply it as a mask to the incoming requests in the next cycle.
assign mask_d = (valid_o && (!ready_i)) ? req : {N{1'b1}};
assign req = mask_q & req_i;
always_ff @(posedge clk_i) begin : p_lock_regs
if (!rst_ni) begin
mask_q <= {N{1'b1}};
end else begin
mask_q <= mask_d;
end
end
end else begin : gen_no_lock
assign req = req_i;
end
for (genvar level = 0; level < NumLevels+1; level++) begin : gen_tree
for (genvar level = 0; level < IdxW+1; level++) begin : gen_tree
//
// level+1 C0 C1 <- "Base1" points to the first node on "level+1",
// \ / these nodes are the children of the nodes one level below
@ -97,89 +94,114 @@ module prim_arbiter_tree #(
// C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset
// C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1
//
localparam int unsigned Base0 = (2**level)-1;
localparam int unsigned Base1 = (2**(level+1))-1;
localparam int Base0 = (2**level)-1;
localparam int Base1 = (2**(level+1))-1;
for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level
localparam int unsigned Pa = Base0 + offset;
localparam int unsigned C0 = Base1 + 2*offset;
localparam int unsigned C1 = Base1 + 2*offset + 1;
localparam int Pa = Base0 + offset;
localparam int C0 = Base1 + 2*offset;
localparam int C1 = Base1 + 2*offset + 1;
// this assigns the gated interrupt source signals, their
// corresponding IDs and priorities to the tree leafs
if (level == NumLevels) begin : gen_leafs
if (level == IdxW) begin : gen_leafs
if (offset < N) begin : gen_assign
// forward path
assign req_tree[Pa] = req[offset];
assign idx_tree[Pa] = offset;
assign data_tree[Pa] = data_i[offset];
// backward (grant) path
assign gnt_o[offset] = gnt_tree[Pa];
// forward path (requests and data)
// all requests inputs are assigned to the request tree
assign req_tree[Pa] = req_i[offset];
// we basically split the incoming request vector into two halves with the following
// priority assignment. the prio_mask_q register contains a prefix sum that has been
// computed using the last winning index, and hence masks out all requests at offsets
// lower or equal the previously granted index. hence, all higher indices are considered
// first in the arbitration tree nodes below, before considering the lower indices.
assign prio_tree[Pa] = req_i[offset] & prio_mask_q[offset];
// input for the index muxes (used to compute the winner index)
assign idx_tree[Pa] = offset;
// input for the data muxes
assign data_tree[Pa] = data_i[offset];
// backward path (grants and prefix sum)
// grant if selected, ready and request asserted
assign gnt_o[offset] = req_i[offset] & sel_tree[Pa] & ready_i;
// only update mask if there is a valid request
assign prio_mask_d[offset] = (|req_i) ?
mask_tree[Pa] | sel_tree[Pa] & ~ready_i :
prio_mask_q[offset];
end else begin : gen_tie_off
// forward path
assign req_tree[Pa] = '0;
assign idx_tree[Pa] = '0;
assign data_tree[Pa] = '0;
assign prio_mask_d[offset] = '0;
end
// this creates the node assignments
end else begin : gen_nodes
// NOTE: the code below has been written in this way in order to work
// around a synthesis issue in Vivado 2018.3 and 2019.2 where the whole
// module would be optimized away if these assign statements contained
// ternary statements to implement the muxes.
//
// TODO: rewrite these lines with ternary statmements onec the problem
// has been fixed in the tool.
//
// See also originating issue:
// https://github.com/lowRISC/opentitan/issues/1355
// Xilinx issue:
// https://forums.xilinx.com/t5/Synthesis/Simulation-Synthesis-Mismatch-with-Vivado-2018-3/m-p/1065923#M33849
// local helper variable
logic sel;
always_comb begin : p_node
// forward path (requests and data)
// each node looks at its two children, and selects the one with higher priority
sel = ~req_tree[C0] | ~prio_tree[C0] & prio_tree[C1];
// propagate requests
req_tree[Pa] = req_tree[C0] | req_tree[C1];
prio_tree[Pa] = prio_tree[C1] | prio_tree[C0];
// data and index muxes
idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0];
data_tree[Pa] = (sel) ? data_tree[C1] : data_tree[C0];
// forward path
logic sel; // local helper variable
// this performs a (local) round robin arbitration using the associated rr counter bit
assign sel = ~req_tree[C0] | req_tree[C1] & rr_q[NumLevels-1-level];
// propagate requests
assign req_tree[Pa] = req_tree[C0] | req_tree[C1];
// muxes
assign idx_tree[Pa] = ({NumLevels{sel}} & idx_tree[C1]) |
({NumLevels{~sel}} & idx_tree[C0]);
assign data_tree[Pa] = ({DW{sel}} & data_tree[C1]) |
({DW{~sel}} & data_tree[C0]);
// backward (grant) path
assign gnt_tree[C0] = gnt_tree[Pa] & ~sel;
assign gnt_tree[C1] = gnt_tree[Pa] & sel;
// backward path (grants and prefix sum)
// this propagates the selction index back and computes a hot one mask
sel_tree[C0] = sel_tree[Pa] & ~sel;
sel_tree[C1] = sel_tree[Pa] & sel;
// this performs a prefix sum for masking the input requests in the next cycle
mask_tree[C0] = mask_tree[Pa];
mask_tree[C1] = mask_tree[Pa] | sel_tree[C0];
end
end
end : gen_level
end : gen_tree
// the results can be found at the tree root
assign idx_o = idx_tree[0];
assign data_o = data_tree[0];
assign valid_o = req_tree[0];
// propagate the grant back to the requestors
assign gnt_tree[0] = valid_o & ready_i;
// this is the round robin counter
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if (!rst_ni) begin
rr_q <= '0;
end else begin
if (gnt_tree[0] && (rr_q == N-1)) begin
rr_q <= '0;
end else if (gnt_tree[0]) begin
rr_q <= rr_q + 1'b1;
end
end
if (EnDataPort) begin : gen_data_port
assign data_o = data_tree[0];
end else begin : gen_no_dataport
logic [DW-1:0] unused_data [N];
assign unused_data = data_i;
assign data_o = '1;
end
assign idx_o = idx_tree[0];
assign valid_o = req_tree[0];
// the select tree computes a hot one signal that indicates which request is currently selected
assign sel_tree[0] = 1'b1;
// the mask tree is basically a prefix sum of the hot one select signal computed above
assign mask_tree[0] = 1'b0;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_mask_reg
if (!rst_ni) begin
prio_mask_q <= '0;
end else begin
prio_mask_q <= prio_mask_d;
end
end
end
////////////////
// assertions //
////////////////
// KNOWN assertions on outputs, except for data as that may be partially X in simulation
// e.g. when used on a BUS
`ASSERT_KNOWN(ValidKnown_A, valid_o)
`ASSERT_KNOWN(GrantKnown_A, gnt_o)
`ASSERT_KNOWN(IdxKnown_A, idx_o)
// grant index shall be higher index than previous index, unless no higher requests exist.
`ASSERT(RoundRobin_A,
##1 valid_o && ready_i && $past(ready_i) && $past(valid_o) &&
|(req_i & ~((N'(1) << $past(idx_o)+1) - 1)) |->
idx_o > $past(idx_o))
// we can only grant one requestor at a time
`ASSERT(CheckHotOne_A, $onehot0(gnt_o))
// A grant implies that the sink is ready
@ -196,29 +218,68 @@ module prim_arbiter_tree #(
`ASSERT(NoReadyValidNoGrant_A, !(ready_i || valid_o) |-> gnt_o == 0)
// check index / grant correspond
`ASSERT(IndexIsCorrect_A, ready_i && valid_o |-> gnt_o[idx_o] && req_i[idx_o])
if (EnDataPort) begin: gen_data_port_assertion
// data flow
`ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o])
// KNOWN assertions on outputs, except for data as that may be partially X in simulation
// e.g. when used on a BUS
`ASSERT_KNOWN(ValidKnown_A, valid_o)
`ASSERT_KNOWN(GrantKnown_A, gnt_o)
`ASSERT_KNOWN(IdxKnown_A, idx_o)
end
if (EnReqStabA) begin : gen_lock_assertion
// requests must stay asserted until they have been granted
`ASSUME(ReqStaysHighUntilGranted0_M, (|req_i) && !ready_i |=>
(req_i & $past(req_i)) == $past(req_i))
// check that the arbitration decision is held if the sink is not ready
`ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o))
end
// FPV-only assertions with symbolic variables
`ifdef FPV_ON
// symbolic variables
int unsigned k;
bit ReadyIsStable;
bit ReqsAreStable;
// constraints for symbolic variables
`ASSUME(KStable_M, ##1 $stable(k))
`ASSUME(KRange_M, k < N)
// this is used enable checking for stable and unstable ready_i and req_i signals in the same run.
// the symbolic variables act like a switch that the solver can trun on and off.
`ASSUME(ReadyIsStable_M, ##1 $stable(ReadyIsStable))
`ASSUME(ReqsAreStable_M, ##1 $stable(ReqsAreStable))
`ASSUME(ReadyStable_M, ##1 !ReadyIsStable || $stable(ready_i))
`ASSUME(ReqsStable_M, ##1 !ReqsAreStable || $stable(req_i))
`ifndef SYNTHESIS
// A grant implies a request
int unsigned k; // this is a symbolic variable
`ASSUME(KStable_M, ##1 $stable(k), clk_i, !rst_ni)
`ASSUME(KRange_M, k < N, clk_i, !rst_ni)
`ASSERT(GntImpliesReq_A, gnt_o[k] |-> req_i[k])
if (Lock) begin : gen_lock_assertion
// requests must stay asserted until they have been granted
`ASSUME(ReqStaysHighUntilGranted_M, (|req_i) && !ready_i |=>
(req_i & $past(req_i)) == $past(req_i), clk_i, !rst_ni)
// check that the arbitration decision is held if the sink is not ready
`ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o))
// if request and ready are constantly held at 1, we should eventually get a grant
`ASSERT(NoStarvation_A,
ReqsAreStable && ReadyIsStable && ready_i && req_i[k] |->
strong(##[0:$] gnt_o[k]))
// if N requests are constantly asserted and ready is constant 1, each request must
// be granted exactly once over a time window of N cycles for the arbiter to be fair.
for (genvar n = 1; n <= N; n++) begin : gen_fairness
integer gnt_cnt;
`ASSERT(Fairness_A,
ReqsAreStable && ReadyIsStable && ready_i && req_i[k] &&
$countones(req_i) == n |->
##n gnt_cnt == $past(gnt_cnt, n) + 1)
always_ff @(posedge clk_i or negedge rst_ni) begin : p_cnt
if (!rst_ni) begin
gnt_cnt <= 0;
end else begin
gnt_cnt <= gnt_cnt + gnt_o[k];
end
end
end
if (EnReqStabA) begin : gen_lock_assertion_fpv
// requests must stay asserted until they have been granted
`ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] & !gnt_o[k] |=>
req_i[k], clk_i, !rst_ni)
end
`endif
endmodule
endmodule : prim_arbiter_tree

View file

@ -24,101 +24,59 @@
// Helper macros //
///////////////////
// local helper macro to reduce code clutter. undefined at the end of this file
`ifndef VERILATOR
`ifndef SYNTHESIS
`define INC_ASSERT
`endif
`endif
// Converts an arbitrary block of code into a Verilog string
`define PRIM_STRINGIFY(__x) `"__x`"
// ASSERT_RPT is available to change the reporting mechanism when an assert fails
`define ASSERT_RPT(__name) \
`ifdef UVM \
assert_rpt_pkg::assert_rpt($sformatf("[%m] %s (%s:%0d)", \
__name, `__FILE__, `__LINE__)); \
`else \
$error("[ASSERT FAILED] [%m] %s (%s:%0d)", __name, `__FILE__, `__LINE__); \
`endif
///////////////////////////////////////
// Simple assertion and cover macros //
///////////////////////////////////////
// Default clk and reset signals used by assertion macros below.
`define ASSERT_DEFAULT_CLK clk_i
`define ASSERT_DEFAULT_RST !rst_ni
// Immediate assertion
// Note that immediate assertions are sensitive to simulation glitches.
`define ASSERT_I(__name, __prop) \
`ifdef INC_ASSERT \
__name: assert (__prop) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end \
`endif
// Converts an arbitrary block of code into a Verilog string
`define PRIM_STRINGIFY(__x) `"__x`"
// Assertion in initial block. Can be used for things like parameter checking.
`define ASSERT_INIT(__name, __prop) \
`ifdef INC_ASSERT \
initial begin \
__name: assert (__prop) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end \
end \
`endif
// The basic helper macros are actually defined in "implementation headers". The macros should do
// the same thing in each case (except for the dummy flavour), but in a way that the respective
// tools support.
//
// If the tool supports assertions in some form, we also define INC_ASSERT (which can be used to
// hide signal definitions that are only used for assertions).
//
// The list of basic macros supported is:
//
// ASSERT_I: Immediate assertion. Note that immediate assertions are sensitive to simulation
// glitches.
//
// ASSERT_INIT: Assertion in initial block. Can be used for things like parameter checking.
//
// ASSERT_FINAL: Assertion in final block. Can be used for things like queues being empty at end of
// sim, all credits returned at end of sim, state machines in idle at end of sim.
//
// ASSERT: Assert a concurrent property directly. It can be called as a module (or
// interface) body item.
//
// Note: We use (__rst !== '0) in the disable iff statements instead of (__rst ==
// '1). This properly disables the assertion in cases when reset is X at the
// beginning of a simulation. For that case, (reset == '1) does not disable the
// assertion.
//
// ASSERT_NEVER: Assert a concurrent property NEVER happens
//
// ASSERT_KNOWN: Assert that signal has a known value (each bit is either '0' or '1') after reset.
// It can be called as a module (or interface) body item.
//
// COVER: Cover a concurrent property
//
// ASSUME: Assume a concurrent property
//
// ASSUME_I: Assume an immediate property
// Assertion in final block. Can be used for things like queues being empty
// at end of sim, all credits returned at end of sim, state machines in idle
// at end of sim.
`define ASSERT_FINAL(__name, __prop) \
`ifdef INC_ASSERT \
final begin \
__name: assert (__prop || $test$plusargs("disable_assert_final_checks")) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end \
end \
`endif
// Assert a concurrent property directly.
// It can be called as a module (or interface) body item.
`define ASSERT(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
`ifdef INC_ASSERT \
__name: assert property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop)) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end \
`endif
// Note: Above we use (__rst !== '0) in the disable iff statements instead of
// (__rst == '1). This properly disables the assertion in cases when reset is X at
// the beginning of a simulation. For that case, (reset == '1) does not disable the
// assertion.
// Assert a concurrent property NEVER happens
`define ASSERT_NEVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
`ifdef INC_ASSERT \
__name: assert property (@(posedge __clk) disable iff ((__rst) !== '0) not (__prop)) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end \
`endif
// Assert that signal has a known value (each bit is either '0' or '1') after reset.
// It can be called as a module (or interface) body item.
`define ASSERT_KNOWN(__name, __sig, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
`ifdef INC_ASSERT \
`ASSERT(__name, !$isunknown(__sig), __clk, __rst) \
`endif
// Cover a concurrent property
`define COVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
`ifdef INC_ASSERT \
__name: cover property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop)); \
`ifdef VERILATOR
`include "prim_assert_dummy_macros.svh"
`elsif SYNTHESIS
`include "prim_assert_dummy_macros.svh"
`elsif YOSYS
`include "prim_assert_yosys_macros.svh"
`define INC_ASSERT
`else
`include "prim_assert_standard_macros.svh"
`define INC_ASSERT
`endif
//////////////////////////////
@ -127,46 +85,18 @@
// Assert that signal is an active-high pulse with pulse length of 1 clock cycle
`define ASSERT_PULSE(__name, __sig, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
`ifdef INC_ASSERT \
`ASSERT(__name, $rose(__sig) |=> !(__sig), __clk, __rst) \
`endif
`ASSERT(__name, $rose(__sig) |=> !(__sig), __clk, __rst)
// Assert that a property is true only when an enable signal is set. It can be called as a module
// (or interface) body item.
`define ASSERT_IF(__name, __prop, __enable, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
`ifdef INC_ASSERT \
`ASSERT(__name, (__enable) |-> (__prop), __clk, __rst) \
`endif
`ASSERT(__name, (__enable) |-> (__prop), __clk, __rst)
// Assert that signal has a known value (each bit is either '0' or '1') after reset if enable is
// set. It can be called as a module (or interface) body item.
`define ASSERT_KNOWN_IF(__name, __sig, __enable, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
`ifdef INC_ASSERT \
`ASSERT_KNOWN(__name``KnownEnable, __enable, __clk, __rst) \
`ASSERT_IF(__name, !$isunknown(__sig), __enable, __clk, __rst) \
`endif
///////////////////////
// Assumption macros //
///////////////////////
// Assume a concurrent property
`define ASSUME(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
`ifdef INC_ASSERT \
__name: assume property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop)) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end \
`endif
// Assume an immediate property
`define ASSUME_I(__name, __prop) \
`ifdef INC_ASSERT \
__name: assume (__prop) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end \
`endif
`ASSERT_IF(__name, !$isunknown(__sig), __enable, __clk, __rst)
//////////////////////////////////
// For formal verification only //

View file

@ -0,0 +1,16 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Macro bodies included by prim_assert.sv for tools that don't support assertions. See
// prim_assert.sv for documentation for each of the macros.
`define ASSERT_I(__name, __prop)
`define ASSERT_INIT(__name, __prop)
`define ASSERT_FINAL(__name, __prop)
`define ASSERT(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST)
`define ASSERT_NEVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST)
`define ASSERT_KNOWN(__name, __sig, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST)
`define COVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST)
`define ASSUME(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST)
`define ASSUME_I(__name, __prop)

View file

@ -0,0 +1,67 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Macro bodies included by prim_assert.sv for tools that support full SystemVerilog and SVA syntax.
// See prim_assert.sv for documentation for each of the macros.
// ASSERT_RPT is available to change the reporting mechanism when an assert fails
`define ASSERT_RPT(__name) \
`ifdef UVM \
assert_rpt_pkg::assert_rpt($sformatf("[%m] %s (%s:%0d)", \
__name, `__FILE__, `__LINE__)); \
`else \
$error("[ASSERT FAILED] [%m] %s (%s:%0d)", __name, `__FILE__, `__LINE__); \
`endif
`define ASSERT_I(__name, __prop) \
__name: assert (__prop) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end
`define ASSERT_INIT(__name, __prop) \
initial begin \
__name: assert (__prop) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end \
end
`define ASSERT_FINAL(__name, __prop) \
final begin \
__name: assert (__prop || $test$plusargs("disable_assert_final_checks")) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end \
end
`define ASSERT(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
__name: assert property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop)) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end
`define ASSERT_NEVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
__name: assert property (@(posedge __clk) disable iff ((__rst) !== '0) not (__prop)) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end
`define ASSERT_KNOWN(__name, __sig, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
`ASSERT(__name, !$isunknown(__sig), __clk, __rst)
`define COVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
__name: cover property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop));
`define ASSUME(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
__name: assume property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop)) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end
`define ASSUME_I(__name, __prop) \
__name: assume (__prop) \
else begin \
`ASSERT_RPT(`PRIM_STRINGIFY(__name)) \
end

View file

@ -0,0 +1,47 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Macro bodies included by prim_assert.sv for formal verification with Yosys. See prim_assert.sv
// for documentation for each of the macros.
`define ASSERT_I(__name, __prop) \
always_comb begin : __name \
assert (__prop); \
end
`define ASSERT_INIT(__name, __prop) \
initial begin : __name \
assert (__prop); \
end
// This doesn't make much sense for a formal tool (we never get to the final block!)
`define ASSERT_FINAL(__name, __prop)
`define ASSERT(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
always_ff @(posedge __clk) begin \
if (! (__rst !== '0)) __name: assert (__prop); \
end
`define ASSERT_NEVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
always_ff @(posedge __clk) begin \
if (! (__rst !== '0)) __name: assert (! (__prop)); \
end
// Yosys uses 2-state logic, so this doesn't make sense here
`define ASSERT_KNOWN(__name, __sig, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST)
`define COVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
always_ff @(posedge __clk) begin : __name \
cover ((! (__rst !== '0)) && (__prop)); \
end
`define ASSUME(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \
always_ff @(posedge __clk) begin \
if (! (__rst !== '0)) __name: assume (__prop); \
end
`define ASSUME_I(__name, __prop) \
always_comb begin : __name \
assume (__prop); \
end

View file

@ -235,8 +235,10 @@ package prim_cipher_pkg;
key_out = 128'(key_in << 61) | 128'(key_in >> (128-61));
// sbox on uppermost 4 bits
key_out[127 -: 4] = PRESENT_SBOX4[key_out[127 -: 4]];
// xor in round counter on bits 19 to 15
key_out[19:15] ^= round_idx;
// sbox on second nibble from top
key_out[123 -: 4] = PRESENT_SBOX4[key_out[123 -: 4]];
// xor in round counter on bits 66 to 62
key_out[66:62] ^= round_idx;
return key_out;
endfunction : present_update_key128
@ -246,13 +248,13 @@ package prim_cipher_pkg;
logic [4:0] round_idx,
// total number of rounds employed
logic [4:0] round_cnt);
logic [63:0] key_out;
logic [63:0] key_out = key_in;
// xor in round counter on bits 19 to 15
key_out[19:15] ^= 6'(round_cnt) + 1 - round_idx;
key_out[19:15] ^= round_cnt + 1 - round_idx;
// sbox on uppermost 4 bits
key_out[63 -: 4] = PRESENT_SBOX4_INV[key_out[63 -: 4]];
// rotate by 61 to the right
key_out = 64'(key_in >> 61) | 64'(key_in << (64-61));
key_out = 64'(key_out >> 61) | 64'(key_out << (64-61));
return key_out;
endfunction : present_inv_update_key64
@ -260,13 +262,13 @@ package prim_cipher_pkg;
logic [4:0] round_idx,
// total number of rounds employed
logic [4:0] round_cnt);
logic [79:0] key_out;
logic [79:0] key_out = key_in;
// xor in round counter on bits 19 to 15
key_out[19:15] ^= 6'(round_cnt) + 1 - round_idx;
key_out[19:15] ^= round_cnt + 1 - round_idx;
// sbox on uppermost 4 bits
key_out[79 -: 4] = PRESENT_SBOX4_INV[key_out[79 -: 4]];
// rotate by 61 to the right
key_out = 80'(key_in >> 61) | 80'(key_in << (80-61));
key_out = 80'(key_out >> 61) | 80'(key_out << (80-61));
return key_out;
endfunction : present_inv_update_key80
@ -274,13 +276,15 @@ package prim_cipher_pkg;
logic [4:0] round_idx,
// total number of rounds employed
logic [4:0] round_cnt);
logic [127:0] key_out;
// xor in round counter on bits 19 to 15
key_out[19:15] ^= 6'(round_cnt) + 1 - round_idx;
logic [127:0] key_out = key_in;
// xor in round counter on bits 66 to 62
key_out[66:62] ^= round_cnt + 1 - round_idx;
// sbox on second highest nibble
key_out[123 -: 4] = PRESENT_SBOX4_INV[key_out[123 -: 4]];
// sbox on uppermost 4 bits
key_out[127 -: 4] = PRESENT_SBOX4_INV[key_out[127 -: 4]];
// rotate by 61 to the right
key_out = 128'(key_in >> 61) | 128'(key_in << (128-61));
key_out = 128'(key_out >> 61) | 128'(key_out << (128-61));
return key_out;
endfunction : present_inv_update_key128
@ -324,11 +328,29 @@ package prim_cipher_pkg;
// Common Subfunctions //
/////////////////////////
function automatic logic [7:0] sbox4_8bit(logic [7:0] state_in, logic [15:0][3:0] sbox4);
logic [7:0] state_out;
// note that if simulation performance becomes an issue, this loop can be unrolled
for (int k = 0; k < 8/4; k++) begin
state_out[k*4 +: 4] = sbox4[state_in[k*4 +: 4]];
end
return state_out;
endfunction : sbox4_8bit
function automatic logic [15:0] sbox4_16bit(logic [15:0] state_in, logic [15:0][3:0] sbox4);
logic [15:0] state_out;
// note that if simulation performance becomes an issue, this loop can be unrolled
for (int k = 0; k < 2; k++) begin
state_out[k*8 +: 8] = sbox4_8bit(state_in[k*8 +: 8], sbox4);
end
return state_out;
endfunction : sbox4_16bit
function automatic logic [31:0] sbox4_32bit(logic [31:0] state_in, logic [15:0][3:0] sbox4);
logic [31:0] state_out;
// note that if simulation performance becomes an issue, this loop can be unrolled
for (int k = 0; k < 32/4; k++) begin
state_out[k*4 +: 4] = sbox4[state_in[k*4 +: 4]];
for (int k = 0; k < 4; k++) begin
state_out[k*8 +: 8] = sbox4_8bit(state_in[k*8 +: 8], sbox4);
end
return state_out;
endfunction : sbox4_32bit
@ -336,17 +358,35 @@ package prim_cipher_pkg;
function automatic logic [63:0] sbox4_64bit(logic [63:0] state_in, logic [15:0][3:0] sbox4);
logic [63:0] state_out;
// note that if simulation performance becomes an issue, this loop can be unrolled
for (int k = 0; k < 64/4; k++) begin
state_out[k*4 +: 4] = sbox4[state_in[k*4 +: 4]];
for (int k = 0; k < 8; k++) begin
state_out[k*8 +: 8] = sbox4_8bit(state_in[k*8 +: 8], sbox4);
end
return state_out;
endfunction : sbox4_64bit
function automatic logic [7:0] perm_8bit(logic [7:0] state_in, logic [7:0][2:0] perm);
logic [7:0] state_out;
// note that if simulation performance becomes an issue, this loop can be unrolled
for (int k = 0; k < 8; k++) begin
state_out[perm[k]] = state_in[k];
end
return state_out;
endfunction : perm_8bit
function automatic logic [15:0] perm_16bit(logic [15:0] state_in, logic [15:0][3:0] perm);
logic [15:0] state_out;
// note that if simulation performance becomes an issue, this loop can be unrolled
for (int k = 0; k < 16; k++) begin
state_out[perm[k]] = state_in[k];
end
return state_out;
endfunction : perm_16bit
function automatic logic [31:0] perm_32bit(logic [31:0] state_in, logic [31:0][4:0] perm);
logic [31:0] state_out;
// note that if simulation performance becomes an issue, this loop can be unrolled
for (int k = 0; k < 32; k++) begin
state_out[k] = state_in[perm[k]];
state_out[perm[k]] = state_in[k];
end
return state_out;
endfunction : perm_32bit
@ -355,7 +395,7 @@ package prim_cipher_pkg;
logic [63:0] state_out;
// note that if simulation performance becomes an issue, this loop can be unrolled
for (int k = 0; k < 64; k++) begin
state_out[k] = state_in[perm[k]];
state_out[perm[k]] = state_in[k];
end
return state_out;
endfunction : perm_64bit

View file

@ -53,7 +53,7 @@ module prim_diff_decode #(
prim_flop_2sync #(
.Width(1),
.ResetValue(0)
.ResetValue('0)
) i_sync_p (
.clk_i,
.rst_ni,
@ -63,7 +63,7 @@ module prim_diff_decode #(
prim_flop_2sync #(
.Width(1),
.ResetValue(1)
.ResetValue(1'b1)
) i_sync_n (
.clk_i,
.rst_ni,

View file

@ -12,8 +12,7 @@ module prim_fifo_sync #(
parameter int unsigned Depth = 4,
parameter bit OutputZeroIfEmpty = 1'b1, // if == 1 always output 0 when FIFO is empty
// derived parameter
localparam int unsigned DepthWNorm = $clog2(Depth+1),
localparam int unsigned DepthW = (DepthWNorm == 0) ? 1 : DepthWNorm
localparam int DepthW = prim_util_pkg::vbits(Depth+1)
) (
input clk_i,
input rst_ni,
@ -51,8 +50,7 @@ module prim_fifo_sync #(
// Normal FIFO construction
end else begin : gen_normal_fifo
// consider Depth == 1 case when $clog2(1) == 0
localparam int unsigned PTRV_W = $clog2(Depth) + ~|$clog2(Depth);
localparam int unsigned PTRV_W = prim_util_pkg::vbits(Depth);
localparam int unsigned PTR_WIDTH = PTRV_W+1;
logic [PTR_WIDTH-1:0] fifo_wptr, fifo_rptr;

View file

@ -6,7 +6,7 @@
module prim_flop_2sync #(
parameter int Width = 16,
parameter bit ResetValue = 0
parameter logic [Width-1:0] ResetValue = '0
) (
input clk_i, // receive clock
input rst_ni,
@ -18,8 +18,8 @@ module prim_flop_2sync #(
always_ff @(posedge clk_i or negedge rst_ni)
if (!rst_ni) begin
intq <= {Width{ResetValue}};
q <= {Width{ResetValue}};
intq <= ResetValue;
q <= ResetValue;
end else begin
intq <= d;
q <= intq;

View file

@ -200,8 +200,10 @@ module prim_packer #(
// Assumption: mask_i should be contiguous ones
// e.g: 0011100 --> OK
// 0100011 --> Not OK
`ASSUME(ContiguousOnesMask_M,
valid_i |-> $countones(mask_i ^ {mask_i[InW-2:0],1'b0}) <= 2)
if (InW > 1) begin : gen_mask_assert
`ASSUME(ContiguousOnesMask_M,
valid_i |-> $countones(mask_i ^ {mask_i[InW-2:0],1'b0}) <= 2)
end
// Assume data pattern to reduce FPV test time
//`ASSUME_FPV(FpvDataWithin_M,

View file

@ -2,15 +2,14 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// This module is an implementation of the 64bit PRINCE block cipher. It is a
// fully unrolled combinational implementation with configurable number of
// rounds. Due to the reflective construction of this cipher, the same circuit
// can be used for encryption and decryption, as described below. Further, the
// primitive supports a 32bit block cipher flavor which is not specified in the
// original paper. It should be noted, however, that the 32bit version is
// **not** secure and must not be used in a setting where cryptographic cipher
// strength is required. The 32bit variant is only intended to be used as a
// lightweight data scrambling device.
// This module is an implementation of the 64bit PRINCE block cipher. It is a fully unrolled
// combinational implementation with configurable number of rounds. Optionally, registers for the
// data and key states can be enabled, if this is required. Due to the reflective construction of
// this cipher, the same circuit can be used for encryption and decryption, as described below.
// Further, the primitive supports a 32bit block cipher flavor which is not specified in the
// original paper. It should be noted, however, that the 32bit version is **not** secure and must
// not be used in a setting where cryptographic cipher strength is required. The 32bit variant is
// only intended to be used as a lightweight data scrambling device.
//
// See also: prim_present, prim_cipher_pkg
//
@ -33,11 +32,20 @@ module prim_prince #(
parameter int NumRoundsHalf = 5,
// This primitive uses the new key schedule proposed in https://eprint.iacr.org/2014/656.pdf
// Setting this parameter to 1 falls back to the original key schedule.
parameter bit UseOldKeySched = 1'b0
parameter bit UseOldKeySched = 1'b0,
// This instantiates a data register halfway in the primitive.
parameter bit HalfwayDataReg = 1'b0,
// This instantiates a key register halfway in the primitive.
parameter bit HalfwayKeyReg = 1'b0
) (
input clk_i,
input rst_ni,
input valid_i,
input [DataWidth-1:0] data_i,
input [KeyWidth-1:0] key_i,
input dec_i, // set to 1 for decryption
input dec_i, // set to 1 for decryption
output logic valid_o,
output logic [DataWidth-1:0] data_o
);
@ -45,26 +53,46 @@ module prim_prince #(
// key expansion //
///////////////////
logic [DataWidth-1:0] k0, k0_prime, k1, k0_new;
logic [DataWidth-1:0] k0, k0_prime_d, k1_d, k0_new_d, k0_prime_q, k1_q, k0_new_q;
always_comb begin : p_key_expansion
k0 = key_i[DataWidth-1:0];
k0_prime = {k0[0], k0[DataWidth-1:2], k0[DataWidth-1] ^ k0[1]};
k1 = key_i[2*DataWidth-1 : DataWidth];
k0 = key_i[DataWidth-1:0];
k0_prime_d = {k0[0], k0[DataWidth-1:2], k0[DataWidth-1] ^ k0[1]};
k1_d = key_i[2*DataWidth-1 : DataWidth];
// modify key for decryption
if (dec_i) begin
k0 = k0_prime;
k0_prime = key_i[DataWidth-1:0];
k1 ^= prim_cipher_pkg::PRINCE_ALPHA_CONST[DataWidth-1:0];
k0 = k0_prime_d;
k0_prime_d = key_i[DataWidth-1:0];
k1_d ^= prim_cipher_pkg::PRINCE_ALPHA_CONST[DataWidth-1:0];
end
end
if (UseOldKeySched) begin : gen_legacy_keyschedule
assign k0_new = k1;
assign k0_new_d = k1_d;
end else begin : gen_new_keyschedule
// improved keyschedule proposed by https://eprint.iacr.org/2014/656.pdf
assign k0_new = k0;
assign k0_new_d = k0;
end
if (HalfwayKeyReg) begin : gen_key_reg
always_ff @(posedge clk_i or negedge rst_ni) begin : p_key_reg
if (!rst_ni) begin
k1_q <= '0;
k0_prime_q <= '0;
k0_new_q <= '0;
end else begin
if (valid_i) begin
k1_q <= k1_d;
k0_prime_q <= k0_prime_d;
k0_new_q <= k0_new_d;
end
end
end
end else begin : gen_no_key_reg
// just pass the key through in this case
assign k1_q = k1_d;
assign k0_prime_q = k0_prime_d;
assign k0_new_q = k0_new_d;
end
//////////////
@ -77,7 +105,7 @@ module prim_prince #(
// pre-round XOR
always_comb begin : p_pre_round_xor
data_state[0] = data_i ^ k0;
data_state[0] ^= k1;
data_state[0] ^= k1_d;
data_state[0] ^= prim_cipher_pkg::PRINCE_ROUND_CONST[0][DataWidth-1:0];
end
@ -105,38 +133,58 @@ module prim_prince #(
assign data_state_xor = data_state_round ^
prim_cipher_pkg::PRINCE_ROUND_CONST[k][DataWidth-1:0];
// improved keyschedule proposed by https://eprint.iacr.org/2014/656.pdf
if (k % 2 == 1) assign data_state[k] = data_state_xor ^ k0_new;
else assign data_state[k] = data_state_xor ^ k1;
if (k % 2 == 1) assign data_state[k] = data_state_xor ^ k0_new_d;
else assign data_state[k] = data_state_xor ^ k1_d;
end
// middle part
logic [DataWidth-1:0] data_state_middle;
logic [DataWidth-1:0] data_state_middle_d, data_state_middle_q, data_state_middle;
if (DataWidth == 64) begin : gen_middle_d64
always_comb begin : p_middle_d64
data_state_middle = prim_cipher_pkg::sbox4_64bit(data_state[NumRoundsHalf],
data_state_middle_d = prim_cipher_pkg::sbox4_64bit(data_state[NumRoundsHalf],
prim_cipher_pkg::PRINCE_SBOX4);
data_state_middle = prim_cipher_pkg::prince_mult_prime_64bit(data_state_middle);
data_state_middle = prim_cipher_pkg::prince_mult_prime_64bit(data_state_middle_q);
data_state_middle = prim_cipher_pkg::sbox4_64bit(data_state_middle,
prim_cipher_pkg::PRINCE_SBOX4_INV);
end
end else begin : gen_middle_d32
always_comb begin : p_middle_d32
data_state_middle = prim_cipher_pkg::sbox4_32bit(data_state_middle[NumRoundsHalf],
data_state_middle_d = prim_cipher_pkg::sbox4_32bit(data_state_middle[NumRoundsHalf],
prim_cipher_pkg::PRINCE_SBOX4);
data_state_middle = prim_cipher_pkg::prince_mult_prime_32bit(data_state_middle);
data_state_middle = prim_cipher_pkg::prince_mult_prime_32bit(data_state_middle_q);
data_state_middle = prim_cipher_pkg::sbox4_32bit(data_state_middle,
prim_cipher_pkg::PRINCE_SBOX4_INV);
end
end
if (HalfwayDataReg) begin : gen_data_reg
logic valid_q;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_data_reg
if (!rst_ni) begin
valid_q <= 1'b0;
data_state_middle_q <= '0;
end else begin
valid_q <= valid_i;
if (valid_i) begin
data_state_middle_q <= data_state_middle_d;
end
end
end
assign valid_o = valid_q;
end else begin : gen_no_data_reg
// just pass data through in this case
assign data_state_middle_q = data_state_middle_d;
assign valid_o = valid_i;
end
assign data_state[NumRoundsHalf+1] = data_state_middle;
// backward pass
for (genvar k = 1; k <= NumRoundsHalf; k++) begin : gen_bwd_pass
logic [DataWidth-1:0] data_state_xor0, data_state_xor1;
// improved keyschedule proposed by https://eprint.iacr.org/2014/656.pdf
if (k % 2 == 1) assign data_state_xor0 = data_state[NumRoundsHalf+k] ^ k0_new;
else assign data_state_xor0 = data_state[NumRoundsHalf+k] ^ k1;
if (k % 2 == 1) assign data_state_xor0 = data_state[NumRoundsHalf+k] ^ k0_new_q;
else assign data_state_xor0 = data_state[NumRoundsHalf+k] ^ k1_q;
// the construction is reflective, hence the subtraction with NumRoundsHalf
assign data_state_xor1 = data_state_xor0 ^
prim_cipher_pkg::PRINCE_ROUND_CONST[10-NumRoundsHalf+k][DataWidth-1:0];
@ -165,8 +213,8 @@ module prim_prince #(
always_comb begin : p_post_round_xor
data_o = data_state[2*NumRoundsHalf+1] ^
prim_cipher_pkg::PRINCE_ROUND_CONST[11][DataWidth-1:0];
data_o ^= k1;
data_o ^= k0_prime;
data_o ^= k1_q;
data_o ^= k0_prime_q;
end
////////////////

View file

@ -14,7 +14,6 @@
// mask cannot be used and has to be tied to {Width{1'b1}}.
`include "prim_assert.sv"
`include "prim_util.svh"
module prim_ram_1p_adv #(
parameter int Depth = 512,
@ -29,7 +28,7 @@ module prim_ram_1p_adv #(
parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1)
parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1)
localparam int Aw = vbits(Depth)
localparam int Aw = prim_util_pkg::vbits(Depth)
) (
input clk_i,
input rst_ni,
@ -47,6 +46,8 @@ module prim_ram_1p_adv #(
input [CfgW-1:0] cfg_i
);
logic [CfgW-1:0] unused_cfg = cfg_i;
`ASSERT_INIT(CannotHaveEccAndParity_A, !(EnableParity && EnableECC))
// While we require DataBitsPerMask to be per Byte (8) at the interface in case Byte parity is

View file

@ -14,7 +14,6 @@
// mask cannot be used and has to be tied to {Width{1'b1}}.
`include "prim_assert.sv"
`include "prim_util.svh"
module prim_ram_2p_adv #(
parameter int Depth = 512,
@ -29,7 +28,7 @@ module prim_ram_2p_adv #(
parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1)
parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1)
localparam int Aw = vbits(Depth)
localparam int Aw = prim_util_pkg::vbits(Depth)
) (
input clk_i,
input rst_ni,

View file

@ -14,7 +14,6 @@
// mask cannot be used and has to be tied to {Width{1'b1}}.
`include "prim_assert.sv"
`include "prim_util.svh"
module prim_ram_2p_async_adv #(
parameter int Depth = 512,
@ -29,7 +28,7 @@ module prim_ram_2p_async_adv #(
parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1)
parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1)
localparam int Aw = vbits(Depth)
localparam int Aw = prim_util_pkg::vbits(Depth)
) (
input clk_a_i,
input clk_b_i,
@ -58,6 +57,8 @@ module prim_ram_2p_async_adv #(
input [CfgW-1:0] cfg_i
);
logic [CfgW-1:0] unused_cfg = cfg_i;
`ASSERT_INIT(CannotHaveEccAndParity_A, !(EnableParity && EnableECC))
// While we require DataBitsPerMask to be per Byte (8) at the interface in case Byte parity is

View file

@ -76,7 +76,7 @@ module prim_sram_arbiter #(
.ready_i ( 1'b1 )
);
end else if (ArbiterImpl == "BINTREE") begin : gen_tree_arb
prim_arbiter_arb #(
prim_arbiter_tree #(
.N (N),
.DW(ARB_DW)
) u_reqarb (

View file

@ -1,47 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Utility macros
`ifndef PRIM_UTIL_SVH
`define PRIM_UTIL_SVH
/**
* Math function: Number of bits needed to address |value| items.
*
* 0 for value == 0
* vbits = 1 for value == 1
* ceil(log2(value)) for value > 1
*
*
* The primary use case for this function is the definition of registers/arrays
* which are wide enough to contain |value| items.
*
* This function identical to $clog2() for all input values except the value 1;
* it could be considered an "enhanced" $clog2() function.
*
*
* Example 1:
* parameter Items = 1;
* localparam ItemsWidth = vbits(Items); // 1
* logic [ItemsWidth-1:0] item_register; // items_register is now [0:0]
*
* Example 2:
* parameter Items = 64;
* localparam ItemsWidth = vbits(Items); // 6
* logic [ItemsWidth-1:0] item_register; // items_register is now [5:0]
*
* Note: If you want to store the number "value" inside a register, you need
* a register with size vbits(value + 1), since you also need to store
* the number 0.
*
* Example 3:
* logic [vbits(64)-1:0] store_64_logic_values; // width is [5:0]
* logic [vbits(64 + 1)-1:0] store_number_64; // width is [6:0]
*/
function automatic integer vbits(integer value);
return (value == 1) ? 1 : $clog2(value);
endfunction
`endif // PRIM_UTIL_SVH

View file

@ -30,10 +30,10 @@
export "DPI-C" function simutil_verilator_set_mem;
function int simutil_verilator_set_mem(input int index,
input bit [127:0] val);
input bit [255:0] val);
// Function will only work for memories <= 128 bits
if (Width > 128) begin
// Function will only work for memories <= 256 bits
if (Width > 256) begin
return 0;
end
@ -44,6 +44,26 @@
mem[index] = val[Width-1:0];
return 1;
endfunction
// Function for getting a specific element in |mem|
export "DPI-C" function simutil_verilator_get_mem;
function int simutil_verilator_get_mem(input int index,
output bit [255:0] val);
// Function will only work for memories <= 256 bits
if (Width > 256) begin
return 0;
end
if (index >= Depth) begin
return 0;
end
val = 0;
val[Width-1:0] = mem[index];
return 1;
endfunction
`endif
initial begin

View file

@ -0,0 +1,86 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
/**
* Utility functions
*/
package prim_util_pkg;
/**
* Math function: $clog2 as specified in Verilog-2005
*
* Do not use this function if $clog2() is available.
*
* clog2 = 0 for value == 0
* ceil(log2(value)) for value >= 1
*
* This implementation is a synthesizable variant of the $clog2 function as
* specified in the Verilog-2005 standard (IEEE 1364-2005).
*
* To quote the standard:
* The system function $clog2 shall return the ceiling of the log
* base 2 of the argument (the log rounded up to an integer
* value). The argument can be an integer or an arbitrary sized
* vector value. The argument shall be treated as an unsigned
* value, and an argument value of 0 shall produce a result of 0.
*/
function automatic integer _clog2(integer value);
integer result;
value = value - 1;
for (result = 0; value > 0; result = result + 1) begin
value = value >> 1;
end
return result;
endfunction
/**
* Math function: Number of bits needed to address |value| items.
*
* 0 for value == 0
* vbits = 1 for value == 1
* ceil(log2(value)) for value > 1
*
*
* The primary use case for this function is the definition of registers/arrays
* which are wide enough to contain |value| items.
*
* This function identical to $clog2() for all input values except the value 1;
* it could be considered an "enhanced" $clog2() function.
*
*
* Example 1:
* parameter Items = 1;
* localparam ItemsWidth = vbits(Items); // 1
* logic [ItemsWidth-1:0] item_register; // items_register is now [0:0]
*
* Example 2:
* parameter Items = 64;
* localparam ItemsWidth = vbits(Items); // 6
* logic [ItemsWidth-1:0] item_register; // items_register is now [5:0]
*
* Note: If you want to store the number "value" inside a register, you need
* a register with size vbits(value + 1), since you also need to store
* the number 0.
*
* Example 3:
* logic [vbits(64)-1:0] store_64_logic_values; // width is [5:0]
* logic [vbits(64 + 1)-1:0] store_number_64; // width is [6:0]
*/
function automatic integer vbits(integer value);
`ifdef XCELIUM
// The use of system functions was not allowed here in Verilog-2001, but is
// valid since (System)Verilog-2005, which is also when $clog2() first
// appeared.
// Xcelium < 19.10 does not yet support the use of $clog2() here, fall back
// to an implementation without a system function. Remove this workaround
// if we require a newer Xcelium version.
// See #2579 and #2597.
return (value == 1) ? 1 : prim_util_pkg::_clog2(value);
`else
return (value == 1) ? 1 : $clog2(value);
`endif
endfunction
endpackage

View file

@ -383,7 +383,8 @@ def _get_action_from_gapi(gapi, default_action):
def main():
gapi_filepath = sys.argv[1]
gapi = yaml.load(open(gapi_filepath), Loader=YamlLoader)
with open(gapi_filepath) as f:
gapi = yaml.load(f, Loader=YamlLoader)
if not _check_gapi(gapi):
sys.exit(1)

View file

@ -9,6 +9,7 @@ filesets:
files_rtl:
depend:
- lowrisc:prim:ram_1p
- lowrisc:ip:flash_ctrl_pkg
files:
- rtl/prim_generic_flash.sv
file_type: systemVerilogSource

View file

@ -6,7 +6,8 @@
//
module prim_generic_flash #(
parameter int PagesPerBank = 256, // pages per bank
parameter int InfosPerBank = 1, // info pages per bank
parameter int PagesPerBank = 256, // data pages per bank
parameter int WordsPerPage = 256, // words per page
parameter int DataWidth = 32, // bits per word
parameter bit SkipInit = 1, // this is an option to reset flash to all F's at reset
@ -16,17 +17,18 @@ module prim_generic_flash #(
localparam int WordW = $clog2(WordsPerPage),
localparam int AddrW = PageW + WordW
) (
input clk_i,
input rst_ni,
input rd_i,
input prog_i,
input pg_erase_i,
input bk_erase_i,
input [AddrW-1:0] addr_i,
input [DataWidth-1:0] prog_data_i,
output logic ack_o,
output logic [DataWidth-1:0] rd_data_o,
output logic init_busy_o
input clk_i,
input rst_ni,
input rd_i,
input prog_i,
input pg_erase_i,
input bk_erase_i,
input [AddrW-1:0] addr_i,
input flash_ctrl_pkg::flash_part_e part_i,
input [DataWidth-1:0] prog_data_i,
output logic ack_o,
output logic [DataWidth-1:0] rd_data_o,
output logic init_busy_o
);
// Emulated flash macro values
@ -37,6 +39,8 @@ module prim_generic_flash #(
// Locally derived values
localparam int WordsPerBank = PagesPerBank * WordsPerPage;
localparam int WordsPerInfoBank = InfosPerBank * WordsPerPage;
localparam int InfoAddrW = $clog2(WordsPerInfoBank);
typedef enum logic [2:0] {
StReset = 'h0,
@ -59,18 +63,21 @@ module prim_generic_flash #(
logic mem_req;
logic mem_wr;
logic [AddrW-1:0] mem_addr;
flash_ctrl_pkg::flash_part_e mem_part;
logic [DataWidth-1:0] held_rdata;
logic [DataWidth-1:0] held_wdata;
logic [DataWidth-1:0] mem_wdata;
logic hold_cmd;
logic [AddrW-1:0] held_addr;
flash_ctrl_pkg::flash_part_e held_part;
// insert a fifo here to break the large fanout from inputs to memories on reads
logic rd_q;
logic [AddrW-1:0] addr_q;
flash_ctrl_pkg::flash_part_e part_q;
prim_fifo_sync #(
.Width (AddrW),
.Width (AddrW + $bits(flash_ctrl_pkg::flash_part_e)),
.Pass (0),
.Depth (2)
) i_slice (
@ -79,11 +86,11 @@ module prim_generic_flash #(
.clr_i (1'b0),
.wvalid (rd_i),
.wready (),
.wdata (addr_i),
.wdata ({part_i, addr_i}),
.depth (),
.rvalid (rd_q),
.rready (hold_cmd), //whenver command is held, pop
.rdata (addr_q)
.rdata ({part_q, addr_q})
);
@ -95,9 +102,11 @@ module prim_generic_flash #(
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
held_addr <= '0;
held_part <= flash_ctrl_pkg::DataPart;
held_wdata <= '0;
end else if (hold_cmd) begin
held_addr <= rd_q ? addr_q : addr_i;
held_part <= rd_q ? part_q : part_i;
held_wdata <= prog_data_i;
end
end
@ -146,6 +155,7 @@ module prim_generic_flash #(
mem_req = 'h0;
mem_wr = 'h0;
mem_addr = 'h0;
mem_part = flash_ctrl_pkg::DataPart;
mem_wdata = 'h0;
time_cnt_inc = 1'h0;
time_cnt_clr = 1'h0;
@ -185,6 +195,7 @@ module prim_generic_flash #(
// reads begin immediately
hold_cmd = 1'b1;
mem_addr = addr_q;
mem_part = part_q;
mem_req = 1'b1;
time_cnt_inc = 1'b1;
st_d = StRead;
@ -206,6 +217,7 @@ module prim_generic_flash #(
end
StRead: begin
mem_addr = held_addr;
mem_part = held_part;
if (time_cnt < ReadCycles) begin
mem_req = 1'b1;
time_cnt_inc = 1'b1;
@ -216,6 +228,7 @@ module prim_generic_flash #(
if (rd_q) begin
hold_cmd = 1'b1;
mem_addr = addr_q;
mem_part = part_q;
mem_req = 1'b1;
time_cnt_set1 = 1'b1;
st_d = StRead;
@ -232,6 +245,7 @@ module prim_generic_flash #(
end
StProg: begin
mem_addr = held_addr;
mem_part = held_part;
// if data is already 0, cannot program to 1 without erase
mem_wdata = held_wdata & held_rdata;
@ -253,6 +267,7 @@ module prim_generic_flash #(
mem_wdata = {DataWidth{1'b1}};
mem_addr = held_addr + index_cnt[AddrW-1:0];
mem_part = held_part;
time_cnt_inc = (time_cnt < time_limit_q);
index_cnt_inc = (index_cnt < index_limit_q);
end else begin
@ -268,18 +283,36 @@ module prim_generic_flash #(
endcase // unique case (st_q)
end // always_comb
logic [DataWidth-1:0] rd_data_main, rd_data_info;
prim_ram_1p #(
.Width(DataWidth),
.Depth(WordsPerBank),
.DataBitsPerMask(DataWidth)
) u_mem (
.clk_i,
.req_i (mem_req),
.req_i (mem_req & (mem_part == flash_ctrl_pkg::DataPart)),
.write_i (mem_wr),
.addr_i (mem_addr),
.wdata_i (mem_wdata),
.wmask_i ({DataWidth{1'b1}}),
.rdata_o (rd_data_o)
.rdata_o (rd_data_main)
);
prim_ram_1p #(
.Width(DataWidth),
.Depth(WordsPerInfoBank),
.DataBitsPerMask(DataWidth)
) u_info_mem (
.clk_i,
.req_i (mem_req & (mem_part == flash_ctrl_pkg::InfoPart)),
.write_i (mem_wr),
.addr_i (mem_addr[0 +: InfoAddrW]),
.wdata_i (mem_wdata),
.wmask_i ({DataWidth{1'b1}}),
.rdata_o (rd_data_info)
);
assign rd_data_o = held_part == flash_ctrl_pkg::DataPart ? rd_data_main : rd_data_info;
endmodule // prim_generic_flash

View file

@ -31,10 +31,13 @@ module prim_generic_ram_1p #(
logic [Width-1:0] mem [Depth];
logic [MaskWidth-1:0] wmask;
always_comb begin
for (int i=0; i < MaskWidth; i = i + 1) begin : create_wmask
wmask[i] = &wmask_i[i*DataBitsPerMask +: DataBitsPerMask];
end
for (genvar k = 0; k < MaskWidth; k++) begin : gen_wmask
assign wmask[k] = &wmask_i[k*DataBitsPerMask +: DataBitsPerMask];
// Ensure that all mask bits within a group have the same value
`ASSERT(MaskCheck_A, req_i |->
wmask_i[k*DataBitsPerMask +: DataBitsPerMask] inside {{DataBitsPerMask{1'b1}}, '0},
clk_i, '0)
end
// using always instead of always_ff to avoid 'ICPD - illegal combination of drivers' error

View file

@ -40,13 +40,18 @@ module prim_generic_ram_2p #(
logic [MaskWidth-1:0] a_wmask;
logic [MaskWidth-1:0] b_wmask;
always_comb begin
for (int i=0; i < MaskWidth; i = i + 1) begin : create_wmask
a_wmask[i] = &a_wmask_i[i*DataBitsPerMask +: DataBitsPerMask];
b_wmask[i] = &b_wmask_i[i*DataBitsPerMask +: DataBitsPerMask];
end
end
for (genvar k = 0; k < MaskWidth; k++) begin : gen_wmask
assign a_wmask[k] = &a_wmask_i[k*DataBitsPerMask +: DataBitsPerMask];
assign b_wmask[k] = &b_wmask_i[k*DataBitsPerMask +: DataBitsPerMask];
// Ensure that all mask bits within a group have the same value
`ASSERT(MaskCheckPortA_A, a_req_i |->
a_wmask_i[k*DataBitsPerMask +: DataBitsPerMask] inside {{DataBitsPerMask{1'b1}}, '0},
clk_a_i, '0)
`ASSERT(MaskCheckPortB_A, b_req_i |->
b_wmask_i[k*DataBitsPerMask +: DataBitsPerMask] inside {{DataBitsPerMask{1'b1}}, '0},
clk_b_i, '0)
end
// Xilinx FPGA specific Dual-port RAM coding style
// using always instead of always_ff to avoid 'ICPD - illegal combination of drivers' error

View file

@ -57,6 +57,9 @@ Code Quality | [LINT_PASS][] | Not Started |
Code Quality | [CDC_SETUP][] | Not Started |
Code Quality | [FPGA_TIMING][] | Not Started |
Code Quality | [CDC_SYNCMACRO][] | Not Started |
Security | [SEC_CM_IMPLEMENTED][] | Not Started |
Security | [SEC_NON_RESET_FLOPS][] | Not Started |
Security | [SEC_SHADOW_REGS][] | Not Started |
Review | Reviewer(s) | Not Started |
Review | Signoff date | Not Started |
@ -73,8 +76,11 @@ Review | Signoff date | Not Started |
[STYLE_X]: {{<relref "/doc/project/checklist.md#style-x" >}}
[LINT_PASS]: {{<relref "/doc/project/checklist.md#lint-pass" >}}
[CDC_SETUP]: {{<relref "/doc/project/checklist.md#cdc-setup" >}}
[CDC_SYNCMACRO]: {{<relref "/doc/project/checklist.md#cdc-syncmacro" >}}
[FPGA_TIMING]: {{<relref "/doc/project/checklist.md#fpga-timing" >}}
[CDC_SYNCMACRO]: {{<relref "/doc/project/checklist.md#cdc-syncmacro" >}}
[SEC_CM_IMPLEMENTED]: {{<relref "/doc/project/checklist.md#sec-cm-implemented" >}}
[SEC_NON_RESET_FLOPS]: {{<relref "/doc/project/checklist.md#sec-non-reset-flops" >}}
[SEC_SHADOW_REGS]: {{<relref "/doc/project/checklist.md#sec-shadow-regs" >}}
### D3

View file

@ -66,6 +66,11 @@ class ${name}_scoreboard extends dv_base_scoreboard #(
bit write = item.is_write();
uvm_reg_addr_t csr_addr = get_normalized_addr(item.a_addr);
bit addr_phase_read = (!write && channel == AddrChannel);
bit addr_phase_write = (write && channel == AddrChannel);
bit data_phase_read = (!write && channel == DataChannel);
bit data_phase_write = (write && channel == DataChannel);
// if access was to a valid csr, get the csr handle
if (csr_addr inside {cfg.csr_addrs}) begin
csr = ral.default_map.get_reg_by_offset(csr_addr);
@ -75,11 +80,9 @@ class ${name}_scoreboard extends dv_base_scoreboard #(
`uvm_fatal(`gfn, $sformatf("Access unexpected addr 0x%0h", csr_addr))
end
if (channel == AddrChannel) begin
// if incoming access is a write to a valid csr, then make updates right away
if (write) begin
void'(csr.predict(.value(item.a_data), .kind(UVM_PREDICT_WRITE), .be(item.a_mask)));
end
// if incoming access is a write to a valid csr, then make updates right away
if (addr_phase_write) begin
void'(csr.predict(.value(item.a_data), .kind(UVM_PREDICT_WRITE), .be(item.a_mask)));
end
// process the csr req
@ -87,13 +90,23 @@ class ${name}_scoreboard extends dv_base_scoreboard #(
// for read, update predication at address phase and compare at data phase
case (csr.get_name())
// add individual case item for each csr
"intr_state": begin
// FIXME
do_read_check = 1'b0;
end
"intr_enable": begin
// FIXME
end
"intr_test": begin
// FIXME
end
default: begin
`uvm_fatal(`gfn, $sformatf("invalid csr: %0s", csr.get_full_name()))
end
endcase
// On reads, if do_read_check, is set, then check mirrored_value against item.d_data
if (!write && channel == DataChannel) begin
if (data_phase_read) begin
if (do_read_check) begin
`DV_CHECK_EQ(csr.get_mirrored_value(), item.d_data,
$sformatf("reg name: %0s", csr.get_full_name()))