From 2251338f1d043b66178145091c80a6a4fb48d1e1 Mon Sep 17 00:00:00 2001 From: bsdevlin Date: Thu, 28 Feb 2019 18:32:48 -0500 Subject: [PATCH] Update for difficulty check --- ip_cores/common/src/rtl/common_if.sv | 2 +- ip_cores/common/src/rtl/common_pkg.sv | 2 +- ip_cores/fifo/src/rtl/axi_stream_fifo.sv | 2 +- zcash_verif/src/rtl/zcash_verif_equihash.sv | 45 ++++++- .../rtl/zcash_verif_equihash_difficulty.sv | 113 ++++++++++++------ zcash_verif/src/tb/zcash_verif_equihash_tb.sv | 77 ++++++------ 6 files changed, 158 insertions(+), 83 deletions(-) diff --git a/ip_cores/common/src/rtl/common_if.sv b/ip_cores/common/src/rtl/common_if.sv index 9a4c123..02eac94 100644 --- a/ip_cores/common/src/rtl/common_if.sv +++ b/ip_cores/common/src/rtl/common_if.sv @@ -20,7 +20,7 @@ */ interface if_axi_stream # ( - parameter DAT_BYTS = 8, + parameter DAT_BYTS = 64, parameter DAT_BITS = DAT_BYTS*8, parameter CTL_BYTS = 1, parameter CTL_BITS = CTL_BYTS*8, diff --git a/ip_cores/common/src/rtl/common_pkg.sv b/ip_cores/common/src/rtl/common_pkg.sv index dde0ef4..0060f3a 100644 --- a/ip_cores/common/src/rtl/common_pkg.sv +++ b/ip_cores/common/src/rtl/common_pkg.sv @@ -18,7 +18,7 @@ */ package common_pkg; - parameter MAX_SIM_BYTS = 1024; // In simulation tasks how big is the logic register for putting / getting data + parameter MAX_SIM_BYTS = 2048; // In simulation tasks how big is the logic register for putting / getting data // Compare bytes and print if they do not match task compare_and_print(input logic [MAX_SIM_BYTS*8-1:0] data, expected); diff --git a/ip_cores/fifo/src/rtl/axi_stream_fifo.sv b/ip_cores/fifo/src/rtl/axi_stream_fifo.sv index d2bdd46..68fc9fa 100644 --- a/ip_cores/fifo/src/rtl/axi_stream_fifo.sv +++ b/ip_cores/fifo/src/rtl/axi_stream_fifo.sv @@ -23,7 +23,7 @@ module axi_stream_fifo #( parameter SIZE, parameter DAT_BITS, parameter MOD_BITS = $clog2(DAT_BITS/8), - parameter CTL_BITS, + parameter CTL_BITS = 8, parameter USE_BRAM = 0 // If using BRAM there is an extra cycle delay between reads ) ( input i_clk, i_rst, diff --git a/zcash_verif/src/rtl/zcash_verif_equihash.sv b/zcash_verif/src/rtl/zcash_verif_equihash.sv index cf8b9fe..8686b90 100644 --- a/zcash_verif/src/rtl/zcash_verif_equihash.sv +++ b/zcash_verif/src/rtl/zcash_verif_equihash.sv @@ -54,6 +54,8 @@ logic [64*8-1:0] parameters; if_axi_stream #(.DAT_BYTS(BLAKE2B_DIGEST_BYTS), .CTL_BYTS($clog2(INDICIES_PER_HASH))) blake2b_out_hash(i_clk); if_axi_stream #(.DAT_BYTS(EQUIHASH_GEN_BYTS), .CTL_BYTS($clog2(INDICIES_PER_HASH))) blake2b_in_hash(i_clk); +if_axi_stream #(.DAT_BYTS(DAT_BYTS)) difficulty_if_in(i_clk); + // We write the block into a port as it comes in and then read from the b port localparam EQUIHASH_SOL_BRAM_DEPTH = 1 + SOL_LIST_BYTS/DAT_BYTS; localparam EQUIHASH_SOL_BRAM_WIDTH = DAT_BITS; @@ -65,7 +67,8 @@ logic [2*DAT_BITS-1:0] equihash_sol_bram_if_b_l_comb, equihash_sol_bram_if_b_l_c logic [SOL_BITS-1:0] equihash_sol_index; logic [1:0] equihash_sol_bram_read; -logic dup_chk_done, order_chk_done; +logic dup_chk_done, order_chk_done, diff_chk_done, xor_check_done; +logic difficulty_fail, difficulty_fail_val; if_axi_stream #(.DAT_BITS(SOL_BITS), .CTL_BITS(1), .MOD_BITS(1)) dup_check_if_in(i_clk); if_axi_stream #(.DAT_BITS(1), .CTL_BITS(1), .MOD_BITS(1)) dup_check_if_out(i_clk); @@ -112,7 +115,7 @@ always_ff @ (posedge i_clk) begin case (ram_wr_state) // This state we are waiting for an input block STATE_WR_IDLE: begin - i_axi.rdy <= (dup_check_if_in.rdy && equihash_order_if.rdy); + i_axi.rdy <= (dup_check_if_in.rdy && equihash_order_if.rdy && difficulty_if_in.rdy); if (i_axi.val && i_axi.rdy) begin ram_wr_state <= STATE_WR_DATA; equihash_sol_bram_if_a.a <= 0; @@ -254,6 +257,8 @@ always_ff @ (posedge i_clk) begin dup_check_if_out.rdy <= 1; dup_chk_done <= 0; order_chk_done <= 0; + xor_check_done <= 0; + diff_chk_done <= 0; end else begin // Defaults blake2b_out_hash.rdy <= 1; @@ -266,6 +271,12 @@ always_ff @ (posedge i_clk) begin dup_chk_done <= 1; end + // Monitor for output of difficulty check + if (difficulty_fail_val) begin + o_mask.DIFFICULTY_FAIL <= difficulty_fail; + diff_chk_done <= 1; + end + // Monitor for result of order check if (equihash_order_val) begin if (equihash_order_wrong) begin @@ -278,6 +289,7 @@ always_ff @ (posedge i_clk) begin STATE_CHK_IDLE: begin sol_cnt_out <= 0; dup_chk_done <= 0; + diff_chk_done <= 0; order_chk_done <= 0; o_mask_val <= 0; o_mask <= 0; @@ -308,11 +320,14 @@ always_ff @ (posedge i_clk) begin end STATE_CHK_WAIT: begin o_mask.XOR_NON_ZERO <= |sol_hash_xor; + xor_check_done <= 1; if (ram_rd_state == STATE_RD_WAIT && ram_wr_state == STATE_WR_WAIT && dup_chk_done && - order_chk_done ) begin + order_chk_done && + diff_chk_done && + xor_check_done ) begin o_mask_val <= 1; chk_state <= STATE_CHK_DONE; @@ -363,6 +378,30 @@ function bit bad_order_check(input logic [N-1:0] in, input int cnt); return bad_order_check; endfunction +// The difficulty check block - takes a copy of the header as it is streamed in + +always_comb begin + difficulty_if_in.val = difficulty_if_in.rdy && i_axi.val && i_axi.rdy; + difficulty_if_in.dat = i_axi.dat; + difficulty_if_in.sop = i_axi.sop; + difficulty_if_in.eop = i_axi.eop; + difficulty_if_in.mod = i_axi.mod; + difficulty_if_in.err = 0; + difficulty_if_in.ctl = 0; +end + +zcash_verif_equihash_difficulty #( + .DAT_BYTS ( DAT_BYTS ) +) +zcash_verif_equihash_difficulty ( + .i_clk ( i_clk ), + .i_rst ( i_rst ), + .i_axi ( difficulty_if_in ), + .i_bits ( cblockheader.bits ), + .o_difficulty_fail ( difficulty_fail ), + .o_val ( difficulty_fail_val ) +); + // Instantiate the Blake2b block - use high performance pipelined version localparam [EQUIHASH_GEN_BYTS*8-1:0] EQUIHASH_GEN_BYTS_BM = { {32-SOL_BITS-$clog2(INDICIES_PER_HASH){1'b0}}, diff --git a/zcash_verif/src/rtl/zcash_verif_equihash_difficulty.sv b/zcash_verif/src/rtl/zcash_verif_equihash_difficulty.sv index 6806d5d..2f1e090 100644 --- a/zcash_verif/src/rtl/zcash_verif_equihash_difficulty.sv +++ b/zcash_verif/src/rtl/zcash_verif_equihash_difficulty.sv @@ -26,24 +26,26 @@ module zcash_verif_equihash_difficulty import zcash_verif_pkg::*; #( - parameter DAT_BYTS = 8, + 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 + input logic [31:0] i_bits, output logic o_difficulty_fail, output logic o_val ); +localparam HEADER_BYTS = $bits(cblockheader_sol_t)/8; +localparam DAT_BITS = DAT_BYTS*8; logic [$clog2($bits(cblockheader_sol_t)/8)-1:0] byt_cnt; -logic o_fifo_full, o_fifo_emp; -logic [31:0] difficulty; +logic o_fifo_full, o_fifo_emp, bits_err; +logic [255:0] nbits_converted; -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); +if_axi_stream #(.DAT_BYTS(DAT_BYTS)) o_fifo(i_clk); +if_axi_stream #(.DAT_BYTS(64)) i_block(i_clk); +if_axi_stream #(.DAT_BYTS(32)) o_hash(i_clk); enum {IDLE = 0, SHA256_0 = 1, @@ -58,49 +60,61 @@ always_ff @ (posedge i_clk) begin i_block.reset_source(); o_hash.rdy <= 0; o_fifo.rdy <= 0; - difficulty <= 0; state <= IDLE; + nbits_converted <= 0; + bits_err <= 0; end else begin o_val <= 0; o_hash.rdy <= 1; + nbits_converted <= set_compact(i_bits); + bits_err <= check_err(i_bits); + 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 + // Takes around 26 passes as header is 1619 bytes SHA256_0: begin - - if (o_fifo.rdy && o_fifo.val) begin + o_fifo.rdy <= 0; + if (~i_block.val || (i_block.val && i_block.rdy)) 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; + + if (i_block.val && i_block.rdy) + i_block.dat <= 0; + + if (o_fifo.rdy && o_fifo.val) begin + byt_cnt <= byt_cnt + DAT_BYTS; + i_block.dat[(byt_cnt % 64) *8 +: DAT_BITS] <= o_fifo.dat; + + if (((byt_cnt + DAT_BYTS) % 64 == 0) || + (byt_cnt + DAT_BYTS) >= $bits(cblockheader_sol_t)/8) begin + i_block.val <= 1; + o_fifo.rdy <= 0; + i_block.sop <= (byt_cnt + DAT_BYTS)/64 == 1; + i_block.eop <= 0; + i_block.mod <= 0; + if ((byt_cnt + DAT_BYTS) >= $bits(cblockheader_sol_t)/8) begin + i_block.eop <= 1; + i_block.mod <= $bits(cblockheader_sol_t)/8; + state <= SHA256_1; + end + end + end end - + end + // Only single pass SHA256_1: begin + o_fifo.rdy <= 1; // We might have data we don't care about (transactions) if (i_block.val && i_block.rdy) begin i_block.val <= 0; end @@ -112,19 +126,20 @@ always_ff @ (posedge i_clk) begin i_block.eop <= 1; i_block.mod <= 32; state <= FINISHED; - end + end end FINISHED: begin + o_fifo.rdy <= 1; // We might have data we don't care about (transactions) 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_difficulty_fail <= bits_err || (o_hash.dat > nbits_converted); o_val <= 1; state <= IDLE; end - + end endcase @@ -143,18 +158,38 @@ 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; +function [255:0] set_compact(input logic [31:0] ncompact); + logic [31:0] nsize, nword; + nsize = ncompact >> 24; + nword = ncompact; + + if (nsize <= 3) begin + set_compact = nword >> 8 * (3 - nsize); + end else begin + set_compact = nword << 8 * (nsize - 3); + end + endfunction +function check_err(input logic [31:0] ncompact); + logic [31:0] nsize, nword; + nsize = ncompact >> 24; + nword = ncompact; + check_err = 0; + // For sanity checking we set o_err and fail the check + if (ncompact == 0 || + (nword != 0 && ncompact[3*8]) || + (nword != 0 && (nsize > 34 || (nword > 8'hff && nsize > 33) || (nword > 16'hffff && nsize > 32)))) begin + check_err = 1; + end + +endfunction + + // FIFO for storing input stream axi_stream_fifo #( .SIZE ( ($bits(cblockheader_sol_t)/8)/DAT_BYTS ), - .DAT_BITS ( DAT_BYTS/8 ), + .DAT_BITS ( DAT_BYTS*8 ), .USE_BRAM ( 1 ) ) axi_stream_fifo ( @@ -169,7 +204,7 @@ axi_stream_fifo ( // SHA256 block sha256_top sha256_top ( .i_clk ( i_clk ), - .i_rst ( i_rst ), + .i_rst ( i_rst ), .i_block ( i_block ), .o_hash ( o_hash ) ); diff --git a/zcash_verif/src/tb/zcash_verif_equihash_tb.sv b/zcash_verif/src/tb/zcash_verif_equihash_tb.sv index e510703..97c449a 100644 --- a/zcash_verif/src/tb/zcash_verif_equihash_tb.sv +++ b/zcash_verif/src/tb/zcash_verif_equihash_tb.sv @@ -25,43 +25,44 @@ import common_pkg::*; logic clk, rst; equihash_bm_t mask; logic mask_val; -logic start_241 = 0; -logic done_241; -logic start_241_error = 0; -logic done_241_error; +logic start_346 = 0; +logic done_346; +logic start_346_error = 0; +logic done_346_error; parameter DAT_BYTS = 8; string my_file_path_s = get_file_dir(`__FILE__); if_axi_stream #(.DAT_BYTS(DAT_BYTS)) header(clk); -if_axi_stream #(.DAT_BYTS(DAT_BYTS)) header_241(clk); -if_axi_stream #(.DAT_BYTS(DAT_BYTS)) header_241_error(clk); +if_axi_stream #(.DAT_BYTS(DAT_BYTS)) header_346(clk); +if_axi_stream #(.DAT_BYTS(DAT_BYTS)) header_346_error(clk); // Need one for each test so we can multiplex the input always_comb begin - header_241.rdy = 0; - header_241_error.rdy = 0; + header_346.rdy = 0; + header_346_error.rdy = 0; + header.val = 0; - if (start_241) begin - header_241.rdy = header.rdy; - header.val = header_241.val; - header.sop = header_241.sop; - header.eop = header_241.eop; - header.ctl = header_241.ctl; - header.mod = header_241.mod; - header.err = header_241.err; - header.dat = header_241.dat; + if (start_346) begin + header_346.rdy = header.rdy; + header.val = header_346.val; + header.sop = header_346.sop; + header.eop = header_346.eop; + header.ctl = header_346.ctl; + header.mod = header_346.mod; + header.err = header_346.err; + header.dat = header_346.dat; end - if (start_241_error) begin - header_241_error.rdy = header.rdy; - header.val = header_241_error.val; - header.sop = header_241_error.sop; - header.eop = header_241_error.eop; - header.ctl = header_241_error.ctl; - header.mod = header_241_error.mod; - header.err = header_241_error.err; - header.dat = header_241_error.dat; + if (start_346_error) begin + header_346_error.rdy = header.rdy; + header.val = header_346_error.val; + header.sop = header_346_error.sop; + header.eop = header_346_error.eop; + header.ctl = header_346_error.ctl; + header.mod = header_346_error.mod; + header.err = header_346_error.err; + header.dat = header_346_error.dat; end end @@ -81,13 +82,13 @@ file_to_axi #( .DAT_BYTS ( DAT_BYTS ), .FP ( 0 ) ) -file_to_axi_block241 ( +file_to_axi_block346 ( .i_file ({my_file_path_s, "/../data/block_346.bin"}), .i_clk ( clk ), .i_rst ( rst ), - .i_start ( start_241 ), - .o_done ( done_241 ), - .o_axi ( header_241 ) + .i_start ( start_346 ), + .o_done ( done_346 ), + .o_axi ( header_346 ) ); file_to_axi #( @@ -95,13 +96,13 @@ file_to_axi #( .DAT_BYTS ( DAT_BYTS ), .FP ( 0 ) ) -file_to_axi_block241_error ( +file_to_axi_block346_error ( .i_file ({my_file_path_s, "/../data/block_346_errors.bin"}), .i_clk ( clk ), .i_rst ( rst ), - .i_start ( start_241_error ), - .o_done ( done_241_error ), - .o_axi ( header_241_error ) + .i_start ( start_346_error ), + .o_done ( done_346_error ), + .o_axi ( header_346_error ) ); zcash_verif_equihash @@ -117,9 +118,9 @@ DUT ( task test_block_346(); begin $display("Running test_block_346..."); - start_241 = 1; + start_346 = 1; - while(!done_241 || !mask_val) @(posedge clk); + while(!done_346 || !mask_val) @(posedge clk); assert (~(|mask)) else $fatal(1, "%m %t ERROR: test_block_346 mask was non-zero:\n%p", $time, mask); $display("test_block_346 PASSED"); @@ -131,9 +132,9 @@ endtask task test_block_346_error(); begin $display("Running test_block_346_error..."); - start_241_error = 1; + start_346_error = 1; - while(!done_241_error || !mask_val) @(posedge clk); + while(!done_346_error || !mask_val) @(posedge clk); assert (&mask) else $fatal(1, "%m %t ERROR: test_block_346_error mask was zero but should of failed:\n%p", $time, mask); $display("test_block_346_error PASSED");