|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | // | 
|  | // KMAC Application interface | 
|  |  | 
|  | `include "prim_assert.sv" | 
|  |  | 
|  | module kmac_app | 
|  | import kmac_pkg::*; | 
|  | #( | 
|  | // App specific configs are defined in kmac_pkg | 
|  | parameter  bit EnMasking = 1'b0, | 
|  | localparam int Share = (EnMasking) ? 2 : 1, // derived parameter | 
|  | parameter  bit SecIdleAcceptSwMsg = 1'b0 | 
|  | ) ( | 
|  | input clk_i, | 
|  | input rst_ni, | 
|  |  | 
|  | // Secret Key from register | 
|  | input [MaxKeyLen-1:0] reg_key_data_i [Share], | 
|  | input key_len_e       reg_key_len_i, | 
|  |  | 
|  | // Prefix from register | 
|  | input [sha3_pkg::NSRegisterSize*8-1:0] reg_prefix_i, | 
|  |  | 
|  | // mode, strength, kmac_en from register | 
|  | input                             reg_kmac_en_i, | 
|  | input sha3_pkg::sha3_mode_e       reg_sha3_mode_i, | 
|  | input sha3_pkg::keccak_strength_e reg_keccak_strength_i, | 
|  |  | 
|  | // Data from Software | 
|  | input                sw_valid_i, | 
|  | input [MsgWidth-1:0] sw_data_i, | 
|  | input [MsgWidth-1:0] sw_mask_i, | 
|  | output logic         sw_ready_o, | 
|  |  | 
|  | // KeyMgr Sideload Key interface | 
|  | input keymgr_pkg::hw_key_req_t keymgr_key_i, | 
|  |  | 
|  | // Application Message in/ Digest out interface + control signals | 
|  | input  app_req_t [NumAppIntf-1:0] app_i, | 
|  | output app_rsp_t [NumAppIntf-1:0] app_o, | 
|  |  | 
|  | // to KMAC Core: Secret key | 
|  | output [MaxKeyLen-1:0] key_data_o [Share], | 
|  | output key_len_e       key_len_o, | 
|  |  | 
|  | // to MSG_FIFO | 
|  | output logic                kmac_valid_o, | 
|  | output logic [MsgWidth-1:0] kmac_data_o, | 
|  | output logic [MsgWidth-1:0] kmac_mask_o, | 
|  | input                       kmac_ready_i, | 
|  |  | 
|  | // KMAC Core | 
|  | output logic kmac_en_o, | 
|  |  | 
|  | // To Sha3 Core | 
|  | output logic [sha3_pkg::NSRegisterSize*8-1:0] sha3_prefix_o, | 
|  | output sha3_pkg::sha3_mode_e                  sha3_mode_o, | 
|  | output sha3_pkg::keccak_strength_e            keccak_strength_o, | 
|  |  | 
|  | // STATE from SHA3 Core | 
|  | input                        keccak_state_valid_i, | 
|  | input [sha3_pkg::StateW-1:0] keccak_state_i [Share], | 
|  |  | 
|  | // to STATE TL-window if Application is not active, the incoming state goes to | 
|  | // register if kdf_en is set, the state value goes to application and the | 
|  | // output to the register is all zero. | 
|  | output logic                        reg_state_valid_o, | 
|  | output logic [sha3_pkg::StateW-1:0] reg_state_o [Share], | 
|  |  | 
|  | // Configurations If key_en is set, the logic uses KeyMgr's sideloaded key as | 
|  | // a secret key rather than register values. This only affects when software | 
|  | // initiates. If App initiates the hash operation and uses KMAC algorithm, it | 
|  | // always uses sideloaded key. | 
|  | input keymgr_key_en_i, | 
|  |  | 
|  | // Commands | 
|  | // Command from software | 
|  | input kmac_cmd_e sw_cmd_i, | 
|  |  | 
|  | // from SHA3 | 
|  | input absorbed_i, | 
|  |  | 
|  | // to KMAC | 
|  | output kmac_cmd_e cmd_o, | 
|  |  | 
|  | // to SW | 
|  | output logic absorbed_o, | 
|  |  | 
|  | // To status | 
|  | output logic app_active_o, | 
|  |  | 
|  | // Error input | 
|  | // This error comes from KMAC/SHA3 engine. | 
|  | // KeyMgr interface delivers the error signal to KeyMgr to drop the current op | 
|  | // and re-initiate. | 
|  | // If error happens, regardless of SW-initiated or KeyMgr-initiated, the error | 
|  | // is reported to the ERR_CODE so that SW can look into. | 
|  | input error_i, | 
|  |  | 
|  | // SW sets err_processed bit in CTRL then the logic goes to Idle | 
|  | input err_processed_i, | 
|  |  | 
|  | // error_o value is pushed to Error FIFO at KMAC/SHA3 top and reported to SW | 
|  | output kmac_pkg::err_t error_o | 
|  | ); | 
|  |  | 
|  | ///////////////// | 
|  | // Definitions // | 
|  | ///////////////// | 
|  |  | 
|  | // Digest width is same to the key width `keymgr_pkg::KeyWidth`. | 
|  | localparam int KeyMgrKeyW = $bits(keymgr_key_i.key[0]); | 
|  |  | 
|  | localparam key_len_e KeyLen [5] = '{Key128, Key192, Key256, Key384, Key512}; | 
|  |  | 
|  | localparam int SelKeySize = (AppKeyW == 128) ? 0 : | 
|  | (AppKeyW == 192) ? 1 : | 
|  | (AppKeyW == 256) ? 2 : | 
|  | (AppKeyW == 384) ? 3 : | 
|  | (AppKeyW == 512) ? 4 : 0 ; | 
|  | localparam int SelDigSize = (AppDigestW == 128) ? 0 : | 
|  | (AppDigestW == 192) ? 1 : | 
|  | (AppDigestW == 256) ? 2 : | 
|  | (AppDigestW == 384) ? 3 : | 
|  | (AppDigestW == 512) ? 4 : 0 ; | 
|  | localparam key_len_e SideloadedKey = KeyLen[SelKeySize]; | 
|  |  | 
|  | // Define right_encode(outlen) value here | 
|  | // Look at kmac_pkg::key_len_e for the kinds of key size | 
|  | // | 
|  | // These values should be exactly the same as the key length encodings | 
|  | // in kmac_core.sv, with the only difference being that the byte representing | 
|  | // the byte-length of the encoded value is in the MSB position due to right encoding | 
|  | // instead of in the LSB position (left encoding). | 
|  | localparam int OutLenW = 24; | 
|  | localparam logic [OutLenW-1:0] EncodedOutLen [5]= '{ | 
|  | 24'h 0001_80, // Key128 | 
|  | 24'h 0001_C0, // Key192 | 
|  | 24'h 02_0001, // Key256 | 
|  | 24'h 02_8001, // Key384 | 
|  | 24'h 02_0002  // Key512 | 
|  | }; | 
|  |  | 
|  | localparam logic [OutLenW-1:0] EncodedOutLenMask [5] = '{ | 
|  | 24'h 00FFFF, // Key128, | 
|  | 24'h 00FFFF, // Key192 | 
|  | 24'h FFFFFF, // Key256 | 
|  | 24'h FFFFFF, // Key384 | 
|  | 24'h FFFFFF  // Key512 | 
|  | }; | 
|  |  | 
|  | // Encoding generated with: | 
|  | // $ ./util/design/sparse-fsm-encode.py -d 3 -m 9 -n 10 \ | 
|  | //      -s 155490773 --language=sv | 
|  | // | 
|  | // Hamming distance histogram: | 
|  | // | 
|  | //  0: -- | 
|  | //  1: -- | 
|  | //  2: -- | 
|  | //  3: |||||||||| (16.67%) | 
|  | //  4: |||||||||||||||||||| (30.56%) | 
|  | //  5: |||||||||||||||| (25.00%) | 
|  | //  6: ||||||||| (13.89%) | 
|  | //  7: ||||||||| (13.89%) | 
|  | //  8: -- | 
|  | //  9: -- | 
|  | // 10: -- | 
|  | // | 
|  | // Minimum Hamming distance: 3 | 
|  | // Maximum Hamming distance: 7 | 
|  | // Minimum Hamming weight: 2 | 
|  | // Maximum Hamming weight: 9 | 
|  | // | 
|  | localparam int StateWidth = 10; | 
|  |  | 
|  | // States | 
|  | typedef enum logic [StateWidth-1:0] { | 
|  | StIdle = 10'b1011011010, | 
|  |  | 
|  | // Application operation. | 
|  | // | 
|  | // if start request comes from an App first, until the operation ends by the | 
|  | // requested App, all operations are granted to the specific App. SW | 
|  | // requests and other Apps requests will be ignored. | 
|  | // | 
|  | // App interface does not have control signals. When first data valid occurs | 
|  | // from an App, this logic asserts the start command to the downstream. When | 
|  | // last beat pulse comes, this logic asserts the process to downstream | 
|  | // (after the transaction is accepted regardless of partial writes or not) | 
|  | // When absorbed by SHA3 core, the logic sends digest to the requested App | 
|  | // and right next cycle, it triggers done command to downstream. | 
|  |  | 
|  | // In StAppCfg state, it latches the cfg from AppCfg parameter to determine | 
|  | // the kmac_mode, sha3_mode, keccak strength. | 
|  | StAppCfg = 10'b0001010000, | 
|  |  | 
|  | StAppMsg = 10'b0001011111, | 
|  |  | 
|  | // In StKeyOutLen, this module pushes encoded outlen to the MSG_FIFO. | 
|  | // Assume the length is 256 bit, the data will be 48'h 02_0100 | 
|  | StAppOutLen  = 10'b1011001111, | 
|  | StAppProcess = 10'b1000100110, | 
|  | StAppWait    = 10'b0010010110, | 
|  |  | 
|  | // SW Controlled | 
|  | // If start request comes from SW first, until the operation ends, all | 
|  | // requests from KeyMgr will be discarded. | 
|  | StSw = 10'b0111111111, | 
|  |  | 
|  | // Error KeyNotValid | 
|  | // When KeyMgr operates, the secret key is not ready yet. | 
|  | StKeyMgrErrKeyNotValid = 10'b1001110100, | 
|  |  | 
|  | StError = 10'b1101011101 | 
|  | } st_e; | 
|  |  | 
|  | ///////////// | 
|  | // Signals // | 
|  | ///////////// | 
|  |  | 
|  | st_e st, st_d; | 
|  |  | 
|  | // app_rsp_t signals | 
|  | // The state machine controls mux selection, which controls the ready signal | 
|  | // the other responses are controled in separate logic. So define the signals | 
|  | // here and merge them to the response. | 
|  | logic app_data_ready, fsm_data_ready; | 
|  | logic app_digest_done, fsm_digest_done_q, fsm_digest_done_d; | 
|  | logic [AppDigestW-1:0] app_digest [2]; | 
|  |  | 
|  | // One more slot for value NumAppIntf. It is the value when no app intf is | 
|  | // chosen. | 
|  | localparam int unsigned AppIdxW = $clog2(NumAppIntf); | 
|  |  | 
|  | // app_id indicates, which app interface was chosen. various logic use this | 
|  | // value to get the config or return the data. | 
|  | logic [AppIdxW-1:0] app_id, app_id_d; | 
|  | logic               clr_appid, set_appid; | 
|  |  | 
|  | // Output length | 
|  | logic [OutLenW-1:0] encoded_outlen, encoded_outlen_mask; | 
|  |  | 
|  | // state output | 
|  | // Mux selection signal | 
|  | app_mux_sel_e mux_sel; | 
|  |  | 
|  | // Error checking logic | 
|  |  | 
|  | kmac_pkg::err_t fsm_err, mux_err; | 
|  |  | 
|  | //////////////////////////// | 
|  | // Application Mux/ Demux // | 
|  | //////////////////////////// | 
|  |  | 
|  |  | 
|  | // Processing return data. | 
|  | // sends to only selected app intf. | 
|  | // clear digest right after done to not leak info to other interface | 
|  | always_comb begin | 
|  | for (int unsigned i = 0 ; i < NumAppIntf ; i++) begin | 
|  | if (i == app_id) begin | 
|  | app_o[i] = '{ | 
|  | ready:         app_data_ready | fsm_data_ready, | 
|  | done:          app_digest_done | fsm_digest_done_q, | 
|  | digest_share0: app_digest[0], | 
|  | digest_share1: app_digest[1], | 
|  | // if fsm asserts done, should be an error case. | 
|  | error:         error_i | fsm_digest_done_q | 
|  | }; | 
|  | end else begin | 
|  | app_o[i] = '{ | 
|  | ready: 1'b 0, | 
|  | done:  1'b 0, | 
|  | digest_share0: '0, | 
|  | digest_share1: '0, | 
|  | error: 1'b 0 | 
|  | }; | 
|  | end | 
|  | end // for {i, NumAppIntf, i++} | 
|  | end // aiways_comb | 
|  |  | 
|  | // app_id latch | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) app_id <= AppIdxW'(0) ; // Do not select any | 
|  | else if (clr_appid) app_id <= AppIdxW'(0); | 
|  | else if (set_appid) app_id <= app_id_d; | 
|  | end | 
|  |  | 
|  | // app_id selection as of now, app_id uses Priority. The assumption is that | 
|  | //  the request normally does not collide. (ROM_CTRL activates very early | 
|  | //  stage at the boot sequence) | 
|  | // | 
|  | //  If this assumption is not true, consider RR arbiter. | 
|  |  | 
|  | // Prep for arbiter | 
|  | logic [NumAppIntf-1:0] app_reqs; | 
|  | logic [NumAppIntf-1:0] unused_app_gnts; | 
|  | logic [$clog2(NumAppIntf)-1:0] arb_idx; | 
|  | logic arb_valid; | 
|  | logic arb_ready; | 
|  |  | 
|  | always_comb begin | 
|  | app_reqs = '0; | 
|  | for (int unsigned i = 0 ; i < NumAppIntf ; i++) begin | 
|  | app_reqs[i] = app_i[i].valid; | 
|  | end | 
|  | end | 
|  |  | 
|  | prim_arbiter_fixed #( | 
|  | .N (NumAppIntf), | 
|  | .DW(1), | 
|  | .EnDataPort(1'b 0) | 
|  | ) u_appid_arb ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  |  | 
|  | .req_i  (app_reqs), | 
|  | .data_i ('{default:'0}), | 
|  | .gnt_o  (unused_app_gnts), | 
|  | .idx_o  (arb_idx), | 
|  |  | 
|  | .valid_o (arb_valid), | 
|  | .data_o  (), // not used | 
|  | .ready_i (arb_ready) | 
|  | ); | 
|  |  | 
|  | assign app_id_d = AppIdxW'(arb_idx); | 
|  | assign arb_ready = set_appid; | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) fsm_digest_done_q <= 1'b 0; | 
|  | else         fsm_digest_done_q <= fsm_digest_done_d; | 
|  | end | 
|  |  | 
|  | ///////// | 
|  | // FSM // | 
|  | ///////// | 
|  |  | 
|  | // State register | 
|  | logic [StateWidth-1:0] st_raw; | 
|  | assign st = st_e'(st_raw); | 
|  | prim_flop #( | 
|  | .Width      (StateWidth), | 
|  | .ResetValue (StateWidth'(StIdle)) | 
|  | ) u_state_regs ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .d_i ( st_d   ), | 
|  | .q_o ( st_raw ) | 
|  | ); | 
|  |  | 
|  | // Next State & output logic | 
|  | always_comb begin | 
|  | st_d = StIdle; | 
|  |  | 
|  | mux_sel = SecIdleAcceptSwMsg ? SelSw : SelNone; | 
|  |  | 
|  | // app_id control | 
|  | set_appid = 1'b 0; | 
|  | clr_appid = 1'b 0; | 
|  |  | 
|  | // Commands | 
|  | cmd_o = CmdNone; | 
|  |  | 
|  | // Software output | 
|  | absorbed_o = 1'b 0; | 
|  |  | 
|  | // Error | 
|  | fsm_err = '{valid: 1'b 0, code: ErrNone, info: '0}; | 
|  |  | 
|  | // If error happens, FSM asserts data ready but discard incoming msg | 
|  | fsm_data_ready = 1'b 0; | 
|  | fsm_digest_done_d = 1'b 0; | 
|  |  | 
|  | unique case (st) | 
|  | StIdle: begin | 
|  | if (arb_valid) begin | 
|  | st_d = StAppCfg; | 
|  |  | 
|  | // choose app_id | 
|  | set_appid = 1'b 1; | 
|  | end else if (sw_cmd_i == CmdStart) begin | 
|  | st_d = StSw; | 
|  | // Software initiates the sequence | 
|  | cmd_o = CmdStart; | 
|  | end else begin | 
|  | st_d = StIdle; | 
|  | end | 
|  | end | 
|  |  | 
|  | StAppCfg: begin | 
|  |  | 
|  | if ((AppCfg[app_id].Mode == AppKMAC) && !keymgr_key_i.valid) begin | 
|  | st_d = StKeyMgrErrKeyNotValid; | 
|  |  | 
|  | // As mux_sel is not set to SelApp, app_data_ready is still 0. | 
|  | // This logic won't accept the requests from the selected App. | 
|  |  | 
|  | end else begin | 
|  | // As Cfg is stable now, it sends cmd | 
|  | st_d = StAppMsg; | 
|  |  | 
|  | // App initiates the data | 
|  | cmd_o = CmdStart; | 
|  | end | 
|  | end | 
|  |  | 
|  | StAppMsg: begin | 
|  | mux_sel = SelApp; | 
|  | // Wait until the completion (done) from KeyMgr? | 
|  | // Or absorb completion? | 
|  | if (app_i[app_id].valid && app_o[app_id].ready && app_i[app_id].last) begin | 
|  | if (AppCfg[app_id].Mode == AppKMAC) begin | 
|  | st_d = StAppOutLen; | 
|  | end else begin | 
|  | st_d = StAppProcess; | 
|  | end | 
|  | end else begin | 
|  | st_d = StAppMsg; | 
|  | end | 
|  | end | 
|  |  | 
|  | StAppOutLen: begin | 
|  | mux_sel = SelOutLen; | 
|  |  | 
|  | if (kmac_valid_o && kmac_ready_i) begin | 
|  | st_d = StAppProcess; | 
|  | end else begin | 
|  | st_d = StAppOutLen; | 
|  | end | 
|  | end | 
|  |  | 
|  | StAppProcess: begin | 
|  | cmd_o = CmdProcess; | 
|  | st_d = StAppWait; | 
|  | end | 
|  |  | 
|  | StAppWait: begin | 
|  | if (absorbed_i) begin | 
|  | // Send digest to KeyMgr and complete the op | 
|  | st_d = StIdle; | 
|  | cmd_o = CmdDone; | 
|  |  | 
|  | clr_appid = 1'b 1; | 
|  | end else begin | 
|  | st_d = StAppWait; | 
|  | end | 
|  | end | 
|  |  | 
|  | StSw: begin | 
|  | mux_sel = SelSw; | 
|  |  | 
|  | cmd_o = sw_cmd_i; | 
|  | absorbed_o = absorbed_i; | 
|  |  | 
|  | if (sw_cmd_i == CmdDone) begin | 
|  | st_d = StIdle; | 
|  | end else begin | 
|  | st_d = StSw; | 
|  | end | 
|  | end | 
|  |  | 
|  | StKeyMgrErrKeyNotValid: begin | 
|  | st_d = StError; | 
|  |  | 
|  | // As mux_sel is not set to SelApp, app_data_ready is still 0. | 
|  | // This logic won't accept the requests from the selected App. | 
|  | fsm_err.valid = 1'b 1; | 
|  | fsm_err.code = ErrKeyNotValid; | 
|  | fsm_err.info = 24'(app_id); | 
|  | end | 
|  |  | 
|  | StError: begin | 
|  | // In this state, the state machine flush out the request | 
|  | st_d = StError; | 
|  |  | 
|  | fsm_data_ready = 1'b 1; | 
|  |  | 
|  | if (err_processed_i) begin | 
|  | st_d = StIdle; | 
|  |  | 
|  | // clear internal variables | 
|  | clr_appid = 1'b 1; | 
|  | end | 
|  |  | 
|  | if (app_i[app_id].valid && app_i[app_id].last) begin | 
|  | // Send garbage digest to the app interface to complete transaction | 
|  | fsm_digest_done_d = 1'b 1; | 
|  | end | 
|  |  | 
|  | end | 
|  |  | 
|  | default: begin | 
|  | st_d = StIdle; | 
|  | end | 
|  | endcase | 
|  | end | 
|  |  | 
|  | if (SecIdleAcceptSwMsg != 1'b0) begin : gen_lint_err | 
|  | // Create a lint error to reduce the risk of accidentally enabling this feature. | 
|  | logic sec_idle_accept_sw_msg_dummy; | 
|  | assign sec_idle_accept_sw_msg_dummy = (st == StIdle); | 
|  | end | 
|  |  | 
|  | ////////////// | 
|  | // Datapath // | 
|  | ////////////// | 
|  |  | 
|  | // Encoded output length | 
|  | assign encoded_outlen      = EncodedOutLen[SelDigSize]; | 
|  | assign encoded_outlen_mask = EncodedOutLenMask[SelKeySize]; | 
|  |  | 
|  | // Data mux | 
|  | // This is the main part of the KeyMgr interface logic. | 
|  | // The FSM selects KeyMgr interface in a cycle after it receives the first | 
|  | // valid data from KeyMgr. The ready signal to the KeyMgr data interface | 
|  | // represents the MSG_FIFO ready, only when it is in StKeyMgrMsg state. | 
|  | // After KeyMgr sends last beat, the kmac interface (to MSG_FIFO) is switched | 
|  | // to OutLen. OutLen is pre-defined values. See `EncodeOutLen` parameter above. | 
|  | always_comb begin | 
|  | app_data_ready = 1'b 0; | 
|  | sw_ready_o = 1'b 1; | 
|  |  | 
|  | kmac_valid_o = 1'b 0; | 
|  | kmac_data_o = '0; | 
|  | kmac_mask_o = '0; | 
|  |  | 
|  | unique case (mux_sel) | 
|  | SelApp: begin | 
|  | // app_id is valid at this time | 
|  | kmac_valid_o = app_i[app_id].valid; | 
|  | kmac_data_o  = app_i[app_id].data; | 
|  | // Expand strb to bits. prim_packer inside MSG_FIFO accepts the bit masks | 
|  | for (int i = 0 ; i < $bits(app_i[app_id].strb) ; i++) begin | 
|  | kmac_mask_o[8*i+:8] = {8{app_i[app_id].strb[i]}}; | 
|  | end | 
|  | app_data_ready = kmac_ready_i; | 
|  | end | 
|  |  | 
|  | SelOutLen: begin | 
|  | // Write encoded output length value | 
|  | kmac_valid_o = 1'b 1; // always write | 
|  | kmac_data_o  = MsgWidth'(encoded_outlen); | 
|  | kmac_mask_o  = MsgWidth'(encoded_outlen_mask); | 
|  | end | 
|  |  | 
|  | SelSw: begin | 
|  | kmac_valid_o = sw_valid_i; | 
|  | kmac_data_o  = sw_data_i ; | 
|  | kmac_mask_o  = sw_mask_i ; | 
|  | sw_ready_o   = kmac_ready_i ; | 
|  | end | 
|  |  | 
|  | default: begin // Incl. SelNone | 
|  | kmac_valid_o = 1'b 0; | 
|  | kmac_data_o = '0; | 
|  | kmac_mask_o = '0; | 
|  | end | 
|  |  | 
|  | endcase | 
|  | end | 
|  |  | 
|  | // Error checking for Mux | 
|  | always_comb begin | 
|  | mux_err = '{valid: 1'b 0, code: ErrNone, info: '0}; | 
|  |  | 
|  | if (mux_sel != SelSw && sw_valid_i) begin | 
|  | // If SW writes message into FIFO | 
|  | mux_err = '{ | 
|  | valid: 1'b 1, | 
|  | code: ErrSwPushedMsgFifo, | 
|  | info: 24'({8'h 00, 8'(st), 8'(mux_sel)}) | 
|  | }; | 
|  | end else if (app_active_o && sw_cmd_i != CmdNone) begin | 
|  | // If SW issues command except start | 
|  | mux_err = '{ | 
|  | valid: 1'b 1, | 
|  | code: ErrSwIssuedCmdInAppActive, | 
|  | info: 24'(sw_cmd_i) | 
|  | }; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Keccak state Demux | 
|  | // Keccak state --> Register output is enabled when state is in StSw | 
|  | always_comb begin | 
|  | if (mux_sel == SelSw) begin | 
|  | reg_state_valid_o = keccak_state_valid_i; | 
|  | reg_state_o = keccak_state_i; | 
|  | end else begin | 
|  | reg_state_valid_o = 1'b 0; | 
|  | reg_state_o = '{default:'0}; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Keccak state --> KeyMgr | 
|  | always_comb begin | 
|  | app_digest_done = 1'b 0; | 
|  | app_digest = '{default:'0}; | 
|  | if (st == StAppWait && absorbed_i) begin | 
|  | // SHA3 engine has calculated the hash. Return the data to KeyMgr | 
|  | app_digest_done = 1'b 1; | 
|  |  | 
|  | // digest has always 2 entries. If !EnMasking, second is tied to 0. | 
|  | for (int i = 0 ; i < Share ; i++) begin | 
|  | // Return the portion of state. | 
|  | app_digest[i] = keccak_state_i[i][AppDigestW-1:0]; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  |  | 
|  | // Secret Key Mux | 
|  |  | 
|  | // Prepare merged key if EnMasking is not set. | 
|  | // Combine share keys into unpacked array for logic below to assign easily. | 
|  | logic [MaxKeyLen-1:0] keymgr_key [Share]; | 
|  | if (EnMasking == 1) begin : g_masked_key | 
|  | for (genvar i = 0; i < Share; i++) begin : gen_key_pad | 
|  | assign keymgr_key[i] =  {(MaxKeyLen-KeyMgrKeyW)'(0), keymgr_key_i.key[i]}; | 
|  | end | 
|  | end else begin : g_unmasked_key | 
|  | always_comb begin | 
|  | keymgr_key[0] = '0; | 
|  | for (int i = 0; i < Share; i++) begin | 
|  | keymgr_key[0][KeyMgrKeyW-1:0] ^= keymgr_key_i.key[i]; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // Sideloaded key is used when KeyMgr KDF is active or !!CFG.sideload is set | 
|  | always_comb begin | 
|  | if (keymgr_key_en_i || (mux_sel == SelApp)) begin | 
|  | // KeyLen is fixed to the $bits(sideloaded_key) | 
|  | key_len_o = SideloadedKey; | 
|  | end else begin | 
|  | key_len_o = reg_key_len_i; | 
|  | end | 
|  | end | 
|  |  | 
|  | for (genvar i = 0 ; i < Share ; i++) begin : g_key_assign | 
|  | assign key_data_o[i] = (keymgr_key_en_i || (mux_sel == SelApp)) | 
|  | ? keymgr_key[i] | 
|  | : reg_key_data_i[i] ; | 
|  | end | 
|  |  | 
|  | // Prefix Demux | 
|  | // For SW, always prefix register. | 
|  | // For App intf, check PrefixMode cfg and if 1, use Prefix cfg. | 
|  | always_comb begin | 
|  | sha3_prefix_o = '0; | 
|  |  | 
|  | unique case (st) | 
|  | StAppCfg, StAppMsg, StAppOutLen, StAppProcess, StAppWait: begin | 
|  | // Check app intf cfg | 
|  | for (int unsigned i = 0 ; i < NumAppIntf ; i++) begin | 
|  | if (app_id == i) begin | 
|  | if (AppCfg[i].PrefixMode == 1'b 0) begin | 
|  | sha3_prefix_o = reg_prefix_i; | 
|  | end else begin | 
|  | sha3_prefix_o = AppCfg[i].Prefix; | 
|  | end | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | StSw: begin | 
|  | sha3_prefix_o = reg_prefix_i; | 
|  | end | 
|  |  | 
|  | default: begin | 
|  | sha3_prefix_o = reg_prefix_i; | 
|  | end | 
|  | endcase | 
|  | end | 
|  |  | 
|  | // KMAC en / SHA3 mode / Strength | 
|  | //  by default, it uses reg cfg. When app intf reqs come, it uses AppCfg. | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | kmac_en_o         <= 1'b 0; | 
|  | sha3_mode_o       <= sha3_pkg::Sha3; | 
|  | keccak_strength_o <= sha3_pkg::L256; | 
|  | end else if (clr_appid) begin | 
|  | // As App completed, latch reg value | 
|  | kmac_en_o         <= reg_kmac_en_i; | 
|  | sha3_mode_o       <= reg_sha3_mode_i; | 
|  | keccak_strength_o <= reg_keccak_strength_i; | 
|  | end else if (set_appid) begin | 
|  | kmac_en_o         <= AppCfg[arb_idx].Mode == AppKMAC ? 1'b 1 : 1'b 0; | 
|  | sha3_mode_o       <= AppCfg[arb_idx].Mode == AppSHA3 | 
|  | ? sha3_pkg::Sha3 : sha3_pkg::CShake; | 
|  | keccak_strength_o <= AppCfg[arb_idx].Strength ; | 
|  | end else if (st == StIdle) begin | 
|  | kmac_en_o         <= reg_kmac_en_i; | 
|  | sha3_mode_o       <= reg_sha3_mode_i; | 
|  | keccak_strength_o <= reg_keccak_strength_i; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Status | 
|  | assign app_active_o = (st inside {StAppCfg, StAppMsg, StAppOutLen, | 
|  | StAppProcess, StAppWait}); | 
|  |  | 
|  | // Error Reporting ========================================================== | 
|  | always_comb begin | 
|  | priority casez ({fsm_err.valid, mux_err.valid}) | 
|  | 2'b ?1: error_o = mux_err; | 
|  | 2'b 10: error_o = fsm_err; | 
|  | default: error_o = '{valid: 1'b0, code: ErrNone, info: '0}; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | //////////////// | 
|  | // Assertions // | 
|  | //////////////// | 
|  |  | 
|  | // KeyMgr sideload key and the digest should be in the Key Length value | 
|  | `ASSERT_INIT(SideloadKeySameToDigest_A, KeyMgrKeyW <= AppDigestW) | 
|  | `ASSERT_INIT(AppIntfInRange_A, AppDigestW inside {128, 192, 256, 384, 512}) | 
|  |  | 
|  |  | 
|  | endmodule |