blob: 4d93d790e317c0086a06bb018d010c1d7aba958d [file] [log] [blame]
// 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