| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // SHA3 padding logic |
| |
| `include "prim_assert.sv" |
| |
| module sha3pad |
| import sha3_pkg::*; |
| #( |
| parameter bit EnMasking = 0, |
| localparam int Share = (EnMasking) ? 2 : 1 |
| ) ( |
| input clk_i, |
| input rst_ni, |
| |
| // Message interface (FIFO) |
| input msg_valid_i, |
| input [MsgWidth-1:0] msg_data_i [Share], |
| input [MsgStrbW-1:0] msg_strb_i, // one strobe for shares |
| output logic msg_ready_o, |
| |
| // N, S: Used in cSHAKE mode only |
| input [NSRegisterSize*8-1:0] ns_data_i, // See sha3_pkg for details |
| |
| // output to keccak_round: message path |
| output logic keccak_valid_o, |
| output logic [KeccakMsgAddrW-1:0] keccak_addr_o, |
| output logic [MsgWidth-1:0] keccak_data_o [Share], |
| input logic keccak_ready_i, |
| |
| // keccak_round control and status |
| // `run` initiates the keccak_round to process full keccak_f (24rounds). |
| // `complete` is an input from keccak round showing the current keccak_f is |
| // completed. |
| output logic keccak_run_o, |
| input keccak_complete_i, |
| |
| // configurations |
| input sha3_mode_e mode_i, |
| // strength_i is used in bytepad operation. bytepad() is used in cSHAKE only. |
| // SHA3, SHAKE doesn't have encode_N,S |
| input keccak_strength_e strength_i, |
| |
| // control signal |
| // start_i is a pulse signal triggers the padding logic (and the rest of SHA) |
| // to accept the incoming messages. This signal is used in the pad module, |
| // to initiate the prefix transmitting to keccak_round |
| input start_i, |
| // process_i is a pulse signal triggers the pad logic to stop receiving the |
| // message from MSG_FIFO and pad the trailing bits specified in the SHA3 |
| // standard. Look at `funcpad` signal for the values. |
| input process_i, |
| // done_i is a pulse signal to make the pad logic to clear internal variables |
| // and to move back to the Idle state for next hashing process. |
| // done_i may not needed if sw controls the keccak_round directly. |
| input prim_mubi_pkg::mubi4_t done_i, |
| |
| // Indication of the Keccak Sponge Absorbing is complete, it is time for SW to |
| // control the Keccak-round if it needs more digest, or complete by asserting |
| // `done_i` |
| output prim_mubi_pkg::mubi4_t absorbed_o, |
| |
| // Life cycle |
| input lc_ctrl_pkg::lc_tx_t lc_escalate_en_i, |
| |
| // Indication that there was a fault in the sparse encoding |
| output logic sparse_fsm_error_o, |
| |
| // Indication that there was a fault in the counter |
| output logic msg_count_error_o |
| ); |
| |
| ///////////////// |
| // Definitions // |
| ///////////////// |
| |
| // Padding States |
| // Encoding generated with: |
| // $ ./util/design/sparse-fsm-encode.py -d 3 -m 10 -n 7 \ |
| // -s 1116691466 --language=sv |
| // |
| // Hamming distance histogram: |
| // |
| // 0: -- |
| // 1: -- |
| // 2: -- |
| // 3: |||||||||||||||||||| (42.22%) |
| // 4: |||||||||||||||||| (40.00%) |
| // 5: ||||| (11.11%) |
| // 6: || (4.44%) |
| // 7: | (2.22%) |
| // |
| // Minimum Hamming distance: 3 |
| // Maximum Hamming distance: 7 |
| // Minimum Hamming weight: 2 |
| // Maximum Hamming weight: 5 |
| // |
| localparam int StateWidthPad = 7; |
| typedef enum logic [StateWidthPad-1:0] { |
| StPadIdle = 7'b1000010, |
| |
| // Sending a block of prefix, if cSHAKE mode is turned on. For the rest |
| // (SHA3, SHAKE), sending prefix is not needed. FSM moves from Idle to |
| // Message directly in that case. |
| // |
| // As prim_slicer is instantiated, zerofill after the actual prefix is done |
| // by the module. |
| StPrefix = 7'b0111100, |
| StPrefixWait =7'b1001100, |
| |
| // Sending Message. In this state, it directly forwards the incoming data |
| // to Keccak round module. If `process_i` is asserted, then the rest of the |
| // messages will be discarded until new `start_i` is asserted. |
| // |
| // The incoming data can be partial write. Padding logic counts the number |
| // of bytes received and pause if a block size is transferred. |
| StMessage = 7'b0100101, |
| StMessageWait = 7'b0001111, |
| |
| // After sending the messages, then `process_i` is set, the FSM pads at the |
| // end of the message based on `mode_i`. If this is the last byte of the |
| // block, then it pads [7] to 1 to complete `pad10*1()` function. |
| StPad = 7'b1111010, |
| StPadRun = 7'b0011001, |
| |
| // If the padding isn't the end of the block byte (which will be rare case), |
| // FSM moves to another zerofill state. In contrast to StZerofill, this state |
| StPad01 = 7'b1101001, |
| |
| // Flushing the internal packers in front of the Keccak data output port. |
| StPadFlush = 7'b1010111, |
| |
| StTerminalError = 7'b0110011 |
| } pad_st_e; |
| |
| typedef enum logic [2:0] { |
| MuxNone = 3'b 000, |
| MuxFifo = 3'b 001, |
| MuxPrefix = 3'b 010, |
| MuxFuncPad = 3'b 011, |
| MuxZeroEnd = 3'b 100 |
| } mux_sel_e; |
| |
| //////////////////// |
| // Configurations // |
| //////////////////// |
| |
| logic [KeccakCountW-1:0] block_addr_limit; |
| |
| // Block size based on the address. |
| // This is used for bytepad() and also pad10*1() |
| // assign block_addr_limit = KeccakRate[strength_i]; |
| // but below is easier to understand |
| always_comb begin |
| unique case (strength_i) |
| L128: block_addr_limit = KeccakCountW'(KeccakRate[L128]); |
| L224: block_addr_limit = KeccakCountW'(KeccakRate[L224]); |
| L256: block_addr_limit = KeccakCountW'(KeccakRate[L256]); |
| L384: block_addr_limit = KeccakCountW'(KeccakRate[L384]); |
| L512: block_addr_limit = KeccakCountW'(KeccakRate[L512]); |
| |
| default: block_addr_limit = '0; |
| endcase |
| end |
| |
| ///////////////////// |
| // Control Signals // |
| ///////////////////// |
| |
| // `sel_mux` selects the output data among the incoming or internally generated data. |
| // MuxFifo: data from external (msg_data_i) |
| // MuxPrefix: bytepad(encode_string(N)||encode_string(S), ) |
| // MuxFuncPad: function_pad with end of message |
| // MuxZeroEnd: all 0 |
| mux_sel_e sel_mux; |
| |
| // `sent_message` indicates the number of entries sent to keccak round per |
| // block. The value shall be enough to cover Maximum entry of the Keccak |
| // storage as defined in sha3_pkg, `$clog2(KeccakEntries+1)`. Logically, |
| // it is not needed to have more than KeccakEntries but for safety in case of |
| // SHA3 context switch resuming the SHA3 from the middle of sponge |
| // construction. If needed, the software should be able to write whole 1600 |
| // bits. The `sent_message` is used to check sent_blocksize. |
| logic [KeccakCountW-1:0] sent_message; |
| logic inc_sentmsg, clr_sentmsg; |
| |
| // This primitive is used to place a hardened counter |
| // SEC_CM: CTR.REDUN |
| prim_count #( |
| .Width(KeccakCountW) |
| ) u_sentmsg_count ( |
| .clk_i, |
| .rst_ni, |
| .clr_i(clr_sentmsg), |
| .set_i(1'b0), |
| .set_cnt_i(KeccakCountW'(0)), |
| .incr_en_i(inc_sentmsg), |
| .decr_en_i(1'b0), |
| .step_i(KeccakCountW'(1)), |
| .cnt_o(sent_message), |
| .cnt_next_o(), |
| .err_o(msg_count_error_o) |
| ); |
| |
| |
| assign inc_sentmsg = keccak_valid_o & keccak_ready_i ; |
| |
| // Prefix index to slice the `prefix` n-bits into multiple of 64bit. |
| logic [KeccakMsgAddrW-1:0] prefix_index; |
| assign prefix_index = (sent_message < block_addr_limit) ? sent_message : '0; |
| |
| // fsm_keccak_valid is an output signal from FSM which to send data generated |
| // inside the pad logic to keccak_round |
| logic fsm_keccak_valid; |
| |
| // hold_msg to prevent message from being forwarded into keccak_round and |
| // acked. Mainly the usage is to hold the message and initiates the |
| // keccak_round for current block. |
| logic hold_msg; |
| |
| // latch the partial write. Latched data is used for funcpad_merged |
| logic en_msgbuf; |
| logic clr_msgbuf; |
| |
| /////////////////// |
| // State Machine // |
| /////////////////// |
| |
| // Inputs |
| |
| // FSM moves to StPrefix only when cSHAKE is enabled |
| logic mode_eq_cshake; |
| assign mode_eq_cshake = (mode_i == CShake) ? 1'b 1 : 1'b 0; |
| |
| // `sent_blocksize` indicates the pad logic pushed block size data into |
| // keccak round logic. |
| logic sent_blocksize; |
| |
| assign sent_blocksize = (sent_message == block_addr_limit) ? 1'b 1 : 1'b 0; |
| |
| // `keccak_ack` indicates the request is accepted in keccak_round |
| logic keccak_ack; |
| |
| assign keccak_ack = keccak_valid_o & keccak_ready_i ; |
| |
| // msg_partial indicates the incoming message is partial write or not. |
| // This is used to check if the incoming message need to be latched inside or |
| // not. If no partial message is at the end, msg_buf doesn't have to latch |
| // msg_data_i. It is assumed that the partial message is permitted only at |
| // the end of the message. So if (msg_valid_i && msg_partial && msg_ready_o), |
| // there will be no msg_valid_i till process_latched. |
| // Shall be used with msg_valid_i together. |
| logic msg_partial; |
| assign msg_partial = (&msg_strb_i != 1'b 1); |
| |
| |
| // `process_latched` latches the `process_i` input before it is seen in the |
| // FSM. `process_i` may follow `start_i` too fast so that the FSM may not |
| // see it fast enought in case of cSHAKE mode. cSHAKE needs to process the |
| // prefix prior to see the process indicator. |
| logic process_latched; |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| process_latched <= 1'b 0; |
| end else if (process_i) begin |
| process_latched <= 1'b 1; |
| end else if (prim_mubi_pkg::mubi4_test_true_strict(done_i)) begin |
| process_latched <= 1'b0; |
| end |
| end |
| |
| // State Register =========================================================== |
| pad_st_e st, st_d; |
| |
| `PRIM_FLOP_SPARSE_FSM(u_state_regs, st_d, st, pad_st_e, StPadIdle) |
| |
| // `end_of_block` indicates current beat is end of the block |
| // It shall set when the address reaches to the end of the block. End address |
| // is set by the strength_i, which is `block_addr_limit`. |
| logic end_of_block; |
| |
| assign end_of_block = ((sent_message + 1'b1) == block_addr_limit) ? 1'b 1 : 1'b 0; |
| |
| |
| // Next logic and output logic ============================================== |
| // SEC_CM: ABSORBED.CTRL.MUBI |
| prim_mubi_pkg::mubi4_t absorbed_d; |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) absorbed_o <= prim_mubi_pkg::MuBi4False; |
| else absorbed_o <= absorbed_d; |
| end |
| |
| always_comb begin |
| st_d = st; |
| |
| // FSM output : default values |
| keccak_run_o = 1'b 0; |
| sel_mux = MuxNone; |
| |
| fsm_keccak_valid = 1'b 0; |
| |
| hold_msg = 1'b 0; |
| clr_sentmsg = 1'b 0; |
| |
| en_msgbuf = 1'b 0; |
| clr_msgbuf = 1'b 0; |
| |
| absorbed_d = prim_mubi_pkg::MuBi4False; |
| |
| sparse_fsm_error_o = 1'b 0; |
| |
| unique case (st) |
| |
| // In Idle state, the FSM checks if the software (or upper FSM) initiates |
| // the hash process. If `start_i` is asserted (assume it is pulse), FSM |
| // starts to push the data into the keccak round logic. Depending on the |
| // hashing mode, FSM may push additional prefex in front of the actual |
| // message. It means, the message could be back-pressured until the first |
| // prefix is processed. |
| StPadIdle: begin |
| if (start_i) begin |
| // If cSHAKE, move to Prefix state |
| if (mode_eq_cshake) begin |
| st_d = StPrefix; |
| end else begin |
| st_d = StMessage; |
| end |
| end else begin |
| st_d = StPadIdle; |
| end |
| end |
| |
| // At Prefix state, FSM pushes |
| // `bytepad(encode_string(N)||encode_string(S), 168or136)`. The software |
| // already prepared `encode_string(N) || encode_string(S)` in the regs. |
| // So, the FSM adds 2Byte in front of ns_data_i, which is an encoded |
| // block size (see `encoded_bytepad` below) |
| // After pushing the prefix, it initiates the hash process and move to |
| // Message state. |
| StPrefix: begin |
| sel_mux = MuxPrefix; |
| |
| if (sent_blocksize) begin |
| st_d = StPrefixWait; |
| |
| keccak_run_o = 1'b 1; |
| fsm_keccak_valid = 1'b 0; |
| clr_sentmsg = 1'b 1; |
| end else begin |
| st_d = StPrefix; |
| |
| fsm_keccak_valid = 1'b 1; |
| end |
| end |
| |
| StPrefixWait: begin |
| sel_mux = MuxPrefix; |
| |
| if (keccak_complete_i) begin |
| st_d = StMessage; |
| end else begin |
| st_d = StPrefixWait; |
| end |
| end |
| |
| // Message state pushes the incoming message into keccak round logic. |
| // It forwards the message while counting the data and if it reaches |
| // the block size, it triggers the keccak round to run. If `process` is |
| // set, it moves to Pad state. |
| StMessage: begin |
| sel_mux = MuxFifo; |
| |
| if (msg_valid_i && msg_partial) begin |
| st_d = StMessage; |
| |
| en_msgbuf = 1'b 1; |
| end else if (sent_blocksize) begin |
| // Check block completion first even process is set. |
| st_d = StMessageWait; |
| |
| keccak_run_o = 1'b 1; |
| clr_sentmsg = 1'b 1; |
| hold_msg = 1'b 1; |
| end else if (process_latched || process_i) begin |
| st_d = StPad; |
| |
| // Not asserting the msg_ready_o |
| hold_msg = 1'b 1; |
| end else begin |
| st_d = StMessage; |
| |
| end |
| end |
| |
| StMessageWait: begin |
| hold_msg = 1'b 1; |
| |
| if (keccak_complete_i) begin |
| st_d = StMessage; |
| end else begin |
| st_d = StMessageWait; |
| end |
| end |
| |
| // Pad state just pushes the ending suffix. Depending on the mode, the |
| // padding value is unique. SHA3 adds 2'b10, SHAKE adds 4'b1111, and |
| // cSHAKE adds 2'b 00. Refer `function_pad`. The signal has one more bit |
| // defined to accomodate first 1 bit of `pad10*1()` function. |
| StPad: begin |
| sel_mux = MuxFuncPad; |
| |
| fsm_keccak_valid = 1'b 1; |
| |
| if (keccak_ack && end_of_block) begin |
| // If padding is the last block, don't have to move to StPad01, just |
| // run Keccak and complete |
| st_d = StPadRun; |
| |
| // always clear the latched msgbuf |
| clr_msgbuf = 1'b 1; |
| clr_sentmsg = 1'b 1; |
| end else if (keccak_ack) begin |
| st_d = StPad01; |
| clr_msgbuf = 1'b 1; |
| end else begin |
| st_d = StPad; |
| end |
| end |
| |
| StPadRun: begin |
| st_d = StPadFlush; |
| |
| keccak_run_o = 1'b 1; |
| clr_sentmsg = 1'b 1; |
| end |
| |
| // Pad01 pushes the end bit of pad10*1() function. As keccak accepts byte |
| // size only, StPad always pushes partial (5bits). So at this state, it |
| // pushes rest of 3bits. If the data pushed in StPad is the last byte of |
| // the block, then Pad01 pushes to the same byte, if not, it first |
| // zero-fill the block then pad 1 to the end. |
| StPad01: begin |
| sel_mux = MuxZeroEnd; |
| |
| // There's no chance StPad01 can be a start of the block. So can be |
| // discard that the sent_blocksize is set at the beginning. |
| if (sent_blocksize) begin |
| st_d = StPadFlush; |
| |
| fsm_keccak_valid = 1'b 0; |
| keccak_run_o = 1'b 1; |
| clr_sentmsg = 1'b 1; |
| end else begin |
| st_d = StPad01; |
| |
| fsm_keccak_valid = 1'b 1; |
| end |
| end |
| |
| StPadFlush: begin |
| // Wait completion from keccak_round or wait SW indicator. |
| clr_sentmsg = 1'b 1; |
| clr_msgbuf = 1'b 1; |
| |
| if (keccak_complete_i) begin |
| st_d = StPadIdle; |
| |
| absorbed_d = prim_mubi_pkg::MuBi4True; |
| end else begin |
| st_d = StPadFlush; |
| end |
| end |
| |
| StTerminalError: begin |
| // this state is terminal |
| st_d = st; |
| sparse_fsm_error_o = 1'b 1; |
| end |
| |
| default: begin |
| // this state is terminal |
| st_d = StTerminalError; |
| sparse_fsm_error_o = 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_i != lc_ctrl_pkg::Off) begin |
| st_d = StTerminalError; |
| end |
| end |
| |
| ////////////// |
| // Datapath // |
| ////////////// |
| |
| // `encode_bytepad` represents the first two bytes of bytepad() |
| // It depends on the block size. We can reuse KeccakRate |
| // 10000000 || 00010101 // 168 |
| // 10000000 || 00010001 // 136 |
| logic [15:0] encode_bytepad; |
| |
| assign encode_bytepad = encode_bytepad_len(strength_i); |
| |
| // Prefix size ============================================================== |
| // Prefix represents bytepad(encode_string(N) || encode_string(S), 168 or 136) |
| // encode_string(N) || encode_string(S) is prepared by the software and given |
| // through `ns_data_i`. The first part of bytepad is determined by the |
| // `strength_i` and stored into `encode_bytepad`. |
| |
| // It is assumed that the prefix always smaller than the block size. |
| logic [PrefixSize*8-1:0] prefix; |
| |
| assign prefix = {ns_data_i, encode_bytepad}; |
| |
| logic [MsgWidth-1:0] prefix_sliced; |
| logic [MsgWidth-1:0] prefix_data [Share]; |
| |
| prim_slicer #( |
| .InW (PrefixSize*8), |
| .IndexW(KeccakMsgAddrW), |
| .OutW(MsgWidth) |
| ) u_prefix_slicer ( |
| .sel_i (prefix_index), |
| .data_i (prefix), |
| .data_o (prefix_sliced) |
| ); |
| |
| if (EnMasking) begin : gen_prefix_masked |
| // If Masking is enabled, prefix is two share. |
| assign prefix_data[0] = '0; |
| assign prefix_data[1] = prefix_sliced; |
| end else begin : gen_prefix_unmasked |
| // If Unmasked, only one share exists. |
| assign prefix_data[0] = prefix_sliced; |
| end |
| |
| // ========================================================================== |
| // function_pad is the unique value padded at the end of the message based on |
| // the function among SHA3, SHAKE, cSHAKE. The standard mentioned that SHA3 |
| // pads `01` , SHAKE pads `1111`, and cSHAKE pads `00`. |
| // |
| // Then pad10*1() function follows. It adds `1` first then fill 0 until it |
| // reaches the block size -1, then adds `1`. |
| // |
| // It means always `1` is followed by the function pad. |
| logic [4:0] funcpad; |
| |
| logic [MsgWidth-1:0] funcpad_data [Share]; |
| |
| always_comb begin |
| unique case (mode_i) |
| Sha3: funcpad = 5'b 00110; |
| Shake: funcpad = 5'b 11111; |
| CShake: funcpad = 5'b 00100; |
| |
| default: begin |
| // Just create non-padding but pad10*1 only |
| funcpad = 5'b 00001; |
| end |
| endcase |
| end |
| |
| // ========================================================================== |
| // `zero_with_endbit` contains all zero unless the message is for the last |
| // MsgWidth beat in the block. If it is the end of the block, the last bit |
| // will be set to complete pad10*1() functionality. |
| logic [MsgWidth-1:0] zero_with_endbit [Share]; |
| |
| if (EnMasking) begin : gen_zeroend_masked |
| assign zero_with_endbit[0] = '0; |
| assign zero_with_endbit[1][MsgWidth-1] = end_of_block; |
| assign zero_with_endbit[1][MsgWidth-2:0] = '0; |
| end else begin : gen_zeroend_unmasked |
| assign zero_with_endbit[0][MsgWidth-1] = end_of_block; |
| assign zero_with_endbit[0][MsgWidth-2:0] = '0; |
| end |
| |
| // ========================================================================== |
| // Data mux for output data |
| |
| assign keccak_addr_o = (sent_message < block_addr_limit) ? sent_message : '0; |
| |
| always_comb begin |
| unique case (sel_mux) |
| MuxFifo: keccak_data_o = msg_data_i; |
| MuxPrefix: keccak_data_o = prefix_data; |
| MuxFuncPad: keccak_data_o = funcpad_data; |
| MuxZeroEnd: keccak_data_o = zero_with_endbit; |
| |
| // MuxNone |
| default: keccak_data_o = '{default:'0}; |
| endcase |
| end |
| |
| always_comb begin |
| unique case (sel_mux) |
| MuxFifo: keccak_valid_o = msg_valid_i & ~hold_msg & ~en_msgbuf; |
| MuxPrefix: keccak_valid_o = fsm_keccak_valid; |
| MuxFuncPad: keccak_valid_o = fsm_keccak_valid; |
| MuxZeroEnd: keccak_valid_o = fsm_keccak_valid; |
| |
| // MuxNone |
| default: keccak_valid_o = 1'b 0; |
| endcase |
| end |
| |
| always_comb begin |
| unique case (sel_mux) |
| MuxFifo: msg_ready_o = en_msgbuf | (keccak_ready_i & ~hold_msg); |
| MuxPrefix: msg_ready_o = 1'b 0; |
| MuxFuncPad: msg_ready_o = 1'b 0; |
| MuxZeroEnd: msg_ready_o = 1'b 0; |
| |
| // MuxNone |
| default: msg_ready_o = 1'b 0; |
| endcase |
| end |
| |
| // prim_packer : packing to 64bit to update keccak storage |
| // two prim_packer in this module are used to pack the data received from |
| // upper layer (KMAC core) and also the 5bit padding bits. |
| // It is assumed that the message from upper layer could be partial at the |
| // end of the message. Then the 2 or 4bit padding is required. It can be |
| // handled by some custom logic or could be done by prim_packer. |
| // If packer is used, the MSG_FIFO doesn't have to have another prim_packer |
| // in front of the FIFO. This logic can handle the partial writes from the |
| // software. |
| // |
| // If a custom logic is implemented here, prim_packer is necessary in front |
| // of the FIFO, as this logic only appends at the end of the message when |
| // `process_i` is asserted. Also, in this case, even prim_packer is not |
| // needed, still 64bit registers to latch the partial write is required. |
| // If not, the logic has to delay the acceptance of the incoming write |
| // accesses. It may trigger the back-pressuring in some case which may result |
| // that the software(or upper layer) may not set process_i. |
| // |
| // For custom logic, it could be implemented by the 8 mux selection. |
| // for instance: (subject to be changed) |
| // unique case (sent_byte[2:0]) // generated from msg_strb_i |
| // 3'b 000: funcpad_merged = {end_of_block, 63'(function_pad) }; |
| // 3'b 001: funcpad_merged = {end_of_block, 55'(function_pad), msg_data_i[ 7:0]}; |
| // 3'b 010: funcpad_merged = {end_of_block, 47'(function_pad), msg_data_i[15:0]}; |
| // 3'b 011: funcpad_merged = {end_of_block, 39'(function_pad), msg_data_i[23:0]}; |
| // 3'b 100: funcpad_merged = {end_of_block, 31'(function_pad), msg_data_i[31:0]}; |
| // 3'b 101: funcpad_merged = {end_of_block, 23'(function_pad), msg_data_i[39:0]}; |
| // 3'b 110: funcpad_merged = {end_of_block, 15'(function_pad), msg_data_i[47:0]}; |
| // 3'b 111: funcpad_merged = {end_of_block, 7'(function_pad), msg_data_i[55:0]}; |
| // default: funcpad_merged = '0; |
| // endcase |
| |
| // internal buffer to store partial write. It doesn't have to store last byte as it |
| // stores only when partial write. |
| logic [MsgWidth-8-1:0] msg_buf [Share]; |
| logic [MsgStrbW-1-1:0] msg_strb; |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| msg_buf <= '{default:'0}; |
| msg_strb <= '0; |
| end else if (en_msgbuf) begin |
| for (int i = 0 ; i < Share ; i++) begin |
| msg_buf[i] <= msg_data_i[i][0+:(MsgWidth-8)]; |
| end |
| msg_strb <= msg_strb_i[0+:(MsgStrbW-1)]; |
| end else if (clr_msgbuf) begin |
| msg_buf <= '{default:'0}; |
| msg_strb <= '0; |
| end |
| end |
| |
| if (EnMasking) begin : gen_funcpad_data_masked |
| always_comb begin |
| unique case (msg_strb) |
| 7'b 000_0000: begin |
| funcpad_data[0] = '0; |
| funcpad_data[1] = {end_of_block, 63'(funcpad) }; |
| end |
| 7'b 000_0001: begin |
| funcpad_data[0] = {56'h0, msg_buf[0][ 7:0]}; |
| funcpad_data[1] = {end_of_block, 55'(funcpad), msg_buf[1][ 7:0]}; |
| end |
| 7'b 000_0011: begin |
| funcpad_data[0] = {48'h0, msg_buf[0][15:0]}; |
| funcpad_data[1] = {end_of_block, 47'(funcpad), msg_buf[1][15:0]}; |
| end |
| 7'b 000_0111: begin |
| funcpad_data[0] = {40'h0, msg_buf[0][23:0]}; |
| funcpad_data[1] = {end_of_block, 39'(funcpad), msg_buf[1][23:0]}; |
| end |
| 7'b 000_1111: begin |
| funcpad_data[0] = {32'h0, msg_buf[0][31:0]}; |
| funcpad_data[1] = {end_of_block, 31'(funcpad), msg_buf[1][31:0]}; |
| end |
| 7'b 001_1111: begin |
| funcpad_data[0] = {24'h0, msg_buf[0][39:0]}; |
| funcpad_data[1] = {end_of_block, 23'(funcpad), msg_buf[1][39:0]}; |
| end |
| 7'b 011_1111: begin |
| funcpad_data[0] = {16'h0, msg_buf[0][47:0]}; |
| funcpad_data[1] = {end_of_block, 15'(funcpad), msg_buf[1][47:0]}; |
| end |
| 7'b 111_1111: begin |
| funcpad_data[0] = { 8'h0, msg_buf[0][55:0]}; |
| funcpad_data[1] = {end_of_block, 7'(funcpad), msg_buf[1][55:0]}; |
| end |
| |
| default: funcpad_data = '{default:'0}; |
| endcase |
| end |
| end else begin : gen_funcpad_data_unmasked |
| always_comb begin |
| unique case (msg_strb) |
| 7'b 000_0000: funcpad_data[0] = {end_of_block, 63'(funcpad) }; |
| 7'b 000_0001: funcpad_data[0] = {end_of_block, 55'(funcpad), msg_buf[0][ 7:0]}; |
| 7'b 000_0011: funcpad_data[0] = {end_of_block, 47'(funcpad), msg_buf[0][15:0]}; |
| 7'b 000_0111: funcpad_data[0] = {end_of_block, 39'(funcpad), msg_buf[0][23:0]}; |
| 7'b 000_1111: funcpad_data[0] = {end_of_block, 31'(funcpad), msg_buf[0][31:0]}; |
| 7'b 001_1111: funcpad_data[0] = {end_of_block, 23'(funcpad), msg_buf[0][39:0]}; |
| 7'b 011_1111: funcpad_data[0] = {end_of_block, 15'(funcpad), msg_buf[0][47:0]}; |
| 7'b 111_1111: funcpad_data[0] = {end_of_block, 7'(funcpad), msg_buf[0][55:0]}; |
| |
| default: funcpad_data = '{default:'0}; |
| endcase |
| end |
| end |
| |
| //////////////// |
| // Assertions // |
| //////////////// |
| |
| // Prefix size is smaller than the smallest Keccak Block Size, which is 72 bytes. |
| `ASSERT_INIT(PrefixLessThanBlock_A, PrefixSize/8 < KeccakRate[4]) |
| |
| // Some part of datapath in sha3pad assumes Data width as 64bit. |
| // If data width need to be changed, funcpad_data part should be changed too. |
| // Also, The blocksize shall be divided by MsgWidth, which means, MsgWidth |
| // can be {16, 32, 64} even funcpad_data mux is fully flexible. |
| `ASSERT_INIT(MsgWidthidth_A, MsgWidth == 64) |
| |
| // Assume pulse signals: start, process, done |
| `ASSUME(StartPulse_A, start_i |=> !start_i) |
| `ASSUME(ProcessPulse_A, process_i |=> !process_i) |
| `ASSUME(DonePulse_A, |
| prim_mubi_pkg::mubi4_test_true_strict(done_i) |=> |
| prim_mubi_pkg::mubi4_test_false_strict(done_i)) |
| |
| // ASSERT output pulse signals: absorbed_o, keccak_run_o |
| `ASSERT(AbsorbedPulse_A, |
| prim_mubi_pkg::mubi4_test_true_strict(absorbed_o) |=> |
| prim_mubi_pkg::mubi4_test_false_strict(absorbed_o)) |
| `ASSERT(KeccakRunPulse_A, keccak_run_o |=> !keccak_run_o) |
| |
| // start_i, done_i, process_i cannot set high at the same time |
| `ASSUME(StartProcessDoneMutex_a, |
| $onehot0({ |
| start_i, |
| process_i, |
| prim_mubi_pkg::mubi4_test_true_loose(done_i) |
| })) |
| |
| // Sequence, start_i --> process_i --> absorbed_o --> done_i |
| //`ASSUME(Sequence_a, start_i ##[1:$] process_i ##[1:$] ##[1:$] absorbed_o ##[1:$] done_i) |
| |
| `ifndef SYNTHESIS |
| // Process only asserts after start and all message are fed. |
| // These valid signals are qualifier of FPV to trigger the control signal |
| // It is a little bit hard to specify these criteria in SVA property so creating |
| // qualifiers in RTL form is easier. |
| logic start_valid, process_valid, absorb_valid, done_valid; |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| start_valid <= 1'b 1; |
| end else if (start_i) begin |
| start_valid <= 1'b 0; |
| end else if (prim_mubi_pkg::mubi4_test_true_strict(done_i)) begin |
| start_valid <= 1'b 1; |
| end |
| end |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| process_valid <= 1'b 0; |
| end else if (start_i) begin |
| process_valid <= 1'b 1; |
| end else if (process_i) begin |
| process_valid <= 1'b 0; |
| end |
| end |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| done_valid <= 1'b 0; |
| end else if (prim_mubi_pkg::mubi4_test_true_strict(absorbed_o)) begin |
| done_valid <= 1'b 1; |
| end else if (prim_mubi_pkg::mubi4_test_true_strict(done_i)) begin |
| done_valid <= 1'b 0; |
| end |
| end |
| |
| // Message can be fed in between start_i and process_i. |
| `ASSUME(MessageCondition_M, msg_valid_i && msg_ready_o |-> process_valid && !process_i) |
| |
| // Message ready should be asserted only in between start_i and process_i |
| `ASSERT(MsgReadyCondition_A, msg_ready_o |-> process_valid && !process_i) |
| |
| `ASSUME(ProcessCondition_M, process_i |-> process_valid) |
| `ASSUME(StartCondition_M, start_i |-> start_valid) |
| `ASSUME(DoneCondition_M, |
| prim_mubi_pkg::mubi4_test_true_strict(done_i) |-> done_valid) |
| |
| // Assume mode_i and strength_i are stable during the operation |
| // This will be guarded at the kmac top level |
| `ASSUME(ModeStableDuringOp_M, |
| $changed(mode_i) |-> start_valid) |
| `ASSUME(StrengthStableDuringOp_M, |
| $changed(strength_i) |-> start_valid) |
| |
| `endif // SYNTHESIS |
| |
| // If not full block is written, the pad shall send message to keccak_round |
| // If it is end of the message, the state moves to StPad and send the request |
| `ASSERT(CompleteBlockWhenProcess_A, |
| $rose(process_latched) && (!end_of_block && !sent_blocksize ) |
| && !(st inside {StPrefixWait, StMessageWait}) |-> ##[1:5] keccak_valid_o, |
| clk_i, !rst_ni || lc_escalate_en_i != lc_ctrl_pkg::Off) |
| |
| // If process_i asserted, completion shall be asserted shall be asserted |
| //`ASSERT(ProcessToAbsorbed_A, process_i |=> strong(##[24*Share:$] absorbed_o)) |
| |
| |
| // Assumption of input mode_i and strength_i |
| // SHA3 variants: SHA3-224, SHA3-256, SHA3-384, SHA3-512 |
| // SHAKE, cSHAKE variants: SHAKE128, SHAKE256, cSHAKE128, cSHAKE256 |
| `ASSUME_FPV(ModeStrengthCombinations_M, |
| start_i |-> |
| (mode_i == Sha3 && (strength_i inside {L224, L256, L384, L512})) || |
| ((mode_i == Shake || mode_i == CShake) && (strength_i inside {L128, L256})), |
| clk_i, !rst_ni) |
| |
| // No partial write is allowed for Message FIFO interface |
| `ASSUME(NoPartialMsgFifo_M, |
| keccak_valid_o && (sel_mux == MuxFifo) |-> (&msg_strb_i) == 1'b1, |
| clk_i, !rst_ni) |
| |
| // When transaction is stored into msg_buf, it shall be partial write. |
| `ASSUME(AlwaysPartialMsgBuf_M, |
| en_msgbuf |-> msg_valid_i && (msg_strb_i[MsgStrbW-1] == 1'b0), |
| clk_i, !rst_ni) |
| |
| // if partial write comes and is acked, then no more msg_valid_i until |
| // next message |
| `ASSUME(PartialEndOfMsg_M, |
| msg_valid_i && msg_ready_o && msg_partial |=> |
| !msg_valid_i ##[1:$] $stable(msg_valid_i) ##1 process_latched, |
| clk_i, !rst_ni) |
| |
| // At the first clock in StPad01 state, sent_blocksize shall not be set |
| `ASSERT(Pad01NotAttheEndOfBlock_A, |
| (st == StPad && st_d == StPad01) |-> !end_of_block, |
| clk_i, !rst_ni) |
| |
| // When data sent to the keccak_round, the address should be in the range |
| `ASSERT(KeccakAddrInRange_A, |
| keccak_valid_o |-> keccak_addr_o < KeccakRate[strength_i], |
| clk_i, !rst_ni) |
| |
| // NS data shall be stable during the operation. |
| //`ASSUME(NsStableInProcess_A, |
| // $stable(ns_data_i) throughout(start_i ##[1:$] process_i ##[1:$] absorbed_o), |
| // clk_i, !rst_ni) |
| |
| // Functional Coverage |
| `COVER(StMessageFeed_C, st == StMessage) |
| `COVER(StPad_C, st == StPad01 && sent_blocksize) |
| `COVER(StPadSendMsg_C, st == StPad01 && keccak_ack) |
| `COVER(StComplete_C, st == StPadFlush) |
| endmodule |