|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | // | 
|  | // SHA-256 algorithm | 
|  | // | 
|  |  | 
|  | module sha2 import hmac_pkg::*; ( | 
|  | input clk_i, | 
|  | input rst_ni, | 
|  |  | 
|  | input            wipe_secret, | 
|  | input sha_word_t wipe_v, | 
|  |  | 
|  | // FIFO read signal | 
|  | input             fifo_rvalid, | 
|  | input  sha_fifo_t fifo_rdata, | 
|  | output logic      fifo_rready, | 
|  |  | 
|  | // Control signals | 
|  | input        sha_en,   // If disabled, it clears internal content. | 
|  | input        hash_start, | 
|  | input        hash_process, | 
|  | output logic hash_done, | 
|  |  | 
|  | input        [63:0] message_length,   // bits but byte based | 
|  | output sha_word_t [7:0] digest, | 
|  |  | 
|  | output logic idle | 
|  | ); | 
|  |  | 
|  | localparam int unsigned RoundWidth = $clog2(NumRound); | 
|  |  | 
|  | logic msg_feed_complete; | 
|  |  | 
|  | logic      shaf_rready; | 
|  | sha_word_t shaf_rdata; | 
|  | logic      shaf_rvalid; | 
|  |  | 
|  | logic [RoundWidth-1:0] round; | 
|  |  | 
|  | logic      [3:0]  w_index; | 
|  | sha_word_t [15:0] w; | 
|  |  | 
|  | localparam sha_word_t ZeroWord = '0; | 
|  |  | 
|  | // w, hash, digest update logic control signals | 
|  | logic update_w_from_fifo, calculate_next_w; | 
|  | logic init_hash, run_hash, complete_one_chunk; | 
|  | logic update_digest, clear_digest; | 
|  |  | 
|  | logic hash_done_next; // to meet the phase with digest value. | 
|  |  | 
|  | sha_word_t [7:0] hash;    // a,b,c,d,e,f,g,h | 
|  |  | 
|  | // Fill up w | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin : fill_w | 
|  | if (!rst_ni) begin | 
|  | w <= '0; | 
|  | end else if (wipe_secret) begin | 
|  | w <= w ^ {16{wipe_v}}; | 
|  | end else if (!sha_en) begin | 
|  | w <= '0; | 
|  | end else if (!run_hash && update_w_from_fifo) begin | 
|  | // this logic runs at the first stage of SHA. | 
|  | w <= {shaf_rdata, w[15:1]}; | 
|  | end else if (calculate_next_w) begin | 
|  | w <= {calc_w(w[0], w[1], w[9], w[14]), w[15:1]}; | 
|  | //end else if (run_hash && update_w_from_fifo) begin | 
|  | //  // This code runs when round is in [48, 63]. At this time, it reads from the fifo | 
|  | //  // to fill the register if available. If FIFO goes to empty, w_index doesn't increase | 
|  | //  // and it cannot reach 15. Then the sha engine doesn't start, which introduces latency. | 
|  | //  // | 
|  | //  // But in this case, still w should be shifted to feed SHA compress engine. Then | 
|  | //  // fifo_rdata should be inserted in the middle of w index. | 
|  | //  // w[64-round + w_index] <= fifo_rdata; | 
|  | //  for (int i = 0 ; i < 16 ; i++) begin | 
|  | //    if (i == (64 - round + w_index)) begin | 
|  | //      w[i] <= shaf_rdata; | 
|  | //    end else if (i == 15) begin | 
|  | //      w[i] <= '0; | 
|  | //    end else begin | 
|  | //      w[i] <= w[i+1]; | 
|  | //    end | 
|  | //  end | 
|  | end else if (run_hash) begin | 
|  | // Just shift-out. There's no incoming data | 
|  | w <= {ZeroWord, w[15:1]}; | 
|  | end | 
|  | end : fill_w | 
|  |  | 
|  | // Update engine | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin : compress_round | 
|  | if (!rst_ni) begin | 
|  | hash <= '{default:'0}; | 
|  | end else if (wipe_secret) begin | 
|  | for (int i = 0 ; i < 8 ; i++) begin | 
|  | hash[i] <= hash[i] ^ wipe_v; | 
|  | end | 
|  | end else if (init_hash) begin | 
|  | hash <= digest; | 
|  | end else if (run_hash) begin | 
|  | hash <= compress( w[0], CubicRootPrime[round], hash); | 
|  | end | 
|  | end : compress_round | 
|  |  | 
|  | // Digest | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | digest <= '{default: '0}; | 
|  | end else if (wipe_secret) begin | 
|  | for (int i = 0 ; i < 8 ; i++) begin | 
|  | digest[i] <= digest[i] ^ wipe_v; | 
|  | end | 
|  | end else if (hash_start) begin | 
|  | for (int i = 0 ; i < 8 ; i++) begin | 
|  | digest[i] <= InitHash[i]; | 
|  | end | 
|  | end else if (!sha_en || clear_digest) begin | 
|  | digest <= '0; | 
|  | end else if (update_digest) begin | 
|  | for (int i = 0 ; i < 8 ; i++) begin | 
|  | digest[i] <= digest[i] + hash[i]; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // round | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | round <= '0; | 
|  | end else if (!sha_en) begin | 
|  | round <= '0; | 
|  | end else if (run_hash) begin | 
|  | if (round == RoundWidth'(unsigned'(NumRound-1))) begin | 
|  | round <= '0; | 
|  | end else begin | 
|  | round <= round + 1; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // w_index | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | w_index <= '0; | 
|  | end else if (!sha_en) begin | 
|  | w_index <= '0; | 
|  | end else if (update_w_from_fifo) begin | 
|  | w_index <= w_index + 1; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign shaf_rready = update_w_from_fifo; | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) hash_done <= 1'b0; | 
|  | else         hash_done <= hash_done_next; | 
|  | end | 
|  |  | 
|  | typedef enum logic [1:0] { | 
|  | FifoIdle, | 
|  | FifoLoadFromFifo, | 
|  | FifoWait | 
|  | } fifoctl_state_e; | 
|  |  | 
|  | fifoctl_state_e fifo_st_q, fifo_st_d; | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | fifo_st_q <= FifoIdle; | 
|  | end else begin | 
|  | fifo_st_q <= fifo_st_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | always_comb begin | 
|  | fifo_st_d = FifoIdle; | 
|  | update_w_from_fifo = 1'b0; | 
|  | hash_done_next = 1'b0; | 
|  |  | 
|  | unique case (fifo_st_q) | 
|  | FifoIdle: begin | 
|  | if (hash_start) begin | 
|  | fifo_st_d = FifoLoadFromFifo; | 
|  | end else begin | 
|  | fifo_st_d = FifoIdle; | 
|  | end | 
|  | end | 
|  |  | 
|  | FifoLoadFromFifo: begin | 
|  | if (!sha_en) begin | 
|  | fifo_st_d = FifoIdle; | 
|  | update_w_from_fifo = 1'b0; | 
|  | end else if (!shaf_rvalid) begin | 
|  | // Wait until it is filled | 
|  | fifo_st_d = FifoLoadFromFifo; | 
|  | update_w_from_fifo = 1'b0; | 
|  | end else if (w_index == 4'd 15) begin | 
|  | fifo_st_d = FifoWait; | 
|  | update_w_from_fifo = 1'b1; | 
|  | end else begin | 
|  | fifo_st_d = FifoLoadFromFifo; | 
|  | update_w_from_fifo = 1'b1; | 
|  | end | 
|  | end | 
|  |  | 
|  | FifoWait: begin | 
|  | // Wait until next fetch begins (begin at round == 48)a | 
|  | if (msg_feed_complete && complete_one_chunk) begin | 
|  | fifo_st_d = FifoIdle; | 
|  |  | 
|  | hash_done_next = 1'b1; | 
|  | end else if (complete_one_chunk) begin | 
|  | fifo_st_d = FifoLoadFromFifo; | 
|  | end else begin | 
|  | fifo_st_d = FifoWait; | 
|  | end | 
|  | end | 
|  |  | 
|  | default: begin | 
|  | fifo_st_d = FifoIdle; | 
|  | end | 
|  | endcase | 
|  | end | 
|  |  | 
|  | // SHA control | 
|  | typedef enum logic [1:0] { | 
|  | ShaIdle, | 
|  | ShaCompress, | 
|  | ShaUpdateDigest | 
|  | } sha_st_t; | 
|  |  | 
|  | sha_st_t sha_st_q, sha_st_d; | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | sha_st_q <= ShaIdle; | 
|  | end else begin | 
|  | sha_st_q <= sha_st_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign clear_digest = hash_start; | 
|  |  | 
|  | always_comb begin | 
|  | update_digest    = 1'b0; | 
|  | calculate_next_w = 1'b0; | 
|  |  | 
|  | init_hash        = 1'b0; | 
|  | run_hash         = 1'b0; | 
|  |  | 
|  | unique case (sha_st_q) | 
|  | ShaIdle: begin | 
|  | if (fifo_st_q == FifoWait) begin | 
|  | init_hash = 1'b1; | 
|  | sha_st_d = ShaCompress; | 
|  | end else begin | 
|  | sha_st_d = ShaIdle; | 
|  | end | 
|  | end | 
|  |  | 
|  | ShaCompress: begin | 
|  | run_hash = 1'b1; | 
|  |  | 
|  | if (round < 48) begin | 
|  | calculate_next_w = 1'b1; | 
|  | end | 
|  |  | 
|  | if (complete_one_chunk) begin | 
|  | sha_st_d = ShaUpdateDigest; | 
|  | end else begin | 
|  | sha_st_d = ShaCompress; | 
|  | end | 
|  | end | 
|  |  | 
|  | ShaUpdateDigest: begin | 
|  | update_digest = 1'b1; | 
|  | if (fifo_st_q == FifoWait) begin | 
|  | init_hash = 1'b1; | 
|  | sha_st_d = ShaCompress; | 
|  | end else begin | 
|  | sha_st_d = ShaIdle; | 
|  | end | 
|  | end | 
|  |  | 
|  | default: begin | 
|  | sha_st_d = ShaIdle; | 
|  | end | 
|  | endcase | 
|  | end | 
|  |  | 
|  | // complete_one_chunk | 
|  | assign complete_one_chunk = (round == 6'd63); | 
|  |  | 
|  | sha2_pad u_pad ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  |  | 
|  | .wipe_secret, | 
|  | .wipe_v, | 
|  |  | 
|  | .fifo_rvalid, | 
|  | .fifo_rdata, | 
|  | .fifo_rready, | 
|  |  | 
|  | .shaf_rvalid, | 
|  | .shaf_rdata, | 
|  | .shaf_rready, | 
|  |  | 
|  | .sha_en, | 
|  | .hash_start, | 
|  | .hash_process, | 
|  | .hash_done, | 
|  |  | 
|  | .message_length, | 
|  | .msg_feed_complete | 
|  | ); | 
|  |  | 
|  | // Idle | 
|  | assign idle = (fifo_st_q == FifoIdle) && (sha_st_q == ShaIdle) && !hash_start; | 
|  |  | 
|  | endmodule : sha2 |