diff --git a/ip_cores/sha256/src/rtl/sha256_pkg.sv b/ip_cores/sha256/src/rtl/sha256_pkg.sv
index b3dda68..6467f2b 100644
--- a/ip_cores/sha256/src/rtl/sha256_pkg.sv
+++ b/ip_cores/sha256/src/rtl/sha256_pkg.sv
@@ -15,7 +15,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-*/
+*/
package sha256_pkg;
@@ -37,4 +37,47 @@ package sha256_pkg;
32'ha54ff53a, 32'h3c6ef372, 32'hbb67ae85, 32'h6a09e667
};
+ // Functions used in bit manipulations
+ function [31:0] little_sig0(input logic [31:0] in);
+ little_sig0 = {rotr(in, 7)} ^ {rotr(in, 18)} ^ {shr(in, 3)};
+ endfunction
+
+ function [31:0] little_sig1(input logic [31:0] in);
+ little_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
+
+ // Swap bytes (used to convert between little and big endian)
+ function [31:0] bs32(input logic [31:0] in);
+ for (int i = 0; i < 4; i++) bs32[i*8 +: 8] = in[(4-1-i)*8 +: 8];
+ endfunction
+
+ function [63:0] bs64(input logic [63:0] in);
+ for (int i = 0; i < 8; i++) bs64[i*8 +: 8] = in[(8-1-i)*8 +: 8];
+ endfunction
+
endpackage
\ No newline at end of file
diff --git a/ip_cores/sha256/src/rtl/sha256_top.sv b/ip_cores/sha256/src/rtl/sha256_top.sv
index 808a953..520e136 100644
--- a/ip_cores/sha256/src/rtl/sha256_top.sv
+++ b/ip_cores/sha256/src/rtl/sha256_top.sv
@@ -19,26 +19,28 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-
+
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
+localparam ROUNDS = 64;
-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 [7:0][0:31] V, H; // Internal states, V[0] == A, V[7] == H (in big endian)
+logic [0:31] T1, T2;
+logic [15:0][0:31] W;
+logic [0:31] W_nxt;
+logic padding_only, final_block;
logic [63:0] bit_len;
-logic [$clog2(64)-1:0] rnd_cntr;
+logic [$clog2(ROUNDS)-1:0] rnd_cntr;
+logic [1:0][0:31] bit_len_c;
enum {SHA_IDLE = 0,
SHA_ROUNDS = 1,
@@ -46,14 +48,14 @@ enum {SHA_IDLE = 0,
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;
+localparam VAR_A = 0;
+localparam VAR_B = 1;
+localparam VAR_C = 2;
+localparam VAR_D = 3;
+localparam VAR_E = 4;
+localparam VAR_F = 5;
+localparam VAR_G = 6;
+localparam VAR_H = 7;
always_ff @ (posedge i_clk) begin
@@ -64,7 +66,6 @@ always_ff @ (posedge i_clk) begin
i_block.rdy <= 0;
bit_len <= 0;
W <= 0;
- eop_l <= 0;
padding_only <= 0;
final_block <= 0;
end else begin
@@ -75,7 +76,6 @@ always_ff @ (posedge i_clk) 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
@@ -89,26 +89,29 @@ always_ff @ (posedge i_clk) begin
end
end
SHA_ROUNDS: begin
- for (int i = 0; i < 16; i++) W[i] <= W[i+i];
+ for (int i = 0; i < 15; i++)
+ W[i] <= W[i+1];
W[15] <= W_nxt;
compress();
rnd_cntr <= rnd_cntr + 1;
- if (rnd_cntr == 62) begin
+ if (rnd_cntr == ROUNDS - 1) 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);
+ update_HV(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;
+ W[15:14] <= {bit_len_c[0], bit_len_c[1]};
+ if (bit_len % 512 == 0)
+ W[0] <= sha256_pkg::bs32(32'd1);
+ sha_state <= SHA_ROUNDS;
end else if (i_block.rdy && i_block.val) begin
W <= i_block.dat;
bit_len <= bit_len + DAT_BITS;
@@ -121,9 +124,15 @@ always_ff @ (posedge i_clk) begin
end
SHA_FINAL: begin
o_hash.val <= 1;
- o_hash.dat <= H;
+ for (int i = 0; i < 8; i++)
+ o_hash.dat[i*32 +: 32] <= sha256_pkg::bs32(H[i]); // Shift back to little endian
if (o_hash.val && o_hash.rdy) begin
o_hash.val <= 0;
+ rnd_cntr <= 0;
+ bit_len <= 0;
+ W <= 0;
+ padding_only <= 0;
+ final_block <= 0;
sha_state <= SHA_IDLE;
end
end
@@ -135,7 +144,6 @@ end
// 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)
@@ -143,41 +151,52 @@ task msg_eop();
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;
+ // Every 32 bit word needs to be swapped to big endian
+ for (int i = 0; i < 16; i++)
+ W[i] <= (i_block.mod == 0 || i < i_block.mod) ? sha256_pkg::bs32(i_block.dat[i*32 +: 32]) : 0;
if (i_block.mod != 0)
- M[i_block.mod*8 +: 8] <= 1;
+ W[i_block.mod/4][8*(i_block.mod % 4) +: 8] <= 8'h80; // Since we operate in big endian
if (i_block.mod < 64-9)
- M[(64-8)*8 +: 64] <= bit_len + i_block.mod*8;
+ W[15:14] <= {bit_len_c[0], bit_len_c[1]};
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]);
+ bit_len_c = (bit_len + i_block.mod*8);
+
+ W_nxt = sha256_pkg::little_sig1(W[14]) +
+ W[9] +
+ sha256_pkg::little_sig0(W[1]) +
+ W[0];
+
+ T1 = V[VAR_H] +
+ sha256_pkg::big_sig1(V[VAR_E]) +
+ sha256_pkg::ch(V[VAR_E], V[VAR_F], V[VAR_G]) +
+ sha256_pkg::K[rnd_cntr] +
+ W[0];
+
+ T2 = sha256_pkg::big_sig0(V[VAR_A]) +
+ sha256_pkg::maj(V[VAR_A], V[VAR_B], V[VAR_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;
+ V[VAR_H] <= V[VAR_G];
+ V[VAR_G] <= V[VAR_F];
+ V[VAR_F] <= V[VAR_E];
+ V[VAR_E] <= V[VAR_D] + T1;
+ V[VAR_D] <= V[VAR_C];
+ V[VAR_C] <= V[VAR_B];
+ V[VAR_B] <= V[VAR_A];
+ V[VAR_A] <= T1 + T2;
endtask
-task update_HV(logic init);
+task update_HV(input 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];
+ H[i] <= (sha256_pkg::IV[i]);
+ V[i] <= (sha256_pkg::IV[i]);
end
end else begin
for (int i = 0; i < 8; i++) begin
@@ -187,38 +206,10 @@ task update_HV(logic init);
end
endtask
-function [31:0] litte_sig0(input logic [31:0] in);
- litte_sig0 = {rotr(in, 7)} ^ {rotr(in, 18)} ^ {shr(in, 3)};
-endfunction
+ // Check that input size is correct
+ initial begin
+ assert ($bits(i_block.dat) == DAT_BITS) else $fatal(1, "%m %t ERROR: sha256_top DAT_BITS (%d) does not match interface .dat (%d)", $time, DAT_BITS, $bits(i_block.dat));
+ end
-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
\ No newline at end of file
diff --git a/ip_cores/sha256/src/tb/sha256_top_tb.sv b/ip_cores/sha256/src/tb/sha256_top_tb.sv
index 87ff33a..7eef046 100644
--- a/ip_cores/sha256/src/tb/sha256_top_tb.sv
+++ b/ip_cores/sha256/src/tb/sha256_top_tb.sv
@@ -16,22 +16,22 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-
-module sha256_top_tb();
-
-import common_pkg::*;
-
-logic clk, rst;
+module sha256_top_tb();
+
+import common_pkg::*;
+
+logic clk, rst;
+logic [255:0] expected;
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;
@@ -44,33 +44,48 @@ sha256_top DUT (
.o_hash ( out_hash )
);
-
-// This test runs the hash which is shown in the RFC, for "abc"
-task rfc_test();
+
+// NIST testcase for single 512 bit block "abc"
+task nist_single_block_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);
+ $display("Running nist_single_block_test...\n");
+ expected = 'had1500f261ff10b49c7a1796a36103b02322ae5dde404141eacf018fbf1678ba; // Both in little endian
+ i_block.put_stream("cba", 3); // abc in little endian
out_hash.get_stream(get_dat, get_len);
common_pkg::compare_and_print(get_dat, expected);
- $display("rfc_test PASSED");
+ $display("nist_single_block_test PASSED");
end
endtask
-
+// NIST testcase for double 512 bit block "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+task nist_double_block_test();
+ begin
+ integer signed get_len;
+ logic [common_pkg::MAX_SIM_BYTS*8-1:0] get_dat;
+ $display("Running nist_double_block_test...\n");
+ expected = 'hc106db19d4edecf66721ff6459e43ca339603e0c9326c0e5b83806d2616a8d24; // Both in little endian
+ i_block.put_stream("qponponmonmlnmlkmlkjlkjikjihjihgihgfhgfegfedfedcedcbdcba", 56);
+ out_hash.get_stream(get_dat, get_len);
+ common_pkg::compare_and_print(get_dat, expected);
+ $display("nist_double_block_test PASSED");
+ end
+endtask
+
+
// Main testbench calls
initial begin
i_block.reset_source();
out_hash.rdy = 1;
#200ns;
-
- rfc_test();
-
+
+ nist_single_block_test();
+ nist_double_block_test();
+
#10us $finish();
-
+
end
-
+
endmodule
\ No newline at end of file
diff --git a/zcash_verif/src/rtl/zcash_verif_equihash_difficulty.sv b/zcash_verif/src/rtl/zcash_verif_equihash_difficulty.sv
new file mode 100644
index 0000000..6806d5d
--- /dev/null
+++ b/zcash_verif/src/rtl/zcash_verif_equihash_difficulty.sv
@@ -0,0 +1,177 @@
+/*
+ This verifies that a Zcash equihash solution has the correct difficulty.
+
+ Take input stream of entire block header (including equihash solution and size) and
+ calculate the SHA256d (double SHA256)
+
+ We take in the stream in DAT_BYTS in a FIFO, and load the output into 512 bit words
+ for the SHA256 block. Then the 256 bit output it inputted into the same SHA256 block.
+
+ 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 .
+ */
+
+module zcash_verif_equihash_difficulty
+ import zcash_verif_pkg::*;
+#(
+ parameter DAT_BYTS = 8,
+)(
+ input i_clk, i_rst,
+
+ if_axi_stream.sink i_axi,
+ input logic [31:0] i_difficulty, // Must be valid on .sop && .val
+ output logic o_difficulty_fail,
+ output logic o_val
+);
+
+
+logic [$clog2($bits(cblockheader_sol_t)/8)-1:0] byt_cnt;
+logic o_fifo_full, o_fifo_emp;
+logic [31:0] difficulty;
+
+if_axi_stream #(.DAT_BYTS(DAT_BYTS)) o_fifo(clk);
+if_axi_stream #(.DAT_BYTS(64)) i_block(clk);
+if_axi_stream #(.DAT_BYTS(32)) o_hash(clk);
+
+enum {IDLE = 0,
+ SHA256_0 = 1,
+ SHA256_1 = 2,
+ FINISHED = 3} state;
+
+always_ff @ (posedge i_clk) begin
+ if (i_rst) begin
+ o_difficulty_fail <= 0;
+ o_val <= 0;
+ byt_cnt <= 0;
+ i_block.reset_source();
+ o_hash.rdy <= 0;
+ o_fifo.rdy <= 0;
+ difficulty <= 0;
+ state <= IDLE;
+ end else begin
+ o_val <= 0;
+ o_hash.rdy <= 1;
+
+ case(state)
+ IDLE: begin
+ i_block.reset_source();
+ o_fifo.rdy <= 0;
+ byt_cnt <= 0;
+ if (i_axi.rdy && i_axi.val && i_axi.sop) begin
+ difficulty <= i_difficulty;
+ state <= SHA256_0;
+ o_fifo.rdy <= 1;
+ end
+ end
+ // Convert data to 512 bit wide
+ SHA256_0: begin
+
+ if (o_fifo.rdy && o_fifo.val) begin
+ i_block.val <= 0;
+ byt_cnt <= byt_cnt + DAT_BYTS;
+ i_block.dat[byt_cnt +: DAT_BYTS] <= o_fifo.dat;
+ end
+
+ if (((byt_cnt + DAT_BYTS) % 64 == 0) ||
+ (byt_cnt + DAT_BYTS) == $bits(cblockheader_sol_t)/8) begin
+ i_block.val <= 1;
+ i_block.sop <= (byt_cnt + DAT_BYTS)/64 == 1;
+ i_block.eop <= 0;
+ i_block.mod <= (byt_cnt + DAT_BYTS);
+ o_fifo.rdy <= i_block.rdy;
+ if ((byt_cnt + DAT_BYTS) == $bits(cblockheader_sol_t)/8) begin
+ i_block.eop <= 1;
+ state <= SHA256_1;
+ end
+ end else begin
+ o_fifo.rdy <= 1;
+ end
+
+ end
+ SHA256_1: begin
+ if (i_block.val && i_block.rdy) begin
+ i_block.val <= 0;
+ end
+
+ if (o_hash.val && o_hash.rdy) begin
+ i_block.val <= 1;
+ i_block.dat <= o_hash.dat;
+ i_block.sop <= 1;
+ i_block.eop <= 1;
+ i_block.mod <= 32;
+ state <= FINISHED;
+ end
+ end
+ FINISHED: begin
+ if (i_block.val && i_block.rdy) begin
+ i_block.val <= 0;
+ end
+
+ if (o_hash.val && o_hash.rdy) begin
+ o_difficulty_fail <= check_difficulty(o_hash.dat, difficulty);
+ o_val <= 1;
+ state <= IDLE;
+ end
+
+ end
+ endcase
+
+ if ( o_fifo_full ) begin
+ o_difficulty_fail <= 1;
+ o_val <= 1;
+ o_hash.rdy <= 1;
+ o_fifo.rdy <= 1;
+ i_block.reset_source();
+ if ( o_fifo_emp )
+ state <= IDLE;
+ end
+
+ end
+end
+
+// Function to check if difficulty passes - bits is the number of 0s we
+// need
+// TODO target function
+function check_difficulty(input logic [255:0] hash, logic [31:0] bits);
+ check_difficulty = 0;
+ for (int i = 0; i < 64; i++)
+ if (i > bits && hash[(64-1-i)*8 +: 8] != 0)
+ check_difficulty = 1;
+endfunction
+
+// FIFO for storing input stream
+axi_stream_fifo #(
+ .SIZE ( ($bits(cblockheader_sol_t)/8)/DAT_BYTS ),
+ .DAT_BITS ( DAT_BYTS/8 ),
+ .USE_BRAM ( 1 )
+)
+axi_stream_fifo (
+ .i_clk ( i_clk ),
+ .i_rst ( i_rst ),
+ .i_axi ( i_axi ),
+ .o_axi ( o_fifo ),
+ .o_full ( o_fifo_full ),
+ .o_emp ( o_fifo_emp )
+);
+
+// SHA256 block
+sha256_top sha256_top (
+ .i_clk ( i_clk ),
+ .i_rst ( i_rst ),
+ .i_block ( i_block ),
+ .o_hash ( o_hash )
+);
+
+endmodule
\ No newline at end of file
diff --git a/zcash_verif/src/rtl/zcash_verif_pkg.sv b/zcash_verif/src/rtl/zcash_verif_pkg.sv
index 83a7a66..2547070 100644
--- a/zcash_verif/src/rtl/zcash_verif_pkg.sv
+++ b/zcash_verif/src/rtl/zcash_verif_pkg.sv
@@ -37,6 +37,7 @@ package zcash_verif_pkg;
logic BAD_ZERO_ORDER;
logic BAD_IDX_ORDER;
logic XOR_NON_ZERO;
+ logic DIFFICULTY_FAIL;
} equihash_bm_t;
// Format for equihash input - should be 144 bytes