| // Copyright lowRISC contributors. | 
 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 | // | 
 | // Key manager interface to kmac | 
 | // | 
 |  | 
 | `include "prim_assert.sv" | 
 |  | 
 | module keymgr_kmac_if import keymgr_pkg::*;( | 
 |   input clk_i, | 
 |   input rst_ni, | 
 |  | 
 |   // data input interfaces | 
 |   input [AdvDataWidth-1:0] adv_data_i, | 
 |   input [IdDataWidth-1:0] id_data_i, | 
 |   input [GenDataWidth-1:0] gen_data_i, | 
 |   input [3:0] inputs_invalid_i, | 
 |   output logic inputs_invalid_o, | 
 |  | 
 |   // keymgr control to select appropriate inputs | 
 |   input adv_en_i, | 
 |   input id_en_i, | 
 |   input gen_en_i, | 
 |   output logic done_o, | 
 |   output logic [Shares-1:0][kmac_pkg::AppDigestW-1:0] data_o, | 
 |  | 
 |   // actual connection to kmac | 
 |   output kmac_pkg::app_req_t kmac_data_o, | 
 |   input  kmac_pkg::app_rsp_t kmac_data_i, | 
 |  | 
 |   // entropy input | 
 |   output logic prng_en_o, | 
 |   input [Shares-1:0][RandWidth-1:0] entropy_i, | 
 |  | 
 |   // error outputs | 
 |   output logic fsm_error_o, | 
 |   output logic kmac_error_o, | 
 |   output logic cmd_error_o | 
 | ); | 
 |  | 
 |  | 
 |   // Encoding generated with: | 
 |   // $ ./util/design/sparse-fsm-encode.py -d 5 -m 6 -n 10 \ | 
 |   //      -s 2292624416 --language=sv | 
 |   // | 
 |   // Hamming distance histogram: | 
 |   // | 
 |   //  0: -- | 
 |   //  1: -- | 
 |   //  2: -- | 
 |   //  3: -- | 
 |   //  4: -- | 
 |   //  5: |||||||||||||||||||| (46.67%) | 
 |   //  6: ||||||||||||||||| (40.00%) | 
 |   //  7: ||||| (13.33%) | 
 |   //  8: -- | 
 |   //  9: -- | 
 |   // 10: -- | 
 |   // | 
 |   // Minimum Hamming distance: 5 | 
 |   // Maximum Hamming distance: 7 | 
 |   // Minimum Hamming weight: 2 | 
 |   // Maximum Hamming weight: 9 | 
 |   // | 
 |   localparam int StateWidth = 10; | 
 |   typedef enum logic [StateWidth-1:0] { | 
 |     StIdle    = 10'b1110100010, | 
 |     StTx      = 10'b0010011011, | 
 |     StTxLast  = 10'b0101000000, | 
 |     StOpWait  = 10'b1000101001, | 
 |     StClean   = 10'b1111111101, | 
 |     StError   = 10'b0011101110 | 
 |   } data_state_e; | 
 |  | 
 |   localparam int AdvRem = AdvDataWidth % KmacDataIfWidth; | 
 |   localparam int IdRem  = IdDataWidth  % KmacDataIfWidth; | 
 |   localparam int GenRem = GenDataWidth % KmacDataIfWidth; | 
 |  | 
 |   // the remainder must be in number of bytes | 
 |   `ASSERT_INIT(AdvRemBytes_A, AdvRem % 8 == 0) | 
 |   `ASSERT_INIT(IdRemBytes_A,  IdRem  % 8 == 0) | 
 |   `ASSERT_INIT(GenRemBytes_A, GenRem % 8 == 0) | 
 |  | 
 |   // Number of kmac transactions required | 
 |   localparam int AdvRounds = (AdvDataWidth + KmacDataIfWidth - 1) / KmacDataIfWidth; | 
 |   localparam int IdRounds  = (IdDataWidth + KmacDataIfWidth - 1) / KmacDataIfWidth; | 
 |   localparam int GenRounds = (GenDataWidth + KmacDataIfWidth - 1) / KmacDataIfWidth; | 
 |   localparam int MaxRounds = KDFMaxWidth  / KmacDataIfWidth; | 
 |  | 
 |   // calculated parameters for number of roudns and interface width | 
 |   localparam int CntWidth = $clog2(MaxRounds); | 
 |   localparam int IfBytes = KmacDataIfWidth / 8; | 
 |   localparam int DecoyCopies = KmacDataIfWidth / 32; | 
 |   localparam int DecoyOutputCopies = (kmac_pkg::AppDigestW / 32) * Shares; | 
 |  | 
 |   localparam int unsigned LastAdvRoundInt = AdvRounds - 1; | 
 |   localparam int unsigned LastIdRoundInt = IdRounds - 1; | 
 |   localparam int unsigned LastGenRoundInt = GenRounds - 1; | 
 |   localparam bit [CntWidth-1:0] LastAdvRound = LastAdvRoundInt[CntWidth-1:0]; | 
 |   localparam bit [CntWidth-1:0] LastIdRound = LastIdRoundInt[CntWidth-1:0]; | 
 |   localparam bit [CntWidth-1:0] LastGenRound = LastGenRoundInt[CntWidth-1:0]; | 
 |  | 
 |   // byte mask for the last transfer | 
 |   localparam logic [IfBytes-1:0] AdvByteMask = (AdvRem > 0) ? (2**(AdvRem/8)-1) : {IfBytes{1'b1}}; | 
 |   localparam logic [IfBytes-1:0] IdByteMask  = (IdRem > 0)  ? (2**(IdRem/8)-1)  : {IfBytes{1'b1}}; | 
 |   localparam logic [IfBytes-1:0] GenByteMask = (GenRem > 0) ? (2**(GenRem/8)-1) : {IfBytes{1'b1}}; | 
 |  | 
 |   logic [MaxRounds-1:0][KmacDataIfWidth-1:0] adv_data; | 
 |   logic [MaxRounds-1:0][KmacDataIfWidth-1:0] id_data; | 
 |   logic [MaxRounds-1:0][KmacDataIfWidth-1:0] gen_data; | 
 |   logic [CntWidth-1:0] cnt; | 
 |   logic [CntWidth-1:0] rounds; | 
 |   logic [KmacDataIfWidth-1:0] decoy_data; | 
 |   logic valid; | 
 |   logic last; | 
 |   logic [IfBytes-1:0] strb; | 
 |   logic cnt_clr, cnt_set, cnt_en; | 
 |   logic start; | 
 |   logic [3:0] inputs_invalid_d, inputs_invalid_q; | 
 |   logic clr_err; | 
 |  | 
 |   data_state_e state_q, state_d; | 
 |  | 
 |   // 0 pad to the appropriate width | 
 |   // this is basically for scenarios where *DataWidth % KmacDataIfWidth != 0 | 
 |   assign adv_data = KDFMaxWidth'(adv_data_i); | 
 |   assign id_data  = KDFMaxWidth'(id_data_i); | 
 |   assign gen_data = KDFMaxWidth'(gen_data_i); | 
 |  | 
 |   assign start = adv_en_i | id_en_i | gen_en_i; | 
 |  | 
 |   logic cnt_err; | 
 |   // SEC_CM: KMAC_IF.CTR.REDUN | 
 |   prim_count #( | 
 |     .Width(CntWidth), | 
 |     .OutSelDnCnt(1'b1), | 
 |     .CntStyle(prim_count_pkg::CrossCnt) | 
 |   ) u_cnt ( | 
 |     .clk_i, | 
 |     .rst_ni, | 
 |     .clr_i(cnt_clr), | 
 |     .set_i(cnt_set), | 
 |     .set_cnt_i(rounds), | 
 |     .en_i(cnt_en), | 
 |     .step_i(CntWidth'(1'b1)), | 
 |     .cnt_o(cnt), | 
 |     .err_o(cnt_err) | 
 |   ); | 
 |  | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       inputs_invalid_q <= '0; | 
 |     end else begin | 
 |       inputs_invalid_q <= inputs_invalid_d; | 
 |     end | 
 |    end | 
 |  | 
 |   // This primitive is used to place a size-only constraint on the | 
 |   // flops in order to prevent FSM state encoding optimizations. | 
 |   logic [StateWidth-1:0] state_raw_q; | 
 |   assign state_q = data_state_e'(state_raw_q); | 
 |  | 
 |   // SEC_CM: KMAC_IF.FSM.SPARSE | 
 |   prim_sparse_fsm_flop #( | 
 |     .StateEnumT(data_state_e), | 
 |     .Width(StateWidth), | 
 |     .ResetValue(StateWidth'(StIdle)) | 
 |   ) u_state_regs ( | 
 |     .clk_i, | 
 |     .rst_ni, | 
 |     .state_i ( state_d     ), | 
 |     .state_o ( state_raw_q ) | 
 |   ); | 
 |  | 
 |   always_comb begin | 
 |     cnt_clr = 1'b0; | 
 |     cnt_set = 1'b0; | 
 |     cnt_en  = 1'b0; | 
 |     valid   = 1'b0; | 
 |     last    = 1'b0; | 
 |     strb    = '0; | 
 |     done_o  = 1'b0; | 
 |     state_d = state_q; | 
 |     rounds  = '0; | 
 |  | 
 |     clr_err = '0; | 
 |     fsm_error_o = '0; | 
 |     kmac_error_o = '0; | 
 |  | 
 |     unique case (state_q) | 
 |  | 
 |       StIdle: begin | 
 |         // if for some reason multiple bits are set, adv_en has priority | 
 |         // as the current key state will be destroyed | 
 |         if (start) begin | 
 |           cnt_set = 1'b1; | 
 |           if (adv_en_i) begin | 
 |             rounds = LastAdvRound; | 
 |           end else if (id_en_i) begin | 
 |             rounds = LastIdRound; | 
 |           end else if (gen_en_i) begin | 
 |             rounds = LastGenRound; | 
 |           end | 
 |  | 
 |           // we are sending only 1 entry | 
 |           state_d = (rounds == 0) ? StTxLast : StTx; | 
 |         end | 
 |       end | 
 |  | 
 |       StTx: begin | 
 |         valid = 1'b1; | 
 |         strb = {IfBytes{1'b1}}; | 
 |  | 
 |         // transaction accepted | 
 |         if (kmac_data_i.ready) begin | 
 |           cnt_en = 1'b1; | 
 |  | 
 |           // second to last beat | 
 |           if (cnt == CntWidth'(1'b1)) begin | 
 |             state_d = StTxLast; | 
 |           end | 
 |         end | 
 |  | 
 |         if (cnt_err) begin | 
 |           state_d = StError; | 
 |         end | 
 |  | 
 |       end | 
 |  | 
 |       StTxLast: begin | 
 |         valid = 1'b1; | 
 |         last = 1'b1; | 
 |  | 
 |         if (adv_en_i) begin | 
 |           strb = AdvByteMask; | 
 |         end else if (id_en_i) begin | 
 |           strb = IdByteMask; | 
 |         end else if (gen_en_i) begin | 
 |           strb = GenByteMask; | 
 |         end | 
 |  | 
 |         // transaction accepted | 
 |         cnt_clr = kmac_data_i.ready; | 
 |         state_d = kmac_data_i.ready ? StOpWait : StTxLast; | 
 |  | 
 |         if (cnt_err) begin | 
 |           state_d = StError; | 
 |         end | 
 |       end | 
 |  | 
 |       StOpWait: begin | 
 |         if (kmac_data_i.done) begin | 
 |           kmac_error_o = kmac_data_i.error; | 
 |           done_o = 1'b1; | 
 |           state_d = StClean; | 
 |         end | 
 |       end | 
 |  | 
 |       StClean: begin | 
 |         done_o = 1'b1; | 
 |  | 
 |         // wait for control side to ack done by waiting start de-assertion | 
 |         if (!start) begin | 
 |           done_o = 1'b0; | 
 |           clr_err = 1'b1; | 
 |           state_d = StIdle; | 
 |         end | 
 |       end | 
 |  | 
 |       // trigger error | 
 |       default: begin | 
 |         // This state is terminal | 
 |         done_o = 1'b1; | 
 |         fsm_error_o = 1'b1; | 
 |       end | 
 |  | 
 |  | 
 |     endcase // unique case (state_q) | 
 |   end | 
 |  | 
 |   // If an fsm error is detected, there is no guarantee the transaction can completely gracefully. | 
 |   // Allow the transaction to terminate early with random data. | 
 |   assign data_o = start && done_o && !fsm_error_o ? {kmac_data_i.digest_share1, | 
 |                                                      kmac_data_i.digest_share0} : | 
 |                                                     {DecoyOutputCopies{entropy_i[0]}}; | 
 |  | 
 |   // The input invalid check is done whenever transactions are ongoing with kmac | 
 |   // once set, it cannot be unset until transactions are fully complete | 
 |   always_comb begin | 
 |     inputs_invalid_d = inputs_invalid_q; | 
 |  | 
 |     if (clr_err) begin | 
 |       inputs_invalid_d = '0; | 
 |     end else if (valid) begin | 
 |       inputs_invalid_d[OpAdvance]  = adv_en_i & (inputs_invalid_i[OpAdvance] | | 
 |                                                  inputs_invalid_q[OpAdvance]); | 
 |       inputs_invalid_d[OpGenId]    = id_en_i  & (inputs_invalid_i[OpGenId]   | | 
 |                                                  inputs_invalid_q[OpGenId]); | 
 |       inputs_invalid_d[OpGenSwOut] = gen_en_i & (inputs_invalid_i[OpGenSwOut]| | 
 |                                                  inputs_invalid_q[OpGenSwOut]); | 
 |       inputs_invalid_d[OpGenHwOut] = gen_en_i & (inputs_invalid_i[OpGenHwOut]| | 
 |                                                  inputs_invalid_q[OpGenHwOut]); | 
 |     end | 
 |   end | 
 |  | 
 |   // immediately assert errors | 
 |   assign inputs_invalid_o = |inputs_invalid_d; | 
 |  | 
 |   logic [CntWidth-1:0] adv_sel, id_sel, gen_sel; | 
 |   assign adv_sel = LastAdvRound - cnt; | 
 |   assign id_sel = LastIdRound - cnt; | 
 |   assign gen_sel = LastGenRound - cnt; | 
 |  | 
 |   // The count is maintained as a downcount | 
 |   // so a subtract is necessary to send the right byte | 
 |   // alternatively we can also reverse the order of the input | 
 |   assign decoy_data = {DecoyCopies{entropy_i[1]}}; | 
 |   always_comb begin | 
 |     kmac_data_o.data  = decoy_data; | 
 |     if (|cmd_error_o || inputs_invalid_o || fsm_error_o) begin | 
 |       kmac_data_o.data  = decoy_data; | 
 |     end else if (valid && adv_en_i) begin | 
 |       kmac_data_o.data  = adv_data[adv_sel]; | 
 |     end else if (valid && id_en_i) begin | 
 |       kmac_data_o.data  = id_data[id_sel]; | 
 |     end else if (valid && gen_en_i) begin | 
 |       kmac_data_o.data  = gen_data[gen_sel]; | 
 |     end | 
 |   end | 
 |  | 
 |   assign kmac_data_o.valid = valid; | 
 |   assign kmac_data_o.last  = last; | 
 |   assign kmac_data_o.strb  = strb; | 
 |  | 
 |   // the enables must be 1 hot | 
 |   logic [2:0] enables, enables_sub; | 
 |   assign enables = {adv_en_i, id_en_i, gen_en_i}; | 
 |   assign enables_sub = enables - 1'b1; | 
 |  | 
 |   // if a one hot error occurs, latch onto it permanently | 
 |   logic one_hot_err_q, one_hot_err_d; | 
 |   assign one_hot_err_d = |(enables & enables_sub); | 
 |  | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       one_hot_err_q <= '0; | 
 |     end else if (one_hot_err_d) begin | 
 |       one_hot_err_q <= '1; | 
 |     end | 
 |   end | 
 |  | 
 |   // command error occurs if kmac errors or if the command itself is invalid | 
 |   assign cmd_error_o = one_hot_err_q; | 
 |  | 
 |   // request entropy to churn whenever a transaction is accepted | 
 |   assign prng_en_o = kmac_data_o.valid & kmac_data_i.ready; | 
 |  | 
 |   // as long as we are transmitting, the strobe should never be 0. | 
 |   `ASSERT(LastStrb_A, valid |-> strb != '0) | 
 |  | 
 |  | 
 | endmodule // keymgr_kmac_if |