blob: 5a189c62bb600e179dfd9ff96e34f178590678a5 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// KMAC/SHA3
`include "prim_assert.sv"
module kmac
import kmac_pkg::*;
import kmac_reg_pkg::*;
#(
// EnMasking: Enable masking security hardening inside keccak_round
// If it is enabled, the result digest will be two set of 1600bit.
parameter bit EnMasking = 1,
// In case EnMasking == 0, this defines whether SW can provide a masked key or whether Share 1 of
// the SW key is simply ignored. In case EnMasking == 1, this parameter has no meaning, always
// both shares of the key provided by SW are used.
// This is useful to allow both for area-optimized unmasked designs as well as unmasked designs
// having a SW interface fully compatible with the masked design.
parameter bit SwKeyMasked = 0,
// Command delay, useful for SCA measurements only. A value of e.g. 40 allows the processor to go
// into sleep before KMAC starts operation. If a value > 0 is chosen, the processor can provide
// two commands subsquently and then go to sleep. The second command is buffered internally and
// will be presented to the hardware SecCmdDelay number of cycles after the first one.
parameter int SecCmdDelay = 0,
// Accept SW message when idle and before receiving a START command. Useful for SCA only.
parameter bit SecIdleAcceptSwMsg = 1'b0,
parameter lfsr_perm_t RndCnstLfsrPerm = RndCnstLfsrPermDefault,
parameter lfsr_seed_t RndCnstLfsrSeed = RndCnstLfsrSeedDefault,
parameter lfsr_fwd_perm_t RndCnstLfsrFwdPerm = RndCnstLfsrFwdPermDefault,
parameter msg_perm_t RndCnstMsgPerm = RndCnstMsgPermDefault,
parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}
) (
input clk_i,
input rst_ni,
input rst_shadowed_ni,
input clk_edn_i,
input rst_edn_ni,
input tlul_pkg::tl_h2d_t tl_i,
output tlul_pkg::tl_d2h_t tl_o,
// Alerts
input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o,
// KeyMgr sideload (secret key) interface
input keymgr_pkg::hw_key_req_t keymgr_key_i,
// KeyMgr KDF data path
input app_req_t [NumAppIntf-1:0] app_i,
output app_rsp_t [NumAppIntf-1:0] app_o,
// EDN interface
output edn_pkg::edn_req_t entropy_o,
input edn_pkg::edn_rsp_t entropy_i,
// Life cycle
input lc_ctrl_pkg::lc_tx_t lc_escalate_en_i,
// interrupts
output logic intr_kmac_done_o,
output logic intr_fifo_empty_o,
output logic intr_kmac_err_o,
// parameter consistency check with keymgr
output logic en_masking_o,
// Idle signal
output prim_mubi_pkg::mubi4_t idle_o
);
////////////////
// Parameters //
////////////////
localparam int Share = (EnMasking) ? 2 : 1 ;
localparam int SwKeyShare = (EnMasking || SwKeyMasked) ? 2 : 1;
/////////////////
// Definitions //
/////////////////
// This state machine is to track the current process based on SW input and
// KMAC operation.
// Encoding generated with:
// $ ./util/design/sparse-fsm-encode.py -d 3 -m 6 -n 6 \
// -s 1966361510 --language=sv
//
// Hamming distance histogram:
//
// 0: --
// 1: --
// 2: --
// 3: |||||||||||||||||||| (53.33%)
// 4: ||||||||||||||| (40.00%)
// 5: || (6.67%)
// 6: --
//
// Minimum Hamming distance: 3
// Maximum Hamming distance: 5
// Minimum Hamming weight: 2
// Maximum Hamming weight: 5
//
localparam int StateWidth = 6;
typedef enum logic [StateWidth-1:0] {
// Idle state
KmacIdle = 6'b001011,
// When software writes CmdStart @ KmacIdle and kmac_en, FSM moves to this
KmacPrefix = 6'b000110,
// When SHA3 engine processes Key block, FSM moves to here.
KmacKeyBlock = 6'b111110,
// Message Feed
KmacMsgFeed = 6'b010101,
// Complete and squeeze
KmacDigest = 6'b101101,
// Error
KmacTerminalError = 6'b110000
} kmac_st_e;
kmac_st_e kmac_st, kmac_st_d;
/////////////
// Signals //
/////////////
kmac_reg2hw_t reg2hw;
kmac_hw2reg_t hw2reg;
// devmode ties to 1 as KMAC should be operated at the beginning for ROM_CTRL.
logic devmode;
assign devmode = 1'b 1;
// Window
typedef enum int {
WinState = 0,
WinMsgFifo = 1
} tl_window_e;
tlul_pkg::tl_h2d_t tl_win_h2d[2];
tlul_pkg::tl_d2h_t tl_win_d2h[2];
// SHA3 core control signals and its response.
// Sequence: start --> process(multiple) --> get absorbed event --> {run -->} done
logic sha3_start, sha3_run, unused_sha3_squeeze;
prim_mubi_pkg::mubi4_t sha3_done;
prim_mubi_pkg::mubi4_t sha3_done_d;
prim_mubi_pkg::mubi4_t sha3_absorbed;
// Indicate one block processed
logic sha3_block_processed;
// EStatus for entropy
logic entropy_in_keyblock;
// Application interface logic generates absorbed from sha3_absorbed.
// It is active only if SW initiates the hashing engine.
prim_mubi_pkg::mubi4_t app_absorbed;
logic event_absorbed;
sha3_pkg::sha3_st_e sha3_fsm;
// Prefix: kmac_pkg defines Prefix based on N size and S size.
// Then computes left_encode(len(N)) size and left_encode(len(S))
// For given default value 32, 256 bits, the max
// encode_string(N) || encode_string(S) is 328. So 11 Prefix registers are
// created.
logic [sha3_pkg::NSRegisterSize*8-1:0] reg_ns_prefix;
logic [sha3_pkg::NSRegisterSize*8-1:0] ns_prefix;
// NumWordsPrefix from kmac_reg_pkg
`ASSERT_INIT(PrefixRegSameToPrefixPkg_A,
kmac_reg_pkg::NumWordsPrefix*4 == sha3_pkg::NSRegisterSize)
// NumEntriesMsgFifo from kmac_reg_pkg must match calculated MsgFifoDepth
// from kmac_pkg.
`ASSERT_INIT(NumEntriesRegSameToNumEntriesPkg_A,
kmac_reg_pkg::NumEntriesMsgFifo == kmac_pkg::MsgFifoDepth)
// NumBytesMsgFifoEntry from kmac_reg_pkg must match the MsgWidth calculated
// in kmac_pkg (although MsgWidth is in bits, so we multiply by 8).
`ASSERT_INIT(EntrySizeRegSameToEntrySizePkg_A,
kmac_reg_pkg::NumBytesMsgFifoEntry * 8 == kmac_pkg::MsgWidth)
// Output state: this is used to redirect the digest to KeyMgr or Software
// depends on the configuration.
logic state_valid;
logic [sha3_pkg::StateW-1:0] state [Share];
// state is de-muxed in keymgr interface logic.
// the output from keymgr logic goes into staterd module to be visible to SW
logic reg_state_valid;
logic [sha3_pkg::StateW-1:0] reg_state [Share];
// SHA3 Entropy interface
logic sha3_rand_valid, sha3_rand_early, sha3_rand_consumed;
logic [sha3_pkg::StateW/2-1:0] sha3_rand_data;
logic sha3_rand_aux;
// FIFO related signals
logic msgfifo_empty, msgfifo_full;
logic [kmac_pkg::MsgFifoDepthW-1:0] msgfifo_depth;
logic msgfifo_valid ;
logic [kmac_pkg::MsgWidth-1:0] msgfifo_data [Share];
logic [kmac_pkg::MsgStrbW-1:0] msgfifo_strb ;
logic msgfifo_ready ;
if (EnMasking) begin : gen_msgfifo_data_masked
// In Masked mode, the input message data is split into two shares.
// Only concern, however, here is the secret key. So message can be
// put into only one share and other is 0.
assign msgfifo_data[1] = '0;
end
// TL-UL Adapter(MSG_FIFO) signals
logic tlram_req;
logic tlram_gnt;
logic tlram_we;
logic [8:0] tlram_addr; // NOT_READ
logic [31:0] tlram_wdata;
logic [31:0] tlram_wmask;
logic [31:0] tlram_rdata;
logic tlram_rvalid;
logic [1:0] tlram_rerror;
logic [31:0] tlram_wdata_endian;
logic [31:0] tlram_wmask_endian;
logic sw_msg_valid;
logic [kmac_pkg::MsgWidth-1:0] sw_msg_data ;
logic [kmac_pkg::MsgWidth-1:0] sw_msg_mask ;
logic sw_msg_ready;
// KeyMgr interface to MSG_FIFO
logic mux2fifo_valid;
logic [kmac_pkg::MsgWidth-1:0] mux2fifo_data ;
logic [kmac_pkg::MsgWidth-1:0] mux2fifo_mask ;
logic mux2fifo_ready;
// KMAC to SHA3 core
logic msg_valid ;
logic [kmac_pkg::MsgWidth-1:0] msg_data [Share];
logic [kmac_pkg::MsgWidth-1:0] msg_data_masked [Share];
logic [kmac_pkg::MsgStrbW-1:0] msg_strb ;
logic msg_ready ;
// Process control signals
// Process pulse propagates from register to SHA3 engine one by one.
// Each module (MSG_FIFO, KMAC core, SHA3 core) generates the process pulse
// after flushing internal data to the next module.
logic reg2msgfifo_process, msgfifo2kmac_process, kmac2sha3_process;
// Secret Key signals
logic [MaxKeyLen-1:0] sw_key_data_reg [SwKeyShare];
logic [MaxKeyLen-1:0] sw_key_data [Share];
key_len_e sw_key_len;
logic [MaxKeyLen-1:0] key_data [Share];
key_len_e key_len;
// SHA3 Mode, Strength, KMAC enable for app interface
logic reg_kmac_en, app_kmac_en;
sha3_pkg::sha3_mode_e reg_sha3_mode, app_sha3_mode;
sha3_pkg::keccak_strength_e reg_keccak_strength, app_keccak_strength;
// RegIF of enabling unsupported mode & strength
logic cfg_en_unsupported_modestrength;
// Indicating AppIntf is active. This signal is used to check SW error
logic app_active;
// Command
// sw_cmd is the command written by SW
// checked_sw_cmd is checked in the kmac_errchk module.
// Invalid command is filtered out in the module.
// kmac_cmd is generated in KeyMgr interface.
// If SW initiates the KMAC/SHA3, kmac_cmd represents SW command,
// if KeyMgr drives the data, kmac_cmd is controled in the state machine
// in KeyMgr interface logic.
kmac_cmd_e sw_cmd, checked_sw_cmd, kmac_cmd, cmd_q;
logic cmd_update;
// Entropy configurations
logic [9:0] wait_timer_prescaler;
logic [15:0] wait_timer_limit;
logic entropy_refresh_req;
logic [NumSeedsEntropyLfsr-1:0] entropy_seed_update;
logic [NumSeedsEntropyLfsr-1:0][31:0] entropy_seed_data;
logic [HashCntW-1:0] entropy_hash_threshold;
logic [HashCntW-1:0] entropy_hash_cnt;
logic entropy_hash_clr;
logic entropy_ready;
entropy_mode_e entropy_mode;
logic entropy_fast_process;
prim_mubi_pkg::mubi4_t entropy_configured;
// Message Masking
logic msg_mask_en, cfg_msg_mask;
logic [MsgWidth-1:0] msg_mask;
// SHA3 Error response
sha3_pkg::err_t sha3_err;
// KeyMgr Error response
kmac_pkg::err_t app_err;
// Entropy Generator Error
kmac_pkg::err_t entropy_err;
// Error checker
kmac_pkg::err_t errchecker_err;
// MsgFIFO Error
kmac_pkg::err_t msgfifo_err;
logic err_processed;
logic alert_fatal, alert_recov_operation;
logic alert_intg_err;
// Life cycle
localparam int unsigned NumLcSyncCopies = 6;
lc_ctrl_pkg::lc_tx_t [NumLcSyncCopies-1:0] lc_escalate_en_sync;
lc_ctrl_pkg::lc_tx_t [NumLcSyncCopies-1:0] lc_escalate_en;
//////////////////////////////////////
// Connecting Register IF to logics //
//////////////////////////////////////
// Function-name N and Customization input string S
always_comb begin
for (int i = 0 ; i < NumWordsPrefix; i++) begin
reg_ns_prefix[32*i+:32] = reg2hw.prefix[i].q;
end
end
// Create a lint error to reduce the risk of accidentally enabling this feature.
`ASSERT_STATIC_LINT_ERROR(KmacSecCmdDelayNonDefault, SecCmdDelay == 0)
if (SecCmdDelay > 0) begin : gen_cmd_delay_buf
// Delay and buffer commands for SCA measurements.
localparam int unsigned WidthCounter = $clog2(SecCmdDelay+1);
logic [WidthCounter-1:0] count_d, count_q;
logic counting_d, counting_q;
logic cmd_buf_empty;
kmac_cmd_e cmd_buf_q;
assign cmd_buf_empty = (cmd_buf_q == CmdNone);
// When seeing a write to the cmd register, we start counting. We stop counting once the
// counter has expired and the command buffer is empty.
assign counting_d = reg2hw.cmd.cmd.qe ? 1'b1 :
cmd_update & cmd_buf_empty ? 1'b0 : counting_q;
// Clear counter upon writes to the cmd register or if the specified delay is reached.
assign count_d = reg2hw.cmd.cmd.qe ? '0 :
cmd_update ? '0 :
counting_q ? count_q + 1'b1 : count_q;
// The manual run command cannot be delayed. Software expects this to be triggered immediately
// and will poll the status register to wait for the SHA3 engine to return back to the squeeze
// state.
assign cmd_update = (cmd_q == CmdManualRun) ? 1'b1 :
(count_q == SecCmdDelay[WidthCounter-1:0]) ? 1'b1 : 1'b0;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
count_q <= '0;
counting_q <= 1'b0;
end else begin
count_q <= count_d;
counting_q <= counting_d;
end
end
// cmd.q is valid while cmd.qe is high, meaning it needs to be registered. We buffer one
// additional command such that software can write START followed by PROCESS and then go to
// sleep.
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cmd_q <= CmdNone;
cmd_buf_q <= CmdNone;
end else begin
if (reg2hw.cmd.cmd.qe && cmd_update) begin
// New write & counter expired.
cmd_q <= cmd_buf_q;
cmd_buf_q <= kmac_cmd_e'(reg2hw.cmd.cmd.q);
end else if (reg2hw.cmd.cmd.qe) begin
// New write.
if (counting_q == 1'b0) begin
cmd_q <= kmac_cmd_e'(reg2hw.cmd.cmd.q);
end else begin
cmd_buf_q <= kmac_cmd_e'(reg2hw.cmd.cmd.q);
end
end else if (cmd_update) begin
// Counter expired.
cmd_q <= cmd_buf_q;
cmd_buf_q <= CmdNone;
end
end
end
end else begin : gen_no_cmd_delay_buf
// Directly forward signals from register IF.
assign cmd_update = reg2hw.cmd.cmd.qe;
assign cmd_q = kmac_cmd_e'(reg2hw.cmd.cmd.q);
end
// Command signals
assign sw_cmd = (cmd_update) ? cmd_q : CmdNone;
`ASSERT_KNOWN(KmacCmd_A, sw_cmd)
always_comb begin
sha3_start = 1'b 0;
sha3_run = 1'b 0;
sha3_done_d = prim_mubi_pkg::MuBi4False;
reg2msgfifo_process = 1'b 0;
unique case (kmac_cmd)
CmdStart: begin
sha3_start = 1'b 1;
end
CmdProcess: begin
reg2msgfifo_process = 1'b 1;
end
CmdManualRun: begin
sha3_run = 1'b 1;
end
CmdDone: begin
sha3_done_d = prim_mubi_pkg::MuBi4True;
end
CmdNone: begin
// inactive state
end
default: begin
end
endcase
end
// Status register ==========================================================
// status.squeeze is valid only when SHA3 engine completes the Absorb and not
// running the manual keccak rounds. This status is for SW to determine when
// to read the STATE values.
assign hw2reg.status.sha3_idle.d = sha3_fsm == sha3_pkg::StIdle;
assign hw2reg.status.sha3_absorb.d = sha3_fsm == sha3_pkg::StAbsorb;
assign hw2reg.status.sha3_squeeze.d = sha3_fsm == sha3_pkg::StSqueeze;
// FIFO related status
assign hw2reg.status.fifo_depth.d[MsgFifoDepthW-1:0] = msgfifo_depth;
if ($bits(hw2reg.status.fifo_depth.d) != MsgFifoDepthW) begin : gen_fifo_depth_tie
assign hw2reg.status.fifo_depth.d[$bits(hw2reg.status.fifo_depth.d)-1:MsgFifoDepthW] = '0;
end
assign hw2reg.status.fifo_empty.d = msgfifo_empty;
assign hw2reg.status.fifo_full.d = msgfifo_full;
// Configuration Register
logic engine_stable;
assign engine_stable = sha3_fsm == sha3_pkg::StIdle;
// SEC_CM: CFG_SHADOWED.CONFIG.REGWEN
assign hw2reg.cfg_regwen.d = engine_stable;
// Secret Key
// Secret key is defined as external register. So the logic latches when SW
// writes to KEY_SHARE0 , KEY_SHARE1 registers.
// SEC_CM: SW_KEY.KEY.MASKING
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
sw_key_data_reg[0] <= '0;
end else if (engine_stable) begin
for (int j = 0 ; j < MaxKeyLen/32 ; j++) begin
if (reg2hw.key_share0[j].qe) begin
sw_key_data_reg[0][32*j+:32] <= reg2hw.key_share0[j].q;
end
end // for j
end // else if engine_stable
end // always_ff
if (EnMasking || SwKeyMasked) begin : gen_key_share1_reg
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
sw_key_data_reg[1] <= '0;
end else if (engine_stable) begin
for (int j = 0 ; j < MaxKeyLen/32 ; j++) begin
if (reg2hw.key_share1[j].qe) begin
sw_key_data_reg[1][32*j+:32] <= reg2hw.key_share1[j].q;
end
end // for j
end // else if engine_stable
end // always_ff
end else begin : gen_no_key_share1_reg
logic unused_key_share1;
assign unused_key_share1 = ^reg2hw.key_share1;
end
if (EnMasking || !SwKeyMasked) begin : gen_key_forward
// Forward all available key shares as is.
assign sw_key_data = sw_key_data_reg;
end else begin : gen_key_unmask
// Masking is disabled but the SW still provides the key in two shares.
// Unmask the key for processing.
assign sw_key_data[0] = sw_key_data_reg[0] ^ sw_key_data_reg[1];
end
assign sw_key_len = key_len_e'(reg2hw.key_len.q);
// Entropy configurations
assign wait_timer_prescaler = reg2hw.entropy_period.prescaler.q;
assign wait_timer_limit = reg2hw.entropy_period.wait_timer.q;
assign entropy_refresh_req = reg2hw.cmd.entropy_req.q
&& reg2hw.cmd.entropy_req.qe;
for (genvar i = 0; i < NumSeedsEntropyLfsr; i++) begin : gen_entropy_seed
assign entropy_seed_update[i] = reg2hw.entropy_seed[i].qe;
assign entropy_seed_data[i] = reg2hw.entropy_seed[i].q;
end
assign entropy_hash_threshold = reg2hw.entropy_refresh_threshold_shadowed.q;
assign hw2reg.entropy_refresh_hash_cnt.de = 1'b 1;
assign hw2reg.entropy_refresh_hash_cnt.d = entropy_hash_cnt;
assign entropy_hash_clr = reg2hw.cmd.hash_cnt_clr.qe
&& reg2hw.cmd.hash_cnt_clr.q;
// Entropy config
assign entropy_ready = reg2hw.cfg_shadowed.entropy_ready.q
& reg2hw.cfg_shadowed.entropy_ready.qe;
assign entropy_mode = entropy_mode_e'(reg2hw.cfg_shadowed.entropy_mode.q);
assign entropy_fast_process = reg2hw.cfg_shadowed.entropy_fast_process.q;
// msg_mask_en turns on the message LFSR when KMAC is enabled.
assign cfg_msg_mask = reg2hw.cfg_shadowed.msg_mask.q;
assign msg_mask_en = cfg_msg_mask & msg_valid & msg_ready;
// Enable unsupported mode & strength combination
assign cfg_en_unsupported_modestrength =
reg2hw.cfg_shadowed.en_unsupported_modestrength.q;
`ASSERT(EntropyReadyLatched_A, $rose(entropy_ready) |=> !entropy_ready)
// Idle control (registered output)
// The logic checks idle of SHA3 engine, MSG_FIFO, KMAC_CORE, KEYMGR interface
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
idle_o <= prim_mubi_pkg::MuBi4True;
end else if ((sha3_fsm == sha3_pkg::StIdle) && (msgfifo_empty || SecIdleAcceptSwMsg)) begin
idle_o <= prim_mubi_pkg::MuBi4True;
end else begin
idle_o <= prim_mubi_pkg::MuBi4False;
end
end
// Clear the error processed
assign err_processed = reg2hw.cfg_shadowed.err_processed.q
& reg2hw.cfg_shadowed.err_processed.qe;
// Make sure the field has latch in reg_top
`ASSERT(ErrProcessedLatched_A, $rose(err_processed) |=> !err_processed)
// App mode, strength, kmac_en
assign reg_kmac_en = reg2hw.cfg_shadowed.kmac_en.q;
assign reg_sha3_mode = sha3_pkg::sha3_mode_e'(reg2hw.cfg_shadowed.mode.q);
assign reg_keccak_strength = sha3_pkg::keccak_strength_e'(reg2hw.cfg_shadowed.kstrength.q);
///////////////
// Interrupt //
///////////////
logic event_msgfifo_empty, msgfifo_empty_q;
// Hash process absorbed interrupt
// Convert mubi4_t to logic to generate interrupts
assign event_absorbed = prim_mubi_pkg::mubi4_test_true_strict(app_absorbed);
prim_intr_hw #(.Width(1)) intr_kmac_done (
.clk_i,
.rst_ni,
.event_intr_i (event_absorbed),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.kmac_done.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.kmac_done.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.kmac_done.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.kmac_done.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.kmac_done.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.kmac_done.d),
.intr_o (intr_kmac_done_o)
);
`ASSERT(Sha3AbsorbedPulse_A,
$rose(prim_mubi_pkg::mubi4_test_true_strict(sha3_absorbed)) |=>
prim_mubi_pkg::mubi4_test_false_strict(sha3_absorbed))
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) msgfifo_empty_q <= 1'b1;
else msgfifo_empty_q <= msgfifo_empty;
end
assign event_msgfifo_empty = ~msgfifo_empty_q & msgfifo_empty;
prim_intr_hw #(.Width(1)) intr_fifo_empty (
.clk_i,
.rst_ni,
.event_intr_i (event_msgfifo_empty),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.fifo_empty.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.fifo_empty.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.fifo_empty.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.fifo_empty.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.fifo_empty.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.fifo_empty.d),
.intr_o (intr_fifo_empty_o)
);
// Error
// As of now, only SHA3 error exists. More error codes will be added.
logic event_error;
assign event_error = sha3_err.valid | app_err.valid
| entropy_err.valid | errchecker_err.valid
;
// Assing error code to the register
assign hw2reg.err_code.de = event_error;
always_comb begin
hw2reg.err_code.d = '0;
priority case (1'b 1)
// app_err has the highest priority. If SW issues an incorrect command
// while app is in active state, the error from AppIntf is passed
// through.
app_err.valid: begin
hw2reg.err_code.d = {app_err.code, app_err.info};
end
errchecker_err.valid: begin
hw2reg.err_code.d = {errchecker_err.code , errchecker_err.info};
end
sha3_err.valid: begin
hw2reg.err_code.d = {sha3_err.code , sha3_err.info};
end
entropy_err.valid: begin
hw2reg.err_code.d = {entropy_err.code, entropy_err.info};
end
msgfifo_err.valid: begin
hw2reg.err_code.d = {msgfifo_err.code, msgfifo_err.info};
end
default: begin
hw2reg.err_code.d = '0;
end
endcase
end
// Counter errors
logic counter_error, sha3_count_error, key_index_error;
logic msgfifo_counter_error;
logic kmac_entropy_hash_counter_error;
assign counter_error = sha3_count_error
| kmac_entropy_hash_counter_error
| key_index_error
| msgfifo_counter_error;
assign msgfifo_counter_error = msgfifo_err.valid;
// State Errors
logic sparse_fsm_error;
logic sha3_state_error, kmac_errchk_state_error;
logic kmac_core_state_error, kmac_app_state_error;
logic kmac_entropy_state_error, kmac_state_error;
assign sparse_fsm_error = sha3_state_error
| kmac_errchk_state_error
| kmac_core_state_error
| kmac_app_state_error
| kmac_entropy_state_error
| kmac_state_error;
// Control Signal Integrity Errors
logic control_integrity_error;
logic sha3_storage_rst_error;
assign control_integrity_error = sha3_storage_rst_error;
prim_intr_hw #(.Width(1)) intr_kmac_err (
.clk_i,
.rst_ni,
.event_intr_i (event_error),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.kmac_err.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.kmac_err.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.kmac_err.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.kmac_err.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.kmac_err.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.kmac_err.d),
.intr_o (intr_kmac_err_o)
);
///////////////////
// State Machine //
///////////////////
// State FF
`PRIM_FLOP_SPARSE_FSM(u_state_regs, kmac_st_d, kmac_st, kmac_st_e, KmacIdle)
always_comb begin
// Default value
kmac_st_d = kmac_st;
entropy_in_keyblock = 1'b 0;
kmac_state_error = 1'b 0;
unique case (kmac_st)
KmacIdle: begin
if (kmac_cmd == CmdStart) begin
// If cSHAKE turned on
if (sha3_pkg::CShake == app_sha3_mode) begin
kmac_st_d = KmacPrefix;
end else begin
// Jump to Msg feed directly
kmac_st_d = KmacMsgFeed;
end
end else begin
kmac_st_d = KmacIdle;
end
end
KmacPrefix: begin
// Wait until SHA3 processes one block
if (sha3_block_processed) begin
kmac_st_d = (app_kmac_en) ? KmacKeyBlock : KmacMsgFeed ;
end else begin
kmac_st_d = KmacPrefix;
end
end
KmacKeyBlock: begin
entropy_in_keyblock = 1'b 1;
if (sha3_block_processed) begin
kmac_st_d = KmacMsgFeed;
end else begin
kmac_st_d = KmacKeyBlock;
end
end
KmacMsgFeed: begin
// If absorbed, move to Digest
if (prim_mubi_pkg::mubi4_test_true_strict(sha3_absorbed) &&
prim_mubi_pkg::mubi4_test_true_strict(sha3_done)) begin
// absorbed and done can be asserted at a cycle if Applications have
// requested the hash operation. kmac_app FSM issues CmdDone command
// if it receives absorbed signal.
kmac_st_d = KmacIdle;
end else if (prim_mubi_pkg::mubi4_test_true_strict(sha3_absorbed) &&
prim_mubi_pkg::mubi4_test_false_loose(sha3_done)) begin
kmac_st_d = KmacDigest;
end else begin
kmac_st_d = KmacMsgFeed;
end
end
KmacDigest: begin
// SW can manually run it, wait till done
if (prim_mubi_pkg::mubi4_test_true_strict(sha3_done)) begin
kmac_st_d = KmacIdle;
end else begin
kmac_st_d = KmacDigest;
end
end
KmacTerminalError: begin
//this state is terminal
kmac_st_d = KmacTerminalError;
kmac_state_error = 1'b 1;
end
default: begin
kmac_st_d = KmacTerminalError;
kmac_state_error = 1'b 1;
end
endcase
// SEC_CM: FSM.GLOBAL_ESC, FSM.LOCAL_ESC
// Unconditionally jump into the terminal error state
// if the life cycle controller triggers an escalation.
if (lc_escalate_en[0] != lc_ctrl_pkg::Off) begin
kmac_st_d = KmacTerminalError;
end
end
`ASSERT_KNOWN(KmacStKnown_A, kmac_st)
///////////////
// Instances //
///////////////
// KMAC core
kmac_core #(
.EnMasking (EnMasking)
) u_kmac_core (
.clk_i,
.rst_ni,
// from Msg FIFO
.fifo_valid_i (msgfifo_valid),
.fifo_data_i (msgfifo_data ),
.fifo_strb_i (msgfifo_strb ),
.fifo_ready_o (msgfifo_ready),
// to SHA3 core
.msg_valid_o (msg_valid),
.msg_data_o (msg_data ),
.msg_strb_o (msg_strb ),
.msg_ready_i (msg_ready),
// Configurations
.kmac_en_i (app_kmac_en),
.mode_i (app_sha3_mode),
.strength_i (app_keccak_strength),
// Secret key interface
.key_data_i (key_data),
.key_len_i (key_len ),
// Controls
.start_i (sha3_start ),
.process_i (msgfifo2kmac_process),
.done_i (sha3_done ),
.process_o (kmac2sha3_process ),
// LC escalation
.lc_escalate_en_i (lc_escalate_en[1]),
// Error detection
.sparse_fsm_error_o (kmac_core_state_error),
.key_index_error_o (key_index_error)
);
// SHA3 hashing engine
// msg_data masking
if (EnMasking == 1) begin: g_msg_mask
logic [MsgWidth-1:0] msg_mask_permuted;
// Permute the LFSR output to avoid same lfsr applied to multiple times
always_comb begin
msg_mask_permuted = '0;
for (int unsigned i = 0 ; i < MsgWidth ; i++) begin
// Loop through the MsgPerm constant and swap between the bits
msg_mask_permuted[i] = msg_mask[RndCnstMsgPerm[i]];
end
end
for (genvar i = 0 ; i < Share ; i++) begin: g_msg_data_mask
assign msg_data_masked[i] = msg_data[i]
^ ({MsgWidth{cfg_msg_mask}} & msg_mask_permuted);
end : g_msg_data_mask
end else begin : g_no_msg_mask
assign msg_data_masked[0] = msg_data[0];
logic unused_msgmask;
assign unused_msgmask = ^{msg_mask, cfg_msg_mask, msg_mask_en};
end
sha3 #(
.EnMasking (EnMasking)
) u_sha3 (
.clk_i,
.rst_ni,
// MSG_FIFO interface (or from KMAC)
.msg_valid_i (msg_valid),
.msg_data_i (msg_data_masked ),
.msg_strb_i (msg_strb ),
.msg_ready_o (msg_ready),
// Entropy interface
.rand_valid_i (sha3_rand_valid),
.rand_early_i (sha3_rand_early),
.rand_data_i (sha3_rand_data),
.rand_aux_i (sha3_rand_aux),
.rand_consumed_o (sha3_rand_consumed),
// N, S: Used in cSHAKE mode
.ns_data_i (ns_prefix),
// Configurations
.mode_i (app_sha3_mode),
.strength_i (app_keccak_strength),
// Controls (CMD register)
.start_i (sha3_start ),
.process_i (kmac2sha3_process),
.run_i (sha3_run ),
.done_i (sha3_done ),
// LC escalation
.lc_escalate_en_i (lc_escalate_en[2]),
.absorbed_o (sha3_absorbed),
.squeezing_o (unused_sha3_squeeze),
.block_processed_o (sha3_block_processed),
.sha3_fsm_o (sha3_fsm),
.state_valid_o (state_valid),
.state_o (state), // [Share]
.error_o (sha3_err),
.sparse_fsm_error_o (sha3_state_error),
.count_error_o (sha3_count_error),
.keccak_storage_rst_error_o (sha3_storage_rst_error)
);
// MSG_FIFO window interface to FIFO interface ===============================
// Tie the read path
assign tlram_rvalid = 1'b 0;
assign tlram_rdata = '0;
assign tlram_rerror = '0;
// Convert endian here
// prim_packer always packs to the right(bit0). If the input DWORD is
// big-endian, it needs to be swapped to little-endian to maintain the
// order. Internal SHA3(Keccak) runs in little-endian in contrast to HMAC
// So, no endian-swap after prim_packer.
assign tlram_wdata_endian = conv_endian32(tlram_wdata,
reg2hw.cfg_shadowed.msg_endianness.q);
assign tlram_wmask_endian = conv_endian32(tlram_wmask,
reg2hw.cfg_shadowed.msg_endianness.q);
// TL Adapter
tlul_adapter_sram #(
.SramAw ($clog2(MsgWindowDepth)),
.SramDw (MsgWindowWidth),
.Outstanding (1),
.ByteAccess (1),
.ErrOnRead (1)
) u_tlul_adapter_msgfifo (
.clk_i,
.rst_ni,
.en_ifetch_i (prim_mubi_pkg::MuBi4False),
.tl_i (tl_win_h2d[WinMsgFifo]),
.tl_o (tl_win_d2h[WinMsgFifo]),
.req_o (tlram_req),
.req_type_o (),
.gnt_i (tlram_gnt),
.we_o (tlram_we ),
.addr_o (tlram_addr),
.wdata_o (tlram_wdata),
.wmask_o (tlram_wmask),
.intg_error_o( ),
.rdata_i (tlram_rdata),
.rvalid_i (tlram_rvalid),
.rerror_i (tlram_rerror)
);
assign sw_msg_valid = tlram_req & tlram_we ;
if (MsgWidth == MsgWindowWidth) begin : gen_sw_msg_samewidth
assign sw_msg_data = tlram_wdata_endian ;
assign sw_msg_mask = tlram_wmask_endian ;
end else begin : gen_sw_msg_diff
assign sw_msg_data = {{MsgWidth-MsgWindowWidth{1'b0}}, tlram_wdata_endian};
assign sw_msg_mask = {{MsgWidth-MsgWindowWidth{1'b0}}, tlram_wmask_endian};
end
assign tlram_gnt = sw_msg_ready ;
logic unused_tlram_addr;
assign unused_tlram_addr = &{1'b0, tlram_addr};
// Application interface Mux/Demux
kmac_app #(
.EnMasking(EnMasking),
.SecIdleAcceptSwMsg(SecIdleAcceptSwMsg)
) u_app_intf (
.clk_i,
.rst_ni,
.reg_key_data_i (sw_key_data),
.reg_key_len_i (sw_key_len),
.reg_prefix_i (reg_ns_prefix),
.reg_kmac_en_i (reg_kmac_en),
.reg_sha3_mode_i (reg_sha3_mode),
.reg_keccak_strength_i (reg_keccak_strength),
// data from tl_adapter
.sw_valid_i (sw_msg_valid),
.sw_data_i (sw_msg_data),
.sw_mask_i (sw_msg_mask),
.sw_ready_o (sw_msg_ready),
// KeyMgr sideloaded key interface
.keymgr_key_i,
// Application data in / digest out interface
.app_i,
.app_o,
// Secret Key output to KMAC Core
.key_data_o (key_data),
.key_len_o (key_len),
// to MSG_FIFO
.kmac_valid_o (mux2fifo_valid),
.kmac_data_o (mux2fifo_data),
.kmac_mask_o (mux2fifo_mask),
.kmac_ready_i (mux2fifo_ready),
// to KMAC Core
.kmac_en_o (app_kmac_en),
// to SHA3 Core
.sha3_prefix_o (ns_prefix),
.sha3_mode_o (app_sha3_mode),
.keccak_strength_o (app_keccak_strength),
// Keccak state from SHA3 core
.keccak_state_valid_i (state_valid),
.keccak_state_i (state),
// to STATE TL Window
.reg_state_valid_o (reg_state_valid),
.reg_state_o (reg_state),
// Configuration: Sideloaded Key
.keymgr_key_en_i (reg2hw.cfg_shadowed.sideload.q),
.absorbed_i (sha3_absorbed), // from SHA3
.absorbed_o (app_absorbed), // to SW
.app_active_o(app_active),
.error_i (sha3_err.valid),
.err_processed_i (err_processed),
// Command interface
.sw_cmd_i (checked_sw_cmd),
.cmd_o (kmac_cmd),
// Status
.entropy_ready_i (entropy_configured),
// LC escalation
.lc_escalate_en_i (lc_escalate_en[3]),
// Error report
.error_o (app_err),
.sparse_fsm_error_o (kmac_app_state_error)
);
// Message FIFO
kmac_msgfifo #(
.OutWidth (kmac_pkg::MsgWidth),
.MsgDepth (kmac_pkg::MsgFifoDepth),
.EnMasking (EnMasking)
) u_msgfifo (
.clk_i,
.rst_ni,
.fifo_valid_i (mux2fifo_valid),
.fifo_data_i (mux2fifo_data),
.fifo_mask_i (mux2fifo_mask),
.fifo_ready_o (mux2fifo_ready),
.msg_valid_o (msgfifo_valid),
.msg_data_o (msgfifo_data[0]),
.msg_strb_o (msgfifo_strb),
.msg_ready_i (msgfifo_ready),
.fifo_empty_o (msgfifo_empty), // intr and status
.fifo_full_o (msgfifo_full), // connected to status only
.fifo_depth_o (msgfifo_depth),
.clear_i (sha3_done),
.process_i (reg2msgfifo_process ),
.process_o (msgfifo2kmac_process),
.err_o (msgfifo_err)
);
logic [sha3_pkg::StateW-1:0] reg_state_tl [Share];
always_comb begin
for (int i = 0 ; i < Share; i++) begin
reg_state_tl[i] = reg_state_valid ? reg_state[i] : 'b0;
end
end
// State (Digest) reader
kmac_staterd #(
.AddrW (9), // 512B
.EnMasking (EnMasking)
) u_staterd (
.clk_i,
.rst_ni,
.tl_i (tl_win_h2d[WinState]),
.tl_o (tl_win_d2h[WinState]),
.state_i (reg_state_tl),
.endian_swap_i (reg2hw.cfg_shadowed.state_endianness.q)
);
// Error checker
kmac_errchk #(
.EnMasking (EnMasking)
) u_errchk (
.clk_i,
.rst_ni,
// Configurations
.cfg_mode_i (reg_sha3_mode ),
.cfg_strength_i(reg_keccak_strength),
.kmac_en_i (reg_kmac_en ),
.cfg_prefix_6B_i(reg_ns_prefix[47:0]), // first 6B of PREFIX
.cfg_en_unsupported_modestrength_i (cfg_en_unsupported_modestrength),
.entropy_ready_pulse_i (entropy_ready),
// SW commands
.sw_cmd_i(sw_cmd),
.sw_cmd_o(checked_sw_cmd),
// Status from KMAC_APP
.app_active_i(app_active),
// Status from SHA3 core
.sha3_absorbed_i(sha3_absorbed ),
.keccak_done_i (sha3_block_processed),
// LC escalation
.lc_escalate_en_i (lc_escalate_en[4]),
.err_processed_i (err_processed),
.error_o (errchecker_err),
.sparse_fsm_error_o (kmac_errchk_state_error)
);
// Entropy Generator
if (EnMasking == 1) begin : gen_entropy
logic entropy_req, entropy_ack;
logic [edn_pkg::ENDPOINT_BUS_WIDTH-1:0] entropy_data;
logic unused_entropy_fips;
// Synchronize EDN interface
prim_sync_reqack_data #(
.Width(edn_pkg::ENDPOINT_BUS_WIDTH),
.DataSrc2Dst(1'b0),
.DataReg(1'b0)
) u_prim_sync_reqack_data (
.clk_src_i (clk_i),
.rst_src_ni(rst_ni),
.clk_dst_i (clk_edn_i),
.rst_dst_ni(rst_edn_ni),
.req_chk_i ((kmac_entropy_state_error == 1'b0) && (entropy_err.valid == 1'b0)),
.src_req_i (entropy_req),
.src_ack_o (entropy_ack),
.dst_req_o (entropy_o.edn_req),
.dst_ack_i (entropy_i.edn_ack),
.data_i (entropy_i.edn_bus),
.data_o (entropy_data)
);
// We don't track whether the entropy is pre-FIPS or not inside KMAC.
assign unused_entropy_fips = entropy_i.edn_fips;
kmac_entropy #(
.RndCnstLfsrPerm(RndCnstLfsrPerm),
.RndCnstLfsrSeed(RndCnstLfsrSeed),
.RndCnstLfsrFwdPerm(RndCnstLfsrFwdPerm)
) u_entropy (
.clk_i,
.rst_ni,
// EDN interface
.entropy_req_o (entropy_req),
.entropy_ack_i (entropy_ack),
.entropy_data_i(entropy_data),
// Entropy to internal logic (DOM AND)
.rand_valid_o (sha3_rand_valid),
.rand_early_o (sha3_rand_early),
.rand_data_o (sha3_rand_data),
.rand_aux_o (sha3_rand_aux),
.rand_consumed_i (sha3_rand_consumed),
// Status from internal logic
//// KMAC secret block handling indicator
.in_keyblock_i (entropy_in_keyblock),
// Configuration
.mode_i (entropy_mode),
.entropy_ready_i (entropy_ready),
.fast_process_i (entropy_fast_process),
//// Entropy refresh period in clk cycles
.wait_timer_prescaler_i (wait_timer_prescaler),
.wait_timer_limit_i (wait_timer_limit),
//// Message Masking
.msg_mask_en_i (msg_mask_en),
.msg_mask_o (msg_mask),
//// SW update of seed
.seed_update_i (entropy_seed_update),
.seed_data_i (entropy_seed_data),
.entropy_refresh_req_i (entropy_refresh_req),
// Status
.hash_cnt_o (entropy_hash_cnt),
.hash_cnt_clr_i (entropy_hash_clr),
.hash_threshold_i (entropy_hash_threshold),
.entropy_configured_o (entropy_configured),
// LC escalation
.lc_escalate_en_i (lc_escalate_en[5]),
// Error
.err_o (entropy_err),
.sparse_fsm_error_o (kmac_entropy_state_error),
.count_error_o (kmac_entropy_hash_counter_error),
.err_processed_i (err_processed)
);
end else begin : gen_empty_entropy
// If Masking is not used, no need of entropy. Ignore inputs and config; tie output to 0.
edn_pkg::edn_rsp_t unused_entropy_input;
entropy_mode_e unused_entropy_mode;
logic unused_entropy_fast_process;
assign unused_entropy_input = entropy_i;
assign unused_entropy_mode = entropy_mode;
assign unused_entropy_fast_process = entropy_fast_process;
assign entropy_o = '{default: '0};
logic unused_sha3_rand_consumed;
assign sha3_rand_valid = 1'b 1;
assign sha3_rand_early = 1'b 1;
assign sha3_rand_data = '0;
assign sha3_rand_aux = '0;
assign unused_sha3_rand_consumed = sha3_rand_consumed;
logic [NumSeedsEntropyLfsr-1:0] unused_seed_update;
logic [NumSeedsEntropyLfsr-1:0][31:0] unused_seed_data;
logic [31:0] unused_refresh_period;
logic unused_entropy_refresh_req;
assign unused_seed_data = entropy_seed_data;
assign unused_seed_update = entropy_seed_update;
assign unused_refresh_period = ^{wait_timer_limit, wait_timer_prescaler};
assign unused_entropy_refresh_req = entropy_refresh_req;
logic unused_entropy_hash;
assign unused_entropy_hash = ^{entropy_hash_clr, entropy_hash_threshold};
assign entropy_hash_cnt = '0;
assign entropy_err = '{valid: 1'b 0, code: ErrNone, info: '0};
assign kmac_entropy_state_error = 1'b 0;
assign kmac_entropy_hash_counter_error = 1'b 0;
logic [1:0] unused_entropy_status;
assign unused_entropy_status = entropy_in_keyblock;
// If Masking is off, always entropy configured
assign entropy_configured = prim_mubi_pkg::MuBi4True;
end
// MUBI4 buf
prim_mubi4_sender #(
.AsyncOn (0)
) u_sha3_done_sender (
.clk_i,
.rst_ni,
.mubi_i (sha3_done_d),
.mubi_o (sha3_done)
);
// Register top
logic [NumAlerts-1:0] alert_test, alerts, alerts_q;
logic shadowed_storage_err, shadowed_update_err;
kmac_reg_top u_reg (
.clk_i,
.rst_ni,
.rst_shadowed_ni,
.tl_i,
.tl_o,
.tl_win_o (tl_win_h2d),
.tl_win_i (tl_win_d2h),
.reg2hw,
.hw2reg,
// SEC_CM: CFG_SHADOWED.CONFIG.SHADOW
.shadowed_storage_err_o (shadowed_storage_err),
.shadowed_update_err_o (shadowed_update_err),
// SEC_CM: BUS.INTEGRITY
.intg_err_o (alert_intg_err),
.devmode_i (devmode)
);
logic unused_cfg_shadowed_qe;
assign unused_cfg_shadowed_qe = ^{
reg2hw.cfg_shadowed.kmac_en.qe ,
reg2hw.cfg_shadowed.kstrength.qe ,
reg2hw.cfg_shadowed.mode.qe ,
reg2hw.cfg_shadowed.msg_endianness.qe ,
reg2hw.cfg_shadowed.state_endianness.qe ,
reg2hw.cfg_shadowed.sideload.qe ,
reg2hw.cfg_shadowed.entropy_mode.qe ,
reg2hw.cfg_shadowed.entropy_fast_process.qe ,
reg2hw.cfg_shadowed.msg_mask.qe ,
reg2hw.cfg_shadowed.en_unsupported_modestrength.qe
};
// Alerts
assign alert_test = {
reg2hw.alert_test.fatal_fault_err.q
& reg2hw.alert_test.fatal_fault_err.qe, // [1]
reg2hw.alert_test.recov_operation_err.q
& reg2hw.alert_test.recov_operation_err.qe // [0]
};
assign alerts = {
alert_fatal, // Alerts[1]
alert_recov_operation // Alerts[0]
};
assign alert_recov_operation = shadowed_update_err;
// The recoverable alert is observable via status register until the KMAC operation is restarted
// by re-writing the Control Register.
logic status_alert_recov_ctrl_update_err;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
status_alert_recov_ctrl_update_err <= 1'b 0;
end else if (alert_recov_operation) begin
status_alert_recov_ctrl_update_err <= 1'b 1;
end else if (err_processed) begin
status_alert_recov_ctrl_update_err <= 1'b 0;
end
end
assign hw2reg.status.alert_recov_ctrl_update_err.d = status_alert_recov_ctrl_update_err;
assign alert_fatal = shadowed_storage_err
| alert_intg_err
| sparse_fsm_error
| counter_error
| control_integrity_error
;
// Make the fatal alert observable via status register.
// Cannot be reset except the hardware reset
logic status_alert_fatal_fault;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
status_alert_fatal_fault <= 1'b 0;
end else if (alert_fatal) begin
status_alert_fatal_fault <= 1'b 1;
end
end
assign hw2reg.status.alert_fatal_fault.d = status_alert_fatal_fault;
for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_tx
prim_alert_sender #(
.AsyncOn(AlertAsyncOn[i]),
.IsFatal(i)
) u_prim_alert_sender (
.clk_i,
.rst_ni,
.alert_test_i ( alert_test[i] ),
.alert_req_i ( alerts[i] ),
.alert_ack_o ( ),
.alert_state_o ( ),
.alert_rx_i ( alert_rx_i[i] ),
.alert_tx_o ( alert_tx_o[i] )
);
end
// Below assumes NumAlerts == 2
`ASSERT_INIT(NumAlerts2_A, NumAlerts == 2)
always_ff @(posedge clk_i or negedge rst_ni) begin
// break up the combinatorial path for local escalation
if (!rst_ni) begin
alerts_q[1] <= 1'b0;
end else if (alerts[1]) begin
// fatal alerts cannot be cleared
alerts_q[1] <= 1'b1;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
// break up the combinatorial path for local escalation
if (!rst_ni) begin
alerts_q[0] <= 1'b0;
end else begin
// recoverable alerts can be cleared so just latch the value
alerts_q[0] <= alerts[0];
end
end
// Latched recoverable alert[0] is not used. Rather removing above,
// keep alert_q[1:0] and make alert_q[0] unused (lint waive).
logic unused_alerts_q0;
assign unused_alerts_q0 = alerts_q[0];
// SEC_CM: LC_ESCALATE_EN.INTERSIG.MUBI, FSM.GLOBAL_ESC, FSM.LOCAL_ESC
lc_ctrl_pkg::lc_tx_t alert_to_lc_tx;
assign alert_to_lc_tx = lc_ctrl_pkg::lc_tx_bool_to_lc_tx(alerts_q[1]);
for (genvar i = 0; i < NumLcSyncCopies; i++) begin : gen_or_alert_lc_sync
assign lc_escalate_en[i] = lc_ctrl_pkg::lc_tx_or_hi(alert_to_lc_tx, lc_escalate_en_sync[i]);
end
// Synchronize life cycle input
prim_lc_sync #(
.NumCopies (NumLcSyncCopies)
) u_prim_lc_sync (
.clk_i,
.rst_ni,
.lc_en_i ( lc_escalate_en_i ),
.lc_en_o ( lc_escalate_en_sync )
);
assign en_masking_o = EnMasking;
////////////////
// Assertions //
////////////////
// Assert known for output values
`ASSERT_KNOWN(KmacDone_A, intr_kmac_done_o)
`ASSERT_KNOWN(FifoEmpty_A, intr_fifo_empty_o)
`ASSERT_KNOWN(KmacErr_A, intr_kmac_err_o)
`ASSERT_KNOWN(TlODValidKnown_A, tl_o.d_valid)
`ASSERT_KNOWN(TlOAReadyKnown_A, tl_o.a_ready)
`ASSERT_KNOWN(AlertKnownO_A, alert_tx_o)
`ASSERT_KNOWN(EnMaskingKnown_A, en_masking_o)
// Parameter as desired
`ASSERT_INIT(SecretKeyDivideBy32_A, (kmac_pkg::MaxKeyLen % 32) == 0)
// Command input should be sparse
`ASSUME(CmdSparse_M, reg2hw.cmd.cmd.qe |-> reg2hw.cmd.cmd.q inside {CmdStart, CmdProcess,
CmdManualRun,CmdDone, CmdNone})
// redundant counter error
`ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(SentMsgCountCheck_A, u_sha3.u_pad.u_sentmsg_count,
alert_tx_o[1])
`ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(RoundCountCheck_A, u_sha3.u_keccak.u_round_count,
alert_tx_o[1])
`ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(KeyIndexCountCheck_A, u_kmac_core.u_key_index_count,
alert_tx_o[1])
// Sparse FSM state error
`ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(KmacCoreFsmCheck_A, u_kmac_core.u_state_regs, alert_tx_o[1])
`ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(KmacAppFsmCheck_A, u_app_intf.u_state_regs, alert_tx_o[1])
`ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(SHA3FsmCheck_A, u_sha3.u_state_regs, alert_tx_o[1])
`ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(SHA3padFsmCheck_A, u_sha3.u_pad.u_state_regs, alert_tx_o[1])
`ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(KeccackFsmCheck_A, u_sha3.u_keccak.u_state_regs,
alert_tx_o[1])
`ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(ErrorCheckFsmCheck_A, u_errchk.u_state_regs, alert_tx_o[1])
`ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(KmacFsmCheck_A, u_state_regs, alert_tx_o[1])
// prim is only instantiated if masking is enabled
if (EnMasking == 1) begin : g_testassertion
`ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(EntropyFsmCheck_A, gen_entropy.u_entropy.u_state_regs,
alert_tx_o[1])
`ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(HashCountCheck_A, gen_entropy.u_entropy.u_hash_count,
alert_tx_o[1])
`ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(SeedIdxCountCheck_A,
gen_entropy.u_entropy.u_seed_idx_count,
alert_tx_o[1])
// MsgFifo.Packer
`ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(
PackerCountCheck_A,
u_msgfifo.u_packer.g_pos_dupcnt.u_pos,
alert_tx_o[1]
)
// MsgFifo.Fifo
`ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(
MsgFifoWptrCheck_A,
u_msgfifo.u_msgfifo.gen_normal_fifo.u_fifo_cnt.gen_secure_ptrs.u_wptr,
alert_tx_o[1]
)
`ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(
MsgFifoRptrCheck_A,
u_msgfifo.u_msgfifo.gen_normal_fifo.u_fifo_cnt.gen_secure_ptrs.u_rptr,
alert_tx_o[1]
)
end
// Alert assertions for reg_we onehot check
`ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg, alert_tx_o[1])
endmodule