| // Copyright lowRISC contributors. | 
 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 | // | 
 | // SHA-256 Padding logic | 
 | // | 
 |  | 
 | module sha2_pad import hmac_pkg::*; ( | 
 |   input clk_i, | 
 |   input rst_ni, | 
 |  | 
 |   input            wipe_secret, | 
 |   input sha_word_t wipe_v, | 
 |  | 
 |   // To actual FIFO | 
 |   input                 fifo_rvalid, | 
 |   input  sha_fifo_t     fifo_rdata, | 
 |   output logic          fifo_rready, | 
 |  | 
 |   // from SHA2 compress engine | 
 |   output logic          shaf_rvalid, | 
 |   output sha_word_t     shaf_rdata, | 
 |   input                 shaf_rready, | 
 |  | 
 |   input sha_en, | 
 |   input hash_start, | 
 |   input hash_process, | 
 |   input hash_done, | 
 |  | 
 |   input        [63:0] message_length, // # of bytes in bits (8 bits granularity) | 
 |   output logic        msg_feed_complete // Indicates, all message is feeded | 
 | ); | 
 |  | 
 |   // TODO: Thorough scenario for sha_en turn off in the middle of the ops. | 
 |  | 
 |   //logic [8:0] length_added; | 
 |  | 
 |   logic [63:0] tx_count;    // fin received data count. | 
 |  | 
 |   logic inc_txcount; | 
 |   logic fifo_partial; | 
 |   logic txcnt_eq_1a0; | 
 |   logic hash_process_flag; // Set by hash_process, clear by hash_done | 
 |  | 
 |   assign fifo_partial = ~&fifo_rdata.mask; | 
 |  | 
 |   // tx_count[8:0] == 'h1c0 --> should send LenHi | 
 |   assign txcnt_eq_1a0 = (tx_count[8:0] == 9'h1a0); | 
 |  | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       hash_process_flag <= 1'b0; | 
 |     end else if (hash_process) begin | 
 |       hash_process_flag <= 1'b1; | 
 |     end else if (hash_done || hash_start) begin | 
 |       hash_process_flag <= 1'b0; | 
 |     end | 
 |   end | 
 |  | 
 |   // Data path: fout_wdata | 
 |   typedef enum logic [2:0] { | 
 |     FifoIn,         // fin_wdata, fin_wstrb | 
 |     Pad80,          // {8'h80, 8'h00} , strb (calc based on len[4:3]) | 
 |     Pad00,          // 32'h0, full strb | 
 |     LenHi,          // len[63:32], full strb | 
 |     LenLo           // len[31:0], full strb | 
 |   } sel_data_e; | 
 |   sel_data_e sel_data; | 
 |  | 
 |   always_comb begin | 
 |     unique case (sel_data) | 
 |       FifoIn: begin | 
 |         shaf_rdata = fifo_rdata.data; | 
 |       end | 
 |  | 
 |       Pad80: begin | 
 |         // {a[7:0], b[7:0], c[7:0], d[7:0]} | 
 |         // msglen[4:3] == 00 |-> {'h80, 'h00, 'h00, 'h00} | 
 |         // msglen[4:3] == 01 |-> {msg,  'h80, 'h00, 'h00} | 
 |         // msglen[4:3] == 10 |-> {msg[15:0],  'h80, 'h00} | 
 |         // msglen[4:3] == 11 |-> {msg[23:0],        'h80} | 
 |         unique case (message_length[4:3]) | 
 |           2'b 00: shaf_rdata = 32'h 8000_0000; | 
 |           2'b 01: shaf_rdata = {fifo_rdata.data[31:24], 24'h 8000_00}; | 
 |           2'b 10: shaf_rdata = {fifo_rdata.data[31:16], 16'h 8000}; | 
 |           2'b 11: shaf_rdata = {fifo_rdata.data[31: 8],  8'h 80}; | 
 |           default: shaf_rdata = 32'h0; | 
 |         endcase | 
 |       end | 
 |  | 
 |       Pad00: begin | 
 |         shaf_rdata = '0; | 
 |       end | 
 |  | 
 |       LenHi: begin | 
 |         shaf_rdata = message_length[63:32]; | 
 |       end | 
 |  | 
 |       LenLo: begin | 
 |         shaf_rdata = message_length[31:0]; | 
 |       end | 
 |  | 
 |       default: begin | 
 |         shaf_rdata = '0; | 
 |       end | 
 |     endcase | 
 |   end | 
 |  | 
 |   // Padded length | 
 |   // $ceil(message_length + 8 + 64, 512) -> message_length [8:0] + 440 and ignore carry | 
 |   //assign length_added = (message_length[8:0] + 9'h1b8) ; | 
 |  | 
 |   // fifo control | 
 |   // add 8'h 80 , N 8'h00, 64'h message_length | 
 |  | 
 |   // Steps | 
 |   // 1. `hash_start` from CPU (or DMA?) | 
 |   // 2. calculate `padded_length` from `message_length` | 
 |   // 3. Check if tx_count == message_length, then go to 5 | 
 |   // 4. Receiving FIFO input (hand over to fifo output) | 
 |   // 5. Padding bit 1 (8'h80) followed by 8'h00 if needed | 
 |   // 6. Padding with length (high -> low) | 
 |  | 
 |   // State Machine | 
 |   typedef enum logic [2:0] { | 
 |     StIdle,        // fin_full to prevent unwanted FIFO write | 
 |     StFifoReceive, // Check tx_count == message_length | 
 |     StPad80,       // 8'h 80 + 8'h 00 X N | 
 |     StPad00, | 
 |     StLenHi, | 
 |     StLenLo | 
 |   } pad_st_e; | 
 |  | 
 |   pad_st_e st_q, st_d; | 
 |  | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       st_q <= StIdle; | 
 |     end else begin | 
 |       st_q <= st_d; | 
 |     end | 
 |   end | 
 |  | 
 |   // Next state | 
 |   // TODO: Error handling (shaf_rready with shaf_rvalid == 1) | 
 |   always_comb begin | 
 |     shaf_rvalid = 1'b0; | 
 |     inc_txcount = 1'b0; | 
 |     sel_data = FifoIn; | 
 |     fifo_rready = 1'b0; | 
 |  | 
 |     st_d = StIdle; | 
 |  | 
 |     unique case (st_q) | 
 |       StIdle: begin | 
 |         sel_data = FifoIn; | 
 |         shaf_rvalid = 1'b0; | 
 |  | 
 |         if (sha_en && hash_start) begin | 
 |           inc_txcount = 1'b0; | 
 |  | 
 |           st_d = StFifoReceive; | 
 |         end else begin | 
 |           st_d = StIdle; | 
 |         end | 
 |       end | 
 |  | 
 |       StFifoReceive: begin | 
 |         sel_data = FifoIn; | 
 |  | 
 |         if (fifo_partial && fifo_rvalid) begin | 
 |           // End of the message, assume hash_process_flag is set | 
 |           shaf_rvalid  = 1'b0; // Update entry at StPad80 | 
 |           inc_txcount = 1'b0; | 
 |           fifo_rready = 1'b0; | 
 |  | 
 |           st_d = StPad80; | 
 |         end else if (!hash_process_flag) begin | 
 |           fifo_rready = shaf_rready; | 
 |           shaf_rvalid  = fifo_rvalid; | 
 |           inc_txcount = shaf_rready; | 
 |  | 
 |           st_d = StFifoReceive; | 
 |         end else if (tx_count == message_length) begin | 
 |           // already received all msg and was waiting process flag | 
 |           shaf_rvalid  = 1'b0; | 
 |           inc_txcount = 1'b0; | 
 |           fifo_rready = 1'b0; | 
 |  | 
 |           st_d = StPad80; | 
 |         end else begin | 
 |           shaf_rvalid  = fifo_rvalid; | 
 |           fifo_rready = shaf_rready; // 0 always | 
 |           inc_txcount = shaf_rready; // 0 always | 
 |  | 
 |           st_d = StFifoReceive; | 
 |         end | 
 |       end | 
 |  | 
 |       StPad80: begin | 
 |         sel_data = Pad80; | 
 |  | 
 |         shaf_rvalid = 1'b1; | 
 |         fifo_rready = shaf_rready && |message_length[4:3]; // Only when partial | 
 |  | 
 |         // exactly 96 bits left, do not need to pad00's | 
 |         if (shaf_rready && txcnt_eq_1a0) begin | 
 |           st_d = StLenHi; | 
 |           inc_txcount = 1'b1; | 
 |         // it does not matter if value is < or > than 416 bits.  If it's the former, 00 pad until | 
 |         // length field.  If >, then the next chunk will contain the length field with appropriate | 
 |         // 0 padding. | 
 |         end else if (shaf_rready && !txcnt_eq_1a0) begin | 
 |           st_d = StPad00; | 
 |           inc_txcount = 1'b1; | 
 |         end else begin | 
 |           st_d = StPad80; | 
 |           inc_txcount = 1'b0; | 
 |         end | 
 |  | 
 |         // # Below part is temporal code to speed up the SHA by 16 clocks per chunk | 
 |         // # (80 clk --> 64 clk) | 
 |         // # leaving this as a reference but needs to verify it. | 
 |         //if (shaf_rready && !txcnt_eq_1a0) begin | 
 |         //  st_d = StPad00; | 
 |         // | 
 |         //  inc_txcount = 1'b1; | 
 |         //  shaf_rvalid = (msg_word_aligned) ? 1'b1 : fifo_rvalid; | 
 |         //  fifo_rready = (msg_word_aligned) ? 1'b0 : 1'b1; | 
 |         //end else if (!shaf_rready && !txcnt_eq_1a0) begin | 
 |         //  st_d = StPad80; | 
 |         // | 
 |         //  inc_txcount = 1'b0; | 
 |         //  shaf_rvalid = (msg_word_aligned) ? 1'b1 : fifo_rvalid; | 
 |         // | 
 |         //end else if (shaf_rready && txcnt_eq_1a0) begin | 
 |         //  st_d = StLenHi; | 
 |         //  inc_txcount = 1'b1; | 
 |         //end else begin | 
 |         //  // !shaf_rready && txcnt_eq_1a0 , just wait until fifo_rready asserted | 
 |         //  st_d = StPad80; | 
 |         //  inc_txcount = 1'b0; | 
 |         //end | 
 |       end | 
 |  | 
 |       StPad00: begin | 
 |         sel_data = Pad00; | 
 |         shaf_rvalid = 1'b1; | 
 |  | 
 |         if (shaf_rready) begin | 
 |           inc_txcount = 1'b1; | 
 |  | 
 |           if (txcnt_eq_1a0) begin | 
 |             st_d = StLenHi; | 
 |           end else begin | 
 |             st_d = StPad00; | 
 |           end | 
 |         end else begin | 
 |           st_d = StPad00; | 
 |         end | 
 |       end | 
 |  | 
 |       StLenHi: begin | 
 |         sel_data = LenHi; | 
 |         shaf_rvalid = 1'b1; | 
 |  | 
 |         if (shaf_rready) begin | 
 |           st_d = StLenLo; | 
 |  | 
 |           inc_txcount = 1'b1; | 
 |         end else begin | 
 |           st_d = StLenHi; | 
 |  | 
 |           inc_txcount = 1'b0; | 
 |         end | 
 |       end | 
 |  | 
 |       StLenLo: begin | 
 |         sel_data = LenLo; | 
 |         shaf_rvalid = 1'b1; | 
 |  | 
 |         if (shaf_rready) begin | 
 |           st_d = StIdle; | 
 |  | 
 |           inc_txcount = 1'b1; | 
 |         end else begin | 
 |           st_d = StLenLo; | 
 |  | 
 |           inc_txcount = 1'b0; | 
 |         end | 
 |       end | 
 |  | 
 |       default: begin | 
 |         st_d = StIdle; | 
 |       end | 
 |     endcase | 
 |   end | 
 |  | 
 |   // tx_count | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       tx_count <= '0; | 
 |     end else if (hash_start) begin | 
 |       tx_count <= '0; | 
 |     end else if (inc_txcount) begin | 
 |       tx_count[63:5] <= tx_count[63:5] + 1'b1; | 
 |     end | 
 |   end | 
 |  | 
 |   // State machine is in Idle only when it meets tx_count == message length | 
 |   assign msg_feed_complete = hash_process_flag && (st_q == StIdle); | 
 |  | 
 |   // When fifo_partial, fifo shouldn't be empty and hash_process was set | 
 |   `ASSERT(ValidPartialConditionAssert, | 
 |           fifo_partial && fifo_rvalid |-> hash_process_flag, | 
 |           clk_i, !rst_ni) | 
 |  | 
 | endmodule |