Update code from upstream repository
https://github.com/lowRISC/opentitan to revision
da3ac7c4eb23a92194874ad2daf2e5f9e3330572

* [memutil] Allow use without scrambled memories (Philipp Wagner)
* [prim_prince] Fix comment (Philipp Wagner)
* [memutil] Fix width mismatch (Philipp Wagner)
* [prim] Allow disabling SVAs ensuring REQ is held until ACK at run
  time (Pirmin Vogel)
* [prim] Fix typo that caused fifo_async to get stuck (Timothy Chen)
* [prim] Add a missing ROM_CFG_DEFAULT to prim_rom_pkg.sv (Rupert
  Swarbrick)
* [dvsim] Do not assume the build failed if "ERROR" is printed
  (Philipp Wagner)
* [prim_subreg_shadow] Invert meaning of SWACCESS in shadow/stage regs
  (Michael Schaffner)
* [prim_arb_tree/rv_plic_target] Remove TODOs due to a Vivado tool bug
  (Michael Schaffner)
* [primgen] Remove unused import (Philipp Wagner)
* [primgen] Add shebang (Philipp Wagner)
* [primgen] Make primgen "portable" again (Philipp Wagner)
* [dv] Small optimization in memutil (Philipp Wagner)
* [tools/ascent] updated ascent to use the --job-prefix option (Rasmus
  Madsen)
* [otp_ctrl] Remove invalid command error (Michael Schaffner)
* [tlul] Add some missing dependencies (Michael Schaffner)
* [otbn/otp_ctrl] Replicate dmem scrambling keystream (Michael
  Schaffner)
* [adc_ctrl] Various preparation steps for d2 (Timothy Chen)
* [tools/dvsim] Fix some VCS flags (Guillermo Maturana)
* Revert "[prim] Do remove prim_esc.core from the dependencies"
  (Rupert Swarbrick)
* [prim] Remove dependency of prim:esc on a hardware block (Rupert
  Swarbrick)
* [lint] prim_ram_1p_scr verilator lint fixes (Greg Chadwick)
* [dv] Add scrambled_ecc32_mem_area for memutils (Greg Chadwick)
* [dv] Add C++ memory scrambling model (Greg Chadwick)
* [tools/dsim] Fix non-LRM compliant code (Guillermo Maturana)
* [prim] Do remove prim_esc.core from the dependencies (Michael
  Schaffner)
* [dv/dv_utils] Improvement on `max` function (Cindy Chen)
* [alert_handler] Implement reverse ping feature (Michael Schaffner)
* [prim_esc] Split the prims into their own core file (Michael
  Schaffner)
* [dvsim] Fix GUI mode and launcher creation fixes (Srikrishna Iyer)
* [dv/common] Stress_all_with_rand_reset apply reset concurrently
  (Cindy Chen)
* [dv/all] update scoreboard `csr_addrs` accesses (Udi Jonnalagadda)
* [dv/csr_utils] update unmapped_addr calculation (Udi Jonnalagadda)
* [dv] Update intg alert names (Weicai Yang)
* [dv, flash_ctrl] Fix the intr test (Srikrishna Iyer)
* [prim_fifo_async] Fix a width calculation issue in case of Depth = 1
  (Michael Schaffner)
* [dv] Update VCS opt for uvm_hdl_* (Weicai Yang)
* [dv, util] Make poll_for_stop() opt-in (Srikrishna Iyer)
* [dvsim] Separate publish report option [PART1] (Cindy Chen)
* [dv/kmac/sram] reduce iterations of smoke test (Udi Jonnalagadda)
* [dv/stress_all_with_reset] Revert back IPs that uses apply_reset
  (Cindy Chen)
* [dv/edn_reset] Fix apply_reset to concurrently deassert resets
  (Cindy Chen)
* [dv] Update VCS cov merge opts (Srikrishna Iyer)
* [dv] Add TL integrity error test for CSR (Weicai Yang)
* [dv, chip] Remove USB clk driver (Srikrishna Iyer)
* [script/dvsim] Update output folder (Cindy Chen)
* [dv/edn_reset] Update IPs that overrides apply_reset task (Cindy
  Chen)
* [dv/edn_reset] Fix stress_all_with_rand_reset error (Cindy Chen)
* [dv/dv_base_scoreboard] remove duplicated code (Cindy Chen)
* [otbn,dv] Teach otbn_memutil to track expected end address (Rupert
  Swarbrick)
* [dv, dv_utils_pkg] Fix common int typedefs (Srikrishna Iyer)
* [prim_lfsr] Fix spyglass lint warnings (Michael Schaffner)
* [prim_clock_gating] Target 7series Xilinx devices (Philipp Wagner)
* [dv/edn_rst] Add coverage to collect edn reset and dut reset (Cindy
  Chen)
* [otp_ctrl/lc_ctrl] Add LC TAP register to control OTP test
  mechanisms (Michael Schaffner)
* [prim_alert*/prim_esc*] Rework placement of size_only bufs/flops
  (Michael Schaffner)
* [dv] fix a typo in tl_device_access_types_testplan (Weicai Yang)
* [prim_otp] Rework generic model to match new error behavior (Michael
  Schaffner)
* [dv/tlul_common_test] Add a testplan for TLUL integrity check (Cindy
  Chen)
* [dvsim] Allow recursive testplan import (Srikrishna Iyer)
* [primgen] Use verible-verilog-syntax for parsing (Mariusz Glebocki)
* [prim] Break always_comb block to avoid apparent loop (Rupert
  Swarbrick)
* [dvsim] Fix testplan bugs (Srikrishna Iyer)
* [fpv] update secded_gen (Cindy Chen)
* [dv/template] small fixes on index.md format (Cindy Chen)
* [prim_otp] Add a waiver for power signal unused in generic prim
  (Michael Schaffner)
* [simutil_verilator] Improve timeout handling (Rupert Swarbrick)
* [testplans] Rename entries with testpoints (Srikrishna Iyer)
* [dvsim/testplan] Fix the rendered testplan (Srikrishna Iyer)
* [dv/cov] exclude prim_lfsr and prim_prince (Udi Jonnalagadda)

Signed-off-by: Philipp Wagner <phw@lowrisc.org>
This commit is contained in:
Philipp Wagner 2021-07-20 10:59:36 +01:00 committed by Philipp Wagner
parent 270cd91b38
commit d003d479ff
122 changed files with 3416 additions and 663 deletions

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/lowRISC/opentitan
rev: 7117c349d5465b5152d3bb774079013924a3e9ba
rev: da3ac7c4eb23a92194874ad2daf2e5f9e3330572
}
}

View file

@ -60,28 +60,6 @@ package csr_utils_pkg;
under_reset = 0;
endfunction
// Get all valid csr addrs - useful to check if incoming addr falls in the csr range.
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);
foreach (csrs[i]) begin
csr_addrs.push_back(csrs[i].get_address());
end
endfunction
// Get all valid mem addr ranges - useful to check if incoming addr falls in the mem range.
function automatic void get_mem_addr_ranges(uvm_reg_block ral, ref addr_range_t mem_ranges[$]);
uvm_mem mems[$];
ral.get_memories(mems);
foreach (mems[i]) begin
addr_range_t mem_range;
mem_range.start_addr = mems[i].get_address();
mem_range.end_addr = mem_range.start_addr +
mems[i].get_size() * mems[i].get_n_bytes() - 1;
mem_ranges.push_back(mem_range);
end
endfunction
// get mem object from address
function automatic uvm_mem get_mem_by_addr(uvm_reg_block ral, uvm_reg_addr_t addr);
uvm_mem mem;

View file

@ -16,6 +16,15 @@ class dv_base_reg_block extends uvm_reg_block;
// This is set by compute_addr_mask(), which must run after locking the model.
protected uvm_reg_addr_t addr_mask[uvm_reg_map];
uvm_reg_addr_t csr_addrs[$];
addr_range_t mem_ranges[$];
addr_range_t mapped_addr_ranges[$];
bit has_unmapped_addrs;
addr_range_t unmapped_addr_ranges[$];
function new (string name = "", int has_coverage = UVM_NO_COVERAGE);
super.new(name, has_coverage);
endfunction
@ -94,6 +103,98 @@ class dv_base_reg_block extends uvm_reg_block;
`DV_CHECK_FATAL(addr_mask[map])
endfunction
// Internal function, used to get a list of all valid CSR addresses.
protected function void compute_csr_addrs();
uvm_reg csrs[$];
get_registers(csrs);
foreach (csrs[i]) begin
csr_addrs.push_back(csrs[i].get_address());
end
endfunction
// Internal function, used to get a list of all valid memory ranges
protected function void compute_mem_addr_ranges();
uvm_mem mems[$];
get_memories(mems);
foreach (mems[i]) begin
addr_range_t mem_range;
mem_range.start_addr = mems[i].get_address();
mem_range.end_addr = mem_range.start_addr +
mems[i].get_size() * mems[i].get_n_bytes() - 1;
mem_ranges.push_back(mem_range);
end
endfunction
// Used to get a list of all valid address ranges covered by this reg block
function void compute_mapped_addr_ranges();
uvm_reg csrs[$];
get_registers(csrs);
// Compute all CSR addresses and mem ranges known to this reg block
compute_csr_addrs();
compute_mem_addr_ranges();
// Convert each CSR into an address range
foreach (csrs[i]) begin
addr_range_t csr_addr_range;
csr_addr_range.start_addr = csrs[i].get_address();
csr_addr_range.end_addr = csr_addr_range.start_addr + csrs[i].get_n_bytes() - 1;
mapped_addr_ranges.push_back(csr_addr_range);
end
mapped_addr_ranges = {mapped_addr_ranges, mem_ranges};
// Sort the mapped address ranges in ascending order based on the start_addr of each range
mapped_addr_ranges.sort(m) with (m.start_addr);
endfunction
// Used to get a list of all invalid address ranges in this reg block
function void compute_unmapped_addr_ranges();
addr_range_t range;
// convert the address mask into a relative address,
// this is the highest addressable location in the register block
uvm_reg_addr_t highest_addr = default_map.get_base_addr() + get_addr_mask();
// unmapped address ranges consist of:
// - the address space between all mapped address ranges (if exists)
// - space between csr_base_addr and the first mapped address range (if exists)
// - space between the last mapped address and the highest address mapped by the address mask
if (mapped_addr_ranges.size() == 0) begin
range.start_addr = default_map.get_base_addr();
range.end_addr = highest_addr;
unmapped_addr_ranges.push_back(range);
end else begin
// 0 -> start_addr-1
if (mapped_addr_ranges[0].start_addr - default_map.get_base_addr() > 0) begin
range.start_addr = default_map.get_base_addr();
range.end_addr = mapped_addr_ranges[0].start_addr - 1;
unmapped_addr_ranges.push_back(range);
end
// all address ranges in the "middle" - only applies if
// there are more than 1 mapped address ranges
if (mapped_addr_ranges.size() > 1) begin
for (int i = 0; i < mapped_addr_ranges.size() - 1; i++) begin
range.start_addr = mapped_addr_ranges[i].end_addr + 1;
range.end_addr = mapped_addr_ranges[i+1].start_addr - 1;
if (range.start_addr < range.end_addr) begin
unmapped_addr_ranges.push_back(range);
end
end
end
// end_addr+1 -> highest_addr
if (mapped_addr_ranges[$].end_addr < highest_addr) begin
range.start_addr = mapped_addr_ranges[$].end_addr + 1;
range.end_addr = highest_addr;
unmapped_addr_ranges.push_back(range);
end
end
has_unmapped_addrs = (unmapped_addr_ranges.size() > 0);
endfunction
// Return the offset of the highest byte contained in either a register or a memory
function uvm_reg_addr_t get_max_offset(uvm_reg_map map = null);
uvm_reg_addr_t max_offset;
@ -143,6 +244,7 @@ class dv_base_reg_block extends uvm_reg_block;
if (map == null) map = get_default_map();
mask = get_addr_mask(map);
// If base_addr is '1, randomly pick an aligned base address
if (base_addr == '1) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(base_addr, (base_addr & mask) == '0;)

View file

@ -17,7 +17,7 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
// useful to keep the runtime down especially in time-sensitive runs such as CI, which is meant
// to check the code health and not find design bugs. It is set via plusarg and retrieved in
// `dv_base_test`.
bit smoke_test = 0;
bit smoke_test = 0;
// bit to configure all uvcs with zero delays to create high bw test
rand bit zero_delays;
@ -38,9 +38,6 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
// super.initialize(csr_base_addr);
string ral_model_names[$] = {RAL_T::type_name};
bit [bus_params_pkg::BUS_AW-1:0] csr_addrs[string][$];
addr_range_t mem_ranges[string][$];
// clk_rst_if & freq
// clk_rst_vif and clk_freq_mhz are used for default clk/rst. If more than one RAL, the other
// clk_rst_vif and clk_freq_mhz can be found from the associative arrays
@ -50,10 +47,10 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
rand clk_freq_mhz_e clk_freqs_mhz[string];
`uvm_object_param_utils_begin(dv_base_env_cfg #(RAL_T))
`uvm_field_int (is_active, UVM_DEFAULT)
`uvm_field_int (en_scb, UVM_DEFAULT)
`uvm_field_int (en_cov, UVM_DEFAULT)
`uvm_field_int (zero_delays, UVM_DEFAULT)
`uvm_field_int (is_active, UVM_DEFAULT)
`uvm_field_int (en_scb, UVM_DEFAULT)
`uvm_field_int (en_cov, UVM_DEFAULT)
`uvm_field_int (zero_delays, UVM_DEFAULT)
`uvm_object_utils_end
`uvm_object_new
@ -98,6 +95,7 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
endfunction
virtual function void create_ral_models(bit [bus_params_pkg::BUS_AW-1:0] csr_base_addr = '1);
foreach (ral_model_names[i]) begin
string ral_name = ral_model_names[i];
uvm_reg_addr_t base_addr;
@ -123,8 +121,16 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
reg_blk.set_base_addr(base_addr);
// Get list of valid csr addresses (useful in seq to randomize addr as well as in scb checks)
get_csr_addrs(reg_blk, csr_addrs[ral_name]);
get_mem_addr_ranges(reg_blk, mem_ranges[ral_name]);
reg_blk.compute_mapped_addr_ranges();
reg_blk.compute_unmapped_addr_ranges();
`uvm_info(msg_id,
$sformatf("RAL[%0s] mapped addresses: %0p",
ral_name, reg_blk.mapped_addr_ranges),
UVM_HIGH)
`uvm_info(msg_id,
$sformatf("RAL[%0s] unmapped addresses: %0p",
ral_name, reg_blk.unmapped_addr_ranges),
UVM_HIGH)
ral_models[ral_name] = reg_blk;
end

View file

@ -25,6 +25,7 @@ class dv_base_scoreboard #(type RAL_T = dv_base_reg_block,
super.run_phase(phase);
fork
monitor_reset();
sample_resets();
join_none
endtask
@ -33,12 +34,10 @@ class dv_base_scoreboard #(type RAL_T = dv_base_reg_block,
if (!cfg.clk_rst_vif.rst_n) begin
`uvm_info(`gfn, "reset occurred", UVM_HIGH)
cfg.reset_asserted();
csr_utils_pkg::reset_asserted();
@(posedge cfg.clk_rst_vif.rst_n);
reset();
cfg.reset_deasserted();
csr_utils_pkg::clear_outstanding_access();
csr_utils_pkg::reset_deasserted();
`uvm_info(`gfn, "out of reset", UVM_HIGH)
end
else begin
@ -48,6 +47,10 @@ class dv_base_scoreboard #(type RAL_T = dv_base_reg_block,
end
endtask
virtual task sample_resets();
// Do nothing, actual coverage collection is under extended classes.
endtask
virtual function void reset(string kind = "HARD");
// reset the ral model
foreach (cfg.ral_models[i]) cfg.ral_models[i].reset(kind);

View file

@ -14,7 +14,7 @@ class dv_base_test #(type CFG_T = dv_base_env_cfg,
uint max_quit_count = 1;
uint64 test_timeout_ns = 200_000_000; // 200ms
uint drain_time_ns = 2_000; // 2us
bit poll_for_stop = 1'b1;
bit poll_for_stop = 1'b0;
uint poll_for_stop_interval_ns = 1000;
`uvm_component_new

View file

@ -86,7 +86,7 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
virtual task apply_reset(string kind = "HARD");
if (kind == "HARD") begin
if (cfg.clk_rst_vifs.size > 0) begin
if (cfg.clk_rst_vifs.size() > 0) begin
fork
begin : isolation_fork
foreach (cfg.clk_rst_vifs[i]) begin
@ -104,6 +104,39 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
end // if (kind == "HARD")
endtask
// Apply all resets in the DUT concurrently to generate a random in-test reset scenario.
//
// - Assert resets concurrently to make sure all resets are issued.
// - Deassert resets concurrently is a specific requirement of the `stress_all_with_rand_reset`
// sequence, which will randomly issue resets and terminate the parallel sequence once all DUT
// resets are deasserted. If DUT resets are deasserted at different time, the parallel sequence
// might send a transaction request to driver between different resets are deasserting. Then when
// `stress_all_with_rand_reset` sequence tries to terminate the parallel sequence, an UVM_ERROR
// will be thrown by the sequencer saying `task responsible for requesting a wait_for_grant has
// been killed`.
// In order to ensure all resets at least being asserted for one clock cycle, this task takes an
// optional input `reset_duration_ps` if the DUT has additional resets. The task uses this input
// to compute the minimal time required to keep all resets asserted.
virtual task apply_resets_concurrently(int reset_duration_ps = 0);
// Has one or more RAL models in DUT.
if (cfg.clk_rst_vifs.size() > 0) begin
foreach (cfg.clk_rst_vifs[i]) begin
cfg.clk_rst_vifs[i].drive_rst_pin(0);
reset_duration_ps = max2(reset_duration_ps, cfg.clk_rst_vifs[i].clk_period_ps);
end
#(reset_duration_ps * $urandom_range(2, 10) * 1ps);
foreach (cfg.clk_rst_vifs[i]) cfg.clk_rst_vifs[i].drive_rst_pin(1);
// No RAL model and only has default clk_rst_vif.
end else begin
cfg.clk_rst_vif.drive_rst_pin(0);
reset_duration_ps = max2(reset_duration_ps, cfg.clk_rst_vif.clk_period_ps);
#(reset_duration_ps * $urandom_range(2, 10) * 1ps);
cfg.clk_rst_vif.drive_rst_pin(1);
end
endtask
virtual task wait_for_reset(string reset_kind = "HARD",
bit wait_for_assert = 1,
bit wait_for_deassert = 1);
@ -179,7 +212,11 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
// run write-only sequence to randomize the csr values
m_csr_write_seq = csr_write_seq::type_id::create("m_csr_write_seq");
m_csr_write_seq.models = cfg.ral_models;
// We have to assign this array in a loop because the element types aren't equivalent, so
// the array types aren't assignment compatible.
foreach (cfg.ral_models[i]) begin
m_csr_write_seq.models[i] = cfg.ral_models[i];
end
m_csr_write_seq.external_checker = cfg.en_scb;
m_csr_write_seq.en_rand_backdoor_write = 1'b1;
m_csr_write_seq.start(null);
@ -196,7 +233,11 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
// create base csr seq and pass our ral
m_csr_seq = csr_base_seq::type_id::create("m_csr_seq");
m_csr_seq.models = cfg.ral_models;
// We have to assign this array in a loop because the element types aren't equivalent, so
// the array types aren't assignment compatible.
foreach (cfg.ral_models[i]) begin
m_csr_seq.models[i] = cfg.ral_models[i];
end
m_csr_seq.external_checker = cfg.en_scb;
m_csr_seq.num_test_csrs = num_test_csrs;
m_csr_seq.start(null);

