SHA256 core

First cut of SHA256 core
This commit is contained in:
bsdevlin 2019-02-27 19:45:00 -05:00
parent 1abb875397
commit 547a557a14
3 changed files with 340 additions and 0 deletions

View File

@ -0,0 +1,40 @@
/*
The SHA256 package file.
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package sha256_pkg;
// K values used during each round
parameter [63:0][31:0] K = {
32'hc67178f2,32'hbef9a3f7,32'ha4506ceb,32'h90befffa,32'h8cc70208,32'h84c87814,32'h78a5636f,32'h748f82ee,
32'h682e6ff3,32'h5b9cca4f,32'h4ed8aa4a,32'h391c0cb3,32'h34b0bcb5,32'h2748774c,32'h1e376c08,32'h19a4c116,
32'h106aa070,32'hf40e3585,32'hd6990624,32'hd192e819,32'hc76c51a3,32'hc24b8b70,32'ha81a664b,32'ha2bfe8a1,
32'h92722c85,32'h81c2c92e,32'h766a0abb,32'h650a7354,32'h53380d13,32'h4d2c6dfc,32'h2e1b2138,32'h27b70a85,
32'h14292967,32'h06ca6351,32'hd5a79147,32'hc6e00bf3,32'hbf597fc7,32'hb00327c8,32'ha831c66d,32'h983e5152,
32'h76f988da,32'h5cb0a9dc,32'h4a7484aa,32'h2de92c6f,32'h240ca1cc,32'h0fc19dc6,32'hefbe4786,32'he49b69c1,
32'hc19bf174,32'h9bdc06a7,32'h80deb1fe,32'h72be5d74,32'h550c7dc3,32'h243185be,32'h12835b01,32'hd807aa98,
32'hab1c5ed5,32'h923f82a4,32'h59f111f1,32'h3956c25b,32'he9b5dba5,32'hb5c0fbcf,32'h71374491,32'h428a2f98
};
// Initial values used for H
parameter [7:0][31:0] IV = {
32'h5be0cd19, 32'h1f83d9ab, 32'h9b05688c, 32'h510e527f,
32'ha54ff53a, 32'h3c6ef372, 32'hbb67ae85, 32'h6a09e667
};
endpackage

View File

@ -0,0 +1,224 @@
/*
This is an implementation of the SHA256 hash algorithm.
Takes an AXI stream 512 bit interface. We back pressure the input
if the message size is > 512 bits while we process the 64 rounds.
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
module sha256_top
import sha256_pkg::*;
(
input i_clk, i_rst,
if_axi_stream.sink i_block, // Message stream to be hashed, must be 512 bits
if_axi_stream.source o_hash // Resulting hash digest (32 bytes)
);
localparam DAT_BYTS = 64;
localparam DAT_BITS = DAT_BYTS*8; // Must be 512
logic [7:0][31:0] V; // Internal state, V[0] == A, V[7] == H
logic [31:0] T1, T2;
logic [15:0][31:0] W;
logic [15:0] W_nxt;
logic eop_l, padding_only, final_block;
logic [63:0] bit_len;
logic [$clog2(64)-1:0] rnd_cntr;
enum {SHA_IDLE = 0,
SHA_ROUNDS = 1,
SHA_UPDATE_HV = 2,
SHA_FINAL = 3} sha_state;
// Used to make compression function easier to read
localparam A = 0;
localparam B = 1;
localparam C = 2;
localparam D = 3;
localparam E = 4;
localparam F = 5;
localparam G = 6;
localparam H = 7;
always_ff @ (posedge i_clk) begin
if (i_rst) begin
sha_state <= SHA_IDLE;
rnd_cntr <= 0;
o_hash.reset_source();
i_block.rdy <= 0;
bit_len <= 0;
W <= 0;
eop_l <= 0;
padding_only <= 0;
final_block <= 0;
end else begin
o_hash.sop <= 1;
o_hash.eop <= 1;
case(sha_state)
SHA_IDLE: begin
update_HV(1);
rnd_cntr <= 0;
final_block <= 0;
eop_l <= 0;
padding_only <= 0;
i_block.rdy <= 1;
// As soon as we have one write on the input we can start
if (i_block.rdy && i_block.val && i_block.sop) begin
W <= i_block.dat;
bit_len <= DAT_BITS;
i_block.rdy <= 0;
if (i_block.eop)
msg_eop();
sha_state <= SHA_ROUNDS;
end
end
SHA_ROUNDS: begin
for (int i = 0; i < 16; i++) W[i] <= W[i+i];
W[15] <= W_nxt;
compress();
rnd_cntr <= rnd_cntr + 1;
if (rnd_cntr == 62) begin
rnd_cntr <= 0;
i_block.rdy <= ~(final_block || padding_only);
sha_state <= SHA_UPDATE_HV;
end
end
SHA_UPDATE_HV: begin
update_H(0);
if (final_block) begin
sha_state <= SHA_FINAL;
i_block.rdy <= 0;
end else if (padding_only) begin
final_block <= 1;
W <= 0;
W[(64-8)*8 +: 64] <= bit_len;
W[(bit_len/8) % 64 +: 8] <= 1;
end else if (i_block.rdy && i_block.val) begin
W <= i_block.dat;
bit_len <= bit_len + DAT_BITS;
if (i_block.eop) begin
msg_eop();
i_block.rdy <= 0;
end
sha_state <= SHA_ROUNDS;
end
end
SHA_FINAL: begin
o_hash.val <= 1;
o_hash.dat <= H;
if (o_hash.val && o_hash.rdy) begin
o_hash.val <= 0;
sha_state <= SHA_IDLE;
end
end
endcase
end
end
// On the msg .eop we need to make sure we add padding if there is space other
// wise set a flag so next time we call we only add the padding block
task msg_eop();
eop_l <= 1;
bit_len <= bit_len + (i_block.mod == 0 ? DAT_BITS : i_block.mod*8);
if (i_block.mod == 0 || i_block.mod > 64-9) begin
padding_only <= 1; // Means we need one block extra with only len (and possibly the terminating 0x1)
end else begin
final_block <= 1; // This is the final block and includes padding
end
for (int i = 0; i < 64; i++)
M[i*8 +: 8] <= (i_block.mod == 0 || i < i_block.mod) ? i_block.dat[i*8 +: 8] : 0;
if (i_block.mod != 0)
M[i_block.mod*8 +: 8] <= 1;
if (i_block.mod < 64-9)
M[(64-8)*8 +: 64] <= bit_len + i_block.mod*8;
endtask
always_comb begin
W_nxt = little_sig1(W[14]) + W[9] + little_sig0(W[1]) + W[0];
end
always_comb begin
T1 = V[H] + big_sig1(V[E]) + ch(V[E], V[F], V[G]) + sha256_pkg::K[rnd_cntr] + W[0];
T2 = big_sig0(V[A]) + maj(V[A], V[B], V[C]);
end
task compress();
V[H] <= V[G];
V[G] <= V[F];
V[F] <= V[E];
V[E] <= V[D] + T1;
V[D] <= V[C];
V[C] <= V[B];
V[B] <= V[A];
V[A] <= T1 + T2;
endtask
task update_HV(logic init);
if (init) begin
for (int i = 0; i < 8; i++) begin
H[i] <= sha256_pkg::IV[i];
V[i] <= sha256_pkg::IV[i];
end
end else begin
for (int i = 0; i < 8; i++) begin
H[i] <= H[i] + V[i];
V[i] <= H[i] + V[i];
end
end
endtask
function [31:0] litte_sig0(input logic [31:0] in);
litte_sig0 = {rotr(in, 7)} ^ {rotr(in, 18)} ^ {shr(in, 3)};
endfunction
function [31:0] litte_sig1(input logic [31:0] in);
litte_sig1 = {rotr(in, 17)} ^ {rotr(in, 19)} ^ {shr(in, 10)};
endfunction
function [31:0] big_sig0(input logic [31:0] in);
big_sig0 = {rotr(in, 2)} ^ {rotr(in, 13)} ^ {rotr(in, 22)};
endfunction
function [31:0] big_sig1(input logic [31:0] in);
big_sig1 = {rotr(in, 6)} ^ {rotr(in, 11)} ^ {rotr(in, 25)};
endfunction
function [31:0] ch(input logic [31:0] x, y, z);
ch = (x & y) ^ (~x & z);
endfunction
function [31:0] maj(input logic [31:0] x, y, z);
maj = (x & y) ^ (x & z) ^ (y & z);
endfunction
function [31:0] rotr(input logic [31:0] in, input int bits);
for (int i = 0; i < 32; i++) rotr[i] = in[(i+bits) % 32];
endfunction
function [31:0] shr(input logic [31:0] in, input int bits);
shr = 0;
shr = in >> bits;
endfunction
endmodule

View File

@ -0,0 +1,76 @@
/*
The SHA256 testbench.
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
module sha256_top_tb();
import common_pkg::*;
logic clk, rst;
if_axi_stream #(.DAT_BYTS(64)) i_block(clk);
if_axi_stream #(.DAT_BYTS(32)) out_hash(clk);
initial begin
rst = 0;
#100ns rst = 1;
#100ns rst = 0;
end
initial begin
clk = 0;
forever #10ns clk = ~clk;
end
sha256_top DUT (
.i_clk ( clk ),
.i_rst ( rst ),
.i_block ( i_block ),
.o_hash ( out_hash )
);
// This test runs the hash which is shown in the RFC, for "abc"
task rfc_test();
begin
integer signed get_len;
logic [common_pkg::MAX_SIM_BYTS*8-1:0] get_dat;
$display("Running rfc_test...\n");
expected = 'h239900d4ed8623b95a92f1dba88ad31895cc3345ded552c22d79ab2a39c5877dd1a2ffdb6fbb124bb7c45a68142f214ce9f6129fb697276a0d4d1c983fa580ba;
i_block.put_stream("cba", 3);
out_hash.get_stream(get_dat, get_len);
common_pkg::compare_and_print(get_dat, expected);
$display("rfc_test PASSED");
end
endtask
// Main testbench calls
initial begin
i_block.reset_source();
out_hash.rdy = 1;
#200ns;
rfc_test();
#10us $finish();
end
endmodule