This commit also adds memory manipulation package in ibex repository.

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

* [mubi] Fix path in auto-gen header (Rupert Swarbrick)
* [dv] Allow using memutil_dpi_scrambled even without prim_ram_1p_scr
  (Rupert Swarbrick)
* [prim] Fix prim_ram_1p_scr Dependencies (Canberk Topal)
* [dv/clk_rst_if] Split clk_rst_if jitter to 2 different values (Eitan
  Shapira)
* [dv] Add external hjson path support in ralgen (Srikrishna Iyer)
* [dv] Add sub RAL block creation knobs (Srikrishna Iyer)
* [pwrmgr] Make rom_ctrl check signals multi-bit (Timothy Chen)
* [dv/alert_handler] Randomize mubi input (Cindy Chen)
* [flash_ctrl] Fix bank erase / info partition issue (Timothy Chen)
* [ci] Fix CI failure (Weicai Yang)
* [Cleanup] Remove lc_tx_e type and replace it with lc_tx_t (Weicai
  Yang)
* [aes] Add gtech synthesis setup (Michael Schaffner)
* [mubi] Enhance mubi_sync with stability check (Timothy Chen)
* [prim] Fix prim_packer_fifo when ClearOnRead is false (Rupert
  Swarbrick)
* [cleanup] Remove mubi4_e and replace it with mubi4_t (Weicai Yang)
* [dv] Fix shape calculations for replicated ECC (Rupert Swarbrick)
* [dv/alert] Support LPG in alert_sender/receiver pair (Cindy Chen)
* [dv] Add a ReadWithIntegrity method to Ecc32MemArea (Rupert
  Swarbrick)
* [dv] Simplify Ecc32MemArea read/write functions (Rupert Swarbrick)
* [prim] Add option to not clear the packer FIFO upon read (Pirmin
  Vogel)
* [dv] Change intg_err test from V3 to V2S (Weicai Yang)
* [util] Delete generate_prim_mubi.py (Rupert Swarbrick)
* [dv] Slightly generalise run_stress_all_with_rand_reset_vseq (Rupert
  Swarbrick)
* [fpv] Fix some assumptions in prim_count (Cindy Chen)
* [prim] quick path to prim_count assertion (Timothy Chen)
* [dv] Support Multiple EDN Interfaces in OpenTitan (Canberk Topal)
* [prim] Add xoshiro256pp primitive. (Vladimir Rozic)
* [dv/prim_alert] Fix async fatal alert regression error (Cindy Chen)
* [prim] Add missing include to prim_xilinx_pad_wrapper (Rupert
  Swarbrick)
* [prim] Add missing include to prim_mubi_dec* (Rupert Swarbrick)
* [dv/prim_alert_receiver] Fix assertion that consumes large mem
  (Cindy Chen)
* [prim] Remove extra semicolon (Weicai Yang)
* [chip,dv] Refactor CSR exclusion method (Srikrishna Iyer)
* [top, all] update connects for mubi (Timothy Chen)
* [flash_ctrl] Add plain text integrity in flash (Timothy Chen)
* [prim] Add time-out functionality to prim_clock_meas (Timothy Chen)
* [prim] Fix DC sythesis error (Weicai Yang)
* [fpv] Fix regression failures (Cindy Chen)
* [dv/ralgen] Update `dv_base_names` input from a string to a list
  (Cindy Chen)
* [dv/ralgen] Update the `dv-base-prefix` optional input (Cindy Chen)
* [doc] Add D2S and V2S checklist items to all checklists (Michael
  Schaffner)
* [dv] Test security countermeasures (Weicai Yang)
* [dv] Fix ASSERT_INIT race condition (Weicai Yang)
* [syn/aes/otbn] Minor fixes to fix block level synthesis (Michael
  Schaffner)
* [all] updated assert rtl ifdef (Timothy Chen)
* [dv] Update TL intg testplan (Weicai Yang)
* [prim] Add prim_fifo_async_sram_adapter to FPV list (Eunchan Kim)
* [spi_device] Upload Cmd/Addr FIFO status revision (Eunchan Kim)
* [dvsim] Modify resolve_branch to handle branch names with forward
  slash. (Todd Broch)
* [prim_clock_inv] Add option to disable FPGA BUFG (Michael Schaffner)
* [ralgen] Be more explicit which tool is called (Philipp Wagner)
* [prim] Tweak prim_sync_reqack_data assertion so it can be disabled
  (Rupert Swarbrick)
* [verible] Rename rule file (Philipp Wagner)
* [dv/base_monitor] Cleaned up base monitor (Rasmus Madsen)
* [fpv] prim_counter_fpv (Cindy Chen)
* [dv/shadow_reg] Cross shadow reg error sequence with csr rw (Cindy
  Chen)
* [dv] Fix scb multi-ral (Weicai Yang)
* [dvsim] Enabling glob-style patterns for -i switch (Srikrishna Iyer)
* [dv] Split sec_cm_testplan into multiple testplans (Weicai Yang)
* [dv/dsim] Remove dsim's system_lib from library path (Guillermo
  Maturana)
* [prim_packer] Resolve width mismatch (Philipp Wagner)
* [prim] Fix lint error in prim_util_memload (Philipp Wagner)
* [prim] Minor fix to make conn checks easy (Srikrishna Iyer)
* [fpv] prim_secded FPV testbench updates bind file naming (Cindy
  Chen)
* [dv_macros.svh] minor cleanup (Srikrishna Iyer)
* [dv,xcelium] minor cleanup (Srikrishna Iyer)
* [dv/shadowed_reset] Add a shadowed_rst_n interface (Cindy Chen)
* [fpv] Update FPV file naming (Cindy Chen)
* [top] Convert to mubi usage in some areas (Timothy Chen)
* [entropy_src] mubi updates (Timothy Chen)
* [prim] Add test for mubi invalid (Timothy Chen)
* [prim_double_lfsr] Add duplicated LFSR primitive (Michael Schaffner)
* [dv] Fix shadow reg backdoor path and enable csr_reset sequence
  (Weicai Yang)
* [prim] Fix unused net (Timothy Chen)
* [dv, clk_rst_if] Improve jitter and add scaling (Srikrishna Iyer)
* [prim] Anchor buffers around register flip flops (Timothy Chen)
* [alert_handler/top] Lint fixes and lc_tx_t to mubi4_t conversions
  (Michael Schaffner)
* [prim_mubi] Replace true/false_value() functions with parameter
  (Michael Schaffner)
* [dv/dsim] Get dsim to work at full chip (Guillermo Maturana)
* [prim] Fixes for prim_count (Timothy Chen)
* [top] Add various anchor points to modules (Timothy Chen)
* [dv/pwrmgr] Add wakeup test sequence (Guillermo Maturana)
* [reggen] Add mubi support into hjson (Timothy Chen)
* [dv/shadow_reg] Fix aes shadow reg failure (Cindy Chen)
* [dv/cdc] CDC simulation model (Udi Jonnalagadda)
* [prim_lfsr/lint] Add temporary waiver for LOOP_VAR_OP lint error
  (Michael Schaffner)
* [prim_clock_buf] Add lint waiver for unused parameter (Michael
  Schaffner)
* [dvsim] Correctly set self_dir for included Hjson files (Philipp
  Wagner)
* [util] Add tooling support for V2S milestone (Srikrishna Iyer)
* [prim_mubi] Add decoder module similar to prim_lc_dec (Michael
  Schaffner)
* [prim_mubi] Add mubi sender and sync primitives (Michael Schaffner)
* [prim_mubi_pkg] Switch to True/False terminology (Michael Schaffner)
* [prim] Minor work-around for xcelium (Timothy Chen)

Signed-off-by: Canberk Topal <ctopal@lowrisc.org>
This commit is contained in:
Canberk Topal 2021-11-29 11:52:25 +00:00 committed by Canberk Topal
parent 1bbe27effe
commit 53b1732b19
187 changed files with 5467 additions and 1183 deletions

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/lowRISC/opentitan
rev: ad629e3e6e70c5eaa3c2dd68457b0a020448b35f
rev: 3a672eb36aee5942d0912a15d15055b1d21c33d6
}
}

View file

@ -15,6 +15,7 @@
{from: "hw/dv/sv/csr_utils", to: "dv/sv/csr_utils"},
{from: "hw/dv/sv/dv_base_reg", to: "dv/sv/dv_base_reg"},
{from: "hw/dv/sv/mem_model", to: "dv/sv/mem_model"},
{from: "hw/dv/sv/mem_bkdr_util", to: "dv/sv/mem_bkdr_util"},
{from: "hw/dv/sv/str_utils", to: "dv/sv/str_utils"},
// We apply a patch to fix the bus_params_pkg core file name when

View file

