blob: 791e221dabfac474b6ffedd5e0eda66ffee7962c [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 Error Checking logic
//
// `kmac_err` module checks the SW introduced errors.
// 1. SW command sequencing error.
// 2. SW configuration error.
//
// ## SW Command Sequencing Error
//
// KMAC assumes the application interface and the SW register interface to
// follow the specific sequence. It expects the requester to send the `Start`
// command then push the message body. The `Process` command follows the message
// body. The SW may issue `Run` command if it needs the digest result more than
// a block rate. Then SW completes the hash operation with `Done` command.
//
// This `kmac_err` module checks if the SW issues the correct command. If not,
// it reports the error via ERR_CODE register.
//
// However, the logic does not prevent the error-ed command to be propagated.
// The unexpected commands are filtered by each individual submodule.
//
// st := { Idle, MsgFeed, Processing, Absorbed, Squeeze}
//
// allowed := {
// Idle : { Start },
// MsgFeed: { Process },
// Processing: { None },
// Absorbed: { Run, Done },
// Squeeze: { None }
// }
//
// ## SW Configuration Error
//
// `kmac_errchk` module checks if SW configured correct combinations of the
// configuration registers when the hashing operation begins.
//
// 1. Mode & Strength combinations
// 2. Kmac Prefix
// * sideload & key_valid -> Checker in kmac_core
`include "prim_assert.sv"
module kmac_errchk
import kmac_pkg::*;
import sha3_pkg::sha3_mode_e;
import sha3_pkg::keccak_strength_e;
(
input clk_i,
input rst_ni,
// Configurations
input sha3_mode_e cfg_mode_i,
input keccak_strength_e cfg_strength_i,
input kmac_en_i,
input [47:0] cfg_prefix_6B_i, // first 6B of PREFIX
// If the signal below is set, errchk propagates the command to the rest of
// the blocks even with err_modestrength.
input cfg_en_unsupported_modestrength_i,
// SW commands: Only valid command is sent out to the rest of the modules
input kmac_cmd_e sw_cmd_i,
output kmac_cmd_e sw_cmd_o,
// Status from KMAC_APP
input app_active_i,
// Status from SHA3 core
input sha3_absorbed_i,
input keccak_done_i,
// Life cycle
input lc_ctrl_pkg::lc_tx_t lc_escalate_en_i,
output err_t error_o,
output logic sparse_fsm_error_o
);
// sha3_pkg::sha3_mode_e
import sha3_pkg::L128;
import sha3_pkg::L224;
import sha3_pkg::L256;
import sha3_pkg::L384;
import sha3_pkg::L512;
// sha3_pkg::keccak_strength_e
import sha3_pkg::Sha3;
import sha3_pkg::Shake;
import sha3_pkg::CShake;
/////////////////
// Definitions //
/////////////////
// Encoding generated with:
// $ ./util/design/sparse-fsm-encode.py -d 3 -m 5 -n 6 \
// -s 2239170217 --language=sv
//
// Hamming distance histogram:
//
// 0: --
// 1: --
// 2: --
// 3: |||||||||||||||||||| (50.00%)
// 4: |||||||||||||||| (40.00%)
// 5: |||| (10.00%)
// 6: --
//
// Minimum Hamming distance: 3
// Maximum Hamming distance: 5
// Minimum Hamming weight: 2
// Maximum Hamming weight: 4
//
localparam int StateWidth = 6;
typedef enum logic [StateWidth-1:0] {
StIdle = 6'b001101,
StMsgFeed = 6'b110001,
StProcessing = 6'b010110,
StAbsorbed = 6'b100010,
StSqueezing = 6'b111100,
StTerminalError = 6'b011011
} st_e;
st_e st, st_d;
localparam int StateWidthL = 3;
typedef enum logic [StateWidthL-1:0] {
StIdleL,
StMsgFeedL,
StProcessingL,
StAbsorbedL,
StSqueezingL,
StErrorL
} st_logical_e;
st_logical_e stL;
/////////////
// Signals //
/////////////
// `err_swsequence` occurs when SW issues wrong command
logic err_swsequence;
// `err_modestrength` occcurs when Mode & Strength combinations are not
// allowed. This error does not block the hashing operation.
// UnexpectedModeStrength may stop the processing based on CFG
// The error raises when SW issues CmdStart.
logic err_modestrength;
// `err_prefix` occurs when the first 6B of !!PREFIX is not
// `encode_string("KMAC")` and kmac is enabled. This error does not block the
// KMAC operation.
logic err_prefix;
// Signal to block the SW command propagation
logic block_swcmd;
///////////////////
// Error Checker //
///////////////////
// SW sequence Error
// info field: Current state, Received command
// SEC_CM: FSM.SPARSE
always_comb begin
err_swsequence = 1'b 0;
sparse_fsm_error_o = 1'b 0;
unique case (st)
StIdle: begin
// Allow Start command only
if (!(sw_cmd_i inside {CmdNone, CmdStart})) begin
err_swsequence = 1'b 1;
end
end
StMsgFeed: begin
// Allow Process only
if (!(sw_cmd_i inside {CmdNone, CmdProcess})) begin
err_swsequence = 1'b 1;
end
end
StProcessing: begin
if (sw_cmd_i != CmdNone) begin
err_swsequence = 1'b 1;
end
end
StAbsorbed: begin
// Allow ManualRun and Done
if (!(sw_cmd_i inside {CmdNone, CmdManualRun, CmdDone})) begin
err_swsequence = 1'b 1;
end
end
StSqueezing: begin
if (sw_cmd_i != CmdNone) begin
err_swsequence = 1'b 1;
end
end
StTerminalError: begin
err_swsequence = 1'b 0;
sparse_fsm_error_o = 1'b 1;
end
default: begin
err_swsequence = 1'b 0;
sparse_fsm_error_o = 1'b 1;
end
endcase
end
assign block_swcmd = (err_swsequence)
|| (err_modestrength
&& !cfg_en_unsupported_modestrength_i);
// sw_cmd_o latch
// To reduce the command path delay, sw_cmd is latched here
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) sw_cmd_o <= CmdNone;
else if (!block_swcmd) sw_cmd_o <= sw_cmd_i;
end
// Mode & Strength
always_comb begin : check_modestrength
err_modestrength = 1'b 0;
if (st == StIdle && st_d == StMsgFeed) begin
// When moving to the next stage, checks the config
if (!((cfg_mode_i == Sha3 &&
cfg_strength_i inside {L224, L256, L384, L512}) ||
((cfg_mode_i == Shake || cfg_mode_i == CShake) &&
(cfg_strength_i inside {L128, L256})))) begin
err_modestrength = 1'b 1;
end
end
end : check_modestrength
// Check prefix 6B is `encode_string("KMAC")`
always_comb begin : check_prefix
err_prefix = 1'b 0;
if (st == StIdle && st_d == StMsgFeed && kmac_en_i) begin
if (cfg_prefix_6B_i != EncodedStringKMAC) begin
err_prefix = 1'b 1;
end
end
end : check_prefix
always_comb begin : recode_st
unique case (st)
StIdle : stL = StIdleL;
StMsgFeed : stL = StMsgFeedL;
StProcessing : stL = StProcessingL;
StAbsorbed : stL = StAbsorbedL;
StSqueezing : stL = StSqueezingL;
default : stL = StErrorL;
endcase
end : recode_st
// Return error code
err_t err;
always_comb begin : err_return
err = '{valid: 1'b0, code: ErrNone, info: '0};
priority case (1'b 1)
err_swsequence: begin
err = '{ valid: 1'b 1,
code: ErrSwCmdSequence,
info: {5'h0,
{err_swsequence, err_modestrength, err_prefix},
8'h 0,
{1'b0, stL, sw_cmd_i}
}
};
end
err_modestrength: begin
err = '{ valid: 1'b 1,
code: ErrUnexpectedModeStrength,
info: { 5'h 0,
{err_swsequence, err_modestrength, err_prefix},
8'h 0,
{2'b 00, cfg_mode_i},
{1'b 0, cfg_strength_i}
}
};
end
err_prefix: begin
err = '{ valid: 1'b 1,
code: ErrIncorrectFunctionName,
info: { 5'h 0,
{err_swsequence, err_modestrength, err_prefix},
16'h 0000
}
};
end
default: begin
err = '{valid: 1'b0, code: ErrNone, info: '0};
end
endcase
end : err_return
assign error_o = err;
// If below failed, revise err_swsequence error response info field.
`ASSERT_INIT(ExpectedStSwCmdBits_A, $bits(st) == StateWidth && $bits(sw_cmd_i) == 4)
// If failed, revise err_modestrength error info field.
`ASSERT_INIT(ExpectedModeStrengthBits_A,
$bits(cfg_mode_i) == 2 && $bits(cfg_strength_i) == 3)
///////////////////
// State Machine //
///////////////////
`PRIM_FLOP_SPARSE_FSM(u_state_regs, st_d, st, st_e, StIdle)
always_comb begin : next_state
st_d = st;
unique case (st)
StIdle: begin
if (!app_active_i && sw_cmd_i == CmdStart) begin
// Proceed to the next state only when the SW issues the Start command
// in a valid period.
st_d = StMsgFeed;
end
end
StMsgFeed: begin
if (sw_cmd_i == CmdProcess) begin
st_d = StProcessing;
end
end
StProcessing: begin
if (sha3_absorbed_i) begin
st_d = StAbsorbed;
end
end
StAbsorbed: begin
if (sw_cmd_i == CmdManualRun) begin
st_d = StSqueezing;
end else if (sw_cmd_i == CmdDone) begin
st_d = StIdle;
end
end
StSqueezing: begin
if (keccak_done_i) begin
st_d = StAbsorbed;
end
end
StTerminalError: begin
// this state is terminal
st_d = st;
end
default: begin
// this state is terminal
st_d = StTerminalError;
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_i != lc_ctrl_pkg::Off) begin
st_d = StTerminalError;
end
end : next_state
`ASSERT_KNOWN(StKnown_A, st)
endmodule : kmac_errchk