|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | // | 
|  | // AES cipher core control | 
|  | // | 
|  | // This module controls the AES cipher core including the key expand module. | 
|  |  | 
|  | `include "prim_assert.sv" | 
|  |  | 
|  | module aes_cipher_control import aes_pkg::*; | 
|  | #( | 
|  | parameter bit         Masking  = 0, | 
|  | parameter sbox_impl_e SBoxImpl = SBoxImplLut | 
|  | ) ( | 
|  | input  logic                    clk_i, | 
|  | input  logic                    rst_ni, | 
|  |  | 
|  | // Input handshake signals | 
|  | input  sp2v_e                   in_valid_i, | 
|  | output sp2v_e                   in_ready_o, | 
|  |  | 
|  | // Output handshake signals | 
|  | output sp2v_e                   out_valid_o, | 
|  | input  sp2v_e                   out_ready_i, | 
|  |  | 
|  | // Control and sync signals | 
|  | input  logic                    cfg_valid_i, | 
|  | input  ciph_op_e                op_i, | 
|  | input  key_len_e                key_len_i, | 
|  | input  sp2v_e                   crypt_i, | 
|  | output sp2v_e                   crypt_o, | 
|  | input  sp2v_e                   dec_key_gen_i, | 
|  | output sp2v_e                   dec_key_gen_o, | 
|  | input  logic                    key_clear_i, | 
|  | output logic                    key_clear_o, | 
|  | input  logic                    data_out_clear_i, | 
|  | output logic                    data_out_clear_o, | 
|  | input  logic                    mux_sel_err_i, | 
|  | input  logic                    sp_enc_err_i, | 
|  | output logic                    alert_o, | 
|  |  | 
|  | // Control signals for masking PRNG | 
|  | output logic                    prng_update_o, | 
|  | output logic                    prng_reseed_req_o, | 
|  | input  logic                    prng_reseed_ack_i, | 
|  |  | 
|  | // Control and sync signals for cipher data path | 
|  | output state_sel_e              state_sel_o, | 
|  | output sp2v_e                   state_we_o, | 
|  | output sp2v_e                   sub_bytes_en_o, | 
|  | input  sp2v_e                   sub_bytes_out_req_i, | 
|  | output sp2v_e                   sub_bytes_out_ack_o, | 
|  | output add_rk_sel_e             add_rk_sel_o, | 
|  |  | 
|  | // Control and sync signals for key expand data path | 
|  | output ciph_op_e                key_expand_op_o, | 
|  | output key_full_sel_e           key_full_sel_o, | 
|  | output sp2v_e                   key_full_we_o, | 
|  | output key_dec_sel_e            key_dec_sel_o, | 
|  | output sp2v_e                   key_dec_we_o, | 
|  | output sp2v_e                   key_expand_en_o, | 
|  | input  sp2v_e                   key_expand_out_req_i, | 
|  | output sp2v_e                   key_expand_out_ack_o, | 
|  | output logic                    key_expand_clear_o, | 
|  | output logic [3:0]              key_expand_round_o, | 
|  | output key_words_sel_e          key_words_sel_o, | 
|  | output round_key_sel_e          round_key_sel_o | 
|  | ); | 
|  |  | 
|  | // Types | 
|  | // $ ./sparse-fsm-encode.py -d 3 -m 7 -n 6 \ | 
|  | //      -s 31468618 --language=sv | 
|  | // | 
|  | // Hamming distance histogram: | 
|  | // | 
|  | //  0: -- | 
|  | //  1: -- | 
|  | //  2: -- | 
|  | //  3: |||||||||||||||||||| (57.14%) | 
|  | //  4: ||||||||||||||| (42.86%) | 
|  | //  5: -- | 
|  | //  6: -- | 
|  | // | 
|  | // Minimum Hamming distance: 3 | 
|  | // Maximum Hamming distance: 4 | 
|  | // | 
|  | localparam int StateWidth = 6; | 
|  | typedef enum logic [StateWidth-1:0] { | 
|  | IDLE     = 6'b111100, | 
|  | INIT     = 6'b101001, | 
|  | ROUND    = 6'b010000, | 
|  | FINISH   = 6'b100010, | 
|  | CLEAR_S  = 6'b011011, | 
|  | CLEAR_KD = 6'b110111, | 
|  | ERROR    = 6'b001110 | 
|  | } aes_cipher_ctrl_e; | 
|  |  | 
|  | aes_cipher_ctrl_e aes_cipher_ctrl_ns, aes_cipher_ctrl_cs; | 
|  |  | 
|  | // Signals | 
|  | logic [3:0] rnd_ctr_d, rnd_ctr_q; | 
|  | logic [3:0] rnd_ctr_rem_d, rnd_ctr_rem_q; | 
|  | logic [3:0] rnd_ctr_sum; | 
|  | logic [3:0] num_rounds_d, num_rounds_q; | 
|  | logic [3:0] num_rounds_regular; | 
|  | logic       rnd_ctr_parity, rnd_ctr_parity_d, rnd_ctr_parity_q; | 
|  | logic       rnd_ctr_err, rnd_ctr_err_sum, rnd_ctr_err_parity; | 
|  | sp2v_e      crypt_d, crypt_q; | 
|  | sp2v_e      dec_key_gen_d, dec_key_gen_q; | 
|  | logic       key_clear_d, key_clear_q; | 
|  | logic       data_out_clear_d, data_out_clear_q; | 
|  | logic       prng_reseed_done_d, prng_reseed_done_q; | 
|  | sp2v_e      sub_bytes_out_req; | 
|  | sp2v_e      key_expand_out_req; | 
|  | sp2v_e      advance, advance_chk; | 
|  | sp2v_e      in_valid; | 
|  | sp2v_e      out_ready; | 
|  | sp2v_e      crypt; | 
|  | sp2v_e      dec_key_gen; | 
|  | logic       sp_enc_err; | 
|  |  | 
|  | // cfg_valid_i is used for gating assertions only. | 
|  | logic       unused_cfg_valid; | 
|  | assign unused_cfg_valid = cfg_valid_i; | 
|  |  | 
|  | // FSM | 
|  | always_comb begin : aes_cipher_ctrl_fsm | 
|  |  | 
|  | // Handshake signals | 
|  | in_ready_o           = SP2V_LOW; | 
|  | out_valid_o          = SP2V_LOW; | 
|  |  | 
|  | // Masking PRNG signals | 
|  | prng_update_o        = 1'b0; | 
|  | prng_reseed_req_o    = 1'b0; | 
|  |  | 
|  | // Cipher data path | 
|  | state_sel_o          = STATE_ROUND; | 
|  | state_we_o           = SP2V_LOW; | 
|  | add_rk_sel_o         = ADD_RK_ROUND; | 
|  | sub_bytes_en_o       = SP2V_LOW; | 
|  | sub_bytes_out_ack_o  = SP2V_LOW; | 
|  |  | 
|  | // Key expand data path | 
|  | key_full_sel_o       = KEY_FULL_ROUND; | 
|  | key_full_we_o        = SP2V_LOW; | 
|  | key_dec_sel_o        = KEY_DEC_EXPAND; | 
|  | key_dec_we_o         = SP2V_LOW; | 
|  | key_expand_en_o      = SP2V_LOW; | 
|  | key_expand_out_ack_o = SP2V_LOW; | 
|  | key_expand_clear_o   = 1'b0; | 
|  | key_words_sel_o      = KEY_WORDS_ZERO; | 
|  | round_key_sel_o      = ROUND_KEY_DIRECT; | 
|  |  | 
|  | // FSM | 
|  | aes_cipher_ctrl_ns   = aes_cipher_ctrl_cs; | 
|  | num_rounds_d         = num_rounds_q; | 
|  | rnd_ctr_d            = rnd_ctr_q; | 
|  | rnd_ctr_rem_d        = rnd_ctr_rem_q; | 
|  | crypt_d              = crypt_q; | 
|  | dec_key_gen_d        = dec_key_gen_q; | 
|  | key_clear_d          = key_clear_q; | 
|  | data_out_clear_d     = data_out_clear_q; | 
|  | prng_reseed_done_d   = prng_reseed_done_q | prng_reseed_ack_i; | 
|  | advance              = SP2V_LOW; | 
|  |  | 
|  | // Alert | 
|  | alert_o              = 1'b0; | 
|  |  | 
|  | unique case (aes_cipher_ctrl_cs) | 
|  |  | 
|  | IDLE: begin | 
|  | dec_key_gen_d = SP2V_LOW; | 
|  |  | 
|  | // Signal that we are ready, wait for handshake. | 
|  | in_ready_o = SP2V_HIGH; | 
|  | if (in_valid == SP2V_HIGH) begin | 
|  | if (key_clear_i || data_out_clear_i) begin | 
|  | // Clear internal key registers. The cipher core muxes are used to clear the data | 
|  | // output registers. | 
|  | key_clear_d      = key_clear_i; | 
|  | data_out_clear_d = data_out_clear_i; | 
|  |  | 
|  | // To clear the data output registers, we must first clear the state. | 
|  | aes_cipher_ctrl_ns = data_out_clear_i ? CLEAR_S : CLEAR_KD; | 
|  |  | 
|  | end else if (dec_key_gen == SP2V_HIGH || crypt == SP2V_HIGH) begin | 
|  | // Start encryption/decryption or generation of start key for decryption. | 
|  | crypt_d       = (dec_key_gen_i == SP2V_LOW) ? crypt : SP2V_LOW; | 
|  | dec_key_gen_d =  dec_key_gen_i; | 
|  |  | 
|  | // Load input data to state | 
|  | state_sel_o = (dec_key_gen_i == SP2V_HIGH) ? STATE_CLEAR : STATE_INIT; | 
|  | state_we_o  = SP2V_HIGH; | 
|  |  | 
|  | // Make the masking PRNG advance. The current pseudo-random data is used to mask the | 
|  | // input data. | 
|  | prng_update_o = (dec_key_gen_i == SP2V_HIGH) ? 1'b0 : Masking; | 
|  |  | 
|  | // Init key expand | 
|  | key_expand_clear_o = 1'b1; | 
|  |  | 
|  | // Load full key | 
|  | key_full_sel_o = (dec_key_gen_i == SP2V_HIGH) ? KEY_FULL_ENC_INIT : | 
|  | (op_i == CIPH_FWD) ? KEY_FULL_ENC_INIT : | 
|  | KEY_FULL_DEC_INIT; | 
|  | key_full_we_o  = SP2V_HIGH; | 
|  |  | 
|  | // Load num_rounds, initialize round counters. | 
|  | num_rounds_d = (key_len_i == AES_128) ? 4'd10 : | 
|  | (key_len_i == AES_192) ? 4'd12 : | 
|  | 4'd14; | 
|  | rnd_ctr_rem_d      = num_rounds_d; | 
|  | rnd_ctr_d          = '0; | 
|  | aes_cipher_ctrl_ns = INIT; | 
|  |  | 
|  | end else begin | 
|  | // Handshake without a valid command. We should never get here. If we do (e.g. via a | 
|  | // malicious glitch), error out immediately. | 
|  | aes_cipher_ctrl_ns = ERROR; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | INIT: begin | 
|  | // Initial round: just add key to state | 
|  | add_rk_sel_o = ADD_RK_INIT; | 
|  |  | 
|  | // Select key words for initial add_round_key | 
|  | key_words_sel_o = (dec_key_gen_q == SP2V_HIGH) ? KEY_WORDS_ZERO : | 
|  | (key_len_i == AES_128)                     ? KEY_WORDS_0123 : | 
|  | (key_len_i == AES_192 && op_i == CIPH_FWD) ? KEY_WORDS_0123 : | 
|  | (key_len_i == AES_192 && op_i == CIPH_INV) ? KEY_WORDS_2345 : | 
|  | (key_len_i == AES_256 && op_i == CIPH_FWD) ? KEY_WORDS_0123 : | 
|  | (key_len_i == AES_256 && op_i == CIPH_INV) ? KEY_WORDS_4567 : KEY_WORDS_ZERO; | 
|  |  | 
|  | // Clear masking PRNG reseed status. | 
|  | prng_reseed_done_d = 1'b0; | 
|  |  | 
|  | // AES-256 has two round keys available right from beginning. Pseudo-random data is | 
|  | // required by KeyExpand only. | 
|  | if (key_len_i != AES_256) begin | 
|  | // Advance in sync with KeyExpand. Based on the S-Box implementation, it can take | 
|  | // multiple cycles to finish. Wait for handshake. The DOM S-Boxes take fresh PRD | 
|  | // in every cycle except the last. | 
|  | advance         = key_expand_out_req; | 
|  | prng_update_o   = (SBoxImpl == SBoxImplDom) ? (advance_chk == SP2V_LOW) : Masking; | 
|  | key_expand_en_o = SP2V_HIGH; | 
|  | if (advance_chk == SP2V_HIGH) begin | 
|  | key_expand_out_ack_o = SP2V_HIGH; | 
|  | state_we_o           = (dec_key_gen_q == SP2V_LOW) ? SP2V_HIGH : SP2V_LOW; | 
|  | key_full_we_o        = SP2V_HIGH; | 
|  | rnd_ctr_d            = rnd_ctr_q     + 4'b0001; | 
|  | rnd_ctr_rem_d        = rnd_ctr_rem_q - 4'b0001; | 
|  | aes_cipher_ctrl_ns   = ROUND; | 
|  | end | 
|  | end else begin | 
|  | state_we_o         = (dec_key_gen_q == SP2V_LOW) ? SP2V_HIGH : SP2V_LOW; | 
|  | rnd_ctr_d          = rnd_ctr_q     + 4'b0001; | 
|  | rnd_ctr_rem_d      = rnd_ctr_rem_q - 4'b0001; | 
|  | aes_cipher_ctrl_ns = ROUND; | 
|  | end | 
|  | end | 
|  |  | 
|  | ROUND: begin | 
|  | // Normal rounds | 
|  |  | 
|  | // Select key words for add_round_key | 
|  | key_words_sel_o = (dec_key_gen_q == SP2V_HIGH) ? KEY_WORDS_ZERO : | 
|  | (key_len_i == AES_128)                     ? KEY_WORDS_0123 : | 
|  | (key_len_i == AES_192 && op_i == CIPH_FWD) ? KEY_WORDS_2345 : | 
|  | (key_len_i == AES_192 && op_i == CIPH_INV) ? KEY_WORDS_0123 : | 
|  | (key_len_i == AES_256 && op_i == CIPH_FWD) ? KEY_WORDS_4567 : | 
|  | (key_len_i == AES_256 && op_i == CIPH_INV) ? KEY_WORDS_0123 : KEY_WORDS_ZERO; | 
|  |  | 
|  | // Keep requesting PRNG reseeding until it is acknowledged. | 
|  | prng_reseed_req_o = Masking & ~prng_reseed_done_q; | 
|  |  | 
|  | // Select round key: direct or mixed (equivalent inverse cipher) | 
|  | round_key_sel_o = (op_i == CIPH_FWD) ? ROUND_KEY_DIRECT : ROUND_KEY_MIXED; | 
|  |  | 
|  | // Advance in sync with SubBytes and KeyExpand. Based on the S-Box implementation, both can | 
|  | // take multiple cycles to finish. Wait for handshake. Make the masking PRNG advance every | 
|  | // cycle. The DOM S-Boxes take fresh PRD in every cycle except the last. | 
|  | advance         = ((dec_key_gen_q == SP2V_HIGH || sub_bytes_out_req == SP2V_HIGH) && | 
|  | key_expand_out_req == SP2V_HIGH) ? SP2V_HIGH : SP2V_LOW; | 
|  | prng_update_o   = (SBoxImpl == SBoxImplDom) ? (advance_chk == SP2V_LOW) : Masking; | 
|  | sub_bytes_en_o  = (dec_key_gen_q == SP2V_LOW) ? SP2V_HIGH : SP2V_LOW; | 
|  | key_expand_en_o = SP2V_HIGH; | 
|  | if (advance_chk == SP2V_HIGH) begin | 
|  | sub_bytes_out_ack_o  = (dec_key_gen_q == SP2V_LOW) ? SP2V_HIGH : SP2V_LOW; | 
|  | key_expand_out_ack_o = SP2V_HIGH; | 
|  |  | 
|  | state_we_o    = (dec_key_gen_q == SP2V_LOW) ? SP2V_HIGH : SP2V_LOW; | 
|  | key_full_we_o = SP2V_HIGH; | 
|  |  | 
|  | // Update round | 
|  | rnd_ctr_d     = rnd_ctr_q     + 4'b0001; | 
|  | rnd_ctr_rem_d = rnd_ctr_rem_q - 4'b0001; | 
|  |  | 
|  | // Are we doing the last regular round? | 
|  | if (rnd_ctr_q == num_rounds_regular) begin | 
|  | aes_cipher_ctrl_ns = FINISH; | 
|  |  | 
|  | if (dec_key_gen_q == SP2V_HIGH) begin | 
|  | // Write decryption key. | 
|  | key_dec_we_o = SP2V_HIGH; | 
|  |  | 
|  | // Indicate that we are done, try to perform the handshake. But we don't wait here. | 
|  | // If we don't get the handshake now, we will wait in the finish state. When using | 
|  | // masking, we only finish if the masking PRNG has been reseeded. | 
|  | out_valid_o = Masking ? (prng_reseed_done_q ? SP2V_HIGH : SP2V_LOW) : SP2V_HIGH; | 
|  | if (out_valid_o == SP2V_HIGH && out_ready == SP2V_HIGH) begin | 
|  | // Go to idle state directly. | 
|  | dec_key_gen_d      = SP2V_LOW; | 
|  | aes_cipher_ctrl_ns = IDLE; | 
|  | end | 
|  | end | 
|  | end // rnd_ctr_q | 
|  | end // SubBytes/KeyExpand REQ/ACK | 
|  | end | 
|  |  | 
|  | FINISH: begin | 
|  | // Final round | 
|  |  | 
|  | // Select key words for add_round_key | 
|  | key_words_sel_o = (dec_key_gen_q == SP2V_HIGH) ? KEY_WORDS_ZERO : | 
|  | (key_len_i == AES_128)                     ? KEY_WORDS_0123 : | 
|  | (key_len_i == AES_192 && op_i == CIPH_FWD) ? KEY_WORDS_2345 : | 
|  | (key_len_i == AES_192 && op_i == CIPH_INV) ? KEY_WORDS_0123 : | 
|  | (key_len_i == AES_256 && op_i == CIPH_FWD) ? KEY_WORDS_4567 : | 
|  | (key_len_i == AES_256 && op_i == CIPH_INV) ? KEY_WORDS_0123 : KEY_WORDS_ZERO; | 
|  |  | 
|  | // Skip mix_columns | 
|  | add_rk_sel_o = ADD_RK_FINAL; | 
|  |  | 
|  | // Keep requesting PRNG reseeding until it is acknowledged. | 
|  | prng_reseed_req_o = Masking & ~prng_reseed_done_q; | 
|  |  | 
|  | // Once we're done, we won't need the state anymore. We actually clear it when progressing | 
|  | // to the next state. | 
|  | state_sel_o = STATE_CLEAR; | 
|  |  | 
|  | // Advance in sync with SubBytes. Based on the S-Box implementation, it can take multiple | 
|  | // cycles to finish. Only indicate that we are done if: | 
|  | // - we have valid output (SubBytes finished), | 
|  | // - the masking PRNG has been reseeded (if masking is used), | 
|  | // - all mux selector signals are valid (don't release data in case of errors), and | 
|  | // - all sparsely encoded signals are valid (don't release data in case of errors). | 
|  | // Perform both handshakes simultaneously. | 
|  | advance = (sub_bytes_out_req == SP2V_HIGH || | 
|  | dec_key_gen_q == SP2V_HIGH) ? SP2V_HIGH : SP2V_LOW; | 
|  | sub_bytes_en_o = (dec_key_gen_q == SP2V_LOW)  ? SP2V_HIGH : SP2V_LOW; | 
|  | out_valid_o = (advance_chk == SP2V_HIGH && | 
|  | Masking == prng_reseed_done_q && | 
|  | !mux_sel_err_i && !sp_enc_err) ? SP2V_HIGH : SP2V_LOW; | 
|  | // When using DOM S-Boxes, make the masking PRNG advance every cycle until the output is | 
|  | // ready. For other S-Boxes, make it advance once only. Updating it while being stalled | 
|  | // would cause non-DOM S-Boxes to be re-evaluated, thereby creating additional SCA leakage. | 
|  | prng_update_o = (SBoxImpl == SBoxImplDom) ? (advance_chk == SP2V_LOW) : | 
|  | Masking ? (out_valid_o == SP2V_HIGH && out_ready == SP2V_HIGH)    : 1'b0; | 
|  | if (out_valid_o == SP2V_HIGH && out_ready == SP2V_HIGH) begin | 
|  | sub_bytes_out_ack_o = (dec_key_gen_q == SP2V_LOW) ? SP2V_HIGH : SP2V_LOW; | 
|  |  | 
|  | // Clear the state. | 
|  | state_we_o          = SP2V_HIGH; | 
|  | crypt_d             = SP2V_LOW; | 
|  | // If we were generating the decryption key and didn't get the handshake in the last | 
|  | // regular round, we should clear dec_key_gen now. | 
|  | dec_key_gen_d       = SP2V_LOW; | 
|  | aes_cipher_ctrl_ns  = IDLE; | 
|  | end | 
|  | end | 
|  |  | 
|  | CLEAR_S: begin | 
|  | // Clear the state with pseudo-random data. | 
|  | state_we_o         = SP2V_HIGH; | 
|  | state_sel_o        = STATE_CLEAR; | 
|  | aes_cipher_ctrl_ns = CLEAR_KD; | 
|  | end | 
|  |  | 
|  | CLEAR_KD: begin | 
|  | // Clear internal key registers and/or external data output registers. | 
|  | if (key_clear_q) begin | 
|  | key_full_sel_o = KEY_FULL_CLEAR; | 
|  | key_full_we_o  = SP2V_HIGH; | 
|  | key_dec_sel_o  = KEY_DEC_CLEAR; | 
|  | key_dec_we_o   = SP2V_HIGH; | 
|  | end | 
|  | if (data_out_clear_q) begin | 
|  | // Forward the state (previously cleared with psuedo-random data). | 
|  | add_rk_sel_o    = ADD_RK_INIT; | 
|  | key_words_sel_o = KEY_WORDS_ZERO; | 
|  | round_key_sel_o = ROUND_KEY_DIRECT; | 
|  | end | 
|  | // Indicate that we are done, wait for handshake. | 
|  | out_valid_o = SP2V_HIGH; | 
|  | if (out_ready == SP2V_HIGH) begin | 
|  | key_clear_d        = 1'b0; | 
|  | data_out_clear_d   = 1'b0; | 
|  | aes_cipher_ctrl_ns = IDLE; | 
|  | end | 
|  | end | 
|  |  | 
|  | ERROR: begin | 
|  | // Terminal error state | 
|  | alert_o = 1'b1; | 
|  | end | 
|  |  | 
|  | // We should never get here. If we do (e.g. via a malicious glitch), error out immediately. | 
|  | default: begin | 
|  | aes_cipher_ctrl_ns = ERROR; | 
|  | end | 
|  | endcase | 
|  |  | 
|  | // Unconditionally jump into the terminal error state in case a mux selector or a sparsely | 
|  | // encoded signal becomes invalid, or in case we have detected a fault in the round counter. | 
|  | if (mux_sel_err_i || sp_enc_err || rnd_ctr_err) begin | 
|  | aes_cipher_ctrl_ns = ERROR; | 
|  | 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] aes_cipher_ctrl_cs_raw; | 
|  | assign aes_cipher_ctrl_cs = aes_cipher_ctrl_e'(aes_cipher_ctrl_cs_raw); | 
|  | prim_flop #( | 
|  | .Width(StateWidth), | 
|  | .ResetValue(StateWidth'(IDLE)) | 
|  | ) u_state_regs ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .d_i ( aes_cipher_ctrl_ns     ), | 
|  | .q_o ( aes_cipher_ctrl_cs_raw ) | 
|  | ); | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin : reg_fsm | 
|  | if (!rst_ni) begin | 
|  | num_rounds_q       <= '0; | 
|  | key_clear_q        <= 1'b0; | 
|  | data_out_clear_q   <= 1'b0; | 
|  | prng_reseed_done_q <= 1'b0; | 
|  | end else begin | 
|  | num_rounds_q       <= num_rounds_d; | 
|  | key_clear_q        <= key_clear_d; | 
|  | data_out_clear_q   <= data_out_clear_d; | 
|  | prng_reseed_done_q <= prng_reseed_done_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Use separate signal for number of regular rounds. | 
|  | assign num_rounds_regular = num_rounds_q - 4'd1; | 
|  |  | 
|  | // Use separate signal for key expand operation, forward round. | 
|  | assign key_expand_op_o    = (dec_key_gen_d == SP2V_HIGH || | 
|  | dec_key_gen_q == SP2V_HIGH) ? CIPH_FWD : op_i; | 
|  | assign key_expand_round_o = rnd_ctr_q; | 
|  |  | 
|  | // Let the main controller know whate we are doing. | 
|  | assign crypt_o          = crypt_q; | 
|  | assign dec_key_gen_o    = dec_key_gen_q; | 
|  | assign key_clear_o      = key_clear_q; | 
|  | assign data_out_clear_o = data_out_clear_q; | 
|  |  | 
|  | ////////////////////////////// | 
|  | // Round Counter Protection // | 
|  | ////////////////////////////// | 
|  | // To protect the round counter against fault injection, we use two counters: | 
|  | // - rnd_ctr_d/q counts the executed rounds. It is initialized to 0 and counts up. | 
|  | // - rnd_ctr_rem_d/q counts the remaining rounds. It is initialized to num_rounds_q and counts | 
|  | //   down. | 
|  | // In addition, we use one parity bit for the rnd_ctr_d/q counter. | 
|  | // | 
|  | // An alert is signaled and the FSM goes into the terminal error state if | 
|  | // i) the sum of the counters doesn't add up, i.e. rnd_ctr_q + rnd_ctr_rem_q != num_rounds_q, or | 
|  | // ii) the parity information is incorrect. | 
|  |  | 
|  | // The following primitives are used to place size-only constraints on the | 
|  | // flops in order to prevent optimizations on the protected round counter. | 
|  | prim_flop #( | 
|  | .Width(4), | 
|  | .ResetValue('0) | 
|  | ) u_rnd_ctr_regs ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .d_i ( rnd_ctr_d ), | 
|  | .q_o ( rnd_ctr_q ) | 
|  | ); | 
|  |  | 
|  | prim_flop #( | 
|  | .Width(4), | 
|  | .ResetValue('0) | 
|  | ) u_rnd_ctr_rem_regs ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .d_i ( rnd_ctr_rem_d ), | 
|  | .q_o ( rnd_ctr_rem_q ) | 
|  | ); | 
|  |  | 
|  | prim_flop #( | 
|  | .Width(1), | 
|  | .ResetValue('0) | 
|  | ) u_rnd_ctr_par_reg ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .d_i ( rnd_ctr_parity_d ), | 
|  | .q_o ( rnd_ctr_parity_q ) | 
|  | ); | 
|  |  | 
|  | // Generate parity bits and sum. | 
|  | assign rnd_ctr_parity_d = ^rnd_ctr_d; | 
|  | assign rnd_ctr_parity   = ^rnd_ctr_q; | 
|  | assign rnd_ctr_sum      = rnd_ctr_q + rnd_ctr_rem_q; | 
|  |  | 
|  | // Detect faults. | 
|  | assign rnd_ctr_err_sum    = (rnd_ctr_sum != num_rounds_q)        ? 1'b1 : 1'b0; | 
|  | assign rnd_ctr_err_parity = (rnd_ctr_parity != rnd_ctr_parity_q) ? 1'b1 : 1'b0; | 
|  |  | 
|  | assign rnd_ctr_err = rnd_ctr_err_sum | rnd_ctr_err_parity; | 
|  |  | 
|  | ////////////////////////////// | 
|  | // Sparsely Encoded Signals // | 
|  | ////////////////////////////// | 
|  |  | 
|  | // We use sparse encodings for various critical signals and must ensure that: | 
|  | // 1. The synthesis tool doesn't optimize away the sparse encoding. | 
|  | // 2. The sparsely encoded signal is always valid. More precisely, an alert or SVA is triggered | 
|  | //    if a sparse signal takes on an invalid value. | 
|  | // 3. The alert signal remains asserted until reset even if the sparse signal becomes valid again | 
|  | //    This is achieved by driving the control FSM into the terminal error state whenever any | 
|  | //    sparsely encoded signal becomes invalid. | 
|  | // | 
|  | // If any sparsely encoded signal becomes invalid, the cipher core further immediately de-asserts | 
|  | // the out_valid_o signal to prevent any data from being released. | 
|  |  | 
|  | // The following primitives are used to place a size-only constraint on the | 
|  | // flops in order to prevent optimizations on these status signals. | 
|  | logic [Sp2VWidth-1:0] crypt_q_raw; | 
|  | prim_flop #( | 
|  | .Width      ( Sp2VWidth            ), | 
|  | .ResetValue ( Sp2VWidth'(SP2V_LOW) ) | 
|  | ) u_crypt_regs ( | 
|  | .clk_i  ( clk_i       ), | 
|  | .rst_ni ( rst_ni      ), | 
|  | .d_i    ( crypt_d     ), | 
|  | .q_o    ( crypt_q_raw ) | 
|  | ); | 
|  |  | 
|  | logic [Sp2VWidth-1:0] dec_key_gen_q_raw; | 
|  | prim_flop #( | 
|  | .Width      ( Sp2VWidth            ), | 
|  | .ResetValue ( Sp2VWidth'(SP2V_LOW) ) | 
|  | ) u_dec_key_gen_regs ( | 
|  | .clk_i  ( clk_i             ), | 
|  | .rst_ni ( rst_ni            ), | 
|  | .d_i    ( dec_key_gen_d     ), | 
|  | .q_o    ( dec_key_gen_q_raw ) | 
|  | ); | 
|  |  | 
|  | // We use vectors of sparsely encoded signals to reduce code duplication. | 
|  | localparam int unsigned NumSp2VSig = 9; | 
|  | sp2v_e [NumSp2VSig-1:0]                sp2v_sig; | 
|  | sp2v_e [NumSp2VSig-1:0]                sp2v_sig_chk; | 
|  | logic  [NumSp2VSig-1:0][Sp2VWidth-1:0] sp2v_sig_chk_raw; | 
|  | logic  [NumSp2VSig-1:0]                sp2v_sig_err; | 
|  |  | 
|  | assign sp2v_sig[0] = in_valid_i; | 
|  | assign sp2v_sig[1] = out_ready_i; | 
|  | assign sp2v_sig[2] = crypt_i; | 
|  | assign sp2v_sig[3] = dec_key_gen_i; | 
|  | assign sp2v_sig[4] = sp2v_e'(crypt_q_raw); | 
|  | assign sp2v_sig[5] = sp2v_e'(dec_key_gen_q_raw); | 
|  | assign sp2v_sig[6] = advance; | 
|  | assign sp2v_sig[7] = sub_bytes_out_req_i; | 
|  | assign sp2v_sig[8] = key_expand_out_req_i; | 
|  |  | 
|  | // Individually check sparsely encoded signals. | 
|  | for (genvar i = 0; i < NumSp2VSig; i++) begin : gen_sel_buf_chk | 
|  | aes_sel_buf_chk #( | 
|  | .Num   ( Sp2VNum   ), | 
|  | .Width ( Sp2VWidth ) | 
|  | ) u_aes_sp2v_sig_buf_chk_i ( | 
|  | .clk_i  ( clk_i               ), | 
|  | .rst_ni ( rst_ni              ), | 
|  | .sel_i  ( sp2v_sig[i]         ), | 
|  | .sel_o  ( sp2v_sig_chk_raw[i] ), | 
|  | .err_o  ( sp2v_sig_err[i]     ) | 
|  | ); | 
|  | assign sp2v_sig_chk[i] = sp2v_e'(sp2v_sig_chk_raw[i]); | 
|  | end | 
|  |  | 
|  | assign in_valid           = sp2v_sig_chk[0]; | 
|  | assign out_ready          = sp2v_sig_chk[1]; | 
|  | assign crypt              = sp2v_sig_chk[2]; | 
|  | assign dec_key_gen        = sp2v_sig_chk[3]; | 
|  | assign crypt_q            = sp2v_sig_chk[4]; | 
|  | assign dec_key_gen_q      = sp2v_sig_chk[5]; | 
|  | assign advance_chk        = sp2v_sig_chk[6]; | 
|  | assign sub_bytes_out_req  = sp2v_sig_chk[7]; | 
|  | assign key_expand_out_req = sp2v_sig_chk[8]; | 
|  |  | 
|  | // Collect encoding errors. | 
|  | // We instantiate the checker modules as close as possible to where the sparsely encoded signals | 
|  | // are used. Here, we collect also encoding errors detected in other places of the cipher core. | 
|  | assign sp_enc_err = |sp2v_sig_err | sp_enc_err_i; | 
|  |  | 
|  | //////////////// | 
|  | // Assertions // | 
|  | //////////////// | 
|  |  | 
|  | // Selectors must be known/valid | 
|  | `ASSERT_KNOWN(AesCiphOpKnown, op_i) | 
|  | `ASSERT(AesKeyLenValid, cfg_valid_i |-> key_len_i inside { | 
|  | AES_128, | 
|  | AES_192, | 
|  | AES_256 | 
|  | }) | 
|  | `ASSERT(AesControlStateValid, !alert_o |-> aes_cipher_ctrl_cs inside { | 
|  | IDLE, | 
|  | INIT, | 
|  | ROUND, | 
|  | FINISH, | 
|  | CLEAR_S, | 
|  | CLEAR_KD | 
|  | }) | 
|  |  | 
|  | endmodule |