@ -26,23 +26,75 @@ interface clk_rst_if #(
import uvm_pkg::*;
`endif
bit drive_clk; // enable clk generation
logic o_clk; // output clk
// Enables clock to be generated and driven by this interface.
bit drive_clk;
bit drive_rst_n; // enable rst_n generation
logic o_rst_n; // output rst_n
// The internal output clock value.
logic o_clk;
// clk params
bit clk_gate = 1'b0; // clk gate signal
int clk_period_ps = 20_000; // 50MHz default
real clk_freq_mhz = 50; // 50MHz default
int duty_cycle = 50; // 50% default
int max_jitter_ps = 1000; // 1ns default
bit recompute = 1'b1; // compute half periods when period/freq/duty are changed
int clk_hi_ps; // half period hi in ps
int clk_lo_ps; // half period lo in ps
int jitter_chance_pc = 0; // jitter chance in percentage on clock edge - disabled by default
bit sole_clock = 1'b0; // if true, this is the only clock in the system
// Enables the rst_n to be generated and driven by this interface.
bit drive_rst_n;
// The internal output reset value.
logic o_rst_n;
// Applies clock gating.
bit clk_gate = 1'b0;
// The nominal (chosen) frequency (as period in ps) of the driven clock (50MHz by default).
int clk_period_ps = 20_000;
// The variation of clock period (mimics uncalibrated clocks), to scale the nominal frequency
// down. If set to non-zero value, the clock frequency is scaled randomly on every edge.
int clk_freq_scaling_pc = 0;
// The percentage chance of freq scaled down randomly on each edge.
int clk_freq_scaling_chance_pc = 50;
// Clock frequency is scaled down. This enables the frequency to be randomly scaled up as well.
//
// Note: If set, the randomness of the clock frequency being scaled up or down may result in a
// bigger frequency distribution than the intended clk_freq_scaling_pc setting. For example, 50MHz
// with 10% scaling may result in pulses that are < 45MHz and > 55MHz wide as well.
bit clk_freq_scale_up = 1'b0;
// The computed clock frequency in MHz.
real clk_freq_mhz = 50;
// The duty cycle of the clock period as percentage. If jitter and scaling is applied, then the
// duty cycle will not be maintained.
int duty_cycle = 50;
// Maximum jitter applied to each period of the clock - this is expected to be about 20% or less
// than the clock period.
// The jitter is divided to two values - plus-jitter and minus-jitter.
// Plus jitter is the possible time can be added to the clock period, while the minus jittter is
// the possible time can be subtracted from the clock period.
// _________
// _____:_| : : |_:_______
//
// The actual jitter value is picked randomly within the window
// {[-max_minus_jitter_ps:max_plus_jitter_ps]}
// and is added to the time to next edge.
int max_plus_jitter_ps = 1000;
int max_minus_jitter_ps = 1000;
// The percentage chance of jitter occurring on each edge. If 0 (default value), then jitter is
// disabled altogether. If 100, jitter is computed and applied at every edge.
int jitter_chance_pc = 0;
// Internal signal indicating the clock half periods need to be recomputed.
bit recompute = 1'b1;
// Internal signal indicating the amount of time for which the clock stays high / lo in the next
// cycle.
int clk_hi_ps;
int clk_lo_ps;
real clk_hi_modified_ps;
real clk_lo_modified_ps;
// If true, this is the only clock in the system; there is no need to add initial jitter.
bit sole_clock = 1'b0;
// use IfName as a part of msgs to indicate which clk_rst_vif instance
string msg_id = {"clk_rst_if::", IfName};
@ -81,6 +133,22 @@ interface clk_rst_if #(
set_freq_khz(freq_mhz * 1000);
endfunction
// Set the clk frequency scaling, chance in percentage and scaling up.
//
// freq_scaling_pc is a positive integer that determines by what amount (as percentage of the
// nominal frequency) is the frequency scaled (jittered) down.
// freq_scaling_chance_pc is a percentage number between 0 and 100 that determines how often is
// the scaling randomly recomputed and applied.
// freq_scale_up is a bit that enables the random scaling up of the frequency as well.
function automatic void set_freq_scaling(int freq_scaling_pc, int freq_scaling_chance_pc = 50,
bit freq_scale_up = 1'b0);
`DV_CHECK_FATAL(freq_scaling_pc >= 0, , msg_id)
`DV_CHECK_FATAL(freq_scaling_chance_pc inside {[0:100]}, , msg_id)
clk_freq_scaling_pc = freq_scaling_pc;
clk_freq_scaling_chance_pc = freq_scaling_chance_pc;
clk_freq_scale_up = freq_scale_up;
endfunction
// call this function at t=0 (from tb top) to enable clk and rst_n to be driven
function automatic void set_active(bit drive_clk_val = 1'b1, bit drive_rst_n_val = 1'b1);
time t = $time;
@ -89,11 +157,7 @@ interface clk_rst_if #(
drive_rst_n = drive_rst_n_val;
end
else begin
`ifdef VERILATOR
$error({msg_id, "this function can only be called at t=0"});
`else
`uvm_fatal(msg_id, "this function can only be called at t=0")
`endif
`dv_fatal("This function can only be called at t=0", msg_id)
end
endfunction
@ -106,32 +170,23 @@ interface clk_rst_if #(
// set the duty cycle (1-99)
function automatic void set_duty_cycle(int duty);
if (!(duty inside {[1:99]})) begin
`ifdef VERILATOR
$error({msg_id, $sformatf("duty cycle %0d is not inside [1:99]", duty)});
`else
`uvm_fatal(msg_id, $sformatf("duty cycle %0d is not inside [1:99]", duty))
`endif
end
`DV_CHECK_FATAL(duty inside {[1:99]}, , msg_id)
duty_cycle = duty;
recompute = 1'b1;
endfunction
// set maximum jitter in ps
function automatic void set_max_jitter_ps(int jitter_ps);
max_jitter_ps = jitter_ps;
// set maximum jitter in ps, separating the plus jitter and the minus jitter.
// In default the plus and minus jitters are the same.
function automatic void set_max_jitter_ps(int plus_jitter_ps,
int minus_jitter_ps = plus_jitter_ps);
max_plus_jitter_ps = plus_jitter_ps;
max_minus_jitter_ps = minus_jitter_ps;
endfunction
// set jitter chance in percentage (0 - 100)
// 0 - dont add any jitter; 100 - add jitter on every clock edge
function automatic void set_jitter_chance_pc(int jitter_chance);
if (!(jitter_chance inside {[0:100]})) begin
`ifdef VERILATOR
$error({msg_id, $sformatf("jitter_chance %0d is not inside [0:100]", jitter_chance)});
`else
`uvm_fatal(msg_id, $sformatf("jitter_chance %0d is not inside [0:100]", jitter_chance))
`endif
end
`DV_CHECK_FATAL(jitter_chance inside {[0:100]}, , msg_id)
jitter_chance_pc = jitter_chance;
endfunction
@ -153,22 +208,36 @@ interface clk_rst_if #(
clk_gate = 1'b1;
endfunction
// add jitter to clk_hi and clk_lo half periods based on jitter_chance_pc
function automatic void add_jitter();
int jitter_ps;
// Scales the clock frequency up and down on every edge.
function automatic void apply_freq_scaling();
real scaling;
real mult = $urandom_range(0, clk_freq_scale_up) ? 1.0 : -1.0;
if ($urandom_range(1, 100) <= clk_freq_scaling_chance_pc) begin
scaling = 1.0 + mult * real'($urandom_range(0, clk_freq_scaling_pc)) / 100;
clk_hi_modified_ps = clk_hi_ps * scaling;
scaling = 1.0 + mult * real'($urandom_range(0, clk_freq_scaling_pc)) / 100;
clk_lo_modified_ps = clk_lo_ps * scaling;
end
endfunction
// Applies jitter to clk_hi and clk_lo half periods based on jitter_chance_pc.
function automatic void apply_jitter();
int jitter;
int plus_jitter;
int minus_jitter;
if ($urandom_range(1, 100) <= jitter_chance_pc) begin
`ifndef VERILATOR
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(jitter_ps,
jitter_ps inside {[-1*max_jitter_ps:max_jitter_ps]};, "", msg_id)
`endif
clk_hi_ps += jitter_ps;
plus_jitter = $urandom_range(0, max_plus_jitter_ps/2);
minus_jitter = $urandom_range(0, max_minus_jitter_ps/2);
jitter = ($urandom_range(0, 1) ? plus_jitter : (-1 * minus_jitter));
clk_hi_modified_ps = clk_hi_ps + jitter;
end
if ($urandom_range(1, 100) <= jitter_chance_pc) begin
`ifndef VERILATOR
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(jitter_ps,
jitter_ps inside {[-1*max_jitter_ps:max_jitter_ps]};, "", msg_id)
`endif
clk_lo_ps += jitter_ps;
plus_jitter = $urandom_range(0, max_plus_jitter_ps/2);
minus_jitter = $urandom_range(0, max_minus_jitter_ps/2);
jitter = ($urandom_range(0, 1) ? plus_jitter : (-1 * minus_jitter));
clk_lo_modified_ps = clk_lo_ps + jitter;
end
endfunction
@ -215,11 +284,7 @@ interface clk_rst_if #(
o_rst_n <= 1'b1;
end
default: begin
`ifdef VERILATOR
$error({msg_id, $sformatf("rst_n_scheme %0d not supported", rst_n_scheme)});
`else
`uvm_fatal(msg_id, $sformatf("rst_n_scheme %0d not supported", rst_n_scheme))
`endif
`dv_fatal($sformatf("rst_n_scheme %0d not supported", rst_n_scheme), msg_id)
end
endcase
wait_clks(post_reset_dly_clks);
@ -252,13 +317,15 @@ interface clk_rst_if #(
if (recompute) begin
clk_hi_ps = clk_period_ps * duty_cycle / 100;
clk_lo_ps = clk_period_ps - clk_hi_ps;
clk_hi_modified_ps = clk_hi_ps;
clk_lo_modified_ps = clk_lo_ps;
recompute = 1'b0;
end
if (jitter_chance_pc != 0) add_jitter();
#(clk_lo_ps * 1ps);
// wiggle output clk if not gated
if (clk_freq_scaling_pc && clk_freq_scaling_chance_pc) apply_freq_scaling();
if (jitter_chance_pc) apply_jitter();
#(clk_lo_modified_ps * 1ps);
if (!clk_gate) o_clk = 1'b1;
#(clk_hi_ps * 1ps);
#(clk_hi_modified_ps * 1ps);
o_clk = 1'b0;
end
end

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:dv:rst_shadowed_if"
description: "Shadowed reset pin interfaces used in DV"
filesets:
files_dv:
files:
- rst_shadowed_if.sv
file_type: systemVerilogSource
targets:
default:
filesets:
- files_dv

View file

@ -0,0 +1,39 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Interface: rst_shadowed_if
// An interface to drive rst_shadowed_n pin
// By default, the `rst_shadowed_n` pin is directly connected to `rst_n` pin from the IP.
// This interface provide methods to drive `rst_shadowed_n` pin when `drive_shadow_rst_pin()` task is
// called, and can use `reconnect_shadowed_rst_n_to_rst_n()` to reconnect to `rst_n` pin.
interface rst_shadowed_if (
input rst_n,
output rst_shadowed_n
);
`ifndef VERILATOR
// include macros and import pkgs
`include "dv_macros.svh"
`include "uvm_macros.svh"
import uvm_pkg::*;
`endif
bit drive_shadowed_rst_en;
logic shadowed_rst_n;
// If set `drive_shadowed_rst_en` bit to 0, the `rst_shadowed_n` output will be the same as IP
// level reset pin.
function automatic void reconnect_shadowed_rst_n_to_rst_n();
drive_shadowed_rst_en = 0;
endfunction
task automatic drive_shadow_rst_pin(logic val);
shadowed_rst_n = val;
drive_shadowed_rst_en = 1;
endtask
assign rst_shadowed_n = drive_shadowed_rst_en ? shadowed_rst_n : rst_n;
endinterface

View file

@ -142,7 +142,17 @@ class csr_hw_reset_seq extends csr_base_seq;
test_csrs[i].get_full_name()), UVM_MEDIUM)
compare_mask = get_mask_excl_fields(test_csrs[i], CsrExclInitCheck, CsrHwResetTest);
// Read twice, one from backdoor, the other from frontdoor.
// Reading from backdoor can ensure that we deposit value into the storage rather than just
// a net. If we mistakenly deposit to a net, reset can't clear it and this check will fail.
csr_rd_check(.ptr (test_csrs[i]),
.backdoor (1),
.compare (!external_checker),
.compare_vs_ral(1'b1),
.compare_mask (compare_mask));
// same read but using frontdoor
csr_rd_check(.ptr (test_csrs[i]),
.backdoor (0),
.blocking (0),
.compare (!external_checker),
.compare_vs_ral(1'b1),
@ -208,7 +218,25 @@ class csr_write_seq extends csr_base_seq;
backdoor dist {0 :/ 7, 1 :/ 3};)
end
csr_wr(.ptr(test_csrs[i]), .value(wdata), .blocking(0), .backdoor(backdoor));
if (backdoor) begin
string str_kinds[$];
test_csrs[i].get_hdl_path_kinds(str_kinds);
str_kinds.shuffle();
foreach (str_kinds[j]) begin
bkdr_reg_path_e enum_kind;
// Convert string name to an enum
`DV_CHECK_FATAL(uvm_enum_wrapper#(bkdr_reg_path_e)::from_name(str_kinds[j], enum_kind))
csr_poke(.ptr(test_csrs[i]), .value(wdata), .kind(enum_kind));
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
wdata = get_csr_wdata_with_write_excl(test_csrs[i], wdata, CsrHwResetTest);
end
end else begin
csr_wr(.ptr(test_csrs[i]), .value(wdata), .blocking(0));
end
end
endtask

View file

@ -7,6 +7,7 @@ package csr_utils_pkg;
import uvm_pkg::*;
import dv_utils_pkg::*;
import dv_base_reg_pkg::*;
export dv_base_reg_pkg::csr_field_t, dv_base_reg_pkg::decode_csr_or_field;
// macro includes
`include "uvm_macros.svh"
@ -22,14 +23,6 @@ package csr_utils_pkg;
bit under_reset = 0;
int max_outstanding_accesses = 100;
// csr field struct - hold field specific params
typedef struct {
uvm_reg csr;
uvm_reg_field field;
uvm_reg_data_t mask;
uint shift;
} csr_field_t;
function automatic void increment_outstanding_access();
outstanding_accesses++;
endfunction
@ -75,7 +68,7 @@ package csr_utils_pkg;
return mem.get_access();
endfunction
// This fucntion return mirrored value of reg/field of given RAL
// This function return mirrored value of reg/field of given RAL
function automatic uvm_reg_data_t get_reg_fld_mirror_value(uvm_reg_block ral,
string reg_name,
string field_name = "");
@ -97,34 +90,6 @@ package csr_utils_pkg;
return result;
endfunction : get_reg_fld_mirror_value
// This function attempts to cast a given uvm_object ptr into uvm_reg or uvm_reg_field. If cast
// is successful on either, then set the appropriate csr_field_t return values.
function automatic csr_field_t decode_csr_or_field(input uvm_object ptr);
uvm_reg csr;
uvm_reg_field fld;
csr_field_t result;
string msg_id = {csr_utils_pkg::msg_id, "::decode_csr_or_field"};
if ($cast(csr, ptr)) begin
// return csr object with null field; set the mask to all 1s and shift to 0
result.csr = csr;
result.mask = '1;
result.shift = 0;
end
else if ($cast(fld, ptr)) begin
// return csr field object; return the appropriate mask and shift values
result.csr = fld.get_parent();
result.field = fld;
result.mask = (1 << fld.get_n_bits()) - 1;
result.shift = fld.get_lsb_pos();
end
else begin
`uvm_fatal(msg_id, $sformatf("ptr %0s is not of type uvm_reg or uvm_reg_field",
ptr.get_full_name()))
end
return result;
endfunction : decode_csr_or_field
// get updated reg value by using new specific field value
function automatic uvm_reg_data_t get_csr_val_with_updated_field(uvm_reg_field field,
uvm_reg_data_t csr_value,
@ -726,20 +691,29 @@ package csr_utils_pkg;
return wdata;
endfunction
// Returns the CSR exclusion item associated with the provided object.
//
// If an exclusion item for the immediate block (parent of the CSR if ptr is a CSR or a field) is
// not found, it recurses through the block's ancestors to find an available exclusion item.
// arg ptr: An extention of one of dv_base_reg{, _block or _field} classes.
function automatic csr_excl_item get_excl_item(uvm_object ptr);
csr_field_t csr_or_fld;
dv_base_reg_block blk;
csr_or_fld = decode_csr_or_field(ptr);
`downcast(blk, csr_or_fld.csr.get_parent(), , , msg_id)
// csr_excl is at the highest level of reg block
while (blk.csr_excl == null) begin
`downcast(blk, blk.get_parent(), , , msg_id)
`DV_CHECK_NE_FATAL(blk, null, "", msg_id)
// Attempt cast to blk. If it fails, then attempt to cast to CSR or field.
if (!$cast(blk, ptr)) begin
csr_field_t csr_or_fld = decode_csr_or_field(ptr);
`downcast(blk, csr_or_fld.csr.get_parent(), , , msg_id)
end
return blk.csr_excl;
// Recurse through block's ancestors.
do begin
csr_excl_item csr_excl = blk.get_excl_item();
if (csr_excl != null) return csr_excl;
`downcast(blk, blk.get_parent(), , , msg_id)
end while (blk != null);
return null;
endfunction
// sources
`include "csr_seq_lib.sv"

View file

@ -9,6 +9,7 @@ class csr_excl_item extends uvm_object;
`uvm_object_utils(csr_excl_item)
typedef struct {
bit enable;
int csr_test_type;
csr_excl_type_e csr_excl_type;
} csr_excl_s;
@ -16,87 +17,133 @@ class csr_excl_item extends uvm_object;
`uvm_object_new
// add exclusion for an individual block, csr or field
// arg obj: this is the hierarchical path name to the block, csr or field - passing * and ?
// wildcards for glob style matching is allowed. User needs to take care that wildcards does not
// end up inadvertently matching more that what was desired. Examples:
// To exclude ral.ctrl.tx field from writes, obj can be "ral.ctrl.tx" or "*.ctrl.tx"; passing
// "*.tx" might be too generic
// Adds an exclusion for an individual block, register or field.
//
// arg obj: Hierarchical path to the block, csr or field as string. Passing * and ? wildcards for
// glob style matching is allowed. User needs to take care the wildcards don't match more
// than desired. For example, for the ral.ctrl.tx field:
// obj = "ral.ctrl.tx" or "*.ctrl.tx" should work; "*.tx" might be too generic.
// arg csr_excl_type: The desired exclusion type.
// arg csr_test_type: The desired test type for which the exclusion is in effect.
virtual function void add_excl(string obj,
csr_excl_type_e csr_excl_type,
csr_test_type_e csr_test_type = CsrAllTests);
bit [2:0] val = CsrNoExcl;
bit [NUM_CSR_TESTS-1:0] test = CsrInvalidTest;
if (csr_test_type == CsrInvalidTest) begin
`uvm_fatal(`gfn, $sformatf("add %s exclusion without a test", obj))
`DV_CHECK_NE_FATAL(csr_test_type, CsrInvalidTest,
$sformatf("Test type not specified for the exclusion of %0s.", obj))
if (!exclusions.exists(obj)) begin
exclusions[obj] = '{enable:1'b1, csr_test_type:csr_test_type, csr_excl_type:csr_excl_type};
return;
end
if (!exclusions.exists(obj)) exclusions[obj] = '{default:CsrNoExcl};
val = csr_excl_type | exclusions[obj].csr_excl_type;
test = csr_test_type | exclusions[obj].csr_test_type;
exclusions[obj].csr_excl_type = csr_excl_type_e'(val);
exclusions[obj].csr_test_type = test;
endfunction
// function to check if given blk / csr or field AND its parent has been excluded with the
// supplied exclusion type
// arg uvm_object obj: given blk, csr or field
// arg csr_excl_type_e csr_excl_type: exclusion type
// Turns exclusion on or off for a block, register or field.
//
// The originally set exclusions are untouched. This method only enables or disables the
// application of the exclusions temporarily.
//
// obj: The hierarchical path to block, register or field.
// enable: Bit indicating whether to enable or disable the application of exclusion.
// throw_error: Bit indicating whether to throw error if exclusions associated with obj do not
// exist.
virtual function void enable_excl(string obj, bit enable = 1'b1, bit throw_error = 1'b1);
string index;
if (get_excl_index(obj, index)) begin
exclusions[index].enable = enable;
return;
end
if (throw_error) begin
`uvm_fatal(`gfn, $sformatf("No exclusions found for %0s.", obj))
end
endfunction
// Checks if the given block, register or field is excluded.
//
// arg obj: The given block, register or field.
// arg csr_excl_type: The exclusion checked against.
// arg csr_test_type: The type of test for which the exclusion is in effect.
function bit is_excl(uvm_object obj,
csr_excl_type_e csr_excl_type,
csr_test_type_e csr_test_type);
uvm_reg_block blk;
uvm_reg csr;
// if supplied obj is a uvm_reg_block or uvm_reg, then its parent is a uvm_reg_block
// check if obj's parent is excluded
if ($cast(blk, obj)) begin
if (blk.get_parent() != null) begin
blk = blk.get_parent();
if (has_excl(blk.`gfn, csr_excl_type, csr_test_type)) return 1'b1;
// Attempt cast to block. If it fails, then attempt to cast to CSR or field.
if (!$cast(blk, obj)) begin
csr_field_t csr_or_fld = decode_csr_or_field(obj);
if (csr_or_fld.field != null) begin
if (has_excl(csr_or_fld.field.`gfn, csr_excl_type, csr_test_type, is_excl)) return is_excl;
end else begin
if (has_excl(csr_or_fld.csr.`gfn, csr_excl_type, csr_test_type, is_excl)) return is_excl;
end
`downcast(blk, csr_or_fld.csr.get_parent(), , , msg_id)
end
if ($cast(csr, obj)) begin
blk = csr.get_parent();
if (has_excl(blk.`gfn, csr_excl_type, csr_test_type)) return 1'b1;
end
// TODO: check if any parent in the hierarchy above is excluded
// check if obj is excluded
return (has_excl(obj.`gfn, csr_excl_type, csr_test_type));
// Recurse through block's ancestors.
do begin
if (has_excl(blk.`gfn, csr_excl_type, csr_test_type, is_excl)) return is_excl;
blk = blk.get_parent();
end while (blk != null);
return 1'b0;
endfunction
// check if applied string obj has a match in existing exclusions lookup in defined csr_test_type
// function is to not be called externally
local function bit has_excl(string obj,
csr_excl_type_e csr_excl_type,
csr_test_type_e csr_test_type);
// check if obj exists verbatim
// Retrieves the string index of the exclusions data structure.
//
// The provided object for lookup is a string itself. It however, may or may
// not exist verbatim as an associative array index in the `exclusions` data
// structure, since glob-style wildcards are supported when adding the
// exclusions. The provided object must hence be a fully resolved
// hierarchical path to the CSR block, register or field.
//
// Returns 1 if index is found, else 0.
// Returns the actual index as output arg.
local function bit get_excl_index(input string obj, output string index);
// If obj is a index of exclusions, return it, else loop through available
// keys to find a glob match.
if (exclusions.exists(obj)) begin
`uvm_info(`gfn, $sformatf("has_excl: found exact excl match for %0s: %0s",
obj, exclusions[obj].csr_excl_type.name()), UVM_DEBUG)
// check if bit(s) corresponding to csr_excl_type are set in defined csr_test_type
if ((exclusions[obj].csr_test_type & csr_test_type) != CsrInvalidTest) begin
if ((exclusions[obj].csr_excl_type & csr_excl_type) != CsrNoExcl) return 1'b1;
end
end
else begin
// attempt glob style matching
index = obj;
return 1'b1;
end else begin
foreach (exclusions[str]) begin
if (!uvm_re_match(str, obj)) begin
`uvm_info(`gfn, $sformatf("has_excl: found glob excl match for %0s(%0s): %0s",
obj, str, exclusions[str].csr_excl_type.name()), UVM_DEBUG)
// check if bit(s) corresponding to csr_excl_type are set in defined csr_test_type
if ((exclusions[str].csr_test_type & csr_test_type) != CsrInvalidTest) begin
if ((exclusions[str].csr_excl_type & csr_excl_type) != CsrNoExcl) return 1'b1;
end
index = str;
return 1'b1;
end
end
end
return 1'b0;
endfunction
// print all exclusions for ease of debug (call this ideally after adding all exclusions)
// Checks if a particular exclusion for an object for a test is in effect.
//
// arg obj: The given block, register or field as string lookup.
// arg csr_excl_type: The exclusion checked against.
// arg csr_test_type: The type of test for which the exclusion is in effect.
// arg is_excl: Bit indicating the object is excluded.
//
// Returns 1 if an associated exclusion is found, else 0.
local function bit has_excl(input string obj,
input csr_excl_type_e csr_excl_type,
input csr_test_type_e csr_test_type,
output bit is_excl);
string index;
if (get_excl_index(obj, index)) begin
is_excl = exclusions[index].enable &&
((exclusions[index].csr_test_type & csr_test_type) != CsrInvalidTest) &&
((exclusions[index].csr_excl_type & csr_excl_type) != CsrNoExcl);
return 1'b1;
end
return 1'b0;
endfunction
// Prints all exclusions for ease of debug.
virtual function void print_exclusions(uvm_verbosity verbosity = UVM_HIGH);
string test_names;
for (int i = NUM_CSR_TESTS - 1; i >= 0; i--) begin
@ -104,8 +151,9 @@ class csr_excl_item extends uvm_object;
test_names = {test_names, csr_test.name(), (i > 0) ? " " : ""};
end
foreach (exclusions[item]) begin
`uvm_info(`gfn, $sformatf("CSR/field [%0s] excluded with %0s in csr_tests: {%s} = {%0b}",
item, exclusions[item].csr_excl_type.name(), test_names,
string enabled = exclusions[item].enable ? "[enabled]" : "[disabled]";
`uvm_info(`gfn, $sformatf("CSR/field [%s] exclusion %s %s in csr_tests: {%s} = {%0b}",
item, exclusions[item].csr_excl_type.name(), enabled, test_names,
exclusions[item].csr_test_type), verbosity)
end
endfunction

View file

@ -152,6 +152,7 @@ class dv_base_reg extends uvm_reg;
if (is_shadowed) begin
if (shadow_wr_staged) `uvm_info(`gfn, "clear shadow_wr_staged", UVM_HIGH)
shadow_wr_staged = 0;
clear_shadow_update_err();
end
endfunction
@ -317,7 +318,7 @@ class dv_base_reg extends uvm_reg;
if (kind == "BkdrRegPathRtlShadow") begin
flds[i].update_shadowed_val(get_field_val(flds[i], value));
backdoor_write_shadow_val = 1;
end else if (kind == "BkdrRegPathRtlCommitted") begin
end else if (kind == "BkdrRegPathRtl") begin
flds[i].update_committed_val(get_field_val(flds[i], value));
backdoor_write_shadow_val = 1;
end
@ -334,13 +335,6 @@ class dv_base_reg extends uvm_reg;
end
endfunction
// Callback function to update shadowed values according to specific design.
// Should only be called after post-write.
// If a shadow reg is locked due to fatal error, this function will return without updates
virtual function void update_shadowed_val(uvm_reg_data_t val, bit do_predict = 1);
// TODO: find a better way to support for AES.
endfunction
virtual function void reset(string kind = "HARD");
super.reset(kind);
if (is_shadowed) begin

View file

@ -12,7 +12,8 @@ class dv_base_reg_block extends uvm_reg_block;
// name as {ip_name}_{alert_name}. Hence, we need this ip_name in reg_block
local string ip_name;
csr_excl_item csr_excl;
// CSR exclusion object that holds exclusion tags for the sub-blocks, CSRs and fields.
protected csr_excl_item csr_excl;
// The address mask for the register block specific to a map. This will be (1 << K) - 1 for some
// K. All relative offsets in the register block have addresses less than (1 << K), so an address
@ -46,6 +47,11 @@ class dv_base_reg_block extends uvm_reg_block;
return ip_name;
endfunction
// Returns the CSR exclusion item attached to the block.
virtual function csr_excl_item get_excl_item();
return csr_excl;
endfunction
// provide build function to supply base addr
virtual function void build(uvm_reg_addr_t base_addr,
csr_excl_item csr_excl = null);

View file

@ -13,6 +13,15 @@ package dv_base_reg_pkg;
// global paramters for number of csr tests (including memory test)
parameter uint NUM_CSR_TESTS = 4;
string msg_id = "dv_base_reg_pkg";
// csr field struct - hold field specific params
typedef struct {
uvm_reg csr;
uvm_reg_field field;
uvm_reg_data_t mask;
uint shift;
} csr_field_t;
// csr exclusion indications
typedef enum bit [2:0] {
@ -38,23 +47,45 @@ package dv_base_reg_pkg;
} csr_test_type_e;
typedef enum bit[2:0] {
// If it's a shadow reg, BkdrRegPathRtl is the path to committed reg
BkdrRegPathRtl, // backdoor path for reg's val in RTL
BkdrRegPathRtlCommitted, // backdoor path for shadow reg's committed val in RTL
BkdrRegPathRtlShadow, // backdoor path for shadow reg's shadow val in RTL
BkdrRegPathGls, // backdoor path for reg's val in GLS
BkdrRegPathGlsCommitted, // backdoor path for shadow reg's committed val in GLS
BkdrRegPathGlsShdow // backdoor path for shadow reg's shadow val in GLS
} bkdr_reg_path_e;
// Forward-declare class types for the functions below.
typedef class dv_base_reg_block;
typedef class dv_base_reg;
typedef class dv_base_reg_field;
`include "csr_excl_item.sv"
`include "dv_base_reg_field.sv"
`include "dv_base_reg.sv"
`include "dv_base_mem.sv"
`include "dv_base_reg_block.sv"
`include "dv_base_reg_map.sv"
// This function attempts to cast a given uvm_object ptr into uvm_reg or uvm_reg_field. If cast
// is successful on either, then set the appropriate csr_field_t return values.
function automatic csr_field_t decode_csr_or_field(input uvm_object ptr);
uvm_reg csr;
uvm_reg_field fld;
csr_field_t result;
string msg_id = {dv_base_reg_pkg::msg_id, "::decode_csr_or_field"};
if ($cast(csr, ptr)) begin
// return csr object with null field; set the mask to all 1s and shift to 0
result.csr = csr;
result.mask = '1;
result.shift = 0;
end
else if ($cast(fld, ptr)) begin
// return csr field object; return the appropriate mask and shift values
result.csr = fld.get_parent();
result.field = fld;
result.mask = (1 << fld.get_n_bits()) - 1;
result.shift = fld.get_lsb_pos();
end
else begin
`uvm_fatal(msg_id, $sformatf("ptr %0s is not of type uvm_reg or uvm_reg_field",
ptr.get_full_name()))
end
return result;
endfunction : decode_csr_or_field
function automatic void get_flds_from_uvm_object(input uvm_object obj,
input string msg = "dv_base_reg_pkg",
@ -71,7 +102,7 @@ package dv_base_reg_pkg;
`uvm_fatal(msg, $sformatf("obj %0s is not of type uvm_reg or uvm_reg_field",
obj.get_full_name()))
end
endfunction
endfunction : get_flds_from_uvm_object
// mask and shift data to extract the value specific to that supplied field
function automatic uvm_reg_data_t get_field_val(uvm_reg_field field,
@ -79,6 +110,13 @@ package dv_base_reg_pkg;
uvm_reg_data_t mask = (1 << field.get_n_bits()) - 1;
uint shift = field.get_lsb_pos();
get_field_val = (value >> shift) & mask;
endfunction
endfunction : get_field_val
`include "csr_excl_item.sv"
`include "dv_base_reg_field.sv"
`include "dv_base_reg.sv"
`include "dv_base_mem.sv"
`include "dv_base_reg_block.sv"
`include "dv_base_reg_map.sv"
endpackage

View file

@ -85,10 +85,18 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
end
endfunction
// ral flow is limited in terms of setting correct field access policies and reset values
// We apply those fixes here - please note these fixes need to be reflected in the scoreboard
protected virtual function void apply_ral_fixes();
// fix access policies & reset values
// Set pre-build RAL knobs.
//
// This method enables setting pre-build config knobs that can be used to control how the RAL
// sub-structures are created.
protected virtual function void pre_build_ral_settings(dv_base_reg_block ral);
endfunction
// Perform post-build, pre-lock modifications to the RAL.
//
// For some registers / fields, the correct access policies or reset values may not be set. Fixes
// like those can be made with this method.
protected virtual function void post_build_ral_settings(dv_base_reg_block ral);
endfunction
virtual function void reset_asserted();
@ -112,8 +120,9 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
// Build the register block with an arbitrary base address (we choose 0). We'll change it
// later.
pre_build_ral_settings(reg_blk);
reg_blk.build(.base_addr(0), .csr_excl(null));
apply_ral_fixes();
post_build_ral_settings(reg_blk);
reg_blk.lock_model();
// Now the model is locked, we know its layout. Set the base address for the register block.

View file

@ -77,7 +77,7 @@ class dv_base_monitor #(type ITEM_T = uvm_sequence_item,
end
// Start the timer only when ok_to_end is asserted.
wait(ok_to_end);
wait (ok_to_end);
`uvm_info(`gfn, $sformatf("watchdog_ok_to_end: starting the timer (count: %0d)",
watchdog_restart_count++), UVM_MEDIUM)
fork
@ -88,16 +88,12 @@ class dv_base_monitor #(type ITEM_T = uvm_sequence_item,
#(cfg.ok_to_end_delay_ns * 1ns);
watchdog_done = 1'b1;
end
@(ok_to_end);
wait (!ok_to_end);
join_any
disable fork;
end: isolation_fork
join
// The #0 delay ensures that we sample the stabilized value of ok_to_end in the condition
// below in case it toggles more than once in the same simulation time-step.
#0;
// If ok_to_end stayed high throughout the watchdog timer expiry, then drop the objection.
if (ok_to_end && watchdog_done) begin
`uvm_info(`gfn, "watchdog_ok_to_end: dropping objection", UVM_MEDIUM)
@ -105,7 +101,7 @@ class dv_base_monitor #(type ITEM_T = uvm_sequence_item,
objection_raised = 1'b0;
// Wait for ok_to_end to de-assert again in future.
wait(!ok_to_end);
wait (!ok_to_end);
end
end
endtask

View file

@ -140,7 +140,7 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
endtask
// This is called after apply_reset in this class and after apply_resets_concurrently
// in cip_base_vseq::run_stress_all_with_rand_reset_vseq.
// in cip_base_vseq::run_seq_with_rand_reset_vseq.
virtual task post_apply_reset(string reset_kind = "HARD");
endtask
@ -205,6 +205,12 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
default : `uvm_fatal(`gfn, $sformatf("specified opt is invalid: +csr_%0s", csr_test_type))
endcase
// Print the list of available exclusions that are in effect for debug.
foreach (cfg.ral_models[i]) begin
csr_excl_item csr_excl = csr_utils_pkg::get_excl_item(cfg.ral_models[i]);
if (csr_excl != null) csr_excl.print_exclusions();
end
// if hw_reset test, then write all CSRs first and reset the whole dut
if (csr_test_type == "hw_reset" && do_rand_wr_and_reset) begin
string reset_type = "HARD";

View file

@ -76,6 +76,10 @@
`define DV_STRINGIFY(I_) `"I_`"
`endif
`ifndef DUT_HIER_STR
`define DUT_HIER_STR `DV_STRINGIFY(`DUT_HIER)
`endif
// Common check macros used by DV_CHECK error and fatal macros.
// Note: Should not be called by user code
`ifndef DV_CHECK
@ -390,7 +394,7 @@
// SCOPE_ : Hierarchical string path to the testbench where this macro is invoked, example: %m.
// ID_ : Identifier string used for UVM logs.
`ifndef DV_ASSERT_CTRL
`define DV_ASSERT_CTRL(LABEL_, HIER_, LEVELS_ = 0, SCOPE_ = "", ID_ = "%m") \
`define DV_ASSERT_CTRL(LABEL_, HIER_, LEVELS_ = 0, SCOPE_ = "", ID_ = $sformatf("%m")) \
initial begin \
bit assert_en; \
forever begin \

View file

@ -0,0 +1,19 @@
## Memory backdoor utility class
The `mem_bkdr_util` class provides a way to manipulate the memory array directly via backdoor.
It includes a set of functions to backdoor read or write any address location within the memory.
The class instance is created in the testbench module and passed to the UVM environment via `uvm_config_db`.
### Methods
This interface supports basic backdoor methods to access memory. Useful methods are:
* `is_addr_valid`: Check if input address is valid
The input address is assumed to be the byte addressable address into memory
starting at 0. It is user's responsibility to mask the upper bits.
* `read8`, `read16`, `read32`, `read64`: Functions to read one byte, two bytes, four bytes, and eight bytes respectively
at specified input address
* `write8`, `write16`, `write32`, `write64`: Functions to write one byte, two bytes, four bytes, and eight bytes respectively
with input data at specified input address
* `load_mem_from_file`: Load memory from a file specified by input string
* `print_mem`: Print the content of the memory
* `clear_mem`: Clear the memory to all 0s
* `set_mem`: Set the memory to all 1s
* `randomize_mem`: Randomize contents of the memory

View file

@ -0,0 +1,31 @@
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:mem_bkdr_util"
description: "Backdoor read/write memory for DV"
filesets:
files_dv:
depend:
- lowrisc:opentitan:bus_params_pkg
- lowrisc:dv:dv_utils
- lowrisc:dv:crypto_dpi_prince:0.1
- lowrisc:dv:crypto_dpi_present:0.1
- lowrisc:prim:cipher_pkg:0.1
- lowrisc:prim:secded:0.1
- lowrisc:ip:otp_ctrl_pkg:0.1
files:
- otp_scrambler_pkg.sv
- sram_scrambler_pkg.sv
- mem_bkdr_util_pkg.sv
- mem_bkdr_util.sv: {is_include_file: true}
- mem_bkdr_util__otp.sv: {is_include_file: true}
- mem_bkdr_util__rom.sv: {is_include_file: true}
- mem_bkdr_util__sram.sv: {is_include_file: true}
file_type: systemVerilogSource
targets:
default:
filesets:
- files_dv

View file

@ -0,0 +1,542 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Provides a mechanism to manipulate and access a memory instance in the design via backdoor.
//
// This is a class based implementation, which on initialization (`new()`) takes the path to the
// memory hierarchy, the size in bits, the depth, integrity protection and scrambling needs as
// arguments. All memory specifics are set / computed at runtime. There are no parameterizations, so
// that the implementation is flexible, extensible, and easy to use.
//
// Create an instance of this class in the testbench module itself, so that the hierarchical path to
// the memory element and its size and depth information is available. Pass the instance to the UVM
// side via uvm_config_db.
class mem_bkdr_util extends uvm_object;
// Hierarchical path to the memory.
protected string path;
// The depth of the memory.
protected uint32_t depth;
// The width of the memory.
protected uint32_t width;
// Indicates the error detection scheme implemented for this memory.
protected err_detection_e err_detection_scheme = ErrDetectionNone;
// Convenience macro to check if ECC / parity is enabled.
`define HAS_ECC (!(err_detection_scheme inside {ErrDetectionNone, ParityEven, ParityOdd}))
`define HAS_PARITY (err_detection_scheme inside {ParityEven, ParityOdd})
// TODO: Indicates whether the memory implements scrambling.
// Other memory specifics derived from the settings above.
protected uint32_t data_width; // ignoring ECC bits
protected uint32_t byte_width;
protected uint32_t bytes_per_word; // addressable bytes
protected uint32_t size_bytes; // addressable bytes
protected uint32_t addr_lsb;
protected uint32_t addr_width;
protected uint32_t byte_addr_width;
// Indicates the maximum number of errors that can be injected.
//
// If parity is enabled, this limit applies to a single byte in the memory width. We cannot inject
// more than 1 error per each byte of data. In case of ECC, it applies to the entire width.
protected uint32_t max_errors;
// File operations.
//
// We unfortunately cannot use the system tasks $readmemh and $writememh due to class based
// implementation. This is done externally in the testbench module where the class instance is
// created instead. The following signals and events are used by the testbench to know when to
// read or write the memory with the contents of the file.
protected string file;
event readmemh_event;
event writememh_event;
// Initialize the class instance.
function new(string name = "", string path, int unsigned depth,
longint unsigned n_bits, err_detection_e err_detection_scheme);
bit res;
super.new(name);
`DV_CHECK_FATAL(!(n_bits % depth), "n_bits must be divisible by depth.")
res = uvm_hdl_check_path(path);
`DV_CHECK_EQ_FATAL(res, 1, $sformatf("Hierarchical path %0s appears to be invalid.", path))
this.path = path;
this.depth = depth;
this.width = n_bits / depth;
this.err_detection_scheme = err_detection_scheme;
if (`HAS_ECC) begin
import prim_secded_pkg::prim_secded_e;
import prim_secded_pkg::get_ecc_data_width;
import prim_secded_pkg::get_ecc_parity_width;
prim_secded_e secded_eds = prim_secded_e'(err_detection_scheme);
int non_ecc_bits_per_subword = get_ecc_data_width(secded_eds);
int ecc_bits_per_subword = get_ecc_parity_width(secded_eds);
int bits_per_subword = non_ecc_bits_per_subword + ecc_bits_per_subword;
int subwords_per_word;
// We shouldn't truncate the actual data word. This check ensures that err_detection_scheme
// and width are related sensibly. This only checks we've got enough space for one data word
// and at least one check bit. The next check will make sure that we don't truncate if there
// are multiple subwords.
`DV_CHECK_FATAL(non_ecc_bits_per_subword < this.width)
// Normally, we'd want width to be divisible by bits_per_subword, which means that we get a
// whole number of subwords in a word. As a special case, we also allow a having exactly one
// subword and only keeping some of the bits. This is used by the flash controller.
`DV_CHECK_FATAL((this.width < bits_per_subword) || (this.width % bits_per_subword == 0),
"With multiple subwords, mem width must be a multiple of the ECC width")
subwords_per_word = (width + bits_per_subword - 1) / bits_per_subword;
this.data_width = subwords_per_word * non_ecc_bits_per_subword;
end else begin
this.data_width = width;
end
byte_width = `HAS_PARITY ? 9 : 8;
bytes_per_word = data_width / byte_width;
`DV_CHECK_LE_FATAL(bytes_per_word, 32, "data width > 32 bytes is not supported")
size_bytes = depth * bytes_per_word;
addr_lsb = $clog2(bytes_per_word);
addr_width = $clog2(depth);
byte_addr_width = addr_width + addr_lsb;
max_errors = width;
if (name == "") set_name({path, "::mem_bkdr_util"});
`uvm_info(`gfn, this.convert2string(), UVM_MEDIUM)
endfunction
virtual function string convert2string();
return {"\n",
$sformatf("path = %0s\n", path),
$sformatf("depth = %0d\n", depth),
$sformatf("width = %0d\n", width),
$sformatf("err_detection_scheme = %0s\n", err_detection_scheme.name),
$sformatf("data_width = %0d\n", data_width),
$sformatf("byte_width = %0d\n", byte_width),
$sformatf("bytes_per_word = %0d\n", bytes_per_word),
$sformatf("size_bytes = 0x%0h\n", size_bytes),
$sformatf("addr_lsb = %0d\n", addr_lsb),
$sformatf("addr_width = %0d\n", addr_width),
$sformatf("byte_addr_width = %0d\n", byte_addr_width),
$sformatf("max_errors = %0d\n", max_errors)};
endfunction
function string get_path();
return path;
endfunction
function uint32_t get_depth();
return depth;
endfunction
function uint32_t get_width();
return width;
endfunction
function err_detection_e get_err_detection_scheme();
return err_detection_scheme;
endfunction
function uint32_t get_data_width();
return data_width;
endfunction
function uint32_t get_byte_width();
return byte_width;
endfunction
function uint32_t get_bytes_per_word();
return bytes_per_word;
endfunction
function uint32_t get_size_bytes();
return size_bytes;
endfunction
function uint32_t get_addr_lsb();
return addr_lsb;
endfunction
function uint32_t get_addr_width();
return addr_width;
endfunction
function uint32_t get_byte_addr_width();
return byte_addr_width;
endfunction
function string get_file();
return file;
endfunction
// Returns 1 if the given address falls within the memory's range, else 0.
//
// If addr is invalid, it throws UVM error before returning 0.
protected virtual function bit check_addr_valid(bit [bus_params_pkg::BUS_AW-1:0] addr);
if (addr >= size_bytes) begin
`uvm_error(`gfn, $sformatf("addr %0h is out of bounds: size = %0h", addr, size_bytes))
return 1'b0;
end
return 1'b1;
endfunction
// Read the entire word at the given address.
//
// addr is the byte address starting at offset 0. Mask the upper address bits as needed before
// invocation.
//
// Returns the entire width of the memory at the given address, including the ECC bits. The data
// returned is 'raw' i.e. it includes the parity bits. It also does not de-scramble the data if
// encryption is enabled.
//
// TODO: Factor in encryption into this function itself?
virtual function uvm_hdl_data_t read(bit [bus_params_pkg::BUS_AW-1:0] addr);
bit res;
uint32_t index;
uvm_hdl_data_t data;
if (!check_addr_valid(addr)) return 'x;
index = addr >> addr_lsb;
res = uvm_hdl_read($sformatf("%0s[%0d]", path, index), data);
`DV_CHECK_EQ(res, 1, $sformatf("uvm_hdl_read failed at index %0d", index))
return data;
endfunction
// Convenience macro to check the addr for each flavor of read and write functions.
`define _ACCESS_CHECKS(_ADDR, _DW) \
`DV_CHECK_EQ_FATAL(_ADDR % (_DW / 8), 0, $sformatf("addr 0x%0h not ``_DW``-bit aligned", _ADDR))
// Read a single byte at specified address.
//
// The data returned does not include the parity bits.
virtual function logic [7:0] read8(bit [bus_params_pkg::BUS_AW-1:0] addr);
uvm_hdl_data_t data = read(addr);
int byte_offset = addr % bytes_per_word;
return (data >> (byte_offset * byte_width)) & 8'hff;
endfunction
virtual function logic [15:0] read16(bit [bus_params_pkg::BUS_AW-1:0] addr);
`_ACCESS_CHECKS(addr, 16)
return {read8(addr + 1), read8(addr)};
endfunction
virtual function logic [31:0] read32(bit [bus_params_pkg::BUS_AW-1:0] addr);
`_ACCESS_CHECKS(addr, 32)
return {read16(addr + 2), read16(addr)};
endfunction
// this is used to read 32bit of data plus 7 raw integrity bits.
virtual function logic [38:0] read39integ(bit [bus_params_pkg::BUS_AW-1:0] addr);
`_ACCESS_CHECKS(addr, 32) // this is essentially an aligned 32bit access.
return read(addr) & 39'h7fffffffff;
endfunction
virtual function logic [63:0] read64(bit [bus_params_pkg::BUS_AW-1:0] addr);
`_ACCESS_CHECKS(addr, 64)
return {read32(addr + 4), read32(addr)};
endfunction
virtual function logic [127:0] read128(bit [bus_params_pkg::BUS_AW-1:0] addr);
`_ACCESS_CHECKS(addr, 128)
return {read64(addr + 8), read64(addr)};
endfunction
virtual function logic [255:0] read256(bit [bus_params_pkg::BUS_AW-1:0] addr);
`_ACCESS_CHECKS(addr, 256)
return {read128(addr + 16), read128(addr)};
endfunction
// Write the entire word at the given address with the specified data.
//
// addr is the byte address starting at offset 0. Mask the upper address bits as needed before
// invocation.
//
// Updates the entire width of the memory at the given address, including the ECC bits.
virtual function void write(bit [bus_params_pkg::BUS_AW-1:0] addr, uvm_hdl_data_t data);
bit res;
uint32_t index;
if (!check_addr_valid(addr)) return;
index = addr >> addr_lsb;
res = uvm_hdl_deposit($sformatf("%0s[%0d]", path, index), data);
`DV_CHECK_EQ(res, 1, $sformatf("uvm_hdl_deposit failed at index %0d", index))
endfunction
// Write a single byte at specified address.
//
// Does a read-modify-write on the whole word. It updates the byte at the given address and
// computes the parity and ECC bits as applicable.
virtual function void write8(bit [bus_params_pkg::BUS_AW-1:0] addr, logic [7:0] data);
uvm_hdl_data_t rw_data;
uint32_t word_idx;
uint32_t byte_idx;
if (!check_addr_valid(addr)) return;
rw_data = read(addr);
word_idx = addr >> addr_lsb;
byte_idx = addr - (word_idx << addr_lsb);
if (`HAS_PARITY) begin
bit parity = (err_detection_scheme == ParityOdd) ? ~(^data) : (^data);
rw_data[byte_idx * 9 +: 9] = {parity, data};
write(addr, rw_data);
return;
end
rw_data[byte_idx * 8 +: 8] = data;
case (err_detection_scheme)
ErrDetectionNone: ;
Ecc_22_16: begin
rw_data = prim_secded_pkg::prim_secded_22_16_enc(rw_data[15:0]);
end
EccHamming_22_16: begin
rw_data = prim_secded_pkg::prim_secded_hamming_22_16_enc(rw_data[15:0]);
end
Ecc_39_32: begin
rw_data = prim_secded_pkg::prim_secded_39_32_enc(rw_data[31:0]);
end
EccHamming_39_32: begin
rw_data = prim_secded_pkg::prim_secded_hamming_39_32_enc(rw_data[31:0]);
end
Ecc_72_64: begin
rw_data = prim_secded_pkg::prim_secded_72_64_enc(rw_data[63:0]);
end
EccHamming_72_64: begin
rw_data = prim_secded_pkg::prim_secded_hamming_72_64_enc(rw_data[63:0]);
end
EccHamming_76_68: begin
rw_data = prim_secded_pkg::prim_secded_hamming_76_68_enc(rw_data[63:0]);
end
default: begin
`uvm_error(`gfn, $sformatf("ECC scheme %0s is unsupported.", err_detection_scheme))
end
endcase
write(addr, rw_data);
endfunction
virtual function void write16(bit [bus_params_pkg::BUS_AW-1:0] addr, logic [15:0] data);
`_ACCESS_CHECKS(addr, 16)
if (!check_addr_valid(addr)) return;
write8(addr, data[7:0]);
write8(addr + 1, data[15:8]);
endfunction
virtual function void write32(bit [bus_params_pkg::BUS_AW-1:0] addr, logic [31:0] data);
`_ACCESS_CHECKS(addr, 32)
if (!check_addr_valid(addr)) return;
write16(addr, data[15:0]);
write16(addr + 2, data[31:16]);
endfunction
// this is used to write 32bit of data plus 7 raw integrity bits.
virtual function void write39integ(bit [bus_params_pkg::BUS_AW-1:0] addr, logic [38:0] data);
`_ACCESS_CHECKS(addr, 32) // this is essentially an aligned 32bit access.
if (!check_addr_valid(addr)) return;
write(addr, data);
endfunction
virtual function void write64(bit [bus_params_pkg::BUS_AW-1:0] addr, logic [63:0] data);
`_ACCESS_CHECKS(addr, 64)
if (!check_addr_valid(addr)) return;
write32(addr, data[31:0]);
write32(addr + 4, data[63:32]);
endfunction
virtual function void write128(bit [bus_params_pkg::BUS_AW-1:0] addr, logic [127:0] data);
`_ACCESS_CHECKS(addr, 128)
if (!check_addr_valid(addr)) return;
write64(addr, data[63:0]);
write64(addr + 8, data[127:63]);
endfunction
virtual function void write256(bit [bus_params_pkg::BUS_AW-1:0] addr, logic [255:0] data);
`_ACCESS_CHECKS(addr, 256)
if (!check_addr_valid(addr)) return;
write128(addr, data[127:0]);
write128(addr + 16, data[255:128]);
endfunction
`undef _ACCESS_CHECKS
/////////////////////////////////////////////////////////
// Wrapper functions for memory reads with ECC enabled //
/////////////////////////////////////////////////////////
// Some notes:
// - ECC isn't supported for 8-bit wide memories
// - (28, 22) and (64, 57) ECC configurations aren't supported
// Intended for use with memories which have data width of 16 bits and 6 ECC bits.
virtual function secded_22_16_t ecc_read16(bit [bus_params_pkg::BUS_AW-1:0] addr);
uvm_hdl_data_t data;
if (!check_addr_valid(addr)) return 'x;
data = read(addr);
case (err_detection_scheme)
Ecc_22_16: begin
return prim_secded_pkg::prim_secded_22_16_dec(data);
end
EccHamming_22_16: begin
return prim_secded_pkg::prim_secded_hamming_22_16_dec(data);
end
default: return 'x;
endcase
endfunction
// Intended for use with memories which have data width of 32 bits and 7 ECC bits.
virtual function secded_39_32_t ecc_read32(bit [bus_params_pkg::BUS_AW-1:0] addr);
uvm_hdl_data_t data;
if (!check_addr_valid(addr)) return 'x;
data = read(addr);
case (err_detection_scheme)
Ecc_39_32: begin
return prim_secded_pkg::prim_secded_39_32_dec(data);
end
EccHamming_39_32: begin
return prim_secded_pkg::prim_secded_hamming_39_32_dec(data);
end
default: return 'x;
endcase
endfunction
// Intended for use with memories which have data width of 64 bits and 8 ECC bits.
virtual function secded_72_64_t ecc_read64(bit [bus_params_pkg::BUS_AW-1:0] addr);
uvm_hdl_data_t data;
if (!check_addr_valid(addr)) return 'x;
data = read(addr);
case (err_detection_scheme)
Ecc_72_64: begin
return prim_secded_pkg::prim_secded_72_64_dec(data);
end
EccHamming_72_64: begin
return prim_secded_pkg::prim_secded_hamming_72_64_dec(data);
end
default: return 'x;
endcase
endfunction
// check if input file is read/writable
virtual function void check_file(string file, string mode);
int fh = $fopen(file, mode);
if (!fh) begin
`uvm_fatal(`gfn, $sformatf("file %0s could not be opened for %0s mode", file, mode))
end
$fclose(fh);
endfunction
// load mem from file
virtual function void load_mem_from_file(string file);
check_file(file, "r");
this.file = file;
->readmemh_event;
endfunction
// save mem contents to file
virtual function void write_mem_to_file(string file);
check_file(file, "w");
this.file = file;
->writememh_event;
endfunction
// print mem
virtual function void print_mem();
for (int i = 0; i < depth; i++) begin
`uvm_info(`gfn, $sformatf("mem[%0d] = 0x%0h", i, read(i)), UVM_LOW)
end
endfunction
// clear or set memory
virtual function void clear_mem();
`uvm_info(`gfn, "Clear memory", UVM_LOW)
for (int i = 0; i < size_bytes; i++) begin
write8(i, '0);
end
endfunction
virtual function void set_mem();
`uvm_info(`gfn, "Set memory", UVM_LOW)
for (int i = 0; i < size_bytes; i++) begin
write8(i, '1);
end
endfunction
// randomize the memory
virtual function void randomize_mem();
logic [7:0] rand_val;
`uvm_info(`gfn, "Randomizing mem contents", UVM_LOW)
for (int i = 0; i < size_bytes; i++) begin
`DV_CHECK_STD_RANDOMIZE_FATAL(rand_val, "Randomization failed!", path)
write8(i, rand_val);
end
endfunction
// invalidate the memory.
virtual function void invalidate_mem();
`uvm_info(`gfn, "Invalidating (Xs) mem contents", UVM_LOW)
for (int i = 0; i < size_bytes; i++) begin
write8(i, 'x);
end
endfunction
// Inject ECC or parity errors to the memory word at the given address.
virtual function void inject_errors(bit [bus_params_pkg::BUS_AW-1:0] addr,
uint32_t inject_num_errors);
uvm_hdl_data_t rw_data, err_mask;
if (!check_addr_valid(addr)) return;
`DV_CHECK_LE_FATAL(inject_num_errors, max_errors)
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(err_mask,
$countones(err_mask) == inject_num_errors;
(err_mask >> width) == '0;)
rw_data = read(addr);
write(addr, rw_data ^ err_mask);
`uvm_info(`gfn, $sformatf(
"Addr: %0h, original data: %0h, error_mask: %0h, backdoor inject data: %0h",
addr, rw_data, err_mask, rw_data ^ err_mask), UVM_HIGH)
endfunction
// Wrapper function for backdoor write OTP partitions.
`include "mem_bkdr_util__otp.sv"
// Wrapper functions for encrypted SRAM reads and writes.
`include "mem_bkdr_util__sram.sv"
// Wrapper function for encrypted ROM reads.
`include "mem_bkdr_util__rom.sv"
`undef HAS_ECC
`undef HAS_PARITY
endclass
// Convenience macro to enable file operations on the memory.
//
// The class based approach prevents us from invoking the system tasks $readmemh and $writememh
// directly. This macro is invoked in the top level testbench where the instance of the backdoor
// accessor is created, within an initial block. It forks off two threads that monitor separately
// events when the UVM sequences invoke either the task `load_mem_from_file()` to write to the
// memory with the contents of the file and `write_mem_to_file()` methods, to read the contents of
// the memory into the file.
//
// inst is the mem_bkdr_util instance created in the tesbench module.
// path is the raw path to the memory element in the design.
`define MEM_BKDR_UTIL_FILE_OP(inst, path) \
fork \
forever begin \
string file; \
@(inst.readmemh_event); \
file = inst.get_file(); \
`uvm_info(inst.`gfn, $sformatf("Loading mem from file:\n%0s", file), UVM_LOW) \
$readmemh(file, path); \
end \
forever begin \
string file; \
@(inst.writememh_event); \
file = inst.get_file(); \
`uvm_info(inst.`gfn, $sformatf("Writing mem to file:\n%0s", file), UVM_LOW) \
$writememh(file, path); \
end \
join_none

View file

@ -0,0 +1,40 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Wrapper functions to write different partitions in OTP.
// This file is included in `mem_bkdr_util.sv` as a continuation of `mem_bkdr_util` class.
virtual function void otp_write_lc_partition(lc_ctrl_state_pkg::lc_state_e lc_state);
for (int i = 0; i < LcStateSize; i+=4) begin
write32(i + LcStateOffset, lc_state[i*8+:32]);
end
endfunction
// The following steps are needed to backdoor write a secret partition:
// 1). Scramble the RAW input data.
// 2). Backdoor write the scrambled input data to OTP memory.
// 3). Calculate the correct digest for the secret partition.
// 4). Backdoor write digest data to OTP memory.
virtual function void otp_write_secret0_partition(bit [TestUnlockTokenSize*8-1:0] unlock_token,
bit [TestExitTokenSize*8-1:0] exit_token);
bit [Secret0DigestSize*8-1:0] digest;
bit [TestUnlockTokenSize*8-1:0] scrambled_unlock_token;
bit [TestExitTokenSize*8-1:0] scrambled_exit_token;
bit [bus_params_pkg::BUS_DW-1:0] secret_data[$];
for (int i = 0; i < TestUnlockTokenSize; i+=8) begin
scrambled_unlock_token[i*8+:64] = scramble_data(unlock_token[i*8+:64], Secret0Idx);
write64(i + TestUnlockTokenOffset, scrambled_unlock_token[i*8+:64]);
end
for (int i = 0; i < TestExitTokenSize; i+=8) begin
scrambled_exit_token[i*8+:64] = scramble_data(exit_token[i*8+:64], Secret0Idx);
write64(i + TestExitTokenOffset, scrambled_exit_token[i*8+:64]);
end
secret_data = {<<32 {scrambled_exit_token, scrambled_unlock_token}};
digest = cal_digest(Secret0Idx, secret_data);
write64(Secret0DigestOffset, digest);
endfunction

View file

@ -0,0 +1,68 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Wrapper functions for ROM's encrypted read operation.
// This file is included in `mem_bkdr_util.sv` as a continuation of `mem_bkdr_util` class.
// The data decoding is different from SRAM, but most of the underlying SRAM functions are reused
// Also note that this function returns the raw data rather than data + syndrome + error because
// the rom_ctrl testbench needs this for checking.
virtual function bit [38:0] rom_encrypt_read32(bit [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce,
bit unscramble_data);
logic [bus_params_pkg::BUS_AW-1:0] mem_addr = '0;
logic [38:0] data = '0;
logic addr_arr [] = new[addr_width];
logic scrambled_addr[] = new[addr_width];
logic data_arr [] = new[39];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
logic keystream [] = new[SRAM_BLOCK_WIDTH];
logic zero_key [] = new[39];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
addr_arr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(addr_arr, addr_width, nonce_arr);
for (int i = 0; i < addr_width; i++) begin
mem_addr[i] = scrambled_addr[i];
end
// Read memory and get the encrypted data
if (!check_addr_valid(mem_addr << addr_lsb)) begin
return 'x;
end
// 39-bit memory word includes 32-bit data + 7-bit ECC
data = read(mem_addr << addr_lsb);
if (!unscramble_data) begin
return data;
end
data_arr = {<<{data}};
// Generate the keystream
keystream = sram_scrambler_pkg::gen_keystream(addr_arr, addr_width, key_arr, nonce_arr);
for (int i = 0; i < 39; i++) begin
zero_key[i] = '0;
end
data_arr = sram_scrambler_pkg::sp_decrypt(data_arr, 39, zero_key);
for (int i = 0; i < 39; i++) begin
data[i] = data_arr[i] ^ keystream[i];
end
return data;
endfunction

View file

@ -0,0 +1,423 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Wrapper functions for SRAM's encrypted read/write operations.
// This file is included in `mem_bkdr_util.sv` as a continuation of `mem_bkdr_util` class.
virtual function logic [7:0] sram_encrypt_read8(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [7:0] rdata = '0;
logic rdata_arr [] = new[8];
logic scrambled_addr[] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Read memory, and return the decrypted data
rdata = read8(bus_addr);
rdata_arr = {<<{rdata}};
rdata_arr = sram_scrambler_pkg::decrypt_sram_data(
rdata_arr, 8, 1, sram_addr, addr_width, key_arr, nonce_arr
);
rdata = {<<{rdata_arr}};
return rdata;
endfunction
virtual function logic [15:0] sram_encrypt_read16(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [15:0] rdata = '0;
logic rdata_arr [] = new[16];
logic scrambled_addr[] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Read memory and return the decrypted data
rdata = read16(bus_addr);
rdata_arr = {<<{rdata}};
rdata_arr = sram_scrambler_pkg::decrypt_sram_data(
rdata_arr, 16, 1, sram_addr, addr_width, key_arr, nonce_arr
);
rdata = {<<{rdata_arr}};
return rdata;
endfunction
virtual function logic [31:0] sram_encrypt_read32(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [31:0] rdata = '0;
logic rdata_arr [] = new[32];
logic scrambled_addr[] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Read memory and return the decrypted data
rdata = read32(bus_addr);
rdata_arr = {<<{rdata}};
rdata_arr = sram_scrambler_pkg::decrypt_sram_data(
rdata_arr, 32, 1, sram_addr, addr_width, key_arr, nonce_arr
);
rdata = {<<{rdata_arr}};
return rdata;
endfunction
virtual function logic [38:0] sram_encrypt_read32_integ(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [38:0] rdata = '0;
logic rdata_arr [] = new[39];
logic scrambled_addr[] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Read memory and return the decrypted data
rdata = read39integ(bus_addr);
`uvm_info(`gfn, $sformatf("scr data: 0x%0x", rdata), UVM_HIGH)
rdata_arr = {<<{rdata}};
rdata_arr = sram_scrambler_pkg::decrypt_sram_data(
rdata_arr, 39, 0, sram_addr, addr_width, key_arr, nonce_arr
);
rdata = {<<{rdata_arr}};
// Only return the data payload without ECC bits.
return rdata[31:0];
endfunction
virtual function logic [63:0] sram_encrypt_read64(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [63:0] rdata = '0;
logic rdata_arr [] = new[64];
logic scrambled_addr[] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Read memory and return the decrypted data
rdata = read64(bus_addr);
rdata_arr = {<<{rdata}};
rdata_arr = sram_scrambler_pkg::decrypt_sram_data(
rdata_arr, 64, 1, sram_addr, addr_width, key_arr, nonce_arr
);
rdata = {<<{rdata_arr}};
return rdata;
endfunction
virtual function void sram_encrypt_write8(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [7:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [7:0] scrambled_data;
logic wdata_arr [] = new[8];
logic scrambled_addr [] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Calculate the scrambled data
wdata_arr = {<<{data}};
wdata_arr = sram_scrambler_pkg::encrypt_sram_data(
wdata_arr, 8, 1, sram_addr, addr_width, key_arr, nonce_arr
);
scrambled_data = {<<{wdata_arr}};
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Write the scrambled data to memory
write8(bus_addr, scrambled_data);
endfunction
virtual function void sram_encrypt_write16(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [15:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [15:0] scrambled_data;
logic wdata_arr [] = new[16];
logic scrambled_addr [] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Calculate the scrambled data
wdata_arr = {<<{data}};
wdata_arr = sram_scrambler_pkg::encrypt_sram_data(
wdata_arr, 16, 1, sram_addr, addr_width, key_arr, nonce_arr
);
scrambled_data = {<<{wdata_arr}};
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Write the scrambled data to memory
write16(bus_addr, scrambled_data);
endfunction
virtual function void sram_encrypt_write32(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [31:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [31:0] scrambled_data;
logic wdata_arr [] = new[32];
logic scrambled_addr [] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Calculate the scrambled data
wdata_arr = {<<{data}};
wdata_arr = sram_scrambler_pkg::encrypt_sram_data(
wdata_arr, 32, 1, sram_addr, addr_width, key_arr, nonce_arr
);
scrambled_data = {<<{wdata_arr}};
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Write the scrambled data to memory
write32(bus_addr, scrambled_data);
endfunction
virtual function void sram_encrypt_write32_integ(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [31:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [38:0] integ_data;
logic [38:0] scrambled_data;
logic wdata_arr [] = new[39];
logic scrambled_addr [] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Calculate the integrity constant
integ_data = prim_secded_pkg::prim_secded_39_32_enc(data);
// Calculate the scrambled data
wdata_arr = {<<{integ_data}};
wdata_arr = sram_scrambler_pkg::encrypt_sram_data(
wdata_arr, 39, 0, sram_addr, addr_width, key_arr, nonce_arr
);
scrambled_data = {<<{wdata_arr}};
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Write the scrambled data to memory
write39integ(bus_addr, scrambled_data);
endfunction
virtual function void sram_encrypt_write64(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [63:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [63:0] scrambled_data;
logic wdata_arr [] = new[64];
logic scrambled_addr [] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Calculate the scrambled data
wdata_arr = {<<{data}};
wdata_arr = sram_scrambler_pkg::encrypt_sram_data(
wdata_arr, 64, 1, sram_addr, addr_width, key_arr, nonce_arr
);
scrambled_data = {<<{wdata_arr}};
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Write the scrambled data to memory
write64(bus_addr, scrambled_data);
endfunction

View file

@ -0,0 +1,40 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
package mem_bkdr_util_pkg;
// dep packages
import bus_params_pkg::BUS_AW;
import dv_utils_pkg::uint32_t;
import lc_ctrl_state_pkg::*;
import otp_ctrl_part_pkg::*;
import otp_ctrl_reg_pkg::*;
import otp_scrambler_pkg::*;
import prim_secded_pkg::*;
import sram_scrambler_pkg::*;
import uvm_pkg::*;
// Represents the various forms of error detection / correction supported.
typedef enum int {
ErrDetectionNone = prim_secded_pkg::SecdedNone,
Ecc_22_16 = prim_secded_pkg::Secded_22_16,
Ecc_28_22 = prim_secded_pkg::Secded_28_22,
Ecc_39_32 = prim_secded_pkg::Secded_39_32,
Ecc_64_57 = prim_secded_pkg::Secded_64_57,
Ecc_72_64 = prim_secded_pkg::Secded_72_64,
EccHamming_22_16 = prim_secded_pkg::SecdedHamming_22_16,
EccHamming_39_32 = prim_secded_pkg::SecdedHamming_39_32,
EccHamming_72_64 = prim_secded_pkg::SecdedHamming_72_64,
EccHamming_76_68 = prim_secded_pkg::SecdedHamming_76_68,
ParityEven,
ParityOdd
} err_detection_e;
// macro includes
`include "uvm_macros.svh"
`include "dv_macros.svh"
// sources
`include "mem_bkdr_util.sv"
endpackage

View file

@ -0,0 +1,95 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
/////////////////////////////////////////////////
// OTP secret data and digest scrambling logic //
/////////////////////////////////////////////////
package otp_scrambler_pkg;
import uvm_pkg::*;
import otp_ctrl_reg_pkg::*;
import otp_ctrl_part_pkg::*;
import bus_params_pkg::*;
`include "uvm_macros.svh"
parameter int SCRAMBLE_DATA_SIZE = 64;
parameter int SCRAMBLE_KEY_SIZE = 128;
parameter int NUM_ROUND = 31;
string path = "otp_scrambler_pkg";
// When secret data write into otp_array, it will be scrambled.
function automatic bit [SCRAMBLE_DATA_SIZE-1:0] scramble_data(
bit [SCRAMBLE_DATA_SIZE-1:0] input_data,
int part_idx
);
int secret_idx = part_idx - Secret0Idx;
bit [NUM_ROUND-1:0][SCRAMBLE_DATA_SIZE-1:0] output_data;
crypto_dpi_present_pkg::sv_dpi_present_encrypt(input_data,
RndCnstKey[secret_idx],
SCRAMBLE_KEY_SIZE == 80,
output_data);
scramble_data = output_data[NUM_ROUND-1];
endfunction
// When secret data read out of otp_array, it will be descrambled.
function automatic bit [SCRAMBLE_DATA_SIZE-1:0] descramble_data(
bit [SCRAMBLE_DATA_SIZE-1:0] input_data,
int part_idx
);
int secret_idx = part_idx - Secret0Idx;
bit [NUM_ROUND-1:0][SCRAMBLE_DATA_SIZE-1:0] output_data;
bit [NUM_ROUND-1:0][SCRAMBLE_DATA_SIZE-1:0] padded_input;
padded_input[NUM_ROUND-1] = input_data;
crypto_dpi_present_pkg::sv_dpi_present_decrypt(padded_input,
RndCnstKey[secret_idx],
SCRAMBLE_KEY_SIZE == 80,
output_data);
descramble_data = output_data[NUM_ROUND-1];
if (input_data != 0) begin
end
endfunction
function automatic bit [SCRAMBLE_DATA_SIZE-1:0] cal_digest(int part_idx,
ref bit [BUS_DW-1:0] mem_q[$]);
int array_size = mem_q.size();
real key_factor = SCRAMBLE_KEY_SIZE / BUS_DW;
bit [NUM_ROUND-1:0] [SCRAMBLE_DATA_SIZE-1:0] enc_array;
bit [SCRAMBLE_DATA_SIZE-1:0] init_vec = RndCnstDigestIV[0];
bit [SCRAMBLE_DATA_SIZE-1:0] digest;
for (int i = 0; i < $ceil(array_size / key_factor); i++) begin
bit [SCRAMBLE_DATA_SIZE-1:0] input_data = (i == 0) ? init_vec : digest;
bit [SCRAMBLE_KEY_SIZE-1:0] key;
// Pad 32-bit partition data into 128-bit key input.
// Because the mem_q size is a multiple of 64-bit, so if the last round only has 64-bits key,
// it will repeat the last 64-bits twice.
for (int j = 0; j < key_factor; j++) begin
int index = i * key_factor + j;
key |= ((index >= array_size ? mem_q[index-2] : mem_q[index]) << (j * BUS_DW));
end
// Trigger 32 round of PRESENT encrypt.
crypto_dpi_present_pkg::sv_dpi_present_encrypt(input_data, key, SCRAMBLE_KEY_SIZE == 80,
enc_array);
// XOR the previous state into the digest result according to the Davies-Meyer scheme.
digest = enc_array[NUM_ROUND-1] ^ input_data;
end
// Last 32 round of digest is calculated with a digest constant.
crypto_dpi_present_pkg::sv_dpi_present_encrypt(digest,
RndCnstDigestConst[0],
SCRAMBLE_KEY_SIZE == 80,
enc_array);
// XOR the previous state into the digest result according to the Davies-Meyer scheme.
digest ^= enc_array[NUM_ROUND-1];
return digest;
endfunction
endpackage

View file

@ -0,0 +1,298 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
////////////////////////////////////////////
// SRAM address and data scrambling logic //
///////////////////////////////////////////
//
// There are a few general things to note here:
//
// - SRAM data scrambling relies on a reduced-round PRINCE cipher, plus a custom substitution
// and permutation network loosely based off of PRESENT.
//
// - SRAM address scrambling relies solely on the custom substitution and permutation network.
//
// - The custom subst/perm network used for data scrambling operates at byte granularity,
// while for address scrambling it operates at a granularity of the address width.
//
// - For DV purposes we can safely rely on the PRESENT sboxes, as nightly regressions are
// completely passing plus the sboxes in `prim_cipher_pkg` are copied directly from the
// PRESENT specifications.
// This has the side effect of allowing us to avoid duplication of the sboxes, which lowers the
// probability of an error in translation.
package sram_scrambler_pkg;
import uvm_pkg::*;
import bus_params_pkg::BUS_AW;
import crypto_dpi_prince_pkg::*;
import prim_cipher_pkg::*;
`include "uvm_macros.svh"
string path = "sram_scrambler_pkg";
// Fixed key size - PRINCE cipher operates on a 128-bit key,
// and the same key is used for all parallel cipher instances.
parameter int SRAM_KEY_WIDTH = 128;
// Fixed data block size - PRINCE cipher operates on 64-bit data blocks.
parameter int SRAM_BLOCK_WIDTH = 64;
parameter int NUM_ROUNDS = 2;
// Create a generic typedef for dynamic array of logic to be able to return these values.
typedef logic state_t[];
// The sboxes operate on nibbles.
//
// If `WIDTH % 4 != 0`, the uppermost bits will get shifted to lower positions
// during either the `flip_vector` or `perm_layer` stage of the network,
// so it is guaranted that all bits will eventually go through an sbox.
function automatic state_t sbox_layer(state_t state, int width, bit inv);
logic state_out[] = new[width](state);
logic [3:0] sbox_in;
logic [3:0] sbox_out;
for (int i = 0; i < width / 4; i++) begin
// `with` syntax is currently unsupported by Verible,
// uncomment once support has been added
//
//sbox_in = {<< {state with [i*4 +: 4]}};
for (int j = 0; j < 4; j++) begin
sbox_in[j] = state[i*4 + j];
end
if (inv) begin
sbox_out = prim_cipher_pkg::PRESENT_SBOX4_INV[sbox_in];
end else begin
sbox_out = prim_cipher_pkg::PRESENT_SBOX4[sbox_in];
end
for (int j = 0; j < 4; j++) begin
state_out[i*4 + j] = sbox_out[j];
end
end
return state_out;
endfunction : sbox_layer
// Reverse the input bit vector.
function automatic state_t flip_vector(state_t state);
return {<< {state}};
endfunction : flip_vector
// Permutation layer - all even indexed bits move to the lower half,
// and all odd indexed bits move to the top half.
function automatic state_t perm_layer(state_t state, int width, bit inv);
logic state_out[] = new[width](state);
for (int i = 0; i < width / 2; i++) begin
if (inv) begin
state_out[i * 2] = state[i];
state_out[i * 2 + 1] = state[i + width / 2];
end else begin
state_out[i] = state[i * 2];
state_out[i + width / 2] = state[i * 2 + 1];
end
end
return state_out;
endfunction : perm_layer
// Performs NUM_ROUNDS full encryption rounds
function automatic state_t sp_encrypt(state_t data, int width, state_t key);
logic state[] = new[width](data);
for (int i = 0; i < NUM_ROUNDS; i++) begin
// xor the data and key
for (int j = 0; j < width; j++) begin
state[j] = state[j] ^ key[j];
end
// sbox layer
state = sbox_layer(state, width, 0);
// flip the bit vector
state = flip_vector(state);
// permutation layer
state = perm_layer(state, width, 0);
end
// final xor
for (int i = 0; i < width; i++) begin
state[i] = state[i] ^ key[i];
end
return state;
endfunction : sp_encrypt
// Performs NUM_ROUNDS full decryption rounds
function automatic state_t sp_decrypt(state_t data, int width, state_t key);
logic state[] = new[width](data);
for (int i = 0; i < NUM_ROUNDS; i++) begin
// xor data and key
for (int j = 0; j < width; j++) begin
state[j] = state[j] ^ key[j];
end
// permutation layer
state = perm_layer(state, width, 1);
// flip bit vector
state = flip_vector(state);
// sbox layer
state = sbox_layer(state, width, 1'b1);
end
// final xor
for (int i = 0; i < width; i++) begin
state[i] = state[i] ^ key[i];
end
return state;
endfunction : sp_decrypt
// Generates the 64-bit keystream that is XORed with the data to obtain a ciphertext.
// Assumes that the data is at most 64 bits wide.
//
// Should not be called directly.
function automatic state_t gen_keystream(logic addr[], int addr_width,
logic key[], logic nonce[]);
logic [NUM_ROUNDS-1:0][SRAM_BLOCK_WIDTH-1:0] prince_result_arr;
logic [SRAM_BLOCK_WIDTH-1:0] prince_plaintext;
logic [SRAM_KEY_WIDTH-1:0] prince_key;
logic [SRAM_BLOCK_WIDTH-1:0] prince_result;
logic iv[] = new[SRAM_BLOCK_WIDTH];
logic key_out[] = new[SRAM_BLOCK_WIDTH];
// IV is composed of nonce concatenated with address
for (int i = 0; i < addr_width; i++) begin
iv[i] = addr[i];
end
for (int i = 0; i < SRAM_BLOCK_WIDTH - addr_width; i++) begin
iv[addr_width + i] = nonce[i];
end
//for (int i = 0; i < SRAM_BLOCK_WIDTH - addr_width; i++) begin
// iv[addr_width + i] = nonce[i];
//end
// convert arrays to packed vectors before invoking prince DPI model
prince_plaintext = {<< {iv}};
// `with` syntax is currently unsupported by Verible,
// uncomment once support has been added
//
// prince_key = {<< {key with [0 +: SRAM_KEY_WIDTH]}};
for (int i = 0; i < SRAM_KEY_WIDTH; i++) begin
prince_key[i] = key[i];
end
crypto_dpi_prince_pkg::sv_dpi_prince_encrypt(.plaintext(prince_plaintext),
.key(prince_key),
.old_key_schedule(0),
.ciphertext(prince_result_arr));
prince_result = prince_result_arr[NUM_ROUNDS-1];
key_out = {<< {prince_result}};
return key_out;
endfunction : gen_keystream
// Encrypts the target SRAM address using the custom S&P network.
function automatic state_t encrypt_sram_addr(logic addr[], int addr_width,
logic full_nonce[]);
logic nonce[] = new[addr_width];
logic encrypted_addr[] = new[addr_width];
// The address encryption nonce is the same width as the address,
// and is constructed from the top addr_width bits of the full nonce.
//
// `with` syntax is currently unsupported by Verible,
// uncomment once support has been added
//
// nonce = {>> {full_nonce with [SRAM_BLOCK_WIDTH - addr_width +: addr_width]}};
for (int i = 0; i < addr_width; i++) begin
nonce[i] = full_nonce[SRAM_BLOCK_WIDTH - addr_width + i];
end
encrypted_addr = sp_encrypt(addr, addr_width, nonce);
return encrypted_addr;
endfunction : encrypt_sram_addr
// SRAM data encryption is more involved, we need to run 2 rounds of PRINCE on the nonce and key
// and then XOR the result with the data.
//
// After that, the XORed data neeeds to them be passed through the S&P network one byte at a time.
//
// TODO: We currently do not support data size of >64bits.
function automatic state_t encrypt_sram_data(logic data[], int data_width, bit byte_diff,
logic addr[], int addr_width,
logic key[], logic nonce[]);
// Generate the keystream
logic keystream[] = new[SRAM_BLOCK_WIDTH];
logic data_enc[] = new[data_width];
logic byte_to_enc[] = new[8];
logic enc_byte[] = new[8];
logic zero_key[] = new[data_width];
// the key used for byte diffusion is all-zero.
for (int i = 0; i < data_width; i++) begin
zero_key[i] = '0;
end
// Generate the keystream
keystream = gen_keystream(addr, addr_width, key, nonce);
// XOR keystream with input data
// Assumes data width <= 64.
for (int i = 0; i < data_width; i++) begin
data_enc[i] = data[i] ^ keystream[i];
end
// pass each byte of the encoded result through the subst/perm network
if (byte_diff) begin
for (int i = 0; i < data_width / 8; i++) begin
byte_to_enc = data_enc[i*8 +: 8];
enc_byte = sp_encrypt(byte_to_enc, 8, zero_key);
data_enc[i*8 +: 8] = enc_byte;
end
// pass the entire word through the subst/perm network
end else begin
data_enc = sp_encrypt(data_enc, data_width, zero_key);
end
return data_enc;
endfunction : encrypt_sram_data
function automatic state_t decrypt_sram_data(logic data[], int data_width, bit byte_diff,
logic addr[], int addr_width,
logic key[], logic nonce[]);
logic keystream[] = new[SRAM_BLOCK_WIDTH];
logic data_dec[] = new[data_width];
logic byte_to_dec[] = new[8];
logic dec_byte[] = new[8];
logic zero_key[] = new[data_width];
// the key used for byte diffusion is all-zero.
for (int i = 0; i < data_width; i++) begin
zero_key[i] = '0;
end
// Generate the keystream
keystream = gen_keystream(addr, addr_width, key, nonce);
// pass each byte of the data through the subst/perm network
if (byte_diff) begin
for (int i = 0; i < data_width / 8; i++) begin
byte_to_dec = data[i*8 +: 8];
dec_byte = sp_decrypt(byte_to_dec, 8, zero_key);
data_dec[i*8 +: 8] = dec_byte;
end
// pass the entire word through the subst/perm network
end else begin
data_dec = sp_decrypt(data, data_width, zero_key);
end
// XOR result data with the keystream
for (int i = 0; i < data_width; i++) begin
data_dec[i] = data_dec[i] ^ keystream[i];
end
return data_dec;
endfunction : decrypt_sram_data
endpackage : sram_scrambler_pkg

View file

@ -66,7 +66,8 @@
"+define+UVM_REG_ADDR_WIDTH={tl_aw}",
"+define+UVM_REG_DATA_WIDTH={tl_dw}",
"+define+UVM_REG_BYTENABLE_WIDTH={tl_dbw}",
"+define+SIMULATION"]
"+define+SIMULATION",
"+define+DUT_HIER={dut_instance}"]
run_opts: ["+UVM_NO_RELNOTES",
"+UVM_VERBOSITY={expand_uvm_verbosity_{verbosity}}"]

View file

@ -14,7 +14,9 @@
"+incdir+{UVM_HOME}/src",
"{UVM_HOME}/src/uvm_pkg.sv",
// Add dpi/vpi include path.
"-c-opts -I{DSIM_HOME}/include",
"-c-opts -I{DSIM_HOME}/../../include",
// Needed for including "secded_enc.h".
"-c-opts -I{build_dir}/src/lowrisc_dv_secded_enc_0",
"-timescale 1ns/1ps",
"-f {sv_flist}",
// List multiple tops for the simulation. Prepend each top level with `-top`.
@ -80,7 +82,7 @@
// Vars that need to exported to the env.
exports: [
{ PATH: "{DSIM_HOME}:{PATH}" }
{ LD_LIBRARY_PATH: "{DSIM_HOME}/lib:{DSIM_HOME}/system_lib:{LD_LIBRARY_PATH}" }
{ LD_LIBRARY_PATH: "{DSIM_HOME}/lib:{LD_LIBRARY_PATH}" }
]
// Defaults for DSim

View file

@ -0,0 +1,29 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
testpoints: [
{
name: prim_count_check
desc: ''' Verify that violating prim_count counter properties generate a fatal alert.
Stimulus:
- At the falling edge (non-active edge), force the counter to a different value than
expected.
- Randomly force the counter back to a normal value to ensure the error is latched and
won't go away until reset.
- Within the next few cycles, the violation of hardened counter property should
generate a fatal alert.
- Repeat for ALL prim_count instances in the DUT.
Checks:
- Check that fatal alert is triggered.
- Check that err_code/fault_status is updated correctly and preserved until reset.
- Verify any operations that follow fail (as applicable).
'''
milestone: V2S
tests: ["{name}_sec_cm"]
}
]
}

View file

@ -0,0 +1,28 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
testpoints: [
{
name: prim_fsm_check
desc: ''' Verify that entering to an undefined state generates a fatal alert.
Stimulus:
- Backdoor force the FSM to any of the undefined values.
- Randomly force the FSM back to a defined state to ensure the error is latched and
won't go away until reset.
- Within the next few cycles, the FSM landing in an invalid state should trigger a
fatal alert.
- Repeat for ALL prim_fsm instances in the DUT.
Checks:
- Check that fatal alert is triggered.
- Check that err_code/fault_status is updated correctly and preserved until reset.
- Verify any operations that follow fail (as applicable).
'''
milestone: V2S
tests: ["{name}_sec_cm"]
}
]
}

View file

@ -0,0 +1,29 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
testpoints: [
{
name: prim_one_hot_check
desc: ''' Verify that violating one-hot property generates a fatal alert.
One-hot encoded signals are captured in `prim_one_hot` module.
Stimulus:
- Backdoor force the one-hot coding signals with multiple bits set.
- Randomly force the value to one-hot to ensure the error is latched and won't go away
until reset.
- Within the next few cycles, the violation of one-hot property should generate a fatal
alert.
- Repeat for ALL prim_one_hot instances in the DUT.
Checks:
- Check that fatal alert is triggered.
- Check that err_code/fault_status is updated correctly and preserved until reset.
- Verify any operations that follow fail (as applicable).
'''
milestone: V2S
tests: ["{name}_sec_cm"]
}
]
}

View file

@ -1,52 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
// TODO, split this into several testplan for each CM
testpoints: [
{
name: one_hot_check
desc: ''' Verify design behavior is correct when one-hot coding is violated.
Stimulus:
- Backdoor force one-hot coding signals to not one-hot.
- Randomly flip the value back to ensure the error is latched and won't go away until
reset.
Checks:
- Check that fatal alert is triggered.
- Check that err_code/fault_status is updated correctly and preserved until reset.
- Check the following operation should be failed if applicable.'''
milestone: V2
tests: ["{name}_sec_cm"]
}
{
name: redundant_coding_fsm_check
desc: ''' Verify design behavior is correct when the redundant FSM enters an invalid state.
Stimulus:
- Backdoor force the FSM to any of the undefined values.
- Randomly flip the value back to a defined state to ensure the error is latched and
won't go away until reset.
Same checks as `one_hot_check`'''
milestone: V2
tests: ["{name}_sec_cm"]
}
{
name: hardened_counter_check
desc: ''' Verify design behavior is correct when the harden counter is changed to an
unexpected value.
Stimulus:
- At the falling edge (non-active edge), force the counter to a different value.
- Randomly flip the value back to any other value to ensure the error is latched and
won't go away until reset.
Same checks as `one_hot_check`'''
milestone: V2
tests: ["{name}_sec_cm"]
}
]
}

View file

@ -51,5 +51,36 @@
milestone: V1
tests: ["{name}_shadow_reg_errors"]
}
{
name: shadowed_reset_glitch
desc: '''Verify toggle shadowed_rst_n pin can trigger storage error.
- Randomly drive `shadowed_rst_n` pin to low or `rst_n` pin to low.
- check if any registers have been written before the reset. If so check if storage
error fatal alert is triggered.
- Check status register.
- Drive `shadowed_rst_n` pin or `rst_n` pin back to high.
- If fatal alert is triggered, reset the DUT.
- Read all CSRs to ensure the DUT is properly reset.
- Repeat the above steps a bunch of times.
'''
milestone: V1
tests: ["{name}_shadow_reg_errors"]
}
{
// this testplan should be imported by all IPs containing shadowed CSRs
name: shadow_reg_update_error_with_csr_rw
desc: '''Run shadow_reg_update_error sequence in parallel with csr_rw sequence.
- Randomly select one of the above sequences.
- Apply csr_rw sequence in parallel but disable the `csr_access_abort` to ensure all
shadowed registers' write/read to be executed without aborting.
- Repeat the above steps a bunch of times.
'''
milestone: V1
tests: ["{name}_shadow_reg_errors_with_csr_rw"]
}
]
}

View file

@ -60,7 +60,7 @@
Randomly inject errors on the control, data, or the ECC bits during CSR accesses.
Verify that triggers the correct fatal alert.'''
milestone: V3
milestone: V2S
tests: ["{name}_tl_intg_err"]
}
]
@ -76,12 +76,8 @@
name: tl_intg_err_cg
desc: '''Cover all kinds of integrity errors (command, data or both) and cover number of
error bits on each integrity check.
'''
}
{
name: tl_intg_err_mem_subword_cg
desc: '''Cover the kinds of integrity errors with byte enabled write on memory.
Cover the kinds of integrity errors with byte enabled write on memory if applicable:
Some memories store the integrity values. When there is a subword write, design
re-calculate the integrity with full word data and update integrity in the memory.
This coverage ensures that memory byte write has been issued and the related design

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
{
tests: [
{
name: "{name}_sec_cm"
uvm_test_seq: "{name}_common_vseq"
run_opts: ["+run_sec_cm_fi", "+en_scb=0"]
reseed: 5
}
]
}

View file

@ -1,6 +1,8 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// this hjson should be imported by all IPs containing shadowed CSRs
{
build_modes: [
{
@ -18,11 +20,18 @@
tests: [
{
// this hjson should be imported by all IPs containing shadowed CSRs
name: "{name}_shadow_reg_errors"
build_mode: "cover_reg_top"
run_opts: ["+run_shadow_reg_errors"]
en_run_modes: ["csr_tests_mode"]
reseed: 20
}
{
name: "{name}_shadow_reg_errors_with_csr_rw"
build_mode: "cover_reg_top"
run_opts: ["+run_shadow_reg_errors_with_csr_rw"]
en_run_modes: ["csr_tests_mode"]
reseed: 20
}
]

View file

@ -5,6 +5,8 @@
build_cmd: "{job_prefix} xrun"
run_cmd: "{job_prefix} xrun"
build_db_dir: "{build_dir}/xcelium.d"
build_opts: ["-elaborate -64bit -sv",
"-licqueue",
// TODO: duplicate primitives between OT and Ibex #1231
@ -13,7 +15,7 @@
"-timescale 1ns/1ps",
"-f {sv_flist}",
"-uvmhome CDNS-1.2",
"-xmlibdirname {build_dir}/xcelium.d",
"-xmlibdirname {build_db_dir}",
// List multiple tops for the simulation. Prepend each top level with `-top`.
"{eval_cmd} echo {sim_tops} | sed -E 's/(\\S+)/-top \\1/g'",
// Set the top level elaborated entity (snapshot name) correctly since there are
@ -37,7 +39,7 @@
run_opts: ["-input {run_script}",
"-licqueue",
"-64bit -xmlibdirname {build_dir}/xcelium.d",
"-64bit -xmlibdirname {build_db_dir}",
// Use the same snapshot name set during the build step.
"-r {tb}",
"+SVSEED={seed}",
@ -145,7 +147,7 @@
// what metrics to use
"-jg_coverage {cov_metrics}",
"-64bit",
"-xmlibdirname {build_dir}/xcelium.d",
"-xmlibdirname {build_db_dir}",
// overwrite previous results
"-covoverwrite",
"-inst_top {dut_instance}",

View file

@ -15,10 +15,20 @@ The adjoining `ralgen.core` file registers the `ralgen` generator. The FuseSoC
core file that 'calls' the generator adds it as a dependency. When calling the
generator, the following parameters are set:
* **name (mandatory)**: Name of the RAL package (typically, same is the IP).
* **dv_base_prefix (optional)**: The prefix added to the base classes from
which the register classes are derived. Set this option to derive the register
classes not from the default `dv_base_reg`, but from user defined custom
class definitions.
* **dv_base_names (optional)**: The base class names from which the register
classes are derived. Set this option to derive the register classes not from
the default `dv_base_reg`, but from user defined custom class definitions.
This argument follows the following format:
`--dv-base-names block:type:entity-name block:type:entity-name ...`.
`block`: can be any block names.
`type`: can be `block`, `reg`, `field`, `pkg`, `mem`, or use `all` to override
all types within the block.
`entity_name`: the name of the base class / package. If the `type` is set to `all`,
then this represents the prefix of the bass class / package. The suffixes
`_reg_block`, `_reg`, `_reg_field`, `_mem`, `_reg_pkg` are applied to infer the
actual base class / package names from which the generated DV classes will extend.
Note that we assume the fusesoc core file naming convention follows the package
name without the `_pkg` suffix.
* **ip_hjson**: Path to the hjson specification written for an IP which includes
the register descriptions. This needs to be a valid input for `reggen`.
* **top_hjson**: Path to the hjson specification for a top level design. This
@ -35,7 +45,9 @@ generate:
parameters:
name: <name>
ip_hjson|top_hjson: <path-to-hjson-spec>
[dv_base_prefix: my_base]
[dv_base_names:
- block_1:type:entity_name_1
- block_2:type:entity_name_2]
targets:
@ -73,7 +85,7 @@ The generated core file adds **`lowrisc:dv:dv_base_reg`** as a dependency for
the generated RAL package. This is required because our DV register block,
register and field models are derived from the
[DV library]({{< relref "hw/dv/sv/dv_lib/README.md" >}}) of classes. This
ensures the right compilation order is maintained. If the `dv_base_prefix`
ensures the right compilation order is maintained. If the `dv_base_names`
argument is set, then it adds **`lowrisc:dv:my_base_reg`** as an extra
dependency, where `my_base` is the value of the argument as shown in the
example above. This core file and the associated sources are assumed to be

View file

@ -6,8 +6,10 @@ r"""FuseSoc generator for UVM RAL package created with either regtool or
topgen tools.
"""
import os
import shlex
import subprocess
import sys
from pathlib import Path
import yaml
@ -21,15 +23,6 @@ except ImportError:
REPO_ROOT = "../../../.."
# Given a root dir and partial path, this function returns the full path.
def get_full_path(root_dir, partial_path):
full_path = os.path.abspath(os.path.join(root_dir, partial_path))
if not os.path.exists(full_path):
print("Error: path appears to be invalid: {}".format(full_path))
sys.exit(1)
return full_path
def main():
if len(sys.argv) != 2:
print("ERROR: This script takes a single YAML file as input argument")
@ -38,17 +31,17 @@ def main():
gapi_filepath = sys.argv[1]
gapi = yaml.load(open(gapi_filepath), Loader=YamlLoader)
# This is just a wrapper around the reggen and topgen tools, which
# are referenced from proj_root area.
self_path = os.path.dirname(os.path.realpath(__file__))
util_path = os.path.abspath(os.path.join(self_path, REPO_ROOT, "util"))
# The reggen and topgen tools live in REPO_ROOT/util area.
util_path = Path(__file__).parent / REPO_ROOT / "util"
# Retrieve the parameters from the yml.
root_dir = gapi['files_root']
root_dir = Path(gapi['files_root'])
name = gapi['parameters'].get('name')
ip_hjson = gapi['parameters'].get('ip_hjson')
top_hjson = gapi['parameters'].get('top_hjson')
dv_base_prefix = gapi['parameters'].get('dv_base_prefix')
dv_base_names = gapi['parameters'].get('dv_base_names')
hjson_path = gapi['parameters'].get('hjson_path')
if not name or (bool(ip_hjson) == bool(top_hjson)):
print("Error: ralgen requires the \"name\" and exactly one of "
"{\"ip_hjson\" and \"top_hjson\"} parameters to be set.")
@ -56,24 +49,28 @@ def main():
# Generate the RAL pkg.
if ip_hjson:
ral_spec = get_full_path(root_dir, ip_hjson)
cmd = os.path.join(util_path, "regtool.py")
args = [cmd, "-s", "-t", ".", ral_spec]
ral_spec = root_dir / ip_hjson
cmd = util_path / "regtool.py"
args = [cmd, "-s", "-t", os.getcwd(), ral_spec]
else:
ral_spec = get_full_path(root_dir, top_hjson)
cmd = os.path.join(util_path, "topgen.py")
args = [cmd, "-r", "-o", ".", "-t", ral_spec]
ral_spec = root_dir / top_hjson
cmd = util_path / "topgen.py"
args = [cmd, "-r", "-o", os.getcwd(), "-t", ral_spec]
if hjson_path:
args += ["--hjson-path", root_dir / hjson_path]
if dv_base_prefix and dv_base_prefix != "dv_base":
args.extend(["--dv-base-prefix", dv_base_prefix])
if dv_base_names:
args += ["--dv-base-names"] + dv_base_names
cmd_str = ' '.join([shlex.quote(str(arg)) for arg in args])
print(f"Calling tool in ralgen.py: {cmd_str}")
try:
subprocess.run(args, check=True)
except subprocess.CalledProcessError as e:
print("Error: RAL pkg generation failed:\n{}".format(str(e)))
print(f"Error: RAL pkg generation failed:\n{e}")
sys.exit(e.returncode)
print("RAL pkg for {} block written to {}"
.format(name, os.path.abspath('.')))
print(f"RAL pkg for {name} written to {Path.cwd()}.")
if __name__ == '__main__':

View file

@ -28,12 +28,28 @@ begin tgl
end
begin assert
// These three assertions in prim_lc_sync check when `lc_ctrl_pkg::lc_tx_e` input is neither `On`
// or `Off`, it is interrupted to the correct `On` or `Off` after one clock cycle. This behavior
// is implemented outside of IP level design thus these assertions are not covered in IP level
// testbenchs.
// These three assertions in prim_lc_sync and prim_mubi* check when `lc_ctrl_pkg::lc_tx_t` or
// `mubi*_t` input are neither `On` or `Off`, it is interrupted to the correct `On` or `Off`
// after one clock cycle. This behavior is implemented outside of IP level design thus these
// assertions are not covered in IP level testbenchs.
// TODO: check these assertions in top-level or FPV.
-assert PrimLcSyncCheckTransients_A
-assert PrimLcSyncCheckTransients0_A
-assert PrimLcSyncCheckTransients1_A
-assert PrimMubi4SyncCheckTransients_A
-assert PrimMubi4SyncCheckTransients0_A
-assert PrimMubi4SyncCheckTransients1_A
-assert PrimMubi8SyncCheckTransients_A
-assert PrimMubi8SyncCheckTransients0_A
-assert PrimMubi8SyncCheckTransients1_A
-assert PrimMubi12SyncCheckTransients_A
-assert PrimMubi12SyncCheckTransients0_A
-assert PrimMubi12SyncCheckTransients1_A
-assert PrimMubi16SyncCheckTransients_A
-assert PrimMubi16SyncCheckTransients0_A
-assert PrimMubi16SyncCheckTransients1_A
end

View file

@ -24,3 +24,28 @@ select_coverage -toggle -module prim_esc_sender
select_coverage -toggle -module prim_esc_receiver
select_coverage -toggle -module prim_prince
select_coverage -toggle -module prim_lfsr
// These three assertions in prim_lc_sync and prim_mubi* check when `lc_ctrl_pkg::lc_tx_t` or
// `mubi*_t` input are neither `On` or `Off`, it is interrupted to the correct `On` or `Off`
// after one clock cycle. This behavior is implemented outside of IP level design thus these
// assertions are not covered in IP level testbenchs.
// TODO: check these assertions in top-level or FPV.
deselect_coverage -assertion *.PrimLcSyncCheckTransients_A
deselect_coverage -assertion *.PrimLcSyncCheckTransients0_A
deselect_coverage -assertion *.PrimLcSyncCheckTransients1_A
deselect_coverage -assertion *.PrimMubi4SyncCheckTransients_A
deselect_coverage -assertion *.PrimMubi4SyncCheckTransients0_A
deselect_coverage -assertion *.PrimMubi4SyncCheckTransients1_A
deselect_coverage -assertion PrimMubi8SyncCheckTransients_A
deselect_coverage -assertion PrimMubi8SyncCheckTransients0_A
deselect_coverage -assertion PrimMubi8SyncCheckTransients1_A
deselect_coverage -assertion PrimMubi12SyncCheckTransients_A
deselect_coverage -assertion PrimMubi12SyncCheckTransients0_A
deselect_coverage -assertion PrimMubi12SyncCheckTransients1_A
deselect_coverage -assertion PrimMubi16SyncCheckTransients_A
deselect_coverage -assertion PrimMubi16SyncCheckTransients0_A
deselect_coverage -assertion PrimMubi16SyncCheckTransients1_A

View file

@ -29,122 +29,133 @@ void Ecc32MemArea::LoadVmem(const std::string &path) const {
"vmem files are not supported for memories with ECC bits");
}
Ecc32MemArea::EccWords Ecc32MemArea::ReadWithIntegrity(
uint32_t word_offset, uint32_t num_words) const {
assert(word_offset + num_words <= num_words_);
// See MemArea::Write for an explanation for this buffer.
uint8_t minibuf[SV_MEM_WIDTH_BYTES];
memset(minibuf, 0, sizeof minibuf);
assert(width_byte_ <= sizeof minibuf);
EccWords ret;
ret.reserve(num_words);
for (uint32_t i = 0; i < num_words; ++i) {
uint32_t src_word = word_offset + i;
uint32_t phys_addr = ToPhysAddr(src_word);
ReadToMinibuf(minibuf, phys_addr);
ReadBufferWithIntegrity(ret, minibuf, src_word);
}
return ret;
}
// Add bits to buf at bit_idx
//
// buf is assumed to be little-endian, so bit_idx 0 will refer to the bottom
// bit of buf[0] and bit_idx 15 will refer to the top bit of buf[1].
//
// This takes the bottom count bits from new_bits (where count <= 8). It
// assumes that the relevant place in buf is zeroed (simplifying the
// read-modify-write cycle).
static void insert_bits(uint8_t *buf, unsigned bit_idx, uint8_t new_bits,
unsigned count) {
assert(count <= 8);
buf += bit_idx / 8;
bit_idx = bit_idx % 8;
while (count) {
unsigned space_avail = 8 - bit_idx;
unsigned to_take = std::min(space_avail, count);
uint8_t masked = ((1 << to_take) - 1) & new_bits;
uint8_t shifted = masked << bit_idx;
*buf |= shifted;
++buf;
bit_idx = 0;
count -= to_take;
new_bits >>= to_take;
}
}
// Extract bits from buf at bit_idx
static uint8_t extract_bits(const uint8_t *buf, unsigned bit_idx,
unsigned count) {
assert(count <= 8);
uint8_t ret = 0;
unsigned out_idx = 0;
buf += bit_idx / 8;
bit_idx = bit_idx % 8;
while (count) {
unsigned bits_avail = 8 - bit_idx;
unsigned to_take = std::min(bits_avail, count);
uint8_t shifted = *buf >> bit_idx;
uint8_t masked = shifted & ((1 << to_take) - 1);
ret |= masked << out_idx;
++buf;
bit_idx = 0;
count -= to_take;
out_idx += to_take;
}
return ret;
}
void Ecc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
const std::vector<uint8_t> &data,
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;
// The insert_bits routine assumes that the buffer will have been zeroed, so
// do that here. Note that this buffer has (width_byte_ / 4) words, each of
// which is 39 bits long. Divide this by 8, rounding up.
size_t phys_size_bytes = (39 * (width_byte_ / 4) + 7) / 8;
memset(buf, 0, phys_size_bytes);
// Start by collecting our width_byte_ input bytes into (width_byte_ / 4)
// 32-bit groupings and adding check bits. (Eventually, we'll be adding
// proper ECC check bits here but, since we're not checking yet, let's
// zero-pad for now).
//
// TODO: Add proper ECC check bits here!
struct expanded_t {
uint8_t bytes[5];
};
std::vector<expanded_t> expanded(log_width_32);
for (int i = 0; i < log_width_32; ++i) {
// Store things little-endian, so the "real bits" go in bytes 0 to 3 and
// the check bits go in byte 4. Bytes 5 to 7 are zero.
expanded_t next;
memcpy(next.bytes, &data[start_idx + 4 * i], 4);
next.bytes[4] = enc_secded_39_32(next.bytes);
expanded[i] = next;
}
// Now write to buf, one output byte at a time.
for (int i = 0; i < phy_width_bytes; ++i) {
int out_bit = i * 8;
// Acc is the accumulator we're building up for the byte that should be
// written out. out_lsb is the LSB in acc to which we're writing at the
// moment.
uint8_t acc = 0;
int out_lsb = 0;
// in_word_idx is the input word that we're reading from and in_word_lsb is
// the first bit of that word that we're reading.
int in_word_idx = out_bit / 39;
int in_word_lsb = out_bit % 39;
// bits_left is the number of bits that we need to read for this byte. It's
// usually initialised to 8, except for the last byte of the output word,
// which might just have a few bits to contribute.
int bits_left = std::min(8, phy_width_bits - out_bit);
while (bits_left) {
// in_byte_idx is the index of the byte within the (expanded_t) input
// word that we're reading from. in_byte_lsb is the bit position within
// that byte.
int in_byte_idx = in_word_lsb / 8;
int in_byte_lsb = in_word_lsb % 8;
// Most of the bytes in the expanded_t hold 8 bits of data, except the
// top one, which only holds 7 (bits 39:32). bits_at_byte is the number
// of bits that we're reading from this input byte, constrained by the
// number of bits available and the number of bits that we want.
int in_byte_width = (in_byte_idx == 4) ? 7 : 8;
int bits_at_byte = std::min(in_byte_width - in_byte_lsb, bits_left);
// Extract bits_at_byte bits of input data from the relevant input byte,
// starting at in_byte_lsb.
uint8_t in_data = expanded[in_word_idx].bytes[in_byte_idx] >> in_byte_lsb;
uint8_t in_mask = (((uint32_t)1 << bits_at_byte) - 1) & 0xff;
uint8_t masked = in_data & in_mask;
// Add the extracted bits to acc, shifting them into position and
// updating out_lsb for next time around.
acc |= masked << out_lsb;
out_lsb += bits_at_byte;
// Update input pointers to step over the byte we just consumed.
if (in_byte_idx == 4) {
++in_word_idx;
in_word_lsb = 0;
} else {
in_word_lsb += bits_at_byte;
}
// Subtract the bits we just read from the count we're expecting to read.
bits_left -= bits_at_byte;
for (int i = 0; i < width_byte_ / 4; ++i) {
uint8_t check_bits = enc_secded_39_32(&data[start_idx + 4 * i]);
for (int j = 0; j < 4; ++j) {
insert_bits(buf, 39 * i + 8 * j, data[start_idx + 4 * i + j], 8);
}
buf[i] = acc;
insert_bits(buf, 39 * i + 32, check_bits, 7);
}
}
void Ecc32MemArea::ReadBuffer(std::vector<uint8_t> &data,
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;
int in_idx = (7 * in_word + 8 * i) / 8;
int in_lsb = (7 * in_word + 8 * i) % 8;
uint8_t acc = 0;
int bits_left = 8;
int out_lsb = 0;
while (bits_left) {
uint8_t in_data = buf[in_idx] >> in_lsb;
int bits_at_idx = std::min(8 - in_lsb, bits_left);
// The mask for the bits to take from in_data.
uint8_t in_mask = ((1u << bits_at_idx) - 1) & 0xff;
acc |= (in_data & in_mask) << out_lsb;
in_lsb = 0;
out_lsb += bits_at_idx;
bits_left -= bits_at_idx;
++in_idx;
for (int i = 0; i < width_byte_ / 4; ++i) {
for (int j = 0; j < 4; ++j) {
data.push_back(extract_bits(buf, 39 * i + 8 * j, 8));
}
data.push_back(acc);
}
}
void Ecc32MemArea::ReadBufferWithIntegrity(
EccWords &data, const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const {
for (int i = 0; i < width_byte_ / 4; ++i) {
uint8_t buf32[4];
uint32_t w32 = 0;
for (int j = 0; j < 4; ++j) {
uint8_t byte = extract_bits(buf, 39 * i + 8 * j, 8);
buf32[j] = byte;
w32 |= (uint32_t)byte << 8 * j;
}
uint8_t exp_check_bits = enc_secded_39_32(buf32);
uint8_t check_bits = extract_bits(buf, 39 * i + 32, 7);
bool good = check_bits == exp_check_bits;
data.push_back(std::make_pair(good, w32));
}
}

View file

@ -24,6 +24,22 @@ class Ecc32MemArea : public MemArea {
void LoadVmem(const std::string &path) const override;
typedef std::pair<bool, uint32_t> EccWord;
typedef std::vector<EccWord> EccWords;
/** Read data with validity bits, starting at the given offset.
*
* This is equivalent to MemArea's Read method, but returns 32 bit
* words, each with a boolean saying whether the integrity bits for
* that word are valid or not.
*
* @param word_offset The offset, in words, of the first word that should be
* read.
*
* @param num_words The number of words to read.
*/
EccWords ReadWithIntegrity(uint32_t word_offset, uint32_t num_words) const;
protected:
void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
const std::vector<uint8_t> &data, size_t start_idx,
@ -32,6 +48,20 @@ class Ecc32MemArea : public MemArea {
void ReadBuffer(std::vector<uint8_t> &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const override;
/** Extract the logical words corresponding to the physical memory contents
* in \p buf, together with validity bits. Append them to \p data.
*
* @param data The target, onto which the extracted memory words should
* be appended.
*
* @param buf Source buffer (physical memory bits)
*
* @param src_word Logical address of the location being read
*/
virtual void ReadBufferWithIntegrity(EccWords &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const;
};
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_ECC32_MEM_AREA_H_

View file

@ -81,21 +81,7 @@ std::vector<uint8_t> MemArea::Read(uint32_t word_offset,
uint32_t src_word = word_offset + i;
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());
}
}
ReadToMinibuf(minibuf, phys_addr);
ReadBuffer(ret, minibuf, src_word);
}
@ -126,3 +112,13 @@ void MemArea::ReadBuffer(std::vector<uint8_t> &data,
std::copy_n(reinterpret_cast<const char *>(buf), width_byte_,
std::back_inserter(data));
}
void MemArea::ReadToMinibuf(uint8_t *minibuf, uint32_t phys_addr) const {
SVScoped scoped(scope_);
if (!simutil_get_mem(phys_addr, (svBitVecVal *)minibuf)) {
std::ostringstream oss;
oss << "Could not read memory word at physical index 0x" << std::hex
<< phys_addr << ".";
throw std::runtime_error(oss.str());
}
}

View file

@ -109,10 +109,11 @@ 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 src_word Logical address of the location being read
*/
virtual void ReadBuffer(std::vector<uint8_t> &data,
@ -129,6 +130,13 @@ class MemArea {
virtual uint32_t ToPhysAddr(uint32_t logical_addr) const {
return logical_addr;
}
/** Read the memory word at phys_addr into minibuf
*
* minibuf should be at least SV_MEM_WIDTH_BYTES in size. See the
* implementation of MemArea::Write() for the details.
*/
void ReadToMinibuf(uint8_t *minibuf, uint32_t phys_addr) const;
};
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_MEM_AREA_H_

View file

@ -81,12 +81,27 @@ static uint32_t vbits(uint32_t size) {
return width;
}
// These functions come from SV code, exposed over DPI. They are defined inside
// a module (prim_ram1p_scr) and, awkwardly, if a design doesn't happen to use
// that module then some simulators (Verilator!) will discard it, together with
// the DPI functions.
//
// We'd like to be able to use the memutil_dpi_scrambled.core whether or not we
// actually instantiated prim_ram1p_scr: we'll just spit out an error if we
// call GetScrambleKey() or GetScrambleNonce() if we didn't instantiate it. To
// make this work, we mark both symbols weak.
extern "C" {
int simutil_get_scramble_key(svBitVecVal *key);
int simutil_get_scramble_nonce(svBitVecVal *nonce);
int __attribute__((weak)) simutil_get_scramble_key(svBitVecVal *key);
int __attribute__((weak)) simutil_get_scramble_nonce(svBitVecVal *nonce);
}
std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleKey() const {
if (!simutil_get_scramble_key) {
throw std::runtime_error(
"No definition of simutil_get_scramble_key. "
"Does the design actually use prim_ram1p_scr?");
}
SVScoped scoped(scr_scope_);
svBitVecVal key_minibuf[((kPrinceWidthByte * 2) + 3) / 4];
@ -102,6 +117,12 @@ std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleKey() const {
std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleNonce() const {
assert(GetNonceWidthByte() <= kScrMaxNonceWidthByte);
if (!simutil_get_scramble_nonce) {
throw std::runtime_error(
"No definition of simutil_get_scramble_nonce. "
"Does the design actually use prim_ram1p_scr?");
}
SVScoped scoped(scr_scope_);
svBitVecVal nonce_minibuf[(kScrMaxNonceWidthByte + 3) / 4];
@ -169,20 +190,30 @@ void ScrambledEcc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
std::copy(scramble_buf.begin(), scramble_buf.end(), &buf[0]);
}
std::vector<uint8_t> ScrambledEcc32MemArea::ReadUnscrambled(
const uint8_t buf[SV_MEM_WIDTH_BYTES], uint32_t src_word) const {
std::vector<uint8_t> scrambled_data(buf, buf + GetPhysWidthByte());
return scramble_decrypt_data(
scrambled_data, GetPhysWidth(), 39, AddrIntToBytes(src_word, addr_width_),
addr_width_, GetScrambleNonce(), GetScrambleKey(), repeat_keystream_);
}
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_);
std::vector<uint8_t> unscrambled_data = ReadUnscrambled(buf, src_word);
// Strip integrity to give final result
Ecc32MemArea::ReadBuffer(data, &unscrambled_data[0], src_word);
}
void ScrambledEcc32MemArea::ReadBufferWithIntegrity(
EccWords &data, const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const {
std::vector<uint8_t> unscrambled_data = ReadUnscrambled(buf, src_word);
// Strip integrity to give final result
Ecc32MemArea::ReadBufferWithIntegrity(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_),

View file

@ -37,10 +37,17 @@ class ScrambledEcc32MemArea : public Ecc32MemArea {
const std::vector<uint8_t> &data, size_t start_idx,
uint32_t dst_word) const override;
std::vector<uint8_t> ReadUnscrambled(const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const;
void ReadBuffer(std::vector<uint8_t> &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const override;
void ReadBufferWithIntegrity(EccWords &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;

View file

@ -40,5 +40,14 @@
"-I{build_dir}/src/{scramble_model_dir}",
"-lelf"]
}
{
name: dsim_memutil_dpi_scrambled_build_opts
build_opts: ["-c-opts -I{build_dir}/src/{memutil_dpi_src_dir}/cpp",
"-c-opts -I{build_dir}/src/{memutil_dpi_scrambled_src_dir}/cpp",
"-c-opts -I{build_dir}/src/{prince_ref_src_dir}",
"-c-opts -I{build_dir}/src/{scramble_model_dir}",
"-ld-opts -lelf"]
}
]
}

View file

@ -1,158 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------//
// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND:
//
// hw/ip/prim/util/generate_prim_mubi_pkg.py hw/ip/prim/data/prim_mubi_pkg.sv.tpl >
// hw/ip/prim/rtl/prim_mubi_pkg.sv
//
// This package defines common multibit signal types, active high and active low values and
// the corresponding functions to test whether the values are set or not.
package prim_mubi_pkg;
% for n in range(1, n_max_nibbles + 1):
<%
nbits = n * 4
hi_val = ''
lo_val = ''
for k in range(1,n+1):
hi_val = ('5' if (k % 2) else 'A') + hi_val
lo_val = ('A' if (k % 2) else '5') + lo_val
%>\
//////////////////////////////////////////////
// ${nbits} Bit Multibit Type and Functions //
//////////////////////////////////////////////
parameter int MuBi${nbits}Width = ${nbits};
typedef enum logic [MuBi${nbits}Width-1:0] {
MuBi${nbits}Hi = ${nbits}'h${hi_val}, // enabled
MuBi${nbits}Lo = ${nbits}'h${lo_val} // disabled
} mubi${nbits}_e;
// make a typedef such that this can be used as an intersignal type as well
typedef mubi${nbits}_e mubi${nbits}_t;
// Return the multibit value to signal "enabled".
function automatic mubi${nbits}_e mubi${nbits}_hi_value();
return MuBi${nbits}Hi;
endfunction : mubi${nbits}_hi_value
// Return the multibit value to signal "disabled".
function automatic mubi${nbits}_e mubi${nbits}_lo_value();
return MuBi${nbits}Lo;
endfunction : mubi${nbits}_lo_value
// Test whether the multibit value signals an "enabled" condition.
// The strict version of this function requires
// the multibit value to equal Hi.
function automatic logic mubi${nbits}_tst_hi_strict(mubi${nbits}_e val);
return MuBi${nbits}Hi == val;
endfunction : mubi${nbits}_tst_hi_strict
// Test whether the multibit value signals a "disabled" condition.
// The strict version of this function requires
// the multibit value to equal Lo.
function automatic logic mubi${nbits}_tst_lo_strict(mubi${nbits}_e val);
return MuBi${nbits}Lo == val;
endfunction : mubi${nbits}_tst_lo_strict
// Test whether the multibit value signals an "enabled" condition.
// The loose version of this function interprets all
// values other than Lo as "enabled".
function automatic logic mubi${nbits}_tst_hi_loose(mubi${nbits}_e val);
return MuBi${nbits}Lo != val;
endfunction : mubi${nbits}_tst_hi_loose
// Test whether the multibit value signals a "disabled" condition.
// The loose version of this function interprets all
// values other than Hi as "disabled".
function automatic logic mubi${nbits}_tst_lo_loose(mubi${nbits}_e val);
return MuBi${nbits}Hi != val;
endfunction : mubi${nbits}_tst_lo_loose
// Performs a logical OR operation between two multibit values.
// This treats "act" as logical 1, and all other values are
// treated as 0. Truth table:
//
// A | B | OUT
//------+------+-----
// !act | !act | !act
// act | !act | act
// !act | act | act
// act | act | act
//
function automatic mubi${nbits}_e mubi${nbits}_or(mubi${nbits}_e a, mubi${nbits}_e b, mubi${nbits}_e act);
logic [MuBi${nbits}Width-1:0] a_in, b_in, act_in, out;
a_in = a;
b_in = b;
act_in = act;
for (int k = 0; k < MuBi${nbits}Width; k++) begin
if (act_in[k]) begin
out[k] = a_in[k] || b_in[k];
end else begin
out[k] = a_in[k] && b_in[k];
end
end
return mubi${nbits}_e'(out);
endfunction : mubi${nbits}_or
// Performs a logical AND operation between two multibit values.
// This treats "act" as logical 1, and all other values are
// treated as 0. Truth table:
//
// A | B | OUT
//------+------+-----
// !act | !act | !act
// act | !act | !act
// !act | act | !act
// act | act | act
//
function automatic mubi${nbits}_e mubi${nbits}_and(mubi${nbits}_e a, mubi${nbits}_e b, mubi${nbits}_e act);
logic [MuBi${nbits}Width-1:0] a_in, b_in, act_in, out;
a_in = a;
b_in = b;
act_in = act;
for (int k = 0; k < MuBi${nbits}Width; k++) begin
if (act_in[k]) begin
out[k] = a_in[k] && b_in[k];
end else begin
out[k] = a_in[k] || b_in[k];
end
end
return mubi${nbits}_e'(out);
endfunction : mubi${nbits}_and
// Performs a logical OR operation between two multibit values.
// This treats "Hi" as logical 1, and all other values are
// treated as 0.
function automatic mubi${nbits}_e mubi${nbits}_or_hi(mubi${nbits}_e a, mubi${nbits}_e b);
return mubi${nbits}_or(a, b, MuBi${nbits}Hi);
endfunction : mubi${nbits}_or_hi
// Performs a logical AND operation between two multibit values.
// This treats "Hi" as logical 1, and all other values are
// treated as 0.
function automatic mubi${nbits}_e mubi${nbits}_and_hi(mubi${nbits}_e a, mubi${nbits}_e b);
return mubi${nbits}_and(a, b, MuBi${nbits}Hi);
endfunction : mubi${nbits}_and_hi
// Performs a logical OR operation between two multibit values.
// This treats "Lo" as logical 1, and all other values are
// treated as 0.
function automatic mubi${nbits}_e mubi${nbits}_or_lo(mubi${nbits}_e a, mubi${nbits}_e b);
return mubi${nbits}_or(a, b, MuBi${nbits}Lo);
endfunction : mubi${nbits}_or_lo
// Performs a logical AND operation between two multibit values.
// Tlos treats "Lo" as logical 1, and all other values are
// treated as 0.
function automatic mubi${nbits}_e mubi${nbits}_and_lo(mubi${nbits}_e a, mubi${nbits}_e b);
return mubi${nbits}_and(a, b, MuBi${nbits}Lo);
endfunction : mubi${nbits}_and_lo
% endfor
endpackage : prim_mubi_pkg

View file

@ -0,0 +1,76 @@
---
title: "Primitive Component: XoShiRo256++"
---
# Overviewtitle
`prim_xoshiro256pp` is a PRNG with 256 bit state.
For more information refer to [this page](https://arxiv.org/pdf/1805.01407.pdf).
## Parameters
Name | type | Description
-------------|--------|----------------------------------------------------------
OutputDw | int | Width of the PRNG output. Must be a multiple of 64.
DefaultSeed | logic | Initial 256-bit state of the PRNG, must be nonzero.
## Signal Interfaces
Name | In/Out | Description
---------------------|--------|---------------------------------
seed_en_i | input | External seed input enable
seed_i[256] | input | External seed input
xoshiro_en_i | input | PRNG enable
entropy_i[256] | input | Entropy input
data_o[OutputDw] | output | PRNG output
all_zero_o | output | Impossible all-zero state
# Theory of Operations
```
/----------------\
seed_en_i | |
------------>| xoshiro256++ |
seed_i | | data_o
=====/======>| |=====/=======>
[256] | | [OutputDw]
xoshiro_en_i | |
------------>| OutputDw |
entropy_i | DefaultSeed | all_zero_o
=====/======>| |------------->
[256] | |
| |
\----------------/
```
Xoshiro256++ PRNG consists of:
* 256b state
* A single-cycle state-update function.
* A state output function.
The basic xoshiro256++ PRNG has a 64 bit output.
This implementation enables the output size to be any multiple of 64 bits.
The output size controlled using the `OutputDW` parameter.
The xoshiro256++ module has an enable input and an additional entropy input that is
XOR'ed into the PRNG state (connect to zero if this feature is unused).
As the PRNG may jump into the all-zero state (e.g. due to an active attack), the PRNG
state-update function contains a lockup protection which re-seeds the state with
`DefaultSeed` and raises the alert signal `all_zero_o`, once this condition is detected.
When the seed enable signal `seed_en_i` is raised, the internal state of xoshiro256++ is updated
with the value provided at the 256b input 'seed_i'.
The state is internaly updated in every clock cycle whenever the enable signal `xoshiro_en_i` is raised.
The timing diagram below visualizes this process.
{{< wavejson >}}
{
signal: [
{name: 'clk', wave: 'p......|....'},
{name: 'xoshiro_en_i', wave: '0...1..|..0.'},
{name: 'seed_en_i', wave: '010....|....'},
{name:'seed_i', wave: '3..x...|....', data: 'Seed' },
{name: 'state', wave: 'x.3..45|678.', data: 'Seed'}
]
}
{{< /wavejson >}}

View file

@ -34,6 +34,24 @@
"prim_sync_fatal_alert"]
}
{
name: prim_alert_init_trigger_test
desc: '''Verify init_trigger input from prim_alert_receiver.
Based on the prim_alert_test, this test adds a parallel sequence to randomly drive
init_trigger_i in prim_alert_receiver.
Check if alert sender/receiver pairs can resume normal handshake after init_trigger_i
is set.
For fatal alert, check if fatal alerts keep firing until reset is issued.
'''
milestone: V1
tests: ["prim_async_alert",
"prim_async_fatal_alert",
"prim_sync_alert",
"prim_sync_fatal_alert"]
}
{
name: prim_alert_ping_request_test
desc: '''Verify ping request from prim_alert_sender.

View file

@ -24,7 +24,7 @@
import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"]
// Default iterations for all tests - each test entry can override this.
reseed: 1
reseed: 20
build_modes: [
{

View file

@ -39,6 +39,9 @@ module prim_alert_tb;
// The main concern here is the minimal wait cycles between each handshake.
localparam int MinHandshakeWait = 2 + WaitCycle;
// Clock cycles for alert init handshake to finish.
localparam int WaitAlertInitDone = 30;
typedef enum bit [3:0]{
AlertSet,
AlertAckSet,
@ -71,7 +74,7 @@ module prim_alert_tb;
logic ping_req, ping_ok, integ_fail, alert_o;
prim_alert_pkg::alert_rx_t alert_rx;
prim_alert_pkg::alert_tx_t alert_tx;
prim_mubi_pkg::mubi4_t init_trig = prim_mubi_pkg::MuBi4False;
prim_alert_sender #(
.AsyncOn(IsAsync),
.IsFatal(IsFatal)
@ -91,8 +94,7 @@ module prim_alert_tb;
) i_alert_receiver (
.clk_i(clk),
.rst_ni(rst_n),
// TODO: randomly trigger this
.init_trig_i(lc_ctrl_pkg::Off),
.init_trig_i(init_trig),
.ping_req_i(ping_req),
.ping_ok_o(ping_ok),
.integ_fail_o(integ_fail),
@ -203,35 +205,54 @@ module prim_alert_tb;
main_clk.apply_reset();
// Wait for initialization sequence to end
// This should take no more than 20 cycles
// if the sender / receiver clocks are on
// the same clock domain.
main_clk.wait_clks(20);
main_clk.wait_clks(WaitAlertInitDone);
// Sequence 1). Alert request sequence.
main_clk.wait_clks($urandom_range(0, 10));
alert_req = 1;
fork
begin
for (int num_trans = 1; num_trans <= 10; num_trans++) begin
int rand_wait_alert_req = $urandom_range(MinHandshakeWait, 10);
int rand_wait_init_trig = $urandom_range(0, 30);
fork
begin
main_clk.wait_clks(rand_wait_alert_req);
alert_req = 1;
fork
begin
main_clk.wait_clks(1);
check_alert_handshake(.exp_ping_value(0));
end
// While waiting to check alert handshake, reset alert_req as soon as alert is acked to
// avoid triggering multiple alert requests.
begin
wait (alert_ack == 1);
alert_req = 0;
end
join
end
begin
main_clk.wait_clks(rand_wait_init_trig);
init_trig = prim_mubi_pkg::MuBi4True;
end
join_any
disable fork;
if (init_trig == prim_mubi_pkg::MuBi4True) begin
alert_req = 0;
main_clk.wait_clks($urandom_range(0, 10));
init_trig = prim_mubi_pkg::MuBi4False;
main_clk.wait_clks(WaitAlertInitDone);
end
// For fatal alert, ensure alert keeps firing until reset.
// This check is valid if the alert is fatal, and alert is requested before init request.
if (IsFatal && (rand_wait_alert_req + 1) <= rand_wait_init_trig) begin
main_clk.wait_clks($urandom_range(10, 100));
wait (alert_tx.alert_p == 0);
wait (alert_tx.alert_p == 1);
main_clk.wait_clks(1);
check_alert_handshake(.exp_ping_value(0));
main_clk.apply_reset();
main_clk.wait_clks(WaitAlertInitDone);
end
begin
wait (alert_ack == 1);
alert_req = 0;
end
join
// If alert is fatal, check alert will continuously fire until reset.
if (IsFatal) begin
main_clk.wait_clks($urandom_range(10, 1000));
wait (alert_tx.alert_p == 0);
wait (alert_tx.alert_p == 1);
main_clk.wait_clks(1);
check_alert_handshake(.exp_ping_value(0));
main_clk.apply_reset();
$display("Alert request sequence %0d/10 finished!", num_trans);
end
$display("Alert request sequence finished!");
// Sequence 2). Alert test sequence.
main_clk.wait_clks($urandom_range(MinHandshakeWait, 10));

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:alert
files:
- vip/prim_alert_rxtx_async_assert_fpv.sv
- tb/prim_alert_rxtx_async_fatal_fpv.sv
- tb/prim_alert_rxtx_async_fatal_tb.sv
- tb/prim_alert_rxtx_async_fatal_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_alert_rxtx_async_fatal_fpv
- prim_alert_rxtx_async_fatal_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:alert
files:
- vip/prim_alert_rxtx_async_assert_fpv.sv
- tb/prim_alert_rxtx_async_fpv.sv
- tb/prim_alert_rxtx_async_tb.sv
- tb/prim_alert_rxtx_async_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_alert_rxtx_async_fpv
- prim_alert_rxtx_async_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:alert
files:
- vip/prim_alert_rxtx_assert_fpv.sv
- tb/prim_alert_rxtx_fatal_fpv.sv
- tb/prim_alert_rxtx_fatal_tb.sv
- tb/prim_alert_rxtx_fatal_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_alert_rxtx_fatal_fpv
- prim_alert_rxtx_fatal_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:alert
files:
- vip/prim_alert_rxtx_assert_fpv.sv
- tb/prim_alert_rxtx_fpv.sv
- tb/prim_alert_rxtx_tb.sv
- tb/prim_alert_rxtx_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_alert_rxtx_fpv
- prim_alert_rxtx_tb
formal:
<<: *default_target

View file

@ -9,7 +9,7 @@ filesets:
depend:
- lowrisc:prim:all
files:
- tb/prim_arbiter_fixed_fpv.sv
- tb/prim_arbiter_fixed_tb.sv
file_type: systemVerilogSource
targets:
@ -19,7 +19,7 @@ targets:
default_tool: icarus
filesets:
- files_formal
toplevel: prim_arbiter_fixed_fpv
toplevel: prim_arbiter_fixed_tb
formal:
<<: *default_target

View file

@ -9,7 +9,7 @@ filesets:
depend:
- lowrisc:prim:all
files:
- tb/prim_arbiter_ppc_fpv.sv
- tb/prim_arbiter_ppc_tb.sv
file_type: systemVerilogSource
targets:
@ -19,7 +19,7 @@ targets:
default_tool: icarus
filesets:
- files_formal
toplevel: prim_arbiter_ppc_fpv
toplevel: prim_arbiter_ppc_tb
formal:
<<: *default_target

View file

@ -9,7 +9,7 @@ filesets:
depend:
- lowrisc:prim:all
files:
- tb/prim_arbiter_tree_fpv.sv
- tb/prim_arbiter_tree_tb.sv
file_type: systemVerilogSource
targets:
@ -19,7 +19,7 @@ targets:
default_tool: icarus
filesets:
- files_formal
toplevel: prim_arbiter_tree_fpv
toplevel: prim_arbiter_tree_tb
formal:
<<: *default_target

View file

@ -0,0 +1,29 @@
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:fpv:prim_count_fpv:0.1"
description: "prim_count FPV target"
filesets:
files_formal:
depend:
- lowrisc:prim:all
- lowrisc:prim:count
files:
- tb/prim_count_tb.sv
file_type: systemVerilogSource
targets:
default: &default_target
# Note this setting is just used to generate a file list for jg.
default_tool: icarus
filesets:
- files_formal
toplevel: prim_count_tb
formal:
<<: *default_target
lint:
<<: *default_target

View file

@ -12,7 +12,7 @@ filesets:
files:
- vip/prim_esc_rxtx_assert_fpv.sv
- tb/prim_esc_rxtx_bind_fpv.sv
- tb/prim_esc_rxtx_fpv.sv
- tb/prim_esc_rxtx_tb.sv
file_type: systemVerilogSource
targets:
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_esc_rxtx_fpv
- prim_esc_rxtx_tb
formal:
<<: *default_target

View file

@ -10,7 +10,7 @@ filesets:
- lowrisc:prim:all
- lowrisc:prim:ram_2p_async_adv
files:
- tb/prim_fifo_async_sram_adapter_fpv.sv
- tb/prim_fifo_async_sram_adapter_tb.sv
file_type: systemVerilogSource
targets:
@ -18,7 +18,7 @@ targets:
default_tool: icarus
filesets:
- files_formal
toplevel: prim_fifo_async_sram_adapter_fpv
toplevel: prim_fifo_async_sram_adapter_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
files:
- vip/prim_fifo_sync_assert_fpv.sv
- tb/prim_fifo_sync_bind_fpv.sv
- tb/prim_fifo_sync_fpv.sv
- tb/prim_fifo_sync_tb.sv
file_type: systemVerilogSource
targets:
@ -21,7 +21,7 @@ targets:
default_tool: icarus
filesets:
- files_formal
toplevel: prim_fifo_sync_fpv
toplevel: prim_fifo_sync_tb
formal:
<<: *default_target

View file

@ -9,7 +9,7 @@ filesets:
depend:
- lowrisc:prim:all
files:
- tb/prim_keccak_fpv.sv
- tb/prim_keccak_tb.sv
file_type: systemVerilogSource
targets:
@ -20,7 +20,7 @@ targets:
filesets:
- files_fpv
toplevel:
- prim_keccak_fpv
- prim_keccak_tb
lint:
<<: *default_target

View file

@ -9,7 +9,7 @@ filesets:
depend:
- lowrisc:prim:lfsr
files:
- tb/prim_lfsr_fpv.sv
- tb/prim_lfsr_tb.sv
file_type: systemVerilogSource
targets:
@ -20,7 +20,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_lfsr_fpv
- prim_lfsr_tb
formal:
<<: *default_target

View file

@ -10,7 +10,7 @@ filesets:
depend:
- lowrisc:prim:all
files:
- tb/prim_packer_fpv.sv
- tb/prim_packer_tb.sv
file_type: systemVerilogSource
targets:
@ -20,7 +20,7 @@ targets:
default_tool: icarus
filesets:
- files_formal
toplevel: prim_packer_fpv
toplevel: prim_packer_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:secded
files:
- vip/prim_secded_22_16_assert_fpv.sv
- tb/prim_secded_22_16_fpv.sv
- tb/prim_secded_22_16_tb.sv
- tb/prim_secded_22_16_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_secded_22_16_fpv
- prim_secded_22_16_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:secded
files:
- vip/prim_secded_28_22_assert_fpv.sv
- tb/prim_secded_28_22_fpv.sv
- tb/prim_secded_28_22_tb.sv
- tb/prim_secded_28_22_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_secded_28_22_fpv
- prim_secded_28_22_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:secded
files:
- vip/prim_secded_39_32_assert_fpv.sv
- tb/prim_secded_39_32_fpv.sv
- tb/prim_secded_39_32_tb.sv
- tb/prim_secded_39_32_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_secded_39_32_fpv
- prim_secded_39_32_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:secded
files:
- vip/prim_secded_64_57_assert_fpv.sv
- tb/prim_secded_64_57_fpv.sv
- tb/prim_secded_64_57_tb.sv
- tb/prim_secded_64_57_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_secded_64_57_fpv
- prim_secded_64_57_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:secded
files:
- vip/prim_secded_72_64_assert_fpv.sv
- tb/prim_secded_72_64_fpv.sv
- tb/prim_secded_72_64_tb.sv
- tb/prim_secded_72_64_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_secded_72_64_fpv
- prim_secded_72_64_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:secded
files:
- vip/prim_secded_hamming_22_16_assert_fpv.sv
- tb/prim_secded_hamming_22_16_fpv.sv
- tb/prim_secded_hamming_22_16_tb.sv
- tb/prim_secded_hamming_22_16_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_secded_hamming_22_16_fpv
- prim_secded_hamming_22_16_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:secded
files:
- vip/prim_secded_hamming_39_32_assert_fpv.sv
- tb/prim_secded_hamming_39_32_fpv.sv
- tb/prim_secded_hamming_39_32_tb.sv
- tb/prim_secded_hamming_39_32_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_secded_hamming_39_32_fpv
- prim_secded_hamming_39_32_tb
formal:
<<: *default_target

View file

@ -11,7 +11,7 @@ filesets:
- lowrisc:prim:secded
files:
- vip/prim_secded_hamming_72_64_assert_fpv.sv
- tb/prim_secded_hamming_72_64_fpv.sv
- tb/prim_secded_hamming_72_64_tb.sv
- tb/prim_secded_hamming_72_64_bind_fpv.sv
file_type: systemVerilogSource
@ -23,7 +23,7 @@ targets:
filesets:
- files_formal
toplevel:
- prim_secded_hamming_72_64_fpv
- prim_secded_hamming_72_64_tb
formal:
<<: *default_target

View file

@ -0,0 +1,33 @@
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:fpv:prim_secded_hamming_76_68_fpv:0.1"
description: "SECDED FPV target"
filesets:
files_formal:
depend:
- lowrisc:prim:all
- lowrisc:prim:secded
files:
- vip/prim_secded_hamming_76_68_assert_fpv.sv
- tb/prim_secded_hamming_76_68_tb.sv
- tb/prim_secded_hamming_76_68_bind_fpv.sv
file_type: systemVerilogSource
targets:
default: &default_target
# note, this setting is just used
# to generate a file list for jg
default_tool: icarus
filesets:
- files_formal
toplevel:
- prim_secded_hamming_76_68_tb
formal:
<<: *default_target
lint:
<<: *default_target

View file

@ -5,31 +5,32 @@
// Testbench module for alert sender/receiver pair. Intended to use with
// a formal tool.
module prim_alert_rxtx_async_fatal_fpv
module prim_alert_rxtx_async_fatal_tb
import prim_alert_pkg::*;
import prim_mubi_pkg::mubi4_t;
(
input clk_i,
input rst_ni,
input clk_i,
input rst_ni,
// for sigint error and skew injection only
input ping_err_pi,
input ping_err_ni,
input [1:0] ping_skew_i,
input ack_err_pi,
input ack_err_ni,
input [1:0] ack_skew_i,
input alert_err_pi,
input alert_err_ni,
input [1:0] alert_skew_i,
input ping_err_pi,
input ping_err_ni,
input [1:0] ping_skew_i,
input ack_err_pi,
input ack_err_ni,
input [1:0] ack_skew_i,
input alert_err_pi,
input alert_err_ni,
input [1:0] alert_skew_i,
// normal I/Os
input alert_test_i,
input alert_req_i,
input lc_ctrl_pkg::lc_tx_t init_trig_i,
input ping_req_i,
output logic alert_ack_o,
output logic alert_state_o,
output logic ping_ok_o,
output logic integ_fail_o,
output logic alert_o
input alert_test_i,
input alert_req_i,
input mubi4_t init_trig_i,
input ping_req_i,
output logic alert_ack_o,
output logic alert_state_o,
output logic ping_ok_o,
output logic integ_fail_o,
output logic alert_o
);
// asynchronous case
@ -115,4 +116,4 @@ module prim_alert_rxtx_async_fatal_fpv
end
end
endmodule : prim_alert_rxtx_async_fatal_fpv
endmodule : prim_alert_rxtx_async_fatal_tb

View file

@ -5,8 +5,9 @@
// Testbench module for alert sender/receiver pair. Intended to use with
// a formal tool.
module prim_alert_rxtx_async_fpv
module prim_alert_rxtx_async_tb
import prim_alert_pkg::*;
import prim_mubi_pkg::mubi4_t;
(
input clk_i,
input rst_ni,
@ -23,7 +24,7 @@ module prim_alert_rxtx_async_fpv
// normal I/Os
input alert_test_i,
input alert_req_i,
input lc_ctrl_pkg::lc_tx_t init_trig_i,
input mubi4_t init_trig_i,
input ping_req_i,
output logic alert_ack_o,
output logic alert_state_o,
@ -116,4 +117,4 @@ module prim_alert_rxtx_async_fpv
end
end
endmodule : prim_alert_rxtx_async_fpv
endmodule : prim_alert_rxtx_async_tb

View file

@ -5,8 +5,9 @@
// Testbench module for alert sender/receiver pair. Intended to use with
// a formal tool.
module prim_alert_rxtx_fatal_fpv
module prim_alert_rxtx_fatal_tb
import prim_alert_pkg::*;
import prim_mubi_pkg::mubi4_t;
(
input clk_i,
input rst_ni,
@ -20,7 +21,7 @@ module prim_alert_rxtx_fatal_fpv
// normal I/Os
input alert_test_i,
input alert_req_i,
input lc_ctrl_pkg::lc_tx_t init_trig_i,
input mubi4_t init_trig_i,
input ping_req_i,
output logic alert_ack_o,
output logic alert_state_o,
@ -72,4 +73,4 @@ module prim_alert_rxtx_fatal_fpv
.alert_tx_i ( alert_tx_in )
);
endmodule : prim_alert_rxtx_fatal_fpv
endmodule : prim_alert_rxtx_fatal_tb

View file

@ -5,8 +5,9 @@
// Testbench module for alert sender/receiver pair. Intended to use with
// a formal tool.
module prim_alert_rxtx_fpv
module prim_alert_rxtx_tb
import prim_alert_pkg::*;
import prim_mubi_pkg::mubi4_t;
(
input clk_i,
input rst_ni,
@ -20,7 +21,7 @@ module prim_alert_rxtx_fpv
// normal I/Os
input alert_test_i,
input alert_req_i,
input lc_ctrl_pkg::lc_tx_t init_trig_i,
input mubi4_t init_trig_i,
input ping_req_i,
output logic alert_ack_o,
output logic alert_state_o,
@ -72,4 +73,4 @@ module prim_alert_rxtx_fpv
.alert_tx_i ( alert_tx_in )
);
endmodule : prim_alert_rxtx_fpv
endmodule : prim_alert_rxtx_tb

View file

@ -5,7 +5,7 @@
// Testbench module for prim_arbiter_fixed.
// Intended to be used with a formal tool.
module prim_arbiter_fixed_fpv #(
module prim_arbiter_fixed_tb #(
parameter int N = 8,
parameter int DW = 32,
parameter bit EnDataPort = 1,
@ -40,4 +40,4 @@ module prim_arbiter_fixed_fpv #(
);
endmodule : prim_arbiter_fixed_fpv
endmodule : prim_arbiter_fixed_tb

View file

@ -5,7 +5,7 @@
// Testbench module for prim_arbiter_ppc.
// Intended to be used with a formal tool.
module prim_arbiter_ppc_fpv #(
module prim_arbiter_ppc_tb #(
parameter int unsigned N = 8,
parameter int unsigned DW = 32,
parameter bit EnDataPort = 1,
@ -15,12 +15,10 @@ module prim_arbiter_ppc_fpv #(
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,
output logic [IdxW-1:0] idx_o,
output logic valid_o,
output logic [DW-1:0] data_o,
input ready_i
@ -45,4 +43,4 @@ module prim_arbiter_ppc_fpv #(
);
endmodule : prim_arbiter_ppc_fpv
endmodule : prim_arbiter_ppc_tb

View file

@ -5,7 +5,7 @@
// Testbench module for prim_arbiter_tree.
// Intended to be used with a formal tool.
module prim_arbiter_tree_fpv #(
module prim_arbiter_tree_tb #(
parameter int N = 8,
parameter int DW = 32,
parameter bit EnDataPort = 1,
@ -15,12 +15,10 @@ module prim_arbiter_tree_fpv #(
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,
output logic [IdxW-1:0] idx_o,
output logic valid_o,
output logic [DW-1:0] data_o,
input ready_i
@ -45,4 +43,4 @@ module prim_arbiter_tree_fpv #(
);
endmodule : prim_arbiter_tree_fpv
endmodule : prim_arbiter_tree_tb

View file

@ -0,0 +1,41 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_count.
// Intended to be used with a formal tool.
module prim_count_tb
import prim_count_pkg::*; #(
parameter int Width = 2,
parameter bit OutSelDnCnt = 1, // 0 selects up count
parameter prim_count_style_e CntStyle = CrossCnt
) (
input clk_i,
input rst_ni,
input clr_i,
input set_i,
input [Width-1:0] set_cnt_i,
input en_i,
input [Width-1:0] step_i,
output logic[Width-1:0] cnt_o,
output logic err_o
);
prim_count #(
.Width(Width),
.OutSelDnCnt(OutSelDnCnt),
.CntStyle(CntStyle)
) u_counter (
.clk_i,
.rst_ni,
.clr_i,
.set_i,
.set_cnt_i,
.en_i,
.step_i,
.cnt_o,
.err_o
);
endmodule : prim_count_tb

View file

@ -5,7 +5,7 @@
// Testbench module for escalation sender/receiver pair. Intended to use with
// a formal tool.
module prim_esc_rxtx_fpv
module prim_esc_rxtx_tb
import prim_esc_pkg::*;
(
input clk_i,
@ -54,4 +54,4 @@ module prim_esc_rxtx_fpv
.esc_tx_i ( esc_tx_in )
);
endmodule : prim_esc_rxtx_fpv
endmodule : prim_esc_rxtx_tb

View file

@ -4,7 +4,7 @@
//
// Testbench module for prim_fifo_sram_async
module prim_fifo_async_sram_adapter_fpv #(
module prim_fifo_async_sram_adapter_tb #(
parameter int unsigned Width = 32,
parameter int unsigned Depth = 16,
@ -201,4 +201,4 @@ end // !FpgaSram
clk_rd_i, !rst_ni)
`endif // FPV_ON
endmodule : prim_fifo_async_sram_adapter_fpv
endmodule : prim_fifo_async_sram_adapter_tb

View file

@ -14,7 +14,7 @@
// Data/depth value checks are enabled up to depth 8 in order to constrain the
// runtime.
module prim_fifo_sync_fpv #(
module prim_fifo_sync_tb #(
// number of DUTs instantiated in this FPV testbench
parameter int unsigned NumDuts = 11,
// fifo params
@ -233,4 +233,4 @@ module prim_fifo_sync_fpv #(
.depth_o(depth_o[10][4:0])
);
endmodule : prim_fifo_sync_fpv
endmodule : prim_fifo_sync_tb

View file

@ -4,7 +4,7 @@
//
// Testbench module for prim_keccak. Intended to be used with a formal tool.
module prim_keccak_fpv #(
module prim_keccak_tb #(
parameter int Width = 1600
) (
input clk_i,

View file

@ -4,7 +4,7 @@
//
// Testbench module for prim_lfsr. Intended to be used with a formal tool.
module prim_lfsr_fpv #(
module prim_lfsr_tb #(
// LFSR entropy and output bitwidths (set to 1 here as they are unused)
parameter int unsigned EntropyDw = 1,
parameter int unsigned StateOutDw = 1,
@ -120,4 +120,4 @@ module prim_lfsr_fpv #(
);
end
endmodule : prim_lfsr_fpv
endmodule : prim_lfsr_tb

View file

@ -5,7 +5,7 @@
// Testbench module for prim_packer. Intended to be used with a formal tool.
// To reduce the runtime for prim_packer, we limited the width parameter.
module prim_packer_fpv #(
module prim_packer_tb #(
parameter int unsigned MaxInW = 64,
parameter int unsigned MaxOutW = 64
) (
@ -60,4 +60,4 @@ module prim_packer_fpv #(
.flush_done_o
);
endmodule : prim_packer_fpv
endmodule : prim_packer_tb

View file

@ -6,7 +6,7 @@
module prim_secded_22_16_bind_fpv;
bind prim_secded_22_16_fpv
bind prim_secded_22_16_tb
prim_secded_22_16_assert_fpv prim_secded_22_16_assert_fpv (
.clk_i,
.rst_ni,

View file

@ -4,7 +4,7 @@
//
// SECDED FPV testbench generated by util/design/secded_gen.py
module prim_secded_22_16_fpv (
module prim_secded_22_16_tb (
input clk_i,
input rst_ni,
input [15:0] data_i,
@ -28,4 +28,4 @@ module prim_secded_22_16_fpv (
.err_o
);
endmodule : prim_secded_22_16_fpv
endmodule : prim_secded_22_16_tb

View file

@ -6,7 +6,7 @@
module prim_secded_28_22_bind_fpv;
bind prim_secded_28_22_fpv
bind prim_secded_28_22_tb
prim_secded_28_22_assert_fpv prim_secded_28_22_assert_fpv (
.clk_i,
.rst_ni,

View file

@ -4,7 +4,7 @@
//
// SECDED FPV testbench generated by util/design/secded_gen.py
module prim_secded_28_22_fpv (
module prim_secded_28_22_tb (
input clk_i,
input rst_ni,
input [21:0] data_i,
@ -28,4 +28,4 @@ module prim_secded_28_22_fpv (
.err_o
);
endmodule : prim_secded_28_22_fpv
endmodule : prim_secded_28_22_tb

View file

@ -6,7 +6,7 @@
module prim_secded_39_32_bind_fpv;
bind prim_secded_39_32_fpv
bind prim_secded_39_32_tb
prim_secded_39_32_assert_fpv prim_secded_39_32_assert_fpv (
.clk_i,
.rst_ni,

View file

@ -4,7 +4,7 @@
//
// SECDED FPV testbench generated by util/design/secded_gen.py
module prim_secded_39_32_fpv (
module prim_secded_39_32_tb (
input clk_i,
input rst_ni,
input [31:0] data_i,
@ -28,4 +28,4 @@ module prim_secded_39_32_fpv (
.err_o
);
endmodule : prim_secded_39_32_fpv
endmodule : prim_secded_39_32_tb

View file

@ -6,7 +6,7 @@
module prim_secded_64_57_bind_fpv;
bind prim_secded_64_57_fpv
bind prim_secded_64_57_tb
prim_secded_64_57_assert_fpv prim_secded_64_57_assert_fpv (
.clk_i,
.rst_ni,

View file

@ -4,7 +4,7 @@
//
// SECDED FPV testbench generated by util/design/secded_gen.py
module prim_secded_64_57_fpv (
module prim_secded_64_57_tb (
input clk_i,
input rst_ni,
input [56:0] data_i,
@ -28,4 +28,4 @@ module prim_secded_64_57_fpv (
.err_o
);
endmodule : prim_secded_64_57_fpv
endmodule : prim_secded_64_57_tb

View file

@ -6,7 +6,7 @@
module prim_secded_72_64_bind_fpv;
bind prim_secded_72_64_fpv
bind prim_secded_72_64_tb
prim_secded_72_64_assert_fpv prim_secded_72_64_assert_fpv (
.clk_i,
.rst_ni,

View file

@ -4,7 +4,7 @@
//
// SECDED FPV testbench generated by util/design/secded_gen.py
module prim_secded_72_64_fpv (
module prim_secded_72_64_tb (
input clk_i,
input rst_ni,
input [63:0] data_i,
@ -28,4 +28,4 @@ module prim_secded_72_64_fpv (
.err_o
);
endmodule : prim_secded_72_64_fpv
endmodule : prim_secded_72_64_tb

View file

@ -6,7 +6,7 @@
module prim_secded_hamming_22_16_bind_fpv;
bind prim_secded_hamming_22_16_fpv
bind prim_secded_hamming_22_16_tb
prim_secded_hamming_22_16_assert_fpv prim_secded_hamming_22_16_assert_fpv (
.clk_i,
.rst_ni,

View file

@ -4,7 +4,7 @@
//
// SECDED FPV testbench generated by util/design/secded_gen.py
module prim_secded_hamming_22_16_fpv (
module prim_secded_hamming_22_16_tb (
input clk_i,
input rst_ni,
input [15:0] data_i,
@ -28,4 +28,4 @@ module prim_secded_hamming_22_16_fpv (
.err_o
);
endmodule : prim_secded_hamming_22_16_fpv
endmodule : prim_secded_hamming_22_16_tb

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