View file

@ -14,8 +14,11 @@ package dv_utils_pkg;
`endif
// common parameters used across all benches
parameter int NUM_MAX_INTERRUPTS = 32;
parameter int NUM_MAX_ALERTS = 32;
parameter int NUM_MAX_INTERRUPTS = 32;
typedef logic [NUM_MAX_INTERRUPTS-1:0] interrupt_t;
parameter int NUM_MAX_ALERTS = 32;
typedef logic [NUM_MAX_ALERTS-1:0] alert_t;
// types & variables
typedef bit [31:0] uint;
@ -24,6 +27,13 @@ package dv_utils_pkg;
typedef bit [31:0] uint32;
typedef bit [63:0] uint64;
// TODO: The above typedefs violate the name rule, which is fixed below. Cleanup the codebase to
// use the typedefs below and remove the ones above.
typedef bit [7:0] uint8_t;
typedef bit [15:0] uint16_t;
typedef bit [31:0] uint32_t;
typedef bit [63:0] uint64_t;
// typedef parameterized pins_if for ease of implementation for interrupts and alerts
typedef virtual pins_if #(NUM_MAX_INTERRUPTS) intr_vif;
typedef virtual pins_if #(1) devmode_vif;
@ -87,6 +97,16 @@ package dv_utils_pkg;
return (a > b) ? a : b;
endfunction
// return the biggest value within the given queue of integers.
function automatic int max(const ref int int_q[$]);
`DV_CHECK_GT_FATAL(int_q.size(), 0, "max function cannot accept an empty queue of integers!",
msg_id)
// Assign the first value from the queue in case of negative integers.
max = int_q[0];
foreach (int_q[i]) max = max2(max, int_q[i]);
return max;
endfunction
// get absolute value of the input. Usage: absolute(val) or absolute(a - b)
function automatic uint absolute(int val);
return val >= 0 ? val : -val;
@ -182,7 +202,7 @@ package dv_utils_pkg;
// Periodically check for the existence of a magic file (dv.stop). Exit if it exists. This
// provides a mechanism to gracefully kill a simulation without direct access to the process.
task automatic poll_for_stop(uint interval_ns = 1000, string filename = "dv.stop");
task automatic poll_for_stop(uint interval_ns = 10_000, string filename = "dv.stop");
fork
while (1) begin
#(interval_ns * 1ns);

View file

@ -21,7 +21,9 @@
// pass and fail patterns
build_pass_patterns: []
build_fail_patterns: ["^ERROR:.*$"] // fusesoc build error
// TODO: Add back FuseSoC fail pattern after
// https://github.com/lowRISC/opentitan/issues/7348 is resolved.
build_fail_patterns: []
run_pass_patterns: ["^TEST PASSED (UVM_)?CHECKS$"]
run_fail_patterns: ["^UVM_ERROR\\s[^:].*$",
"^UVM_FATAL\\s[^:].*$",

View file

@ -12,21 +12,22 @@
{
name: tl_d_illegal_access
desc: '''Drive unsupported requests via TL interface and verify correctness of response
/ behavior. Below error cases are tested
/ behavior. Below error cases are tested bases on the
[TLUL spec]({{< relref "hw/ip/tlul/doc/_index.md#explicit-error-cases" >}})
- TL-UL protocol error cases
- Unsupported opcode. e.g a_opcode isn't Get, PutPartialData or PutFullData
- Mask isn't all active if opcode = PutFullData
- Mask isn't in enabled lanes, e.g. a_address = 0x00, a_size = 0, a_mask = 'b0010
- Mask doesn't align with address, e.g. a_address = 0x01, a_mask = 'b0001
- Address and size aren't aligned, e.g. a_address = 0x01, a_size != 0
- Size is over 2.
- invalid opcode
- some mask bits not set when opcode is `PutFullData`
- mask does not match the transfer size, e.g. `a_address = 0x00`, `a_size = 0`,
`a_mask = 'b0010`
- mask and address misaligned, e.g. `a_address = 0x01`, `a_mask = 'b0001`
- address and size aren't aligned, e.g. `a_address = 0x01`, `a_size != 0`
- size is greater than 2
- OpenTitan defined error cases
- Access unmapped address, return d_error = 1 when devmode_i == 1
- Write CSR with unaligned address, e.g. a_address[1:0] != 0
- Write CSR less than its width, e.g. when CSR is 2 bytes wide, only write 1 byte
- Write a memory without enabling all lanes (a_mask = '1) if memory doesn't support
byte enabled write
- Read a WO (write-only) memory'''
- access unmapped address, expect `d_error = 1` when `devmode_i == 1`
- write a CSR with unaligned address, e.g. `a_address[1:0] != 0`
- write a CSR less than its width, e.g. when CSR is 2 bytes wide, only write 1 byte
- write a memory with `a_mask != '1` when it doesn't support partial accesses
- read a WO (write-only) memory'''
milestone: V2
tests: ["{name}_tl_errors"]
}
@ -43,15 +44,24 @@
}
{
name: tl_d_partial_access
desc: '''Access CSR with one or more bytes of data
For read, expect to return all word value of the CSR
For write, enabling bytes should cover all CSR valid fields'''
desc: '''Access CSR with one or more bytes of data.
For read, expect to return all word value of the CSR.
For write, enabling bytes should cover all CSR valid fields.'''
milestone: V2
tests: ["{name}_csr_hw_reset",
"{name}_csr_rw",
"{name}_csr_aliasing",
"{name}_same_csr_outstanding"]
}
{
name: tl_intg_err
desc: ''' Verify that the data integrity check violation generates an alert.
Randomly inject errors on the control, data, or the ECC bits during CSR accesses.
Verify that triggers the correct fatal alert.'''
milestone: V3
tests: ["{name}_tl_intg_err"]
}
]
}

View file

@ -16,5 +16,14 @@
run_opts: ["+run_tl_errors"]
reseed: 20
}
{
name: "{name}_tl_intg_err"
build_mode: "cover_reg_top"
uvm_test_seq: "{name}_common_vseq"
run_opts: ["+run_tl_intg_err", "+en_scb=0"]
// TODO, lower the reseed to avoid having many failed tests in regression as not all the IPs
// have the integrity alert connected
reseed: 1
}
]
}

View file

@ -48,7 +48,10 @@
// - Read capability on registers, variables, and nets
// - Write (deposit) capability on registers and variables
// - Force capability on registers, variables, and nets
// TODO Trim this flag since +f hurts performance.
"-debug_access+f",
// This option is needed for uvm_hdl_*, when it accesses the array under `celldefine
"-debug_region=cell+lib",
// Use this to conditionally compile for VCS (example: LRM interpretations differ
// across tools).
"+define+VCS",
@ -125,31 +128,47 @@
cov_merge_dir: "{scratch_path}/cov_merge"
cov_merge_db_dir: "{cov_merge_dir}/merged.vdb"
cov_merge_cmd: "{job_prefix} urg"
cov_merge_opts: ["-full64",
"+urg+lic+wait",
"-nocheck",
cov_merge_opts: ["-lca",
"-full64",
// No need of generating report when merging coverage.
"-noreport",
"-flex_merge drop",
"-group merge_across_scopes",
// Merge results from tests in parallel.
"-parallel",
"-parallel_split 20",
"-parallel_temproot {cov_merge_dir}",
"+urg+lic+wait",
// Merge same assert instances found in different VDBs.
"-merge_across_libs",
// This enables grading on the merged vdb.
"-show tests",
// Enable union mode of flexible merging for covergroups.
"-flex_merge union",
// Use cov_db_dirs var for dir args; append -dir in front of each
"{eval_cmd} echo {cov_db_dirs} | sed -E 's/(\\S+)/-dir \\1/g'",
"-dbname {cov_merge_db_dir}"]
// Generate coverage reports in text as well as html.
cov_report_dir: "{scratch_path}/cov_report"
cov_report_cmd: "{job_prefix} urg"
cov_report_opts: ["-full64",
"+urg+lic+wait",
"-dir {cov_merge_db_dir}",
"-group instcov_for_score",
"-line nocasedef",
"-format both",
"-elfile {vcs_cov_excl_files}",
"-report {cov_report_dir}"]
cov_report_txt: "{cov_report_dir}/dashboard.txt"
cov_report_page: "dashboard.html"
cov_report_dir: "{scratch_path}/cov_report"
cov_report_cmd: "{job_prefix} urg"
cov_report_opts: ["-lca",
"-full64",
"+urg+lic+wait",
// Lists all the tests that covered a given object.
"-show tests",
// Enable test grading.
"-grade index",
// Use simple ratio of total covered bins over total bins across cps & crs,
"-group ratio",
// Compute overall coverage for per-instance covergroups individually rather
// than cumulatively.
"-group instcov_for_score",
"-dir {cov_merge_db_dir}",
"-line nocasedef",
"-format both",
"-elfile {vcs_cov_excl_files}",
"-report {cov_report_dir}"]
cov_report_txt: "{cov_report_dir}/dashboard.txt"
cov_report_page: "dashboard.html"
// UNR related.
// All code coverage, assert isn't supported
@ -227,7 +246,7 @@
{
name: vcs_waves
is_sim_mode: 1
build_opts: ["-debug_access+all"]
build_opts: ["-debug_access"]
}
{
name: vcs_cov

View file

@ -9,9 +9,13 @@
-module pins_if // DV construct.
-module clk_rst_if // DV construct.
-moduletree prim_alert_sender // prim_alert_sender is verified in FPV.
-moduletree prim_prince // prim_prince is verified in a separate DV environment.
-moduletree prim_lfsr // prim_lfsr is verified in FPV.
begin tgl
-tree tb
+tree tb.dut 1
+module prim_alert_sender
+module prim_prince
+module prim_lfsr
end

View file

@ -431,7 +431,7 @@ void DpiMemUtil::LoadElfToMemories(bool verbose, const std::string &filepath) {
const MemArea &mem_area = *mem_areas_[mem_area_it->second];
for (const auto seg_pr : staged_mem.GetSegs()) {
for (const auto &seg_pr : staged_mem.GetSegs()) {
const AddrRange<uint32_t> &seg_rng = seg_pr.first;
const std::vector<uint8_t> &seg_data = seg_pr.second;
@ -458,6 +458,9 @@ void DpiMemUtil::StageElf(bool verbose, const std::string &path) {
ElfFile elf(path);
// Allow subclasses to get at the loaded ELF data if they need it
OnElfLoaded(elf.ptr_);
size_t file_size;
const char *file_data = elf_rawfile(elf.ptr_, &file_size);
assert(file_data);

View file

@ -13,6 +13,9 @@
#include "mem_area.h"
#include "ranged_map.h"
// Forward declaration for the Elf type from libelf.
struct Elf;
enum MemImageType {
kMemImageUnknown = 0,
kMemImageElf,
@ -59,6 +62,8 @@ class StagedMem {
*/
class DpiMemUtil {
public:
virtual ~DpiMemUtil() {}
/**
* Register a memory as instantiated by generic ram
*
@ -128,6 +133,14 @@ class DpiMemUtil {
*/
const StagedMem &GetMemoryData(const std::string &mem_name) const;
protected:
/**
* A hook for subclasses to do extra computations with loaded ELF data. This
* runs as part of StageElf: after loading the ELF file, but before reading
* in the segments.
*/
virtual void OnElfLoaded(Elf *elf_file) {}
private:
// Memory area registry. The maps give indices pointing into the vectors
// (which all have the same number of elements). Note that mem_areas_ does

View file

@ -31,7 +31,7 @@ void Ecc32MemArea::LoadVmem(const std::string &path) const {
void Ecc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
const std::vector<uint8_t> &data,
size_t start_idx) const {
size_t start_idx, uint32_t dst_word) const {
int log_width_32 = width_byte_ / 4;
int phy_width_bits = 39 * log_width_32;
int phy_width_bytes = (phy_width_bits + 7) / 8;
@ -116,7 +116,8 @@ void Ecc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
}
void Ecc32MemArea::ReadBuffer(std::vector<uint8_t> &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES]) const {
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const {
for (int i = 0; i < width_byte_; ++i) {
int in_word = i / 4;

View file

@ -24,13 +24,14 @@ class Ecc32MemArea : public MemArea {
void LoadVmem(const std::string &path) const override;
private:
protected:
void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
const std::vector<uint8_t> &data,
size_t start_idx) const override;
const std::vector<uint8_t> &data, size_t start_idx,
uint32_t dst_word) const override;
void ReadBuffer(std::vector<uint8_t> &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES]) const override;
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const override;
};
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_ECC32_MEM_AREA_H_

View file

@ -27,10 +27,6 @@ MemArea::MemArea(const std::string &scope, uint32_t num_words,
void MemArea::Write(uint32_t word_offset,
const std::vector<uint8_t> &data) const {
// If this fails to set scope, it will throw an error which should
// be caught at this function's callsite.
SVScoped scoped(scope_);
// This "mini buffer" is used to transfer each write to SystemVerilog.
// `simutil_set_mem` takes a fixed SV_MEM_WIDTH_BITS-bit vector but it will
// only use the bits required for the RAM width. As an example, for a 32-bit
@ -47,8 +43,17 @@ void MemArea::Write(uint32_t word_offset,
for (uint32_t i = 0; i < data_words; ++i) {
uint32_t dst_word = word_offset + i;
WriteBuffer(minibuf, data, i * width_byte_);
if (!simutil_set_mem(dst_word, (svBitVecVal *)minibuf)) {
uint32_t phys_addr = ToPhysAddr(dst_word);
WriteBuffer(minibuf, data, i * width_byte_, dst_word);
// Both ToPhysAddr and WriteBuffer might set the scope with `SVScoped` so
// only construct `SVScoped` once they've both been called so they don't
// interact causing incorrect relative path behaviour. If this fails to set
// scope, it will throw an error which should be caught at this function's
// callsite.
SVScoped scoped(scope_);
if (!simutil_set_mem(phys_addr, (svBitVecVal *)minibuf)) {
std::ostringstream oss;
oss << "Could not set memory at byte offset 0x" << std::hex
<< dst_word * width_byte_ << ".";
@ -64,8 +69,6 @@ std::vector<uint8_t> MemArea::Read(uint32_t word_offset,
uint32_t num_bytes = width_byte_ * num_words;
assert(num_words <= num_bytes);
SVScoped scoped(scope_);
// See Write for an explanation for this buffer.
uint8_t minibuf[SV_MEM_WIDTH_BYTES];
memset(minibuf, 0, sizeof minibuf);
@ -76,13 +79,24 @@ std::vector<uint8_t> MemArea::Read(uint32_t word_offset,
for (uint32_t i = 0; i < num_words; ++i) {
uint32_t src_word = word_offset + i;
if (!simutil_get_mem(src_word, (svBitVecVal *)minibuf)) {
std::ostringstream oss;
oss << "Could not read memory at byte offset 0x" << std::hex
<< src_word * width_byte_ << ".";
throw std::runtime_error(oss.str());
uint32_t phys_addr = ToPhysAddr(src_word);
{
// Both ToPhysAddr and ReadBuffer might set the scope with `SVScoped`.
// Keep the `SVScoped` here confined to an inner scope so they don't
// interact causing incorrect relative path behaviour. If this fails to
// set scope, it will throw an error which should be caught at this
// function's callsite.
SVScoped scoped(scope_);
if (!simutil_get_mem(phys_addr, (svBitVecVal *)minibuf)) {
std::ostringstream oss;
oss << "Could not read memory at byte offset 0x" << std::hex
<< src_word * width_byte_ << ".";
throw std::runtime_error(oss.str());
}
}
ReadBuffer(ret, minibuf);
ReadBuffer(ret, minibuf, src_word);
}
return ret;
@ -95,8 +109,8 @@ void MemArea::LoadVmem(const std::string &path) const {
}
void MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
const std::vector<uint8_t> &data,
size_t start_idx) const {
const std::vector<uint8_t> &data, size_t start_idx,
uint32_t dst_word) const {
size_t words_left = data.size() - start_idx;
size_t to_copy = std::min(words_left, (size_t)width_byte_);
if (to_copy < width_byte_) {
@ -106,7 +120,8 @@ void MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
}
void MemArea::ReadBuffer(std::vector<uint8_t> &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES]) const {
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const {
// Append the first width_byte_ bytes of buf to data.
std::copy_n(reinterpret_cast<const char *>(buf), width_byte_,
std::back_inserter(data));

View file

@ -92,10 +92,11 @@ class MemArea {
* @param buf Destination buffer
* @param data A large buffer that contains the data to be written
* @param start_idx An offset into \p data for the start of the memory word
* @param dst_word Logical address of the location being written
*/
virtual void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
const std::vector<uint8_t> &data,
size_t start_idx) const;
const std::vector<uint8_t> &data, size_t start_idx,
uint32_t dst_word) const;
/** Extract the logical memory contents corresponding to the physical
* memory contents in \p buf and append them to \p data.
@ -104,13 +105,26 @@ class MemArea {
* across. Other implementations might undo scrambling, remove ECC bits or
* similar.
*
* @param data The target, onto which the extracted memory contents should be
* appended.
* @param data The target, onto which the extracted memory contents should
* be appended.
*
* @param buf Source buffer (physical memory bits)
* @param buf Source buffer (physical memory bits)
* @param src_word Logical address of the location being read
*/
virtual void ReadBuffer(std::vector<uint8_t> &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES]) const;
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const;
/** Convert a logical address to physical address
*
* Some memories may have a mapping between the address supplied on the
* request and the physical address used to access the memory array (for
* example scrambled memories). By default logical and physical address are
* the same.
*/
virtual uint32_t ToPhysAddr(uint32_t logical_addr) const {
return logical_addr;
}
};
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_MEM_AREA_H_

View file

@ -0,0 +1,191 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "scrambled_ecc32_mem_area.h"
#include <algorithm>
#include <cassert>
#include <iostream>
#include <sstream>
#include "scramble_model.h"
#include "sv_scoped.h"
// This is the maximum width of a nonce that's supported by the code in
// prim_util_get_scramble_key_nonce.svh
static const uint32_t kScrMaxNonceWidth = 320;
static const uint32_t kScrMaxNonceWidthByte = (kScrMaxNonceWidth + 7) / 8;
// Functions to convert from integer address to/from a little-endian vector of
// bytes, addr_width is given in bits
static std::vector<uint8_t> AddrIntToBytes(uint32_t addr, uint32_t addr_width) {
uint32_t addr_width_bytes = (addr_width + 7) / 8;
std::vector<uint8_t> addr_bytes(addr_width_bytes);
for (int i = 0; i < addr_width_bytes; ++i) {
addr_bytes[i] = addr & 0xff;
addr >>= 8;
}
return addr_bytes;
}
static uint32_t AddrBytesToInt(const std::vector<uint8_t> &addr) {
assert(addr.size() <= 4);
uint32_t addr_out = 0;
int cur_shift = 0;
for (int i = 0; i < addr.size(); ++i) {
addr_out |= (addr[i] << cur_shift);
cur_shift += 8;
}
return addr_out;
}
// Converts svBitVecVal (bit[m:n] SV type) into a byte vector
static std::vector<uint8_t> ByteVecFromSV(svBitVecVal sv_val[],
uint32_t bytes) {
int shift = 0;
std::vector<uint8_t> vec(bytes);
for (int i = 0; i < bytes; ++i) {
vec[i] = (sv_val[i / 4] >> shift) & 0xff;
shift += 8;
if (shift == 32) {
shift = 0;
}
}
return vec;
}
// Analogous to the vbits SystemVerilog function from prim_util_pkg.sv. It
// calculates the number of bits needed to address size items.
static uint32_t vbits(uint32_t size) {
assert(size > 0);
if (size == 1) {
return 1;
}
size -= 1;
uint32_t width = 0;
while (size) {
width++;
size /= 2;
}
return width;
}
extern "C" {
int simutil_get_scramble_key(svBitVecVal *key);
int simutil_get_scramble_nonce(svBitVecVal *nonce);
}
std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleKey() const {
SVScoped scoped(scr_scope_);
svBitVecVal key_minibuf[((kPrinceWidthByte * 2) + 3) / 4];
if (!simutil_get_scramble_key(key_minibuf)) {
std::ostringstream oss;
oss << "Could not read key at scope " << scr_scope_;
throw std::runtime_error(oss.str());
}
return ByteVecFromSV(key_minibuf, kPrinceWidthByte * 2);
}
std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleNonce() const {
assert(GetNonceWidthByte() <= kScrMaxNonceWidthByte);
SVScoped scoped(scr_scope_);
svBitVecVal nonce_minibuf[(kScrMaxNonceWidthByte + 3) / 4];
if (!simutil_get_scramble_nonce((svBitVecVal *)nonce_minibuf)) {
std::ostringstream oss;
oss << "Could not read nonce at scope " << scr_scope_;
throw std::runtime_error(oss.str());
}
return ByteVecFromSV(nonce_minibuf, GetNonceWidthByte());
}
ScrambledEcc32MemArea::ScrambledEcc32MemArea(const std::string &scope,
uint32_t size, uint32_t width_32,
bool repeat_keystream)
: Ecc32MemArea(
SVScoped::join_sv_scopes(
scope, "u_prim_ram_1p_adv.u_mem.gen_generic.u_impl_generic"),
size, width_32),
scr_scope_(scope) {
addr_width_ = vbits(size);
repeat_keystream_ = repeat_keystream;
}
uint32_t ScrambledEcc32MemArea::GetPhysWidth() const {
return (GetWidthByte() / 4) * 39;
}
uint32_t ScrambledEcc32MemArea::GetPhysWidthByte() const {
return (GetPhysWidth() + 7) / 8;
}
uint32_t ScrambledEcc32MemArea::GetPrinceReplications() const {
if (repeat_keystream_) {
return 1;
} else {
return (GetPhysWidthByte() + 7) / 8;
}
}
uint32_t ScrambledEcc32MemArea::GetNonceWidth() const {
return GetPrinceReplications() * 64;
}
uint32_t ScrambledEcc32MemArea::GetNonceWidthByte() const {
return GetPrinceReplications() * 8;
}
void ScrambledEcc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
const std::vector<uint8_t> &data,
size_t start_idx,
uint32_t dst_word) const {
// Compute integrity
Ecc32MemArea::WriteBuffer(buf, data, start_idx, dst_word);
std::vector<uint8_t> scramble_buf =
std::vector<uint8_t>(buf, buf + GetPhysWidthByte());
// Scramble data with integrity
scramble_buf = scramble_encrypt_data(
scramble_buf, GetPhysWidth(), 39, AddrIntToBytes(dst_word, addr_width_),
addr_width_, GetScrambleNonce(), GetScrambleKey(), repeat_keystream_);
// Copy scrambled data to write buffer
std::copy(scramble_buf.begin(), scramble_buf.end(), &buf[0]);
}
void ScrambledEcc32MemArea::ReadBuffer(std::vector<uint8_t> &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const {
// Unscramble data from read buffer
std::vector<uint8_t> scrambled_data =
std::vector<uint8_t>(buf, buf + GetPhysWidthByte());
std::vector<uint8_t> unscrambled_data = scramble_decrypt_data(
scrambled_data, GetPhysWidth(), 39, AddrIntToBytes(src_word, addr_width_),
addr_width_, GetScrambleNonce(), GetScrambleKey(), repeat_keystream_);
// Strip integrity to give final result
Ecc32MemArea::ReadBuffer(data, &unscrambled_data[0], src_word);
}
uint32_t ScrambledEcc32MemArea::ToPhysAddr(uint32_t logical_addr) const {
// Scramble logical address to get physical address
return AddrBytesToInt(scramble_addr(AddrIntToBytes(logical_addr, addr_width_),
addr_width_, GetScrambleNonce(),
GetNonceWidth()));
}

View file

@ -0,0 +1,60 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#ifndef OPENTITAN_HW_DV_VERILATOR_CPP_SCRAMBLED_ECC32_MEM_AREA_H_
#define OPENTITAN_HW_DV_VERILATOR_CPP_SCRAMBLED_ECC32_MEM_AREA_H_
#include <vector>
#include "ecc32_mem_area.h"
/**
* A memory that implements scrambling over a 32-bit ECC integrity protection
* scheme storing 39 = 32 + 7 bits of physical data for each 32 bits of logical
* data.
*/
class ScrambledEcc32MemArea : public Ecc32MemArea {
public:
/**
* Constructor
*
* Create an ScrambledEcc32MemArea that will connect to a SystemVerilog memory
* at scope. It is size words long. Each memory word is 4 * width_32 bytes
* wide in the address space and 39 * width_32 bits wide in the physical
* memory.
*
* If the keystream of one single PRINCE instance should be repeated,
* set "repeat_keystream" to true. If this is set to false, multiple
* PRINCE instances are employed to produce the keystream.
*
*/
ScrambledEcc32MemArea(const std::string &scope, uint32_t size,
uint32_t width_32, bool repeat_keystream = true);
private:
void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
const std::vector<uint8_t> &data, size_t start_idx,
uint32_t dst_word) const override;
void ReadBuffer(std::vector<uint8_t> &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const override;
uint32_t ToPhysAddr(uint32_t logical_addr) const override;
uint32_t GetPhysWidth() const;
uint32_t GetPhysWidthByte() const;
uint32_t GetPrinceReplications() const;
uint32_t GetNonceWidth() const;
uint32_t GetNonceWidthByte() const;
std::vector<uint8_t> GetScrambleKey() const;
std::vector<uint8_t> GetScrambleNonce() const;
std::string scr_scope_;
uint32_t addr_width_;
bool repeat_keystream_;
};
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_SCRAMBLED_ECC32_MEM_AREA_H_

View file

@ -93,3 +93,12 @@ SVScoped::Error::Error(const std::string &scope_name)
oss << "No such SystemVerilog scope: `" << scope_name << "'.";
msg_ = oss.str();
}
std::string SVScoped::join_sv_scopes(const std::string &a,
const std::string &b) {
assert(a.size() && b.size());
// If a = ".." and b = "foo.bar", we want "..foo.bar". Otherwise, a
// = "..foo" and b = "bar.baz", so we want "..foo.bar.baz"
// (inserting a "." between the two)
return (a.back() == '.') ? a + b : a + "." + b;
}

View file

@ -5,6 +5,7 @@
#ifndef OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_
#define OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_
#include <cassert>
#include <stdexcept>
#include <string>
#include <svdpi.h>
@ -44,6 +45,9 @@ class SVScoped {
std::string msg_;
};
// helper function to join two, possibly relative, scopes correctly.
static std::string join_sv_scopes(const std::string &a, const std::string &b);
private:
svScope prev_scope_;
};

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_verilator:memutil_dpi_scrambled"
description: "DPI memory utilities for scrambled memories"
filesets:
files_cpp:
depend:
- lowrisc:dv_verilator:memutil_dpi
- lowrisc:dv:scramble_model
files:
- cpp/scrambled_ecc32_mem_area.cc
- cpp/scrambled_ecc32_mem_area.h: { is_include_file: true }
file_type: cppSource
targets:
default:
filesets:
- files_cpp

View file

@ -0,0 +1,44 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
// Additional build-time options needed to compile C++ sources in
// simulators such as VCS and Xcelium for anything that uses
// memutil_dpi_scrambled.
memutil_dpi_core: "lowrisc:dv_verilator:memutil_dpi:0"
memutil_dpi_src_dir: "{eval_cmd} echo \"{memutil_dpi_core}\" | tr ':' '_'"
memutil_dpi_scrambled_core: "lowrisc:dv_verilator:memutil_dpi_scrambled:0"
memutil_dpi_scrambled_src_dir: "{eval_cmd} echo \"{memutil_dpi_scrambled_core}\" | tr ':' '_'"
secded_enc_core: "lowrisc:dv:secded_enc:0"
secded_enc_src_dir: "{eval_cmd} echo \"{secded_enc_core}\" | tr ':' '_'"
scramble_model_core: "lowrisc:dv:scramble_model:0"
scramble_model_dir: "{eval_cmd} echo \"{scramble_model_core}\" | tr ':' '_'"
prince_ref_core: "lowrisc:dv:crypto_prince_ref:0.1"
prince_ref_src_dir: "{eval_cmd} echo \"{prince_ref_core}\" | tr ':' '_'"
build_modes: [
{
name: vcs_memutil_dpi_scrambled_build_opts
build_opts: ["-CFLAGS -I{build_dir}/src/{memutil_dpi_src_dir}/cpp",
"-CFLAGS -I{build_dir}/src/{memutil_dpi_scrambled_src_dir}/cpp",
"-CFLAGS -I{build_dir}/src/{secded_enc_src_dir}",
"-CFLAGS -I{build_dir}/src/{scramble_model_dir}",
"-CFLAGS -I{build_dir}/src/{prince_ref_src_dir}",
"-lelf"]
}
{
name: xcelium_memutil_dpi_scrambled_build_opts
build_opts: ["-I{build_dir}/src/{memutil_dpi_src_dir}/cpp",
"-I{build_dir}/src/{memutil_dpi_scrambled_src_dir}/cpp",
"-I{build_dir}/src/{prince_ref_src_dir}",
"-I{build_dir}/src/{scramble_model_dir}",
"-lelf"]
}
]
}

View file

@ -60,6 +60,52 @@ std::pair<int, bool> VerilatorSimCtrl::Exec(int argc, char **argv) {
return std::make_pair(retcode, true);
}
static bool read_ul_arg(unsigned long *arg_val, const char *arg_name,
const char *arg_text) {
assert(arg_val && arg_name && arg_text);
bool bad_fmt = false;
bool out_of_range = false;
// We have a stricter input format that strtoul: no leading space and no
// leading plus or minus signs (strtoul has magic behaviour if the input
// starts with a minus sign, but we don't want that). We're using auto base
// detection, but a valid number will always start with 0-9 (since hex
// constants start with "0x")
if (!(('0' <= arg_text[0]) && (arg_text[0] <= '9'))) {
bad_fmt = true;
} else {
char *txt_end;
*arg_val = strtoul(arg_text, &txt_end, 0);
// If txt_end doesn't point at a \0 then we didn't read the entire
// argument.
if (*txt_end) {
bad_fmt = true;
} else {
// If the value was too big to fit in an unsigned long, strtoul sets
// errno to ERANGE.
if (errno != 0) {
assert(errno == ERANGE);
out_of_range = true;
}
}
}
if (bad_fmt) {
std::cerr << "ERROR: Bad format for " << arg_name << " argument: `"
<< arg_text << "' is not an unsigned integer.\n";
return false;
}
if (out_of_range) {
std::cerr << "ERROR: Bad format for " << arg_name << " argument: `"
<< arg_text << "' is too big.\n";
return false;
}
return true;
}
bool VerilatorSimCtrl::ParseCommandArgs(int argc, char **argv, bool &exit_app) {
const struct option long_options[] = {
{"term-after-cycles", required_argument, nullptr, 'c'},
@ -89,7 +135,10 @@ bool VerilatorSimCtrl::ParseCommandArgs(int argc, char **argv, bool &exit_app) {
TraceOn();
break;
case 'c':
term_after_cycles_ = atoi(optarg);
if (!read_ul_arg(&term_after_cycles_, "term-after-cycles", optarg)) {
exit_app = true;
return false;
}
break;
case 'h':
PrintHelp();
@ -159,6 +208,10 @@ void VerilatorSimCtrl::SetResetDuration(unsigned int cycles) {
reset_duration_cycles_ = cycles;
}
void VerilatorSimCtrl::SetTimeout(unsigned int cycles) {
term_after_cycles_ = cycles;
}
void VerilatorSimCtrl::RequestStop(bool simulation_success) {
request_stop_ = true;
simulation_success_ &= simulation_success;
@ -217,7 +270,7 @@ void VerilatorSimCtrl::PrintHelp() const {
" Write a trace file from the start\n\n";
}
std::cout << "-c|--term-after-cycles=N\n"
" Terminate simulation after N cycles\n\n"
" Terminate simulation after N cycles. 0 means no timeout.\n\n"
"-h|--help\n"
" Show help\n\n"
"All arguments are passed to the design and can be used "

View file

@ -97,6 +97,15 @@ class VerilatorSimCtrl {
*/
void SetResetDuration(unsigned int cycles);
/**
* Set a timeout in clock cycles.
*
* This can be overridden by the user (in either direction) with the
* --term-after-cycles command-line argument. Setting to zero means
* no timeout, which is the default behaviour.
*/
void SetTimeout(unsigned int cycles);
/**
* Request the simulation to stop
*/
@ -129,7 +138,7 @@ class VerilatorSimCtrl {
std::chrono::steady_clock::time_point time_begin_;
std::chrono::steady_clock::time_point time_end_;
VerilatedTracer tracer_;
int term_after_cycles_;
unsigned long term_after_cycles_;
std::vector<SimCtrlExtension *> extension_array_;
/**

View file

@ -3,11 +3,12 @@ CAPI=2:
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:dv:crypto_dpi_prince:0.1"
description: "PRINCE block cipher reference C implementation from Sebastien Riou"
description: "PRINCE block cipher reference C implementation DPI interface"
filesets:
files_dv:
depend:
- lowrisc:dv:crypto_prince_ref
files:
- prince_ref.h: {file_type: cSource, is_include_file: true}
- crypto_dpi_prince.c: {file_type: cSource}
- crypto_dpi_prince_pkg.sv: {file_type: systemVerilogSource}

View file

@ -0,0 +1,22 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
// Additional build-time options needed to compile C++ sources in
// simulators such as VCS and Xcelium for anything that uses
// crypto_prince_ref.
crypto_prince_ref_core: "lowrisc:dv:crypto_prince_ref:0.1"
crypto_prince_ref_src_dir: "{eval_cmd} echo \"{crypto_prince_ref_core}\" | tr ':' '_'"
build_modes: [
{
name: vcs_crypto_dpi_prince_build_opts
build_opts: ["-CFLAGS -I{build_dir}/src/{crypto_prince_ref_src_dir}"]
}
{
name: xcelium_crypto_dpi_prince_build_opts
build_opts: ["-I{build_dir}/src/{crypto_prince_ref_src_dir}"]
}
]
}

View file

@ -0,0 +1,15 @@
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_prince_ref:0.1"
description: "PRINCE block cipher reference C implementation from Sebastien Riou"
filesets:
files_dv:
files:
- prince_ref.h: {file_type: cSource, is_include_file: true}
targets:
default:
filesets:
- files_dv

View file

@ -18,7 +18,11 @@
fusesoc_core: lowrisc:dv:prim_prince_sim:0.1
// Import additional common sim cfg files.
import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"]
import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson",
// Config files to get the correct flags for crypto_dpi_prince
"{proj_root}/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_sim_opts.hjson"]
en_build_modes: ["{tool}_crypto_dpi_prince_build_opts"]
// Default iterations for all tests - each test entry can override this.
reseed: 50

View file

@ -0,0 +1,362 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "scramble_model.h"
#include <algorithm>
#include <cassert>
#include <functional>
#include <iostream>
#include <stdint.h>
#include <vector>
#include "prince_ref.h"
uint8_t PRESENT_SBOX4[] = {0xc, 0x5, 0x6, 0xb, 0x9, 0x0, 0xa, 0xd,
0x3, 0xe, 0xf, 0x8, 0x4, 0x7, 0x1, 0x2};
uint8_t PRESENT_SBOX4_INV[] = {0x5, 0xe, 0xf, 0x8, 0xc, 0x1, 0x2, 0xd,
0xb, 0x4, 0x6, 0x3, 0x0, 0x7, 0x9, 0xa};
static const uint32_t kNumAddrSubstPermRounds = 2;
static const uint32_t kNumDataSubstPermRounds = 2;
static const uint32_t kNumPrinceHalfRounds = 2;
static std::vector<uint8_t> byte_reverse_vector(
const std::vector<uint8_t> &vec_in) {
std::vector<uint8_t> vec_out(vec_in.size());
std::reverse_copy(std::begin(vec_in), std::end(vec_in), std::begin(vec_out));
return vec_out;
}
static uint8_t read_vector_bit(const std::vector<uint8_t> &vec,
uint32_t bit_pos) {
assert(bit_pos / 8 < vec.size());
return (vec[bit_pos / 8] >> (bit_pos % 8)) & 1;
}
static void or_vector_bit(std::vector<uint8_t> &vec, uint32_t bit_pos,
uint8_t bit) {
assert(bit_pos / 8 < vec.size());
vec[bit_pos / 8] |= bit << (bit_pos % 8);
}
static std::vector<uint8_t> xor_vectors(const std::vector<uint8_t> &vec_a,
const std::vector<uint8_t> &vec_b) {
assert(vec_a.size() == vec_b.size());
std::vector<uint8_t> vec_out(vec_a.size());
std::transform(vec_a.begin(), vec_a.end(), vec_b.begin(), vec_out.begin(),
std::bit_xor<uint8_t>{});
return vec_out;
}
// Run each 4-bit chunk of bytes from `in` through the SBOX. Where `bit_width`
// isn't a multiple of 4 the remaining bits are just copied straight through.
// `invert` choose whether to use the inverted SBOX or not.
static std::vector<uint8_t> scramble_sbox_layer(const std::vector<uint8_t> &in,
uint32_t bit_width,
uint8_t sbox[16]) {
assert(in.size() == ((bit_width + 7) / 8));
std::vector<uint8_t> out(in.size(), 0);
// Iterate through each 4 bit chunk of the data and apply the appropriate SBOX
for (int i = 0; i < bit_width / 4; ++i) {
uint8_t sbox_in, sbox_out;
sbox_in = in[i / 2];
int shift = (i % 2) ? 4 : 0;
sbox_in = (sbox_in >> shift) & 0xf;
sbox_out = sbox[sbox_in];
out[i / 2] |= sbox_out << shift;
}
// Where bit_width is not a multiple of 4 copy over the remaining bits
if (bit_width % 4) {
int shift = ((bit_width % 8) >= 4) ? 4 : 0;
uint8_t nibble = (in[bit_width / 8] >> shift) & 0xf;
out[bit_width / 8] |= nibble << shift;
}
return out;
}
// Reverse bits from incoming byte vector
static std::vector<uint8_t> scramble_flip_layer(const std::vector<uint8_t> &in,
uint32_t bit_width) {
assert(in.size() == ((bit_width + 7) / 8));
std::vector<uint8_t> out(in.size(), 0);
for (int i = 0; i < bit_width; ++i) {
or_vector_bit(out, bit_width - i - 1, read_vector_bit(in, i));
}
return out;
}
// Apply butterfly to incoming byte vector. Even bits are placed in the lower
// half of the output, odd bits are placed in the upper half of the output.
static std::vector<uint8_t> scramble_perm_layer(const std::vector<uint8_t> &in,
uint32_t bit_width,
bool invert) {
assert(in.size() == ((bit_width + 7) / 8));
std::vector<uint8_t> out(in.size(), 0);
for (int i = 0; i < bit_width / 2; ++i) {
if (invert) {
or_vector_bit(out, i * 2, read_vector_bit(in, i));
or_vector_bit(out, i * 2 + 1, read_vector_bit(in, i + (bit_width / 2)));
} else {
or_vector_bit(out, i, read_vector_bit(in, i * 2));
or_vector_bit(out, i + (bit_width / 2), read_vector_bit(in, i * 2 + 1));
}
}
if (bit_width % 2) {
// Where bit_width isn't even, the final bit is copied across to the same
// position
or_vector_bit(out, bit_width - 1, read_vector_bit(in, bit_width - 1));
}
return out;
}
// Apply a full set of subsitution/permutation rounds for encrypt to the
// incoming byte vector
static std::vector<uint8_t> scramble_subst_perm_enc(
const std::vector<uint8_t> &in, const std::vector<uint8_t> &key,
uint32_t bit_width, uint32_t num_rounds) {
assert(in.size() == ((bit_width + 7) / 8));
assert(key.size() == ((bit_width + 7) / 8));
std::vector<uint8_t> state(in);
for (int i = 0; i < num_rounds; ++i) {
state = xor_vectors(state, key);
state = scramble_sbox_layer(state, bit_width, PRESENT_SBOX4);
state = scramble_flip_layer(state, bit_width);
state = scramble_perm_layer(state, bit_width, false);
}
state = xor_vectors(state, key);
return state;
}
// Apply a full set of substitution/permutation rounds for decrypt to the
// incoming byte vector
static std::vector<uint8_t> scramble_subst_perm_dec(
const std::vector<uint8_t> &in, const std::vector<uint8_t> &key,
uint32_t bit_width, uint32_t num_rounds) {
assert(in.size() == ((bit_width + 7) / 8));
assert(key.size() == ((bit_width + 7) / 8));
std::vector<uint8_t> state(in);
for (int i = 0; i < num_rounds; ++i) {
state = xor_vectors(state, key);
state = scramble_perm_layer(state, bit_width, true);
state = scramble_flip_layer(state, bit_width);
state = scramble_sbox_layer(state, bit_width, PRESENT_SBOX4_INV);
}
state = xor_vectors(state, key);
return state;
}
// Generate a keystream for XORing with data using PRINCE.
// If repeat_keystream is set to true, the output from one PRINCE instance is
// repeated when the keystream is greater than a single PRINCE width (64bit).
// Otherwise, multiple PRINCEs are instantiated to form the keystream.
static std::vector<uint8_t> scramble_gen_keystream(
const std::vector<uint8_t> &addr, uint32_t addr_width,
const std::vector<uint8_t> &nonce, const std::vector<uint8_t> &key,
uint32_t keystream_width, uint32_t num_half_rounds, bool repeat_keystream) {
assert(key.size() == (kPrinceWidthByte * 2));
// Determine how many PRINCE replications are required
uint32_t num_princes, num_repetitions;
if (repeat_keystream) {
num_princes = 1;
num_repetitions = (keystream_width + kPrinceWidth - 1) / kPrinceWidth;
} else {
num_princes = (keystream_width + kPrinceWidth - 1) / kPrinceWidth;
num_repetitions = 1;
}
std::vector<uint8_t> keystream;
for (int i = 0; i < num_princes; ++i) {
// Initial vector is data for PRINCE to encrypt. Formed from nonce and data
// address
std::vector<uint8_t> iv(8, 0);
for (int j = 0; j < kPrinceWidth; ++j) {
if (j < addr_width) {
// Bottom addr_width bits of IV are address
or_vector_bit(iv, j, read_vector_bit(addr, j));
} else {
// Other bits are taken from nonce. Each PRINCE instantiation will use
// different nonce bits.
int nonce_bit = (j - addr_width) + i * (kPrinceWidth - addr_width);
or_vector_bit(iv, j, read_vector_bit(nonce, nonce_bit));
}
}
// PRINCE C reference model works on big-endian byte order
iv = byte_reverse_vector(iv);
auto key_be = byte_reverse_vector(key);
// Apply PRINCE to IV to produce keystream
std::vector<uint8_t> keystream_block(kPrinceWidthByte);
prince_enc_dec(&iv[0], &key_be[0], &keystream_block[0], 0, num_half_rounds,
0);
// Flip keystream into little endian order and add to keystream vector
keystream_block = byte_reverse_vector(keystream_block);
// Repeat the output of a single PRINCE instance if needed
for (int k = 0; k < num_repetitions; ++k) {
keystream.insert(keystream.end(), keystream_block.begin(),
keystream_block.end());
}
}
// Total keystream bits generated are some multiple of kPrinceWidth. This can
// result in unused keystream bits. Remove the unused bytes from the keystream
// vector and zero out top unused bits in the final byte if required.
uint32_t keystream_bytes = (keystream_width + 7) / 8;
uint32_t keystream_bytes_to_erase = keystream.size() - keystream_bytes;
if (keystream_bytes_to_erase) {
keystream.erase(keystream.end() - keystream_bytes_to_erase,
keystream.end());
}
if (keystream_width % 8) {
keystream[keystream.size() - 1] &= (1 << (keystream_width % 8)) - 1;
}
return keystream;
}
// Split incoming data into subst_perm_width chunks and individually apply the
// substitution/permutation layer to each
static std::vector<uint8_t> scramble_subst_perm_full_width(
const std::vector<uint8_t> &in, uint32_t bit_width,
uint32_t subst_perm_width, bool enc) {
assert(in.size() == ((bit_width + 7) / 8));
// Determine how many bytes each subst_perm_width chunk is and how many
// chunks are needed to cover the full bit_width.
uint32_t subst_perm_bytes = (subst_perm_width + 7) / 8;
uint32_t subst_perm_blocks =
(bit_width + subst_perm_width - 1) / subst_perm_width;
std::vector<uint8_t> out(in.size(), 0);
std::vector<uint8_t> zero_key(subst_perm_bytes, 0);
auto sp_scrambler = enc ? scramble_subst_perm_enc : scramble_subst_perm_dec;
for (int i = 0; i < subst_perm_blocks; ++i) {
// Where bit_width does not evenly divide into subst_perm_width the
// final block is smaller.
uint32_t bits_so_far = subst_perm_width * i;
uint32_t block_width = std::min(subst_perm_width, bit_width - bits_so_far);
std::vector<uint8_t> subst_perm_data(subst_perm_bytes, 0);
// Extract bits from in for this chunk
for (int j = 0; j < block_width; ++j) {
or_vector_bit(subst_perm_data, j,
read_vector_bit(in, j + i * subst_perm_width));
}
// Apply the substitution/permutation layer to the chunk
auto subst_perm_out = sp_scrambler(subst_perm_data, zero_key, block_width,
kNumDataSubstPermRounds);
// Write the result to the `out` vector
for (int j = 0; j < block_width; ++j) {
or_vector_bit(out, j + i * subst_perm_width,
read_vector_bit(subst_perm_out, j));
}
}
return out;
}
std::vector<uint8_t> scramble_addr(const std::vector<uint8_t> &addr_in,
uint32_t addr_width,
const std::vector<uint8_t> &nonce,
uint32_t nonce_width) {
assert(addr_in.size() == ((addr_width + 7) / 8));
std::vector<uint8_t> addr_enc_nonce(addr_in.size(), 0);
// Address is scrambled by using substitution/permutation layer with the nonce
// used as a key.
// Extract relevant nonce bits for key
for (int i = 0; i < addr_width; ++i) {
or_vector_bit(addr_enc_nonce, i,
read_vector_bit(nonce, nonce_width - addr_width + i));
}
// Apply substitution/permutation layer
return scramble_subst_perm_enc(addr_in, addr_enc_nonce, addr_width,
kNumAddrSubstPermRounds);
}
std::vector<uint8_t> scramble_encrypt_data(
const std::vector<uint8_t> &data_in, uint32_t data_width,
uint32_t subst_perm_width, const std::vector<uint8_t> &addr,
uint32_t addr_width, const std::vector<uint8_t> &nonce,
const std::vector<uint8_t> &key, bool repeat_keystream) {
assert(data_in.size() == ((data_width + 7) / 8));
assert(addr.size() == ((addr_width + 7) / 8));
// Data is encrypted by XORing with keystream then applying
// substitution/permutation layer
auto keystream =
scramble_gen_keystream(addr, addr_width, nonce, key, data_width,
kNumPrinceHalfRounds, repeat_keystream);
auto data_enc = xor_vectors(data_in, keystream);
return scramble_subst_perm_full_width(data_enc, data_width, subst_perm_width,
true);
}
std::vector<uint8_t> scramble_decrypt_data(
const std::vector<uint8_t> &data_in, uint32_t data_width,
uint32_t subst_perm_width, const std::vector<uint8_t> &addr,
uint32_t addr_width, const std::vector<uint8_t> &nonce,
const std::vector<uint8_t> &key, bool repeat_keystream) {
assert(data_in.size() == ((data_width + 7) / 8));
assert(addr.size() == ((addr_width + 7) / 8));
// Data is decrypted by reversing substitution/permutation layer then XORing
// with keystream
auto data_sp_out = scramble_subst_perm_full_width(data_in, data_width,
subst_perm_width, false);
auto keystream =
scramble_gen_keystream(addr, addr_width, nonce, key, data_width,
kNumPrinceHalfRounds, repeat_keystream);
auto data_dec = xor_vectors(data_sp_out, keystream);
return data_dec;
}

View file

@ -0,0 +1,20 @@
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:scramble_model"
description: "Memory scrambling C++ model"
filesets:
files_cpp:
depend:
- lowrisc:dv:crypto_prince_ref
files:
- scramble_model.cc
- scramble_model.h: { is_include_file: true }
file_type: cppSource
targets:
default:
filesets:
- files_cpp

View file

@ -0,0 +1,71 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#ifndef OPENTITAN_HW_IP_PRIM_DV_PRIM_RAM_SCR_CPP_SCRAMBLE_MODEL_H_
#define OPENTITAN_HW_IP_PRIM_DV_PRIM_RAM_SCR_CPP_SCRAMBLE_MODEL_H_
#include <stdint.h>
#include <vector>
const uint32_t kPrinceWidth = 64;
const uint32_t kPrinceWidthByte = kPrinceWidth / 8;
// C++ model of memory scrambling. All byte vectors are in little endian byte
// order (least significant byte at index 0).
/** Scramble an address to give the physical address used to access the
* scrambled memory. Return vector of scrambled address bytes
*
* @param addr_in Byte vector of address
* @param addr_width Width of the address in bits
* @param nonce Byte vector of scrambling nonce
* @param nonce_width Width of scramble nonce in bits
* @return Byte vector with scrambled address
*/
std::vector<uint8_t> scramble_addr(const std::vector<uint8_t> &addr_in,
uint32_t addr_width,
const std::vector<uint8_t> &nonce,
uint32_t nonce_width);
/** Decrypt scrambled data
* @param data_in Byte vector of data to decrypt
* @param data_width Width of data in bits
* @param subst_perm_width Width over which the substitution/permutation network
* is applied (DiffWidth parameter on prim_ram_1p_scr)
* @param addr Byte vector of data address
* @param addr_width Width of the address in bits
* @param nonce Byte vector of scrambling nonce
* @param key Byte vector of scrambling key
* @param repeat_keystream Repeat the keystream of one single PRINCE instance if
* set to true. Otherwise multiple PRINCE instances are
* used.
* @return Byte vector with decrypted data
*/
std::vector<uint8_t> scramble_decrypt_data(
const std::vector<uint8_t> &data_in, uint32_t data_width,
uint32_t subst_perm_width, const std::vector<uint8_t> &addr,
uint32_t addr_width, const std::vector<uint8_t> &nonce,
const std::vector<uint8_t> &key, bool repeat_keystream);
/** Encrypt scrambled data
* @param data_in Byte vector of data to encrypt
* @param data_width Width of data in bits
* @param subst_perm_width Width over which the substitution/permutation network
* is applied (DiffWidth parameter on prim_ram_1p_scr)
* @param addr Byte vector of data address
* @param addr_width Width of the address in bits
* @param nonce Byte vector of scrambling nonce
* @param key Byte vector of scrambling key
* @param repeat_keystream Repeat the keystream of one single PRINCE instance if
* set to true. Otherwise multiple PRINCE instances are
* used.
* @return Byte vector with encrypted data
*/
std::vector<uint8_t> scramble_encrypt_data(
const std::vector<uint8_t> &data_in, uint32_t data_width,
uint32_t subst_perm_width, const std::vector<uint8_t> &addr,
uint32_t addr_width, const std::vector<uint8_t> &nonce,
const std::vector<uint8_t> &key, bool repeat_keystream);
#endif // OPENTITAN_HW_IP_PRIM_DV_PRIM_RAM_SCR_CPP_SCRAMBLE_MODEL_H_

View file

@ -8,6 +8,7 @@ filesets:
files_formal:
depend:
- lowrisc:prim:all
- lowrisc:prim:alert
files:
- vip/prim_alert_rxtx_async_assert_fpv.sv
- tb/prim_alert_rxtx_async_fatal_fpv.sv

View file

@ -8,6 +8,7 @@ filesets:
files_formal:
depend:
- lowrisc:prim:all
- lowrisc:prim:alert
files:
- vip/prim_alert_rxtx_async_assert_fpv.sv
- tb/prim_alert_rxtx_async_fpv.sv

View file

@ -8,6 +8,7 @@ filesets:
files_formal:
depend:
- lowrisc:prim:all
- lowrisc:prim:alert
files:
- vip/prim_alert_rxtx_assert_fpv.sv
- tb/prim_alert_rxtx_fatal_fpv.sv

View file

@ -8,6 +8,7 @@ filesets:
files_formal:
depend:
- lowrisc:prim:all
- lowrisc:prim:alert
files:
- vip/prim_alert_rxtx_assert_fpv.sv
- tb/prim_alert_rxtx_fpv.sv

View file

@ -8,6 +8,7 @@ filesets:
files_formal:
depend:
- lowrisc:prim:all
- lowrisc:prim:esc
files:
- vip/prim_esc_rxtx_assert_fpv.sv
- tb/prim_esc_rxtx_bind_fpv.sv

View file

@ -7,7 +7,6 @@
module prim_alert_rxtx_async_fatal_fpv
import prim_alert_pkg::*;
import prim_esc_pkg::*;
(
input clk_i,
input rst_ni,

View file

@ -7,7 +7,6 @@
module prim_alert_rxtx_async_fpv
import prim_alert_pkg::*;
import prim_esc_pkg::*;
(
input clk_i,
input rst_ni,

View file

@ -7,7 +7,6 @@
module prim_alert_rxtx_fatal_fpv
import prim_alert_pkg::*;
import prim_esc_pkg::*;
(
input clk_i,
input rst_ni,

View file

@ -7,7 +7,6 @@
module prim_alert_rxtx_fpv
import prim_alert_pkg::*;
import prim_esc_pkg::*;
(
input clk_i,
input rst_ni,

View file

@ -9,12 +9,13 @@ module prim_arbiter_ppc_fpv #(
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 req_chk_i,
input [ N-1:0] req_i,
input [DW-1:0] data_i [N],
output logic [ N-1:0] gnt_o,
@ -29,11 +30,11 @@ module prim_arbiter_ppc_fpv #(
prim_arbiter_ppc #(
.N(N),
.DW(DW),
.EnDataPort(EnDataPort),
.EnReqStabA(EnReqStabA)
.EnDataPort(EnDataPort)
) i_prim_arbiter_ppc (
.clk_i,
.rst_ni,
.req_chk_i,
.req_i,
.data_i,
.gnt_o,

View file

@ -9,12 +9,13 @@ module prim_arbiter_tree_fpv #(
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 req_chk_i,
input [ N-1:0] req_i,
input [DW-1:0] data_i [N],
output logic [ N-1:0] gnt_o,
@ -29,11 +30,11 @@ module prim_arbiter_tree_fpv #(
prim_arbiter_tree #(
.N(N),
.DW(DW),
.EnDataPort(EnDataPort),
.EnReqStabA(EnReqStabA)
.EnDataPort(EnDataPort)
) i_prim_arbiter_tree (
.clk_i,
.rst_ni,
.req_chk_i,
.req_i,
.data_i,
.gnt_o,

View file

@ -6,7 +6,6 @@
// a formal tool.
module prim_esc_rxtx_fpv
import prim_alert_pkg::*;
import prim_esc_pkg::*;
(
input clk_i,
@ -32,7 +31,7 @@ module prim_esc_rxtx_fpv
assign esc_tx_in.esc_p = esc_tx_out.esc_p ^ esc_err_pi;
assign esc_tx_in.esc_n = esc_tx_out.esc_n ^ esc_err_ni;
prim_esc_sender i_prim_esc_sender (
prim_esc_sender u_prim_esc_sender (
.clk_i ,
.rst_ni ,
.ping_req_i ,
@ -43,7 +42,11 @@ module prim_esc_rxtx_fpv
.esc_tx_o ( esc_tx_out )
);
prim_esc_receiver i_prim_esc_receiver (
prim_esc_receiver #(
// This reduces the state space for this counter
// from 2**24 to 2**6 to speed up convergence.
.TimeoutCntDw(6)
) u_prim_esc_receiver (
.clk_i ,
.rst_ni ,
.esc_en_o ,

View file

@ -10,8 +10,8 @@ module prim_secded_22_16_bind_fpv;
prim_secded_22_16_assert_fpv prim_secded_22_16_assert_fpv (
.clk_i,
.rst_ni,
.in,
.d_o,
.data_i,
.data_o,
.syndrome_o,
.err_o,
.error_inject_i

View file

@ -7,8 +7,8 @@
module prim_secded_22_16_fpv (
input clk_i,
input rst_ni,
input [15:0] in,
output logic [15:0] d_o,
input [15:0] data_i,
output logic [15:0] data_o,
output logic [5:0] syndrome_o,
output logic [1:0] err_o,
input [21:0] error_inject_i
@ -17,13 +17,13 @@ module prim_secded_22_16_fpv (
logic [21:0] data_enc;
prim_secded_22_16_enc prim_secded_22_16_enc (
.in,
.out(data_enc)
.data_i,
.data_o(data_enc)
);
prim_secded_22_16_dec prim_secded_22_16_dec (
.in(data_enc ^ error_inject_i),
.d_o,
.data_i(data_enc ^ error_inject_i),
.data_o,
.syndrome_o,
.err_o
);

View file

@ -10,8 +10,8 @@ module prim_secded_28_22_bind_fpv;
prim_secded_28_22_assert_fpv prim_secded_28_22_assert_fpv (
.clk_i,
.rst_ni,
.in,
.d_o,
.data_i,
.data_o,
.syndrome_o,
.err_o,
.error_inject_i

View file

@ -7,8 +7,8 @@
module prim_secded_28_22_fpv (
input clk_i,
input rst_ni,
input [21:0] in,
output logic [21:0] d_o,
input [21:0] data_i,
output logic [21:0] data_o,
output logic [5:0] syndrome_o,
output logic [1:0] err_o,
input [27:0] error_inject_i
@ -17,13 +17,13 @@ module prim_secded_28_22_fpv (
logic [27:0] data_enc;
prim_secded_28_22_enc prim_secded_28_22_enc (
.in,
.out(data_enc)
.data_i,
.data_o(data_enc)
);
prim_secded_28_22_dec prim_secded_28_22_dec (
.in(data_enc ^ error_inject_i),
.d_o,
.data_i(data_enc ^ error_inject_i),
.data_o,
.syndrome_o,
.err_o
);

View file

@ -10,8 +10,8 @@ module prim_secded_39_32_bind_fpv;
prim_secded_39_32_assert_fpv prim_secded_39_32_assert_fpv (
.clk_i,
.rst_ni,
.in,
.d_o,
.data_i,
.data_o,
.syndrome_o,
.err_o,
.error_inject_i

View file

@ -7,8 +7,8 @@
module prim_secded_39_32_fpv (
input clk_i,
input rst_ni,
input [31:0] in,
output logic [31:0] d_o,
input [31:0] data_i,
output logic [31:0] data_o,
output logic [6:0] syndrome_o,
output logic [1:0] err_o,
input [38:0] error_inject_i
@ -17,13 +17,13 @@ module prim_secded_39_32_fpv (
logic [38:0] data_enc;
prim_secded_39_32_enc prim_secded_39_32_enc (
.in,
.out(data_enc)
.data_i,
.data_o(data_enc)
);
prim_secded_39_32_dec prim_secded_39_32_dec (
.in(data_enc ^ error_inject_i),
.d_o,
.data_i(data_enc ^ error_inject_i),
.data_o,
.syndrome_o,
.err_o
);

View file

@ -10,8 +10,8 @@ module prim_secded_64_57_bind_fpv;
prim_secded_64_57_assert_fpv prim_secded_64_57_assert_fpv (
.clk_i,
.rst_ni,
.in,
.d_o,
.data_i,
.data_o,
.syndrome_o,
.err_o,
.error_inject_i

View file

@ -7,8 +7,8 @@
module prim_secded_64_57_fpv (
input clk_i,
input rst_ni,
input [56:0] in,
output logic [56:0] d_o,
input [56:0] data_i,
output logic [56:0] data_o,
output logic [6:0] syndrome_o,
output logic [1:0] err_o,
input [63:0] error_inject_i
@ -17,13 +17,13 @@ module prim_secded_64_57_fpv (
logic [63:0] data_enc;
prim_secded_64_57_enc prim_secded_64_57_enc (
.in,
.out(data_enc)
.data_i,
.data_o(data_enc)
);
prim_secded_64_57_dec prim_secded_64_57_dec (
.in(data_enc ^ error_inject_i),
.d_o,
.data_i(data_enc ^ error_inject_i),
.data_o,
.syndrome_o,
.err_o
);

View file

@ -10,8 +10,8 @@ module prim_secded_72_64_bind_fpv;
prim_secded_72_64_assert_fpv prim_secded_72_64_assert_fpv (
.clk_i,
.rst_ni,
.in,
.d_o,
.data_i,
.data_o,
.syndrome_o,
.err_o,
.error_inject_i

View file

@ -7,8 +7,8 @@
module prim_secded_72_64_fpv (
input clk_i,
input rst_ni,
input [63:0] in,
output logic [63:0] d_o,
input [63:0] data_i,
output logic [63:0] data_o,
output logic [7:0] syndrome_o,
output logic [1:0] err_o,
input [71:0] error_inject_i
@ -17,13 +17,13 @@ module prim_secded_72_64_fpv (
logic [71:0] data_enc;
prim_secded_72_64_enc prim_secded_72_64_enc (
.in,
.out(data_enc)
.data_i,
.data_o(data_enc)
);
prim_secded_72_64_dec prim_secded_72_64_dec (
.in(data_enc ^ error_inject_i),
.d_o,
.data_i(data_enc ^ error_inject_i),
.data_o,
.syndrome_o,
.err_o
);

View file

@ -10,8 +10,8 @@ module prim_secded_hamming_22_16_bind_fpv;
prim_secded_hamming_22_16_assert_fpv prim_secded_hamming_22_16_assert_fpv (
.clk_i,
.rst_ni,
.in,
.d_o,
.data_i,
.data_o,
.syndrome_o,
.err_o,
.error_inject_i

View file

@ -7,8 +7,8 @@
module prim_secded_hamming_22_16_fpv (
input clk_i,
input rst_ni,
input [15:0] in,
output logic [15:0] d_o,
input [15:0] data_i,
output logic [15:0] data_o,
output logic [5:0] syndrome_o,
output logic [1:0] err_o,
input [21:0] error_inject_i
@ -17,13 +17,13 @@ module prim_secded_hamming_22_16_fpv (
logic [21:0] data_enc;
prim_secded_hamming_22_16_enc prim_secded_hamming_22_16_enc (
.in,
.out(data_enc)
.data_i,
.data_o(data_enc)
);
prim_secded_hamming_22_16_dec prim_secded_hamming_22_16_dec (
.in(data_enc ^ error_inject_i),
.d_o,
.data_i(data_enc ^ error_inject_i),
.data_o,
.syndrome_o,
.err_o
);

View file

@ -10,8 +10,8 @@ module prim_secded_hamming_39_32_bind_fpv;
prim_secded_hamming_39_32_assert_fpv prim_secded_hamming_39_32_assert_fpv (
.clk_i,
.rst_ni,
.in,
.d_o,
.data_i,
.data_o,
.syndrome_o,
.err_o,
.error_inject_i

View file

@ -7,8 +7,8 @@
module prim_secded_hamming_39_32_fpv (
input clk_i,
input rst_ni,
input [31:0] in,
output logic [31:0] d_o,
input [31:0] data_i,
output logic [31:0] data_o,
output logic [6:0] syndrome_o,
output logic [1:0] err_o,
input [38:0] error_inject_i
@ -17,13 +17,13 @@ module prim_secded_hamming_39_32_fpv (
logic [38:0] data_enc;
prim_secded_hamming_39_32_enc prim_secded_hamming_39_32_enc (
.in,
.out(data_enc)
.data_i,
.data_o(data_enc)
);
prim_secded_hamming_39_32_dec prim_secded_hamming_39_32_dec (
.in(data_enc ^ error_inject_i),
.d_o,
.data_i(data_enc ^ error_inject_i),
.data_o,
.syndrome_o,
.err_o
);

View file

@ -10,8 +10,8 @@ module prim_secded_hamming_72_64_bind_fpv;
prim_secded_hamming_72_64_assert_fpv prim_secded_hamming_72_64_assert_fpv (
.clk_i,
.rst_ni,
.in,
.d_o,
.data_i,
.data_o,
.syndrome_o,
.err_o,
.error_inject_i

View file

@ -7,8 +7,8 @@
module prim_secded_hamming_72_64_fpv (
input clk_i,
input rst_ni,
input [63:0] in,
output logic [63:0] d_o,
input [63:0] data_i,
output logic [63:0] data_o,
output logic [7:0] syndrome_o,
output logic [1:0] err_o,
input [71:0] error_inject_i
@ -17,13 +17,13 @@ module prim_secded_hamming_72_64_fpv (
logic [71:0] data_enc;
prim_secded_hamming_72_64_enc prim_secded_hamming_72_64_enc (
.in,
.out(data_enc)
.data_i,
.data_o(data_enc)
);
prim_secded_hamming_72_64_dec prim_secded_hamming_72_64_dec (
.in(data_enc ^ error_inject_i),
.d_o,
.data_i(data_enc ^ error_inject_i),
.data_o,
.syndrome_o,
.err_o
);

View file

@ -24,47 +24,147 @@ module prim_esc_rxtx_assert_fpv (
);
logic error_present;
assign error_present = resp_err_pi | resp_err_ni |
esc_err_pi | esc_err_ni;
assign error_present = resp_err_pi ||
resp_err_ni ||
esc_err_pi ||
esc_err_ni;
// tracks whether any error has been injected so far
logic error_d, error_q;
assign error_d = error_q || error_present;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_error_reg
if (!rst_ni) begin
error_q <= 1'b0;
end else begin
error_q <= error_d;
end
end
// tracks whether escalation has been triggered so far
logic esc_d, esc_q;
assign esc_d = esc_q || esc_req_i;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_esc_reg
if (!rst_ni) begin
esc_q <= 1'b0;
end else begin
esc_q <= esc_d;
end
end
// ping will stay high until ping ok received, then it must be deasserted
// TODO: this escludes the case where no ping ok will be returned due to an error
`ASSUME_FPV(PingDeassert_M, ping_req_i && ping_ok_o |=> ping_req_i, clk_i, !rst_ni)
`ASSUME_FPV(PingEnStaysAsserted0_M, ping_req_i |=>
(ping_req_i && !ping_ok_o) or (ping_req_i && ping_ok_o ##1 $fell(ping_req_i)),
clk_i, !rst_ni || error_present)
`ASSUME_FPV(PingReqDeassert_M,
ping_req_i &&
ping_ok_o
|=>
!ping_req_i)
`ASSUME_FPV(PingReqStaysAsserted0_M,
ping_req_i &&
!ping_ok_o
|=>
ping_req_i)
// this timing is guaranteed by the lfsr ping timer.
`ASSUME_FPV(PingReqStaysLowFor3Cycles_M,
$fell(ping_req_i)
|->
!ping_req_i [*3])
// assume that the ping enable and escalation enable signals will eventually be deasserted (and
// esc will stay low for more than 2 cycles)
`ASSUME_FPV(FiniteEsc_M, esc_req_i |-> strong(##[1:$] !esc_req_i [*2]))
`ASSUME_FPV(FinitePing_M, ping_req_i |-> strong(##[1:$] !ping_req_i))
// ping response mus occur within 4 cycles (given that no
// error occured within the previous cycles)
`ASSERT(PingRespCheck_A, !error_present [*4] ##1 $rose(ping_req_i) |->
##[0:4] ping_ok_o, clk_i, !rst_ni || error_present)
// assume that escalation enable signal will eventually be deasserted
// for more than 3 cycles (this assumption is needed such that the FSM liveness
// assertion below can be proven).
`ASSUME_FPV(FiniteEsc_M,
esc_req_i
|->
strong(##[1:$] !esc_req_i [*3]))
// be more specific (i.e. use throughout)
`ASSERT(EscRespCheck_A, ##1 esc_req_i |-> ##[0:1] prim_esc_rxtx_fpv.esc_rx_out.resp_p ##1
!prim_esc_rxtx_fpv.esc_rx_out.resp_p, clk_i, !rst_ni || error_present)
// check that ping response time is bounded if no error has occurred so far, and
// no escalation is being requested.
`ASSERT(PingRespCheck_A,
$rose(ping_req_i) &&
!esc_req_i
|->
##[0:4] ping_ok_o,
clk_i,
!rst_ni ||
error_d ||
esc_req_i)
// check escalation response toggles.
`ASSERT(EscRespCheck_A,
##1 esc_req_i
|->
##[0:1] prim_esc_rxtx_fpv.esc_rx_out.resp_p
##1 !prim_esc_rxtx_fpv.esc_rx_out.resp_p,
clk_i,
!rst_ni ||
error_present)
// check correct transmission of escalation within 0-1 cycles
`ASSERT(EscCheck_A, ##1 esc_req_i |-> ##[0:1] esc_en_o, clk_i, !rst_ni || error_present)
`ASSERT(EscCheck_A,
##1 esc_req_i
|->
##[0:1] esc_en_o,
clk_i,
!rst_ni ||
error_present)
// check that a single error on the diffpairs is detected
`ASSERT(SingleSigIntDetected0_A, {esc_err_pi, esc_err_ni} == '0 ##1
$onehot({resp_err_pi, resp_err_ni}) |-> integ_fail_o)
`ASSERT(SingleSigIntDetected1_A, $onehot({esc_err_pi, esc_err_ni}) ##1
{resp_err_pi, resp_err_ni} == '0 |-> integ_fail_o)
`ASSERT(SingleSigIntDetected0_A,
{esc_err_pi, esc_err_ni} == '0 ##1
$onehot({resp_err_pi, resp_err_ni})
|->
integ_fail_o)
`ASSERT(SingleSigIntDetected1_A,
$onehot({esc_err_pi, esc_err_ni}) ##1
{resp_err_pi, resp_err_ni} == '0
|->
integ_fail_o)
// basic liveness of sender FSM
`ASSERT(FsmLivenessSender_A,
(prim_esc_rxtx_fpv.u_prim_esc_sender.state_q !=
prim_esc_rxtx_fpv.u_prim_esc_sender.Idle)
|->
strong(##[1:$] (prim_esc_rxtx_fpv.u_prim_esc_sender.state_q
== prim_esc_rxtx_fpv.u_prim_esc_sender.Idle)))
// basic liveness of sender FSM (can only be guaranteed if no error is present)
`ASSERT(FsmLivenessReceiver_A,
(prim_esc_rxtx_fpv.u_prim_esc_receiver.state_q !=
prim_esc_rxtx_fpv.u_prim_esc_receiver.Idle)
|->
strong(##[1:$] (prim_esc_rxtx_fpv.u_prim_esc_receiver.state_q
== prim_esc_rxtx_fpv.u_prim_esc_receiver.Idle)),
clk_i,
rst_ni ||
error_present)
// check that auto escalation timeout does not trigger prematurely.
// this requires that no errors have been present so far.
`ASSERT(AutoEscalation0_A,
ping_req_i &&
ping_ok_o &&
!esc_en_o ##1
!ping_req_i [*0 : 2**prim_esc_rxtx_fpv.u_prim_esc_receiver.TimeoutCntDw - 4]
|->
!esc_en_o,
clk_i,
!rst_ni ||
error_d ||
esc_d)
// check that auto escalation timeout kicks in if pings are absent for too long.
// this requires that no errors have been present so far.
`ASSERT(AutoEscalation1_A,
ping_req_i &&
ping_ok_o &&
!esc_en_o ##1
!ping_req_i [* 2**prim_esc_rxtx_fpv.u_prim_esc_receiver.TimeoutCntDw - 3 : $]
|->
esc_en_o,
clk_i,
!rst_ni ||
error_d ||
esc_d)
// basic liveness of FSMs in case no errors are present
`ASSERT(FsmLivenessSender_A, (prim_esc_rxtx_fpv.i_prim_esc_sender.state_q !=
prim_esc_rxtx_fpv.i_prim_esc_sender.Idle) |->
strong(##[1:$] (prim_esc_rxtx_fpv.i_prim_esc_sender.state_q
== prim_esc_rxtx_fpv.i_prim_esc_sender.Idle)), clk_i, !rst_ni || error_present)
`ASSERT(FsmLivenessReceiver_A, (prim_esc_rxtx_fpv.i_prim_esc_receiver.state_q !=
prim_esc_rxtx_fpv.i_prim_esc_receiver.Idle) |->
strong(##[1:$] (prim_esc_rxtx_fpv.i_prim_esc_receiver.state_q
== prim_esc_rxtx_fpv.i_prim_esc_receiver.Idle)), clk_i, !rst_ni || error_present)
endmodule : prim_esc_rxtx_assert_fpv

View file

@ -7,8 +7,8 @@
module prim_secded_22_16_assert_fpv (
input clk_i,
input rst_ni,
input [15:0] in,
input [15:0] d_o,
input [15:0] data_i,
input [15:0] data_o,
input [5:0] syndrome_o,
input [1:0] err_o,
input [21:0] error_inject_i
@ -17,7 +17,7 @@ module prim_secded_22_16_assert_fpv (
// Inject a maximum of two errors simultaneously.
`ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2)
// This bounds the input data state space to make sure the solver converges.
`ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in))
`ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i))
// Single bit error detection
`ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0])
`ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1)
@ -25,7 +25,7 @@ module prim_secded_22_16_assert_fpv (
`ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1])
`ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2)
// Single bit error correction (implicitly tests the syndrome output)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o)
// Basic syndrome check
`ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0)
`ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o)

View file

@ -7,8 +7,8 @@
module prim_secded_28_22_assert_fpv (
input clk_i,
input rst_ni,
input [21:0] in,
input [21:0] d_o,
input [21:0] data_i,
input [21:0] data_o,
input [5:0] syndrome_o,
input [1:0] err_o,
input [27:0] error_inject_i
@ -17,7 +17,7 @@ module prim_secded_28_22_assert_fpv (
// Inject a maximum of two errors simultaneously.
`ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2)
// This bounds the input data state space to make sure the solver converges.
`ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in))
`ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i))
// Single bit error detection
`ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0])
`ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1)
@ -25,7 +25,7 @@ module prim_secded_28_22_assert_fpv (
`ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1])
`ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2)
// Single bit error correction (implicitly tests the syndrome output)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o)
// Basic syndrome check
`ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0)
`ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o)

View file

@ -7,8 +7,8 @@
module prim_secded_39_32_assert_fpv (
input clk_i,
input rst_ni,
input [31:0] in,
input [31:0] d_o,
input [31:0] data_i,
input [31:0] data_o,
input [6:0] syndrome_o,
input [1:0] err_o,
input [38:0] error_inject_i
@ -17,7 +17,7 @@ module prim_secded_39_32_assert_fpv (
// Inject a maximum of two errors simultaneously.
`ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2)
// This bounds the input data state space to make sure the solver converges.
`ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in))
`ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i))
// Single bit error detection
`ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0])
`ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1)
@ -25,7 +25,7 @@ module prim_secded_39_32_assert_fpv (
`ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1])
`ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2)
// Single bit error correction (implicitly tests the syndrome output)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o)
// Basic syndrome check
`ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0)
`ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o)

View file

@ -7,8 +7,8 @@
module prim_secded_64_57_assert_fpv (
input clk_i,
input rst_ni,
input [56:0] in,
input [56:0] d_o,
input [56:0] data_i,
input [56:0] data_o,
input [6:0] syndrome_o,
input [1:0] err_o,
input [63:0] error_inject_i
@ -17,7 +17,7 @@ module prim_secded_64_57_assert_fpv (
// Inject a maximum of two errors simultaneously.
`ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2)
// This bounds the input data state space to make sure the solver converges.
`ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in))
`ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i))
// Single bit error detection
`ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0])
`ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1)
@ -25,7 +25,7 @@ module prim_secded_64_57_assert_fpv (
`ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1])
`ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2)
// Single bit error correction (implicitly tests the syndrome output)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o)
// Basic syndrome check
`ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0)
`ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o)

View file

@ -7,8 +7,8 @@
module prim_secded_72_64_assert_fpv (
input clk_i,
input rst_ni,
input [63:0] in,
input [63:0] d_o,
input [63:0] data_i,
input [63:0] data_o,
input [7:0] syndrome_o,
input [1:0] err_o,
input [71:0] error_inject_i
@ -17,7 +17,7 @@ module prim_secded_72_64_assert_fpv (
// Inject a maximum of two errors simultaneously.
`ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2)
// This bounds the input data state space to make sure the solver converges.
`ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in))
`ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i))
// Single bit error detection
`ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0])
`ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1)
@ -25,7 +25,7 @@ module prim_secded_72_64_assert_fpv (
`ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1])
`ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2)
// Single bit error correction (implicitly tests the syndrome output)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o)
// Basic syndrome check
`ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0)
`ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o)

View file

@ -7,8 +7,8 @@
module prim_secded_hamming_22_16_assert_fpv (
input clk_i,
input rst_ni,
input [15:0] in,
input [15:0] d_o,
input [15:0] data_i,
input [15:0] data_o,
input [5:0] syndrome_o,
input [1:0] err_o,
input [21:0] error_inject_i
@ -17,7 +17,7 @@ module prim_secded_hamming_22_16_assert_fpv (
// Inject a maximum of two errors simultaneously.
`ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2)
// This bounds the input data state space to make sure the solver converges.
`ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in))
`ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i))
// Single bit error detection
`ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0])
`ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1)
@ -25,7 +25,7 @@ module prim_secded_hamming_22_16_assert_fpv (
`ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1])
`ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2)
// Single bit error correction (implicitly tests the syndrome output)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o)
// Basic syndrome check
`ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0)
`ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o)

View file

@ -7,8 +7,8 @@
module prim_secded_hamming_39_32_assert_fpv (
input clk_i,
input rst_ni,
input [31:0] in,
input [31:0] d_o,
input [31:0] data_i,
input [31:0] data_o,
input [6:0] syndrome_o,
input [1:0] err_o,
input [38:0] error_inject_i
@ -17,7 +17,7 @@ module prim_secded_hamming_39_32_assert_fpv (
// Inject a maximum of two errors simultaneously.
`ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2)
// This bounds the input data state space to make sure the solver converges.
`ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in))
`ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i))
// Single bit error detection
`ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0])
`ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1)
@ -25,7 +25,7 @@ module prim_secded_hamming_39_32_assert_fpv (
`ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1])
`ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2)
// Single bit error correction (implicitly tests the syndrome output)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o)
// Basic syndrome check
`ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0)
`ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o)

View file

@ -7,8 +7,8 @@
module prim_secded_hamming_72_64_assert_fpv (
input clk_i,
input rst_ni,
input [63:0] in,
input [63:0] d_o,
input [63:0] data_i,
input [63:0] data_o,
input [7:0] syndrome_o,
input [1:0] err_o,
input [71:0] error_inject_i
@ -17,7 +17,7 @@ module prim_secded_hamming_72_64_assert_fpv (
// Inject a maximum of two errors simultaneously.
`ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2)
// This bounds the input data state space to make sure the solver converges.
`ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in))
`ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i))
// Single bit error detection
`ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0])
`ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1)
@ -25,7 +25,7 @@ module prim_secded_hamming_72_64_assert_fpv (
`ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1])
`ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2)
// Single bit error correction (implicitly tests the syndrome output)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o)
`ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o)
// Basic syndrome check
`ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0)
`ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o)

View file

@ -0,0 +1,9 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`verilator_config
// prim_ram_1p_scr
// waive warnings on comparing 'addr_cnt_q' with 32 bit parameters
lint_off -rule WIDTH -file "*/rtl/prim_ram_1p_scr.sv" -match "Operator * expects 32 bits on the LHS, but LHS's VARREF 'addr_cnt_q' generates * bits."

View file

@ -69,6 +69,8 @@ module prim_sync_reqack_tb #(
.clk_dst_i (clk_dst),
.rst_dst_ni (rst_slow_n),
.req_chk_i (1'b1),
.src_req_i (src_req),
.src_ack_o (src_ack),
.dst_req_o (dst_req),

View file

@ -22,14 +22,12 @@ filesets:
- lowrisc:prim:arbiter
- lowrisc:prim:fifo
- lowrisc:prim:alert
- lowrisc:prim:esc
- lowrisc:prim:subreg
- lowrisc:prim:cipher
- lowrisc:prim:xor2
files:
- rtl/prim_clock_gating_sync.sv
- rtl/prim_esc_pkg.sv
- rtl/prim_esc_receiver.sv
- rtl/prim_esc_sender.sv
- rtl/prim_sram_arbiter.sv
- rtl/prim_slicer.sv
- rtl/prim_sync_reqack.sv

View file

@ -11,6 +11,7 @@ filesets:
- lowrisc:prim:assert
- lowrisc:prim:diff_decode
- lowrisc:prim:buf
- lowrisc:prim:flop
files:
- rtl/prim_alert_pkg.sv
- rtl/prim_alert_receiver.sv

42
vendor/lowrisc_ip/ip/prim/prim_esc.core vendored Normal file
View file

@ -0,0 +1,42 @@
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:prim:esc"
description: "Escalation send and receive"
filesets:
files_rtl:
depend:
- lowrisc:prim:assert
- lowrisc:prim:diff_decode
- lowrisc:prim:buf
- lowrisc:prim:flop
files:
- rtl/prim_esc_pkg.sv
- rtl/prim_esc_receiver.sv
- rtl/prim_esc_sender.sv
file_type: systemVerilogSource
files_verilator_waiver:
depend:
# common waivers
- lowrisc:lint:common
files_ascentlint_waiver:
depend:
# common waivers
- lowrisc:lint:common
files_veriblelint_waiver:
depend:
# common waivers
- lowrisc:lint:common
targets:
default:
filesets:
- tool_verilator ? (files_verilator_waiver)
- tool_ascentlint ? (files_ascentlint_waiver)
- tool_veriblelint ? (files_veriblelint_waiver)
- files_rtl

View file

@ -9,6 +9,7 @@ filesets:
files_rtl:
depend:
- lowrisc:prim:assert
- lowrisc:prim:util
- lowrisc:prim:flop_2sync
files:
- rtl/prim_fifo_async.sv

View file

@ -13,11 +13,22 @@ filesets:
- lowrisc:prim:ram_1p_adv
- lowrisc:prim:lfsr
- lowrisc:prim:all
- lowrisc:prim:util_get_scramble_params
- lowrisc:dv_verilator:memutil_dpi_scrambled
files:
- rtl/prim_ram_1p_scr.sv
file_type: systemVerilogSource
files_verilator_waiver:
depend:
# common waivers
- lowrisc:lint:common
files:
- lint/prim_ram_1p_scr.vlt
file_type: vlt
targets:
default:
filesets:
- tool_verilator ? (files_verilator_waiver)
- files_rtl

View file

@ -0,0 +1,17 @@
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:prim:util_get_scramble_params"
description: "DPI functions to snoop scramble key and nonce for simulation"
filesets:
files_rtl:
files:
- rtl/prim_util_get_scramble_params.svh: {is_include_file: true}
file_type: systemVerilogSource
targets:
default:
filesets:
- files_rtl

View file

@ -56,15 +56,25 @@ module prim_alert_receiver
/////////////////////////////////
// decode differential signals //
/////////////////////////////////
logic alert_level, alert_sigint;
logic alert_level, alert_sigint, alert_p, alert_n;
// This prevents further tool optimizations of the differential signal.
prim_buf #(
.Width(2)
) u_prim_buf (
.in_i({alert_tx_i.alert_n,
alert_tx_i.alert_p}),
.out_o({alert_n,
alert_p})
);
prim_diff_decode #(
.AsyncOn(AsyncOn)
) i_decode_alert (
) u_decode_alert (
.clk_i,
.rst_ni,
.diff_pi ( alert_tx_i.alert_p ),
.diff_ni ( alert_tx_i.alert_n ),
.diff_pi ( alert_p ),
.diff_ni ( alert_n ),
.level_o ( alert_level ),
.rise_o ( ),
.fall_o ( ),
@ -78,33 +88,43 @@ module prim_alert_receiver
typedef enum logic [1:0] {Idle, HsAckWait, Pause0, Pause1} state_e;
state_e state_d, state_q;
logic ping_rise;
logic ping_tog, ping_tog_dp, ping_tog_qp, ping_tog_dn, ping_tog_qn;
logic ack, ack_dp, ack_qp, ack_dn, ack_qn;
logic ping_tog_pd, ping_tog_pq, ping_tog_dn, ping_tog_nq;
logic ack_pd, ack_pq, ack_dn, ack_nq;
logic ping_req_d, ping_req_q;
logic ping_pending_d, ping_pending_q;
// signal ping request upon positive transition on ping_req_i
// signalling is performed by a level change event on the diff output
assign ping_req_d = ping_req_i;
assign ping_rise = ping_req_i && !ping_req_q;
assign ping_tog = (ping_rise) ? ~ping_tog_qp : ping_tog_qp;
assign ping_rise = ping_req_i && !ping_req_q;
assign ping_tog_pd = (ping_rise) ? ~ping_tog_pq : ping_tog_pq;
assign ack_dn = ~ack_pd;
assign ping_tog_dn = ~ping_tog_pd;
// This prevents further tool optimizations of the differential signal.
prim_buf u_prim_buf_ack_p (
.in_i(ack),
.out_o(ack_dp)
prim_generic_flop #(
.Width (2),
.ResetValue(2'b10)
) u_prim_generic_flop_ack (
.clk_i,
.rst_ni,
.d_i({ack_dn,
ack_pd}),
.q_o({ack_nq,
ack_pq})
);
prim_buf u_prim_buf_ack_n (
.in_i(~ack),
.out_o(ack_dn)
);
prim_buf u_prim_buf_ping_p (
.in_i(ping_tog),
.out_o(ping_tog_dp)
);
prim_buf u_prim_buf_ping_n (
.in_i(~ping_tog),
.out_o(ping_tog_dn)
prim_generic_flop #(
.Width (2),
.ResetValue(2'b10)
) u_prim_generic_flop_ping (
.clk_i,
.rst_ni,
.d_i({ping_tog_dn,
ping_tog_pd}),
.q_o({ping_tog_nq,
ping_tog_pq})
);
// the ping pending signal is used to in the FSM to distinguish whether the
@ -115,11 +135,11 @@ module prim_alert_receiver
assign ping_pending_d = ping_rise | ((~ping_ok_o) & ping_req_i & ping_pending_q);
// diff pair outputs
assign alert_rx_o.ack_p = ack_qp;
assign alert_rx_o.ack_n = ack_qn;
assign alert_rx_o.ack_p = ack_pq;
assign alert_rx_o.ack_n = ack_nq;
assign alert_rx_o.ping_p = ping_tog_qp;
assign alert_rx_o.ping_n = ping_tog_qn;
assign alert_rx_o.ping_p = ping_tog_pq;
assign alert_rx_o.ping_n = ping_tog_nq;
// this FSM receives the four phase handshakes from the alert receiver
// note that the latency of the alert_p/n input diff pair is at least one
@ -128,7 +148,7 @@ module prim_alert_receiver
always_comb begin : p_fsm
// default
state_d = state_q;
ack = 1'b0;
ack_pd = 1'b0;
ping_ok_o = 1'b0;
integ_fail_o = 1'b0;
alert_o = 1'b0;
@ -138,7 +158,7 @@ module prim_alert_receiver
// wait for handshake to be initiated
if (alert_level) begin
state_d = HsAckWait;
ack = 1'b1;
ack_pd = 1'b1;
// signal either an alert or ping received on the output
if (ping_pending_q) begin
ping_ok_o = 1'b1;
@ -152,7 +172,7 @@ module prim_alert_receiver
if (!alert_level) begin
state_d = Pause0;
end else begin
ack = 1'b1;
ack_pd = 1'b1;
end
end
// pause cycles between back-to-back handshakes
@ -164,7 +184,7 @@ module prim_alert_receiver
// override in case of sigint
if (alert_sigint) begin
state_d = Idle;
ack = 1'b0;
ack_pd = 1'b0;
ping_ok_o = 1'b0;
integ_fail_o = 1'b1;
alert_o = 1'b0;
@ -174,18 +194,10 @@ module prim_alert_receiver
always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg
if (!rst_ni) begin
state_q <= Idle;
ack_qp <= 1'b0;
ack_qn <= 1'b1;
ping_tog_qp <= 1'b0;
ping_tog_qn <= 1'b1;
ping_req_q <= 1'b0;
ping_pending_q <= 1'b0;
end else begin
state_q <= state_d;
ack_qp <= ack_dp;
ack_qn <= ack_dn;
ping_tog_qp <= ping_tog_dp;
ping_tog_qn <= ping_tog_dn;
ping_req_q <= ping_req_d;
ping_pending_q <= ping_pending_d;
end

View file

@ -69,36 +69,56 @@ module prim_alert_sender
/////////////////////////////////
// decode differential signals //
/////////////////////////////////
logic ping_sigint, ping_event;
logic ping_sigint, ping_event, ping_n, ping_p;
prim_diff_decode #(
.AsyncOn(AsyncOn)
) i_decode_ping (
.clk_i,
.rst_ni,
.diff_pi ( alert_rx_i.ping_p ),
.diff_ni ( alert_rx_i.ping_n ),
.level_o ( ),
.rise_o ( ),
.fall_o ( ),
.event_o ( ping_event ),
.sigint_o ( ping_sigint )
// This prevents further tool optimizations of the differential signal.
prim_buf #(
.Width(2)
) u_prim_buf_ping (
.in_i({alert_rx_i.ping_n,
alert_rx_i.ping_p}),
.out_o({ping_n,
ping_p})
);
logic ack_sigint, ack_level;
prim_diff_decode #(
.AsyncOn(AsyncOn)
) u_decode_ping (
.clk_i,
.rst_ni,
.diff_pi ( ping_p ),
.diff_ni ( ping_n ),
.level_o ( ),
.rise_o ( ),
.fall_o ( ),
.event_o ( ping_event ),
.sigint_o ( ping_sigint )
);
logic ack_sigint, ack_level, ack_n, ack_p;
// This prevents further tool optimizations of the differential signal.
prim_buf #(
.Width(2)
) u_prim_buf_ack (
.in_i({alert_rx_i.ack_n,
alert_rx_i.ack_p}),
.out_o({ack_n,
ack_p})
);
prim_diff_decode #(
.AsyncOn(AsyncOn)
) i_decode_ack (
) u_decode_ack (
.clk_i,
.rst_ni,
.diff_pi ( alert_rx_i.ack_p ),
.diff_ni ( alert_rx_i.ack_n ),
.level_o ( ack_level ),
.rise_o ( ),
.fall_o ( ),
.event_o ( ),
.sigint_o ( ack_sigint )
.diff_pi ( ack_p ),
.diff_ni ( ack_n ),
.level_o ( ack_level ),
.rise_o ( ),
.fall_o ( ),
.event_o ( ),
.sigint_o ( ack_sigint )
);
@ -116,7 +136,7 @@ module prim_alert_sender
Pause1
} state_e;
state_e state_d, state_q;
logic alert_p, alert_n, alert_pq, alert_nq, alert_pd, alert_nd;
logic alert_pq, alert_nq, alert_pd, alert_nd;
logic sigint_detected;
assign sigint_detected = ack_sigint | ping_sigint;
@ -164,8 +184,8 @@ module prim_alert_sender
always_comb begin : p_fsm
// default
state_d = state_q;
alert_p = 1'b0;
alert_n = 1'b1;
alert_pd = 1'b0;
alert_nd = 1'b1;
ping_clr = 1'b0;
alert_clr = 1'b0;
@ -174,8 +194,8 @@ module prim_alert_sender
// alert always takes precedence
if (alert_trigger || ping_trigger) begin
state_d = (alert_trigger) ? AlertHsPhase1 : PingHsPhase1;
alert_p = 1'b1;
alert_n = 1'b0;
alert_pd = 1'b1;
alert_nd = 1'b0;
end
end
// waiting for ack from receiver
@ -183,8 +203,8 @@ module prim_alert_sender
if (ack_level) begin
state_d = AlertHsPhase2;
end else begin
alert_p = 1'b1;
alert_n = 1'b0;
alert_pd = 1'b1;
alert_nd = 1'b0;
end
end
// wait for deassertion of ack
@ -199,8 +219,8 @@ module prim_alert_sender
if (ack_level) begin
state_d = PingHsPhase2;
end else begin
alert_p = 1'b1;
alert_n = 1'b0;
alert_pd = 1'b1;
alert_nd = 1'b0;
end
end
// wait for deassertion of ack
@ -229,8 +249,8 @@ module prim_alert_sender
state_d = Idle;
if (sigint_detected) begin
state_d = SigInt;
alert_p = ~alert_pq;
alert_n = ~alert_pq;
alert_pd = ~alert_pq;
alert_nd = ~alert_pq;
end
end
// catch parasitic states
@ -239,35 +259,32 @@ module prim_alert_sender
// bail out if a signal integrity issue has been detected
if (sigint_detected && (state_q != SigInt)) begin
state_d = SigInt;
alert_p = 1'b0;
alert_n = 1'b0;
alert_pd = 1'b0;
alert_nd = 1'b0;
ping_clr = 1'b0;
alert_clr = 1'b0;
end
end
// This prevents further tool optimizations of the differential signal.
prim_buf u_prim_buf_p (
.in_i(alert_p),
.out_o(alert_pd)
);
prim_buf u_prim_buf_n (
.in_i(alert_n),
.out_o(alert_nd)
prim_generic_flop #(
.Width (2),
.ResetValue(2'b10)
) u_prim_generic_flop (
.clk_i,
.rst_ni,
.d_i({alert_nd, alert_pd}),
.q_o({alert_nq, alert_pq})
);
always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg
if (!rst_ni) begin
state_q <= Idle;
alert_pq <= 1'b0;
alert_nq <= 1'b1;
alert_set_q <= 1'b0;
alert_test_set_q <= 1'b0;
ping_set_q <= 1'b0;
end else begin
state_q <= state_d;
alert_pq <= alert_pd;
alert_nq <= alert_nd;
alert_set_q <= alert_set_d;
alert_test_set_q <= alert_test_set_d;
ping_set_q <= ping_set_d;

View file

@ -8,7 +8,6 @@
// 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.
@ -18,8 +17,8 @@
// 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.
// until they have been served. This assertion can be gated by driving the req_chk_i low. This is
// a non-functional input and does not affect the designs behavior.
//
// See also: prim_arbiter_tree
@ -33,15 +32,14 @@ module prim_arbiter_ppc #(
// 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 req_chk_i, // Used for gating assertions. Drive to 1 during normal
// operation.
input [ N-1:0] req_i,
input [DW-1:0] data_i [N],
output logic [ N-1:0] gnt_o,
@ -52,6 +50,10 @@ module prim_arbiter_ppc #(
input ready_i
);
// req_chk_i is used for gating assertions only.
logic unused_req_chk;
assign unused_req_chk = req_chk_i;
`ASSERT_INIT(CheckNGreaterZero_A, N > 0)
// this case is basically just a bypass
@ -165,13 +167,12 @@ if (EnDataPort) begin: gen_data_port_assertion
`ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[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))
`ASSUME(ReqStaysHighUntilGranted0_M, |req_i && !ready_i |=>
(req_i & $past(req_i)) == $past(req_i), clk_i, !rst_ni || !req_chk_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
`ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o),
clk_i, !rst_ni || !req_chk_i)
// FPV-only assertions with symbolic variables
`ifdef FPV_ON
@ -216,11 +217,9 @@ 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
// 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 || !req_chk_i)
`endif
endmodule : prim_arbiter_ppc

View file

@ -8,7 +8,6 @@
// 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 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
@ -25,8 +24,8 @@
// 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.
// until they have been served. This assertion can be gated by driving the req_chk_i low. This is
// a non-functional input and does not affect the designs behavior.
//
// See also: prim_arbiter_ppc
@ -40,15 +39,14 @@ module prim_arbiter_tree #(
// 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 req_chk_i, // Used for gating assertions. Drive to 1 during normal
// operation.
input [ N-1:0] req_i,
input [DW-1:0] data_i [N],
output logic [ N-1:0] gnt_o,
@ -59,6 +57,10 @@ module prim_arbiter_tree #(
input ready_i
);
// req_chk_i is used for gating assertions only.
logic unused_req_chk;
assign unused_req_chk = req_chk_i;
`ASSERT_INIT(CheckNGreaterZero_A, N > 0)
// this case is basically just a bypass
@ -140,25 +142,26 @@ module prim_arbiter_tree #(
end else begin : gen_nodes
// 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];
// 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
// forward path (requests and data)
// each node looks at its two children, and selects the one with higher priority
assign sel = ~req_tree[C0] | ~prio_tree[C0] & prio_tree[C1];
// propagate requests
assign req_tree[Pa] = req_tree[C0] | req_tree[C1];
assign prio_tree[Pa] = prio_tree[C1] | prio_tree[C0];
// data and index muxes
// Note: these ternaries have triggered a synthesis bug in Vivado versions older
// than 2020.2. If the problem resurfaces again, have a look at issue #1408.
assign idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0];
assign data_tree[Pa] = (sel) ? data_tree[C1] : data_tree[C0];
// backward path (grants and prefix sum)
// this propagates the selction index back and computes a hot one mask
assign sel_tree[C0] = sel_tree[Pa] & ~sel;
assign sel_tree[C1] = sel_tree[Pa] & sel;
// this performs a prefix sum for masking the input requests in the next cycle
assign mask_tree[C0] = mask_tree[Pa];
assign mask_tree[C1] = mask_tree[Pa] | sel_tree[C0];
end
end : gen_level
end : gen_tree
@ -230,13 +233,12 @@ if (EnDataPort) begin: gen_data_port_assertion
`ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[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))
`ASSUME(ReqStaysHighUntilGranted0_M, |req_i && !ready_i |=>
(req_i & $past(req_i)) == $past(req_i), clk_i, !rst_ni || !req_chk_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
`ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o),
clk_i, !rst_ni || !req_chk_i)
// FPV-only assertions with symbolic variables
`ifdef FPV_ON
@ -281,11 +283,9 @@ 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
// 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 || !req_chk_i)
`endif
endmodule : prim_arbiter_tree

View file

@ -16,15 +16,13 @@
module prim_edn_req
import prim_alert_pkg::*;
#(
parameter int OutWidth = 32,
// Non-functional parameter to switch on the request stability assertion.
// Used in submodule `prim_sync_reqack`.
parameter bit EnReqStabA = 1
parameter int OutWidth = 32
) (
// Design side
input clk_i,
input rst_ni,
input req_chk_i, // Used for gating assertions. Drive to 1 during normal
// operation.
input req_i,
output logic ack_o,
output logic [OutWidth-1:0] data_o,
@ -46,13 +44,13 @@ module prim_edn_req
prim_sync_reqack_data #(
.Width(SyncWidth),
.DataSrc2Dst(1'b0),
.DataReg(1'b0),
.EnReqStabA(EnReqStabA)
.DataReg(1'b0)
) u_prim_sync_reqack_data (
.clk_src_i ( clk_i ),
.rst_src_ni ( rst_ni ),
.clk_dst_i ( clk_edn_i ),
.rst_dst_ni ( rst_edn_ni ),
.req_chk_i ( req_chk_i ),
.src_req_i ( word_req ),
.src_ack_o ( word_ack ),
.dst_req_o ( edn_o.edn_req ),

View file

@ -20,7 +20,32 @@
module prim_esc_receiver
import prim_esc_pkg::*;
(
#(
// The number of escalation severities. Should be set to the Alert Handler's N_ESC_SEV when this
// primitive is instantiated.
parameter int N_ESC_SEV = 4,
// The width of the Alert Handler's ping counter. Should be set to the Alert Handler's PING_CNT_DW
// when this primitive is instantiated.
parameter int PING_CNT_DW = 16,
// This counter monitors incoming ping requests and auto-escalates if the alert handler
// ceases to send them regularly. The maximum number of cycles between subsequent ping requests
// is N_ESC_SEV x (2 x 2 x 2**PING_CNT_DW), see also implementation of the ping timer
// (alert_handler_ping_timer.sv). The timeout counter below uses a timeout that is 4x larger than
// that in order to incorporate some margin.
//
// Do NOT modify this counter value, when instantiating it in the design. It is only exposed to
// reduce the state space in the FPV testbench.
localparam int MarginFactor = 4,
localparam int NumWaitCounts = 2,
localparam int NumTimeoutCounts = 2,
parameter int TimeoutCntDw = $clog2(MarginFactor) +
$clog2(N_ESC_SEV) +
$clog2(NumWaitCounts) +
$clog2(NumTimeoutCounts) +
PING_CNT_DW
) (
input clk_i,
input rst_ni,
// escalation enable
@ -35,15 +60,25 @@ module prim_esc_receiver
// decode differential signals //
/////////////////////////////////
logic esc_level, sigint_detected;
logic esc_level, esc_p, esc_n, sigint_detected;
// This prevents further tool optimizations of the differential signal.
prim_buf #(
.Width(2)
) u_prim_buf_esc (
.in_i({esc_tx_i.esc_n,
esc_tx_i.esc_p}),
.out_o({esc_n,
esc_p})
);
prim_diff_decode #(
.AsyncOn(1'b0)
) i_decode_esc (
) u_decode_esc (
.clk_i,
.rst_ni,
.diff_pi ( esc_tx_i.esc_p ),
.diff_ni ( esc_tx_i.esc_n ),
.diff_pi ( esc_p ),
.diff_ni ( esc_n ),
.level_o ( esc_level ),
.rise_o ( ),
.fall_o ( ),
@ -51,23 +86,59 @@ module prim_esc_receiver
.sigint_o ( sigint_detected )
);
////////////////////////////////////////////
// Ping Monitor Counter / Auto Escalation //
////////////////////////////////////////////
logic ping_en, esc_en;
logic [1:0][TimeoutCntDw-1:0] cnt_q;
for (genvar k = 0; k < 2; k++) begin : gen_timeout_cnt
logic [TimeoutCntDw-1:0] cnt_d;
// The timeout counter is kicked off when the first ping occurs, and subsequent pings reset
// the counter to 1. The counter keeps on counting when it is nonzero, and saturates when it
// has reached its maximum (this state is terminal).
assign cnt_d = (ping_en && !(&cnt_q[k])) ? TimeoutCntDw'(1) :
((cnt_q[k] > '0) && !(&cnt_q[k])) ? cnt_q[k] + 1'b1 :
cnt_q[k];
prim_flop #(
.Width(TimeoutCntDw)
) u_prim_flop (
.clk_i,
.rst_ni,
.d_i(cnt_d),
.q_o(cnt_q[k])
);
end
// Escalation is asserted if
// - requested via the escalation sender/receiver path,
// - the ping monitor timeout is reached,
// - the two ping monitor counters are in an inconsistent state.
assign esc_en_o = esc_en ||
(&cnt_q[0]) ||
(cnt_q[0] != cnt_q[1]);
/////////////////
// RX/TX Logic //
/////////////////
typedef enum logic [2:0] {Idle, Check, PingResp, EscResp, SigInt} state_e;
state_e state_d, state_q;
logic resp_p, resp_pd, resp_pq;
logic resp_n, resp_nd, resp_nq;
logic resp_pd, resp_pq;
logic resp_nd, resp_nq;
// This prevents further tool optimizations of the differential signal.
prim_buf u_prim_buf_p (
.in_i(resp_p),
.out_o(resp_pd)
);
prim_buf u_prim_buf_n (
.in_i(resp_n),
.out_o(resp_nd)
prim_generic_flop #(
.Width(2),
.ResetValue(2'b10)
) u_prim_generic_flop (
.clk_i,
.rst_ni,
.d_i({resp_nd, resp_pd}),
.q_o({resp_nq, resp_pq})
);
assign esc_rx_o.resp_p = resp_pq;
@ -76,17 +147,18 @@ module prim_esc_receiver
always_comb begin : p_fsm
// default
state_d = state_q;
resp_p = 1'b0;
resp_n = 1'b1;
esc_en_o = 1'b0;
resp_pd = 1'b0;
resp_nd = 1'b1;
esc_en = 1'b0;
ping_en = 1'b0;
unique case (state_q)
// wait for the esc_p/n diff pair
Idle: begin
if (esc_level) begin
state_d = Check;
resp_p = 1'b1;
resp_n = 1'b0;
resp_pd = 1'b1;
resp_nd = 1'b0;
end
end
// we decide here whether this is only a ping request or
@ -95,18 +167,19 @@ module prim_esc_receiver
state_d = PingResp;
if (esc_level) begin
state_d = EscResp;
esc_en_o = 1'b1;
esc_en = 1'b1;
end
end
// finish ping response. in case esc_level is again asserted,
// we got an escalation signal (pings cannot occur back to back)
PingResp: begin
state_d = Idle;
resp_p = 1'b1;
resp_n = 1'b0;
resp_pd = 1'b1;
resp_nd = 1'b0;
ping_en = 1'b1;
if (esc_level) begin
state_d = EscResp;
esc_en_o = 1'b1;
esc_en = 1'b1;
end
end
// we have got an escalation enable pulse,
@ -115,9 +188,9 @@ module prim_esc_receiver
state_d = Idle;
if (esc_level) begin
state_d = EscResp;
resp_p = ~resp_pq;
resp_n = resp_pq;
esc_en_o = 1'b1;
resp_pd = ~resp_pq;
resp_nd = resp_pq;
esc_en = 1'b1;
end
end
// we have a signal integrity issue at one of
@ -129,8 +202,8 @@ module prim_esc_receiver
state_d = Idle;
if (sigint_detected) begin
state_d = SigInt;
resp_p = ~resp_pq;
resp_n = ~resp_pq;
resp_pd = ~resp_pq;
resp_nd = ~resp_pq;
end
end
default : state_d = Idle;
@ -139,8 +212,8 @@ module prim_esc_receiver
// bail out if a signal integrity issue has been detected
if (sigint_detected && (state_q != SigInt)) begin
state_d = SigInt;
resp_p = 1'b0;
resp_n = 1'b0;
resp_pd = 1'b0;
resp_nd = 1'b0;
end
end
@ -152,12 +225,8 @@ module prim_esc_receiver
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if (!rst_ni) begin
state_q <= Idle;
resp_pq <= 1'b0;
resp_nq <= 1'b1;
end else begin
state_q <= state_d;
resp_pq <= resp_pd;
resp_nq <= resp_nd;
end
end
@ -186,5 +255,9 @@ module prim_esc_receiver
// detect escalation pulse
`ASSERT(EscEnCheck_A, esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && state_q != SigInt
|=> esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) |-> esc_en_o)
// make sure the counter does not wrap around
`ASSERT(EscCntWrap_A, &cnt_q[0] |=> cnt_q[0] != 0)
// if the counter expires, escalation should be asserted
`ASSERT(EscCntEsc_A, &cnt_q[0] |-> esc_en_o)
endmodule : prim_esc_receiver

View file

@ -43,15 +43,25 @@ module prim_esc_sender
// decode differential signals //
/////////////////////////////////
logic resp, sigint_detected;
logic resp, resp_n, resp_p, sigint_detected;
// This prevents further tool optimizations of the differential signal.
prim_buf #(
.Width(2)
) u_prim_buf_resp (
.in_i({esc_rx_i.resp_n,
esc_rx_i.resp_p}),
.out_o({resp_n,
resp_p})
);
prim_diff_decode #(
.AsyncOn(1'b0)
) i_decode_resp (
) u_decode_resp (
.clk_i,
.rst_ni,
.diff_pi ( esc_rx_i.resp_p ),
.diff_ni ( esc_rx_i.resp_n ),
.diff_pi ( resp_p ),
.diff_ni ( resp_n ),
.level_o ( resp ),
.rise_o ( ),
.fall_o ( ),
@ -75,13 +85,13 @@ module prim_esc_sender
assign esc_p = esc_req_i | esc_req_q | (ping_req_d & ~ping_req_q);
// This prevents further tool optimizations of the differential signal.
prim_buf u_prim_buf_p (
.in_i(esc_p),
.out_o(esc_tx_o.esc_p)
);
prim_buf u_prim_buf_n (
.in_i(~esc_p),
.out_o(esc_tx_o.esc_n)
prim_buf #(
.Width(2)
) u_prim_buf_esc (
.in_i({~esc_p,
esc_p}),
.out_o({esc_tx_o.esc_n,
esc_tx_o.esc_p})
);
//////////////

View file

@ -52,7 +52,7 @@ module prim_fifo_async #(
assign fifo_incr_wptr = wvalid_i & wready_o;
// decimal version
assign fifo_wptr_d = fifo_wptr_q + PTR_WIDTH'(1);
assign fifo_wptr_d = fifo_wptr_q + PTR_WIDTH'(1'b1);
always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin
if (!rst_wr_ni) begin
@ -85,7 +85,7 @@ module prim_fifo_async #(
assign fifo_incr_rptr = rvalid_o & rready_i;
// decimal version
assign fifo_rptr_d = fifo_rptr_q + PTR_WIDTH'(1);
assign fifo_rptr_d = fifo_rptr_q + PTR_WIDTH'(1'b1);
always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin
if (!rst_rd_ni) begin
@ -124,8 +124,10 @@ module prim_fifo_async #(
// Empty / Full //
//////////////////
assign full_wclk = (fifo_wptr_q == (fifo_rptr_sync_q ^ {1'b1,{(PTR_WIDTH-1){1'b0}}}));
assign full_rclk = (fifo_wptr_sync_combi == (fifo_rptr_q ^ {1'b1,{(PTR_WIDTH-1){1'b0}}}));
logic [PTR_WIDTH-1:0] xor_mask;
assign xor_mask = PTR_WIDTH'(1'b1) << (PTR_WIDTH-1);
assign full_wclk = (fifo_wptr_q == (fifo_rptr_sync_q ^ xor_mask));
assign full_rclk = (fifo_wptr_sync_combi == (fifo_rptr_q ^ xor_mask));
assign empty_rclk = (fifo_wptr_sync_combi == fifo_rptr_q);
if (Depth > 1) begin : g_depth_calc
@ -210,32 +212,42 @@ module prim_fifo_async #(
function automatic [PTR_WIDTH-1:0] dec2gray(input logic [PTR_WIDTH-1:0] decval);
logic [PTR_WIDTH-1:0] decval_sub;
logic [PTR_WIDTH-2:0] decval_in;
logic [PTR_WIDTH-1:0] decval_in;
logic unused_decval_msb;
decval_sub = (PTR_WIDTH)'(Depth) - {1'b0, decval[PTR_WIDTH-2:0]} - 1'b1;
{unused_decval_msb, decval_in} = decval[PTR_WIDTH-1] ? decval_sub : decval;
// Was done in two assigns for low bits and top bit
// but that generates a (bogus) verilator warning, so do in one assign
dec2gray = {decval[PTR_WIDTH-1],
{1'b0,decval_in[PTR_WIDTH-2:1]} ^ decval_in[PTR_WIDTH-2:0]};
decval_in = decval[PTR_WIDTH-1] ? decval_sub : decval;
// We do not care about the MSB, hence we mask it out
unused_decval_msb = decval_in[PTR_WIDTH-1];
decval_in[PTR_WIDTH-1] = 1'b0;
// Perform the XOR conversion
dec2gray = decval_in;
dec2gray ^= (decval_in >> 1);
// Override the MSB
dec2gray[PTR_WIDTH-1] = decval[PTR_WIDTH-1];
endfunction
// Algorithm walks up from 0..N-1 then flips the upper bit and walks down from N-1 to 0.
function automatic [PTR_WIDTH-1:0] gray2dec(input logic [PTR_WIDTH-1:0] grayval);
logic [PTR_WIDTH-2:0] dec_tmp, dec_tmp_sub;
logic [PTR_WIDTH-1:0] dec_tmp, dec_tmp_sub;
logic unused_decsub_msb;
dec_tmp[PTR_WIDTH-2] = grayval[PTR_WIDTH-2];
for (int i = PTR_WIDTH-3; i >= 0; i--) begin
dec_tmp = '0;
for (int i = PTR_WIDTH-2; i >= 0; i--) begin
dec_tmp[i] = dec_tmp[i+1] ^ grayval[i];
end
{unused_decsub_msb, dec_tmp_sub} = (PTR_WIDTH-1)'(Depth) - {1'b0, dec_tmp} - 1'b1;
dec_tmp_sub = (PTR_WIDTH)'(Depth) - dec_tmp - 1'b1;
if (grayval[PTR_WIDTH-1]) begin
gray2dec = {1'b1, dec_tmp_sub};
gray2dec = dec_tmp_sub;
// Override MSB
gray2dec[PTR_WIDTH-1] = 1'b1;
unused_decsub_msb = dec_tmp_sub[PTR_WIDTH-1];
end else begin
gray2dec = {1'b0, dec_tmp};
gray2dec = dec_tmp;
end
endfunction
@ -250,7 +262,7 @@ module prim_fifo_async #(
end else if (Depth == 2) begin : g_simple_gray_conversion
assign fifo_rptr_sync_combi = {fifo_rptr_gray_sync[PTR_WIDTH-1], ^fifo_rptr_gray_sync};
assign fifo_wptr_sync_combi = {fifo_wptr_gray_sync[PTR_WIDTH-1], ^fifo_rptr_gray_sync};
assign fifo_wptr_sync_combi = {fifo_wptr_gray_sync[PTR_WIDTH-1], ^fifo_wptr_gray_sync};
assign fifo_rptr_gray_d = {fifo_rptr_d[PTR_WIDTH-1], ^fifo_rptr_d};
assign fifo_wptr_gray_d = {fifo_wptr_d[PTR_WIDTH-1], ^fifo_wptr_d};

View file

@ -354,6 +354,9 @@ module prim_lfsr #(
// Unknown //
/////////////
end else begin : gen_unknown_type
assign coeffs = '0;
assign next_lfsr_state = '0;
assign lockup = 1'b0;
`ASSERT_INIT(UnknownLfsrType_A, 0)
end

View file

@ -516,4 +516,6 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #(
.cfg_i
);
`include "prim_util_get_scramble_params.svh"
endmodule : prim_ram_1p_scr

View file

@ -10,4 +10,6 @@ package prim_rom_pkg;
logic [3:0] cfg;
} rom_cfg_t;
parameter rom_cfg_t ROM_CFG_DEFAULT = '0;
endpackage // prim_rom_pkg

View file

@ -67,13 +67,14 @@ module prim_sram_arbiter #(
) u_reqarb (
.clk_i,
.rst_ni,
.req_chk_i ( 1'b1 ),
.req_i,
.data_i ( req_packed ),
.data_i ( req_packed ),
.gnt_o,
.idx_o ( ),
.valid_o ( sram_req_o ),
.data_o ( sram_packed ),
.ready_i ( 1'b1 )
.idx_o ( ),
.valid_o ( sram_req_o ),
.data_o ( sram_packed ),
.ready_i ( 1'b1 )
);
end else if (ArbiterImpl == "BINTREE") begin : gen_tree_arb
prim_arbiter_tree #(
@ -82,13 +83,14 @@ module prim_sram_arbiter #(
) u_reqarb (
.clk_i,
.rst_ni,
.req_chk_i ( 1'b1 ),
.req_i,
.data_i ( req_packed ),
.data_i ( req_packed ),
.gnt_o,
.idx_o ( ),
.valid_o ( sram_req_o ),
.data_o ( sram_packed ),
.ready_i ( 1'b1 )
.idx_o ( ),
.valid_o ( sram_req_o ),
.data_o ( sram_packed ),
.ready_i ( 1'b1 )
);
end else begin : gen_unknown
`ASSERT_INIT(UnknownArbImpl_A, 0)

View file

@ -33,6 +33,13 @@ module prim_subreg_shadow #(
output logic err_storage
);
// Since the shadow and staging registers work with the 1's complement value,
// we need to invert the polarity of the SW access if it is either "W1S" or "W0C".
// W1C is forbidden since the W0S complement is not implemented.
`ASSERT_INIT(CheckSwAccessIsLegal_A, SWACCESS inside {"RW", "RO", "WO", "W1S", "W0C", "RC"})
parameter bit [3*8-1:0] INVERTED_SWACCESS = (SWACCESS == "W1S") ? "W0C" :
(SWACCESS == "W0C") ? "W1S" : SWACCESS;
// Subreg control signals
logic phase_clear;
logic phase_q;
@ -86,9 +93,9 @@ module prim_subreg_shadow #(
assign staged_we = we & ~phase_q;
assign staged_de = de & ~phase_q;
prim_subreg #(
.DW ( DW ),
.SWACCESS ( SWACCESS ),
.RESVAL ( ~RESVAL )
.DW ( DW ),
.SWACCESS ( INVERTED_SWACCESS ),
.RESVAL ( ~RESVAL )
) staged_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
@ -109,9 +116,9 @@ module prim_subreg_shadow #(
assign shadow_we = we & phase_q & ~err_update;
assign shadow_de = de & phase_q & ~err_update;
prim_subreg #(
.DW ( DW ),
.SWACCESS ( SWACCESS ),
.RESVAL ( ~RESVAL )
.DW ( DW ),
.SWACCESS ( INVERTED_SWACCESS ),
.RESVAL ( ~RESVAL )
) shadow_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),

View file

@ -23,21 +23,24 @@
`include "prim_assert.sv"
module prim_sync_reqack #(
// Non-functional parameter to switch on the request stability assertion
parameter bit EnReqStabA = 1
) (
module prim_sync_reqack (
input clk_src_i, // REQ side, SRC domain
input rst_src_ni, // REQ side, SRC domain
input clk_dst_i, // ACK side, DST domain
input rst_dst_ni, // ACK side, DST domain
input logic req_chk_i, // Used for gating assertions. Drive to 1 during normal operation.
input logic src_req_i, // REQ side, SRC domain
output logic src_ack_o, // REQ side, SRC domain
output logic dst_req_o, // ACK side, DST domain
input logic dst_ack_i // ACK side, DST domain
);
// req_chk_i is used for gating assertions only.
logic unused_req_chk;
assign unused_req_chk = req_chk_i;
// Types
typedef enum logic {
EVEN, ODD
@ -167,10 +170,9 @@ module prim_sync_reqack #(
end
end
if (EnReqStabA) begin : gen_lock_assertion
// SRC domain can only de-assert REQ after receiving ACK.
`ASSERT(SyncReqAckHoldReq, $fell(src_req_i) |-> $fell(src_ack_o), clk_src_i, !rst_src_ni)
end
// SRC domain can only de-assert REQ after receiving ACK.
`ASSERT(SyncReqAckHoldReq, $fell(src_req_i) |->
$fell(src_ack_o), clk_src_i, !rst_src_ni || !req_chk_i)
// DST domain cannot assert ACK without REQ.
`ASSERT(SyncReqAckAckNeedsReq, dst_ack_i |-> dst_req_o, clk_dst_i, !rst_dst_ni)

View file

@ -20,15 +20,16 @@ module prim_sync_reqack_data #(
parameter int unsigned Width = 1,
parameter bit DataSrc2Dst = 1'b1, // Direction of data flow: 1'b1 = SRC to DST,
// 1'b0 = DST to SRC
parameter bit DataReg = 1'b0, // Enable optional register stage for data,
parameter bit DataReg = 1'b0 // Enable optional register stage for data,
// only usable with DataSrc2Dst == 1'b0.
parameter bit EnReqStabA = 1 // Used in submodule `prim_sync_reqack`.
) (
input clk_src_i, // REQ side, SRC domain
input rst_src_ni, // REQ side, SRC domain
input clk_dst_i, // ACK side, DST domain
input rst_dst_ni, // ACK side, DST domain
input logic req_chk_i, // Used for gating assertions. Drive to 1 during normal operation.
input logic src_req_i, // REQ side, SRC domain
output logic src_ack_o, // REQ side, SRC domain
output logic dst_req_o, // ACK side, DST domain
@ -41,14 +42,14 @@ module prim_sync_reqack_data #(
////////////////////////////////////
// REQ/ACK synchronizer primitive //
////////////////////////////////////
prim_sync_reqack #(
.EnReqStabA(EnReqStabA)
) u_prim_sync_reqack (
prim_sync_reqack u_prim_sync_reqack (
.clk_src_i,
.rst_src_ni,
.clk_dst_i,
.rst_dst_ni,
.req_chk_i,
.src_req_i,
.src_ack_o,
.dst_req_o,

View file

@ -0,0 +1,36 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`ifndef SYNTHESIS
export "DPI-C" function simutil_get_scramble_key;
function int simutil_get_scramble_key(output bit [127:0] val);
if (!key_valid_i) begin
return 0;
end
if (DataKeyWidth != 128) begin
return 0;
end
val = key_i;
return 1;
endfunction
export "DPI-C" function simutil_get_scramble_nonce;
function int simutil_get_scramble_nonce(output bit [319:0] nonce);
if (!key_valid_i) begin
return 0;
end
if (NonceWidth > 320) begin
return 0;
end
nonce = '0;
nonce[NonceWidth-1:0] = nonce_i;
return 1;
endfunction
`endif

View file

@ -1,15 +1,21 @@
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
import os
import sys
# Make vendored packages available in the search path.
sys.path.append('vendor')
import re
import shutil
import sys
import yaml
from mako.template import Template
try:
from yaml import CSafeLoader as YamlLoader, CSafeDumper as YamlDumper
except ImportError:
@ -84,29 +90,76 @@ def _top_module_file(core_files, module_name):
return file
def _parse_module_header(generic_impl_filepath, module_name):
""" Parse a SystemVerilog file to extract the 'module' header
def _parse_module_header_verible(generic_impl_filepath, module_name):
""" Parse a SystemVerilog file to extract the 'module' header using Verible
Return a dict with the following entries:
- module_header: the whole module header (including the 'module' keyword)
- package_import_declaration: import declarations
- parameter_port_list: parameter/localparam declarations in the header
- ports: the list of ports. The portlist can be ANSI or non-ANSI style (with
or without signal declarations; see the SV spec for details).
Implementation of _parse_module_header() which uses verible-verilog-syntax
to do the parsing. This is the primary implementation and is used when
supported Verible version is available.
See _parse_module_header() for API details.
"""
# TODO: Evaluate different Python SV parsers and use one instead of this
# beautiful regex. A more complete parser would ignore comments, for
# example, or properly handle matching parentheses ... Or write a minimal
# parser that can parse the module header.
#
# Initial evaluations:
# - https://github.com/PyHDI/Pyverilog:
# requires Icarus and Python 3.6
# - https://kevinpt.github.io/hdlparse/:
# Didn't do anything in my tests.
# - https://github.com/sepandhaghighi/verilogparser:
# Doesn't support SystemVerilog
from google_verible_verilog_syntax_py.verible_verilog_syntax import VeribleVerilogSyntax, PreOrderTreeIterator
parser = VeribleVerilogSyntax()
data = parser.parse_file(generic_impl_filepath, options={"skip_null": True})
if data.errors:
for err in data.errors:
print(f'Verible: {err.phase} error in line {err.line} column {err.column}'
+ (': {err.message}' if err.message else '.'))
# Intentionally not raising an exception here.
# There are chances that Verible recovered from errors.
if not data.tree:
raise ValueError(f"Unable to parse {generic_impl_filepath!r}.")
module = data.tree.find({"tag": "kModuleDeclaration"})
header = module.find({"tag": "kModuleHeader"})
if not header:
raise ValueError("Unable to extract module header from %s." %
(generic_impl_filepath, ))
name = header.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]},
iter_=PreOrderTreeIterator)
if not name:
raise ValueError("Unable to extract module name from %s." %
(generic_impl_filepath, ))
imports = header.find_all({"tag": "kPackageImportDeclaration"})
parameters_list = header.find({"tag": "kFormalParameterList"})
parameters = set()
if parameters_list:
for parameter in parameters_list.iter_find_all({"tag":
"kParamDeclaration"}):
if parameter.find({"tag": "parameter"}):
parameter_id = parameter.find({"tag": ["SymbolIdentifier",
"EscapedIdentifier"]})
parameters.add(parameter_id.text)
ports = header.find({"tag": "kPortDeclarationList"})
return {
'module_header': header.text,
'package_import_declaration': '\n'.join([i.text for i in imports]),
'parameter_port_list':
parameters_list.text if parameters_list else '',
'ports': ports.text if ports else '',
'parameters': parameters,
'parser': 'Verible'
}
def _parse_module_header_fallback(generic_impl_filepath, module_name):
""" Parse a SystemVerilog file to extract the 'module' header using RegExp
Legacy implementation of _parse_module_header() using regular expressions.
It is not as robust as Verible-backed implementation, but doesn't need
Verible to work.
See _parse_module_header() for API details.
"""
# Grammar fragments from the SV2017 spec:
#
@ -155,7 +208,8 @@ def _parse_module_header(generic_impl_filepath, module_name):
'ports':
matches.group('ports').strip() or '',
'parameters':
_parse_parameter_port_list(parameter_port_list)
_parse_parameter_port_list(parameter_port_list),
'parser': 'Fallback (regex)'
}
@ -197,6 +251,26 @@ def _parse_parameter_port_list(parameter_port_list):
return parameters
def _parse_module_header(generic_impl_filepath, module_name):
""" Parse a SystemVerilog file to extract the 'module' header
Return a dict with the following entries:
- module_header: the whole module header (including the 'module' keyword)
- package_import_declaration: import declarations
- parameter_port_list: parameter/localparam declarations in the header
- ports: the list of ports. The portlist can be ANSI or non-ANSI style (with
or without signal declarations; see the SV spec for details).
- parser: parser used to extract the data.
"""
try:
return _parse_module_header_verible(generic_impl_filepath, module_name)
except Exception as e:
print(e)
print(f"Verible parser failed, using regex fallback instead.")
return _parse_module_header_fallback(generic_impl_filepath, module_name)
def _check_gapi(gapi):
if 'cores' not in gapi:
print("Key 'cores' not found in GAPI structure. "
@ -331,11 +405,12 @@ def _generate_abstract_impl(gapi):
module_header_imports=generic_hdr['package_import_declaration'],
module_header_params=generic_hdr['parameter_port_list'],
module_header_ports=generic_hdr['ports'],
num_techlibs= len(techlibs),
num_techlibs=len(techlibs),
# Creating the code to instantiate the primitives in the Mako templating
# language is tricky to do; do it in Python instead.
instances=_create_instances(prim_name, techlibs,
generic_hdr['parameters']))
generic_hdr['parameters']),
parser_info=generic_hdr['parser'])
abstract_prim_sv_filepath = 'prim_%s.sv' % (prim_name)
with open(abstract_prim_sv_filepath, 'w') as f:
f.write(abstract_prim_sv)

View file

@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
// This file is auto-generated.
// Used parser: ${parser_info}
% if num_techlibs > 1:
`ifndef PRIM_DEFAULT_IMPL

View file

@ -0,0 +1,15 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// This file is generated by the util/vendor script. Please do not modify it
// manually.
{
upstream:
{
url: https://github.com/google/verible.git
rev: ed1f3a9577047b801854fb36dd14ebe0aecbdddc
only_subdir: verilog/tools/syntax/export_json_examples
}
}

View file

@ -0,0 +1,13 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
name: "google_verible_verilog_syntax_py",
target_dir: "google_verible_verilog_syntax_py",
upstream: {
url: "https://github.com/google/verible.git",
rev: "v0.0-943-ged1f3a9",
only_subdir: "verilog/tools/syntax/export_json_examples",
},
}

View file

@ -0,0 +1,55 @@
load("@rules_python//python:defs.bzl", "py_test", "py_library", "py_binary")
licenses(["notice"])
py_library(
name = "verible_verilog_syntax_py",
srcs = ["verible_verilog_syntax.py"],
srcs_version = "PY3",
imports = ["."],
data = ["//verilog/tools/syntax:verible-verilog-syntax"],
deps = [
"@python_anytree//:anytree",
"//third_party/py/dataclasses",
],
)
py_test(
name = "verible_verilog_syntax_py_test",
size = "small",
srcs = ["verible_verilog_syntax_test.py"],
srcs_version = "PY3",
python_version = "PY3",
main = "verible_verilog_syntax_test.py",
deps = [":verible_verilog_syntax_py"],
data = ["//verilog/tools/syntax:verible-verilog-syntax"],
args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"],
)
py_binary(
name = "print_modules",
srcs = ["print_modules.py"],
srcs_version = "PY3",
python_version = "PY3",
main = "print_modules.py",
deps = [
":verible_verilog_syntax_py",
"@python_anytree//:anytree",
],
data = ["//verilog/tools/syntax:verible-verilog-syntax"],
args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"],
)
py_binary(
name = "print_tree",
srcs = ["print_tree.py"],
srcs_version = "PY3",
python_version = "PY3",
main = "print_tree.py",
deps = [
":verible_verilog_syntax_py",
"@python_anytree//:anytree",
],
data = ["//verilog/tools/syntax:verible-verilog-syntax"],
args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"],
)

Some files were not shown because too many files have changed in this diff Show more