|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | // | 
|  | // AES counter for CTR mode | 
|  | // | 
|  | // This module uses one counter with a width of SliceSizeCtr to iteratively increment the 128-bit | 
|  | // counter value. | 
|  |  | 
|  | `include "prim_assert.sv" | 
|  |  | 
|  | module aes_ctr import aes_pkg::*; | 
|  | ( | 
|  | input  logic                                       clk_i, | 
|  | input  logic                                       rst_ni, | 
|  |  | 
|  | input  sp2v_e                                      incr_i, | 
|  | output sp2v_e                                      ready_o, | 
|  | output logic                                       alert_o, | 
|  |  | 
|  | input  logic  [NumSlicesCtr-1:0][SliceSizeCtr-1:0] ctr_i, | 
|  | output logic  [NumSlicesCtr-1:0][SliceSizeCtr-1:0] ctr_o, | 
|  | output sp2v_e [NumSlicesCtr-1:0]                   ctr_we_o | 
|  | ); | 
|  |  | 
|  | // Reverse byte order - unrelated to NumSlicesCtr and SliceSizeCtr | 
|  | function automatic logic [15:0][7:0] aes_rev_order_byte(logic [15:0][7:0] in); | 
|  | logic [15:0][7:0] out; | 
|  | for (int i = 0; i < 16; i++) begin | 
|  | out[i] = in[15-i]; | 
|  | end | 
|  | return out; | 
|  | endfunction | 
|  |  | 
|  | // Reverse sp2v order | 
|  | function automatic sp2v_e [NumSlicesCtr-1:0] aes_rev_order_sp2v(sp2v_e [NumSlicesCtr-1:0] in); | 
|  | sp2v_e [NumSlicesCtr-1:0] out; | 
|  | for (int i = 0; i < NumSlicesCtr; i++) begin | 
|  | out[i] = in[NumSlicesCtr - 1 - i]; | 
|  | end | 
|  | return out; | 
|  | endfunction | 
|  |  | 
|  | // Local parameters | 
|  | localparam int unsigned SliceIdxWidth = prim_util_pkg::vbits(NumSlicesCtr); | 
|  |  | 
|  | // Types | 
|  | // $ ./sparse-fsm-encode.py -d 3 -m 3 -n 5 \ | 
|  | //      -s 31468618 --language=sv | 
|  | // | 
|  | // Hamming distance histogram: | 
|  | // | 
|  | //  0: -- | 
|  | //  1: -- | 
|  | //  2: -- | 
|  | //  3: |||||||||||||||||||| (66.67%) | 
|  | //  4: |||||||||| (33.33%) | 
|  | //  5: -- | 
|  | // | 
|  | // Minimum Hamming distance: 3 | 
|  | // Maximum Hamming distance: 4 | 
|  | // | 
|  | localparam int StateWidth = 5; | 
|  | typedef enum logic [StateWidth-1:0] { | 
|  | IDLE  = 5'b01110, | 
|  | INCR  = 5'b11000, | 
|  | ERROR = 5'b00001 | 
|  | } aes_ctr_e; | 
|  |  | 
|  | // Signals | 
|  | aes_ctr_e                                   aes_ctr_ns, aes_ctr_cs; | 
|  | logic                   [SliceIdxWidth-1:0] ctr_slice_idx_d, ctr_slice_idx_q; | 
|  | logic                                       ctr_carry_d, ctr_carry_q; | 
|  |  | 
|  | logic  [NumSlicesCtr-1:0][SliceSizeCtr-1:0] ctr_i_rev; // 8 times 2 bytes | 
|  | logic  [NumSlicesCtr-1:0][SliceSizeCtr-1:0] ctr_o_rev; // 8 times 2 bytes | 
|  | sp2v_e [NumSlicesCtr-1:0]                   ctr_we_o_rev; | 
|  | sp2v_e                                      ctr_we; | 
|  |  | 
|  | logic                    [SliceSizeCtr-1:0] ctr_i_slice; | 
|  | logic                    [SliceSizeCtr-1:0] ctr_o_slice; | 
|  | logic                      [SliceSizeCtr:0] ctr_value; | 
|  |  | 
|  | logic                                       alert; | 
|  | sp2v_e                                      incr; | 
|  | logic                                       incr_err_d, incr_err_q; | 
|  |  | 
|  | //////////// | 
|  | // Inputs // | 
|  | //////////// | 
|  |  | 
|  | // Reverse byte order | 
|  | assign ctr_i_rev = aes_rev_order_byte(ctr_i); | 
|  |  | 
|  | // Check sparsely encoded incr signal. | 
|  | logic [Sp2VWidth-1:0] incr_raw; | 
|  | aes_sel_buf_chk #( | 
|  | .Num   ( Sp2VNum   ), | 
|  | .Width ( Sp2VWidth ) | 
|  | ) u_aes_sb_en_buf_chk ( | 
|  | .clk_i  ( clk_i      ), | 
|  | .rst_ni ( rst_ni     ), | 
|  | .sel_i  ( incr_i     ), | 
|  | .sel_o  ( incr_raw   ), | 
|  | .err_o  ( incr_err_d ) | 
|  | ); | 
|  | assign incr = sp2v_e'(incr_raw); | 
|  |  | 
|  | // Need to register errors in incr to avoid circular loops in the main | 
|  | // controller related to start. | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin : reg_out_ack_err | 
|  | if (!rst_ni) begin | 
|  | incr_err_q <= 1'b0; | 
|  | end else if (incr_err_d) begin | 
|  | incr_err_q <= 1'b1; | 
|  | end | 
|  | end | 
|  |  | 
|  | ///////////// | 
|  | // Counter // | 
|  | ///////////// | 
|  |  | 
|  | // We do SliceSizeCtr bits at a time. | 
|  | assign ctr_i_slice = ctr_i_rev[ctr_slice_idx_q]; | 
|  | assign ctr_value   = ctr_i_slice + {{(SliceSizeCtr-1){1'b0}}, ctr_carry_q}; | 
|  | assign ctr_o_slice = ctr_value[SliceSizeCtr-1:0]; | 
|  |  | 
|  | ///////////// | 
|  | // Control // | 
|  | ///////////// | 
|  |  | 
|  | // FSM | 
|  | always_comb begin : aes_ctr_fsm | 
|  |  | 
|  | // Outputs | 
|  | ready_o         = SP2V_LOW; | 
|  | ctr_we          = SP2V_LOW; | 
|  | alert           = 1'b0; | 
|  |  | 
|  | // FSM | 
|  | aes_ctr_ns      = aes_ctr_cs; | 
|  | ctr_slice_idx_d = ctr_slice_idx_q; | 
|  | ctr_carry_d     = ctr_carry_q; | 
|  |  | 
|  | unique case (aes_ctr_cs) | 
|  | IDLE: begin | 
|  | ready_o = SP2V_HIGH; | 
|  | if (incr == SP2V_HIGH) begin | 
|  | // Initialize slice index and carry bit. | 
|  | ctr_slice_idx_d = '0; | 
|  | ctr_carry_d     = 1'b1; | 
|  | aes_ctr_ns      = INCR; | 
|  | end | 
|  | end | 
|  |  | 
|  | INCR: begin | 
|  | // Increment slice index. | 
|  | ctr_slice_idx_d = ctr_slice_idx_q + SliceIdxWidth'(1); | 
|  | ctr_carry_d     = ctr_value[SliceSizeCtr]; | 
|  | ctr_we          = SP2V_HIGH; | 
|  |  | 
|  | if (ctr_slice_idx_q == {SliceIdxWidth{1'b1}}) begin | 
|  | aes_ctr_ns = IDLE; | 
|  | end | 
|  | end | 
|  |  | 
|  | ERROR: begin | 
|  | // Terminal error state | 
|  | alert = 1'b1; | 
|  | end | 
|  |  | 
|  | // We should never get here. If we do (e.g. via a malicious | 
|  | // glitch), error out immediately. | 
|  | default: begin | 
|  | aes_ctr_ns = ERROR; | 
|  | end | 
|  | endcase | 
|  | end | 
|  |  | 
|  | // Registers | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | ctr_slice_idx_q <= '0; | 
|  | ctr_carry_q     <= '0; | 
|  | end else begin | 
|  | ctr_slice_idx_q <= ctr_slice_idx_d; | 
|  | ctr_carry_q     <= ctr_carry_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] aes_ctr_cs_raw; | 
|  | assign aes_ctr_cs = aes_ctr_e'(aes_ctr_cs_raw); | 
|  | prim_flop #( | 
|  | .Width(StateWidth), | 
|  | .ResetValue(StateWidth'(IDLE)) | 
|  | ) u_state_regs ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .d_i ( aes_ctr_ns     ), | 
|  | .q_o ( aes_ctr_cs_raw ) | 
|  | ); | 
|  |  | 
|  | ///////////// | 
|  | // Outputs // | 
|  | ///////////// | 
|  |  | 
|  | // Combine input and counter output. | 
|  | always_comb begin | 
|  | ctr_o_rev                  = ctr_i_rev; | 
|  | ctr_o_rev[ctr_slice_idx_q] = ctr_o_slice; | 
|  | end | 
|  |  | 
|  | // Generate the sliced write enable. | 
|  | always_comb begin | 
|  | ctr_we_o_rev                  = {NumSlicesCtr{SP2V_LOW}}; | 
|  | ctr_we_o_rev[ctr_slice_idx_q] = ctr_we; | 
|  | end | 
|  |  | 
|  | // Reverse byte and bit order. | 
|  | assign ctr_o    = aes_rev_order_byte(ctr_o_rev); | 
|  | assign ctr_we_o = aes_rev_order_sp2v(ctr_we_o_rev); | 
|  |  | 
|  | // Collect alert signals. | 
|  | assign alert_o  = alert | incr_err_q; | 
|  |  | 
|  | //////////////// | 
|  | // Assertions // | 
|  | //////////////// | 
|  | `ASSERT(AesCtrStateValid, !alert_o |-> aes_ctr_cs inside { | 
|  | IDLE, | 
|  | INCR | 
|  | }) | 
|  |  | 
|  | endmodule |