diff --git a/ip_cores/blake2b/scripts/create_project.tcl b/ip_cores/blake2b/scripts/create_project.tcl index 691742a..478a05a 100644 --- a/ip_cores/blake2b/scripts/create_project.tcl +++ b/ip_cores/blake2b/scripts/create_project.tcl @@ -15,27 +15,6 @@ # run results please launch the synthesis/implementation runs as needed. # #***************************************************************************************** -# NOTE: In order to use this script for source control purposes, please make sure that the -# following files are added to the source control system:- -# -# 1. This project restoration tcl script (create_project.tcl) that was generated. -# -# 2. The following source(s) files that were local or imported into the original project. -# (Please see the '$orig_proj_dir' and '$origin_dir' variable setting below at the start of the script) -# -# -# -# 3. The following remote source files that were added to the original project:- -# -# "C:/Users/bsdevlin/git/zcash-fpga/ip_cores/blake2b/src/rtl/blake2b_g.sv" -# "C:/Users/bsdevlin/git/zcash-fpga/ip_cores/blake2b/src/rtl/blake2b_pkg.sv" -# "C:/Users/bsdevlin/git/zcash-fpga/ip_cores/common/src/rtl/common_pkg.sv" -# "C:/Users/bsdevlin/git/zcash-fpga/ip_cores/common/src/rtl/common_if.sv" -# "C:/Users/bsdevlin/git/zcash-fpga/ip_cores/blake2b/src/rtl/blake2b_top.sv" -# "C:/Users/bsdevlin/git/zcash-fpga/ip_cores/blake2b/synth/blake2b_top.xdc" -# "C:/Users/bsdevlin/git/zcash-fpga/ip_cores/blake2b/src/tb/blake2b_top_tb.sv" -# -#***************************************************************************************** # Set the reference directory for source file relative paths (by default the value is script directory path) set script_path [ file dirname [ file normalize [ info script ] ] ] @@ -148,8 +127,6 @@ set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]] set_property -name "file_type" -value "SystemVerilog" -objects $file_obj -# Set 'sources_1' fileset file properties for local files -# None # Set 'sources_1' fileset properties set obj [get_filesets sources_1] diff --git a/zcash_verif/src/rtl/zcash_verif_equihash.sv b/zcash_verif/src/rtl/zcash_verif_equihash.sv index b4c2f30..73ce009 100644 --- a/zcash_verif/src/rtl/zcash_verif_equihash.sv +++ b/zcash_verif/src/rtl/zcash_verif_equihash.sv @@ -6,6 +6,9 @@ 3. No duplicates 4. Difficulty passes + Code is split up into 3 main always blocks, one for loading RAM, one for parsing + output and loading the Blake2b block, and the final for running checks. + Copyright (C) 2019 Benjamin Devlin and Zcash Foundation This program is free software: you can redistribute it and/or modify @@ -45,11 +48,9 @@ equihash_gen_in_t equihash_gen_in; logic [N-1:0] sol_hash_xor; logic [$clog2(SOL_LIST_LEN)-1:0] sol_cnt_out, sol_cnt_in; // This tracks how many solutions we have XORed logic [$clog2(DAT_BITS)-1:0] sol_pos; // This tracks the pos in our DAT_BITS RAM output -logic [SOL_BITS-1:0] ram_out; - logic [64*8-1:0] parameters; logic [7:0] byte_len; -logic all_checks_done; +logic [DAT_BITS-1:0] sol_ram_byte_bit_flip; @@ -60,10 +61,19 @@ if_axi_stream #(.DAT_BYTS(EQUIHASH_GEN_BYTS)) blake2b_in_hash(i_clk); if_ram #(.RAM_WIDTH(DAT_BITS), .RAM_DEPTH(SOL_LIST_BYTS/DAT_BYTS)) equihash_sol_bram_if_a (i_clk, i_rst); if_ram #(.RAM_WIDTH(DAT_BITS), .RAM_DEPTH(SOL_LIST_BYTS/DAT_BYTS)) equihash_sol_bram_if_b (i_clk, i_rst); logic [DAT_BITS-1:0] equihash_sol_bram_if_b_l; +logic [1:0] equihash_sol_bram_read; -enum {STATE_IDLE = 0, - STATE_DATA_WRITE = 1, - STATE_FINISH_WAIT = 2} ram_state; +enum {STATE_WR_IDLE = 0, + STATE_WR_DATA = 1, + STATE_WR_WAIT = 2} ram_wr_state; + +enum {STATE_RD_IDLE = 0, + STATE_RD_DATA = 1, + STATE_RD_WAIT = 2} ram_rd_state; + +enum {STATE_CHK_IDLE = 0, + STATE_CHK_DATA = 1, + STATE_CHK_WAIT = 2} chk_state; // State machine for controlling writing equihash solution into the RAM and registering the header always_ff @ (posedge i_clk) begin @@ -73,7 +83,7 @@ always_ff @ (posedge i_clk) begin cblockheader <= 0; cblockheader_byts <= 0; cblockheader_val <= 0; - ram_state <= STATE_IDLE; + ram_wr_state <= STATE_WR_IDLE; end else begin // Defaults equihash_sol_bram_if_a.we <= 1; @@ -86,31 +96,31 @@ always_ff @ (posedge i_clk) begin cblockheader_byts <= cblockheader_byts + DAT_BYTS; end - case (ram_state) + case (ram_wr_state) // This state we are waiting for an input block - STATE_IDLE: begin + STATE_WR_IDLE: begin i_axi.rdy <= 1; if (i_axi.val && i_axi.rdy) begin - ram_state <= STATE_DATA_WRITE; - equihash_sol_bram_if_a.a <= equihash_sol_bram_if_a.a + 1; + ram_wr_state <= STATE_WR_DATA; + equihash_sol_bram_if_a.a <= 0; end end // Here we are checking header values as well as populating the RAM - STATE_DATA_WRITE: begin + STATE_WR_DATA: begin if (i_axi.val && i_axi.rdy) begin equihash_sol_bram_if_a.a <= equihash_sol_bram_if_a.a + 1; if (i_axi.eop) begin i_axi.rdy <= 0; - ram_state <= STATE_FINISH_WAIT; + ram_wr_state <= STATE_WR_WAIT; end end end // Here we are have finished populating RAM and waiting for all checks to finish - STATE_FINISH_WAIT: begin + STATE_WR_WAIT: begin equihash_sol_bram_if_a.we <= 0; equihash_sol_bram_if_a.a <= equihash_sol_bram_if_a.a; - if (all_checks_done) begin - ram_state <= STATE_IDLE; + if (chk_state == STATE_CHK_WAIT) begin + ram_wr_state <= STATE_WR_IDLE; i_axi.rdy <= 1; cblockheader_val <= 0; equihash_sol_bram_if_a.a <= 0; @@ -120,99 +130,154 @@ always_ff @ (posedge i_clk) begin end end -// State machine for controlling the hash calculation -// and checking the header values +// State machine for loading the output of RAM into the Blake2b block +always_ff @ (posedge i_clk) begin + if (i_rst) begin + blake2b_in_hash.reset_source(); + equihash_sol_bram_if_b.reset_source(); + sol_cnt_in <= 0; + sol_pos <= 0; + equihash_sol_bram_if_b_l <= 0; + equihash_gen_in <= 0; + equihash_sol_bram_read <= 0; + ram_rd_state <= STATE_RD_IDLE; + end else begin + // Defaults + equihash_sol_bram_if_b.re <= 1; + equihash_sol_bram_if_b.en <= 1; + blake2b_in_hash.sop <= 1; + blake2b_in_hash.eop <= 1; + blake2b_in_hash.val <= 0; + equihash_sol_bram_read <= equihash_sol_bram_read << 1; + + case(ram_rd_state) + STATE_RD_IDLE: begin + if (~|equihash_sol_bram_read) + equihash_sol_bram_if_b.a <= $bits(cblockheader_t)/DAT_BITS; + sol_pos <= 3*8 + ($bits(cblockheader_t) % DAT_BITS); // Add on 3*8 as this encodes the size of solution + sol_cnt_in <= 0; + blake2b_in_hash.val <= 0; + + //First case has special state + if (equihash_sol_bram_if_a.a >= ($bits(cblockheader_t)/8) + (DAT_BYTS*2)) begin + if (~|equihash_sol_bram_read) begin + equihash_sol_bram_if_b_l <= sol_ram_byte_bit_flip;// equihash_sol_bram_if_b.q; + equihash_sol_bram_if_b.a <= equihash_sol_bram_if_b.a + 1; + equihash_sol_bram_read[0] <= 1; + end + if (equihash_sol_bram_read[1]) + ram_rd_state <= STATE_RD_DATA; + end + end + STATE_RD_DATA: begin + equihash_gen_in <= 0; + equihash_gen_in.bits <= cblockheader.bits; + equihash_gen_in.my_time <= cblockheader.my_time; + equihash_gen_in.hash_merkle_root <= cblockheader.hash_merkle_root; + equihash_gen_in.hash_prev_block <= cblockheader.hash_prev_block; + equihash_gen_in.version <= cblockheader.version; + equihash_gen_in.nonce <= cblockheader.nonce; + // Load the solution, need to flip it + for (int i = 0; i < SOL_BITS; i++) + if ((sol_pos + SOL_BITS) >= DAT_BITS && (i + sol_pos < DAT_BITS)) + equihash_gen_in.index[SOL_BITS-1-i] <= equihash_sol_bram_if_b_l[i + sol_pos]; + else + equihash_gen_in.index[SOL_BITS-1-i] <= /*equihash_sol_bram_if_b.q*/sol_ram_byte_bit_flip[(i+sol_pos) % DAT_BITS]; + + // Stay 2 clocks behind the RAM write + if ((equihash_sol_bram_if_a.a*DAT_BYTS + DAT_BYTS) >= (equihash_sol_bram_if_b.a + $bits(cblockheader_t)/DAT_BITS) || + ram_wr_state == STATE_WR_WAIT) begin + // Check if we need to load next memory address + if (sol_pos + 2*SOL_BITS >= DAT_BITS && ~|equihash_sol_bram_read) begin + equihash_sol_bram_if_b_l <= sol_ram_byte_bit_flip; + equihash_sol_bram_if_b.a <= equihash_sol_bram_if_b.a + 1; + equihash_sol_bram_read[0] <= 1; + end + + // Load input into Blake2b block + blake2b_in_hash.val <= 1; + sol_cnt_in <= sol_cnt_in + 1; + sol_pos <= sol_pos + SOL_BITS; + if (sol_cnt_in == SOL_LIST_LEN - 2) + ram_rd_state <= STATE_RD_WAIT; + end + + end + STATE_RD_WAIT: begin + if (chk_state == STATE_CHK_WAIT) begin + ram_rd_state <= STATE_RD_IDLE; + end + end + endcase + + + end +end + always_ff @ (posedge i_clk) begin if (i_rst) begin o_mask_val <= 0; o_mask <= 0; sol_hash_xor <= 0; - blake2b_in_hash.reset_source(); blake2b_out_hash.rdy <= 0; - equihash_sol_bram_if_b.reset_source(); - all_checks_done <= 0; - sol_cnt_in <= 0; sol_cnt_out <= 0; - sol_pos <= 0; - equihash_sol_bram_if_b_l <= 0; + chk_state <= STATE_CHK_IDLE; end else begin // Defaults - equihash_sol_bram_if_b.re <= 1; - equihash_sol_bram_if_b.en <= 1; blake2b_out_hash.rdy <= 1; - blake2b_in_hash.sop <= 1; - blake2b_in_hash.eop <= 1; - blake2b_in_hash.val <= 0; - if (ram_state == STATE_IDLE) begin - equihash_sol_bram_if_b.a <= $bits(cblockheader_t)/DAT_BITS; - sol_pos <= $bits(cblockheader_t) % DAT_BITS; - sol_cnt_out <= 0; - sol_cnt_in <= 0; - blake2b_in_hash.val <= 0; - o_mask_val <= 0; - o_mask <= 0; - end - - if (cblockheader_val) begin - equihash_gen_in.bits <= cblockheader.bits; - equihash_gen_in.my_time <= cblockheader.my_time; - equihash_gen_in.hash_reserved <= 0; - equihash_gen_in.hash_merkle_root <= cblockheader.hash_merkle_root; - equihash_gen_in.hash_prev_block <= cblockheader.hash_prev_block; - equihash_gen_in.version <= cblockheader.version; - equihash_gen_in.nonce <= cblockheader.nonce; - for (int i = 0; i < SOL_BITS; i++) - if (i + sol_pos >= DAT_BITS) - equihash_gen_in.index[i] <= equihash_sol_bram_if_b_l[i + sol_pos - DAT_BITS]; - else - equihash_gen_in.index[i] <= equihash_sol_bram_if_b.q[i+sol_pos]; - end - - // We can start loading the hash block - if((sol_cnt_in < SOL_LIST_LEN - 1) && - blake2b_in_hash.rdy && - (equihash_sol_bram_if_a.a >= $bits(cblockheader_t)/8 + DAT_BYTS)) begin - blake2b_in_hash.val <= 1; // TODO control if we take more than one hash per clock - sol_cnt_in <= sol_cnt_in + 1; - sol_pos <= sol_pos + SOL_BITS; - // Calculate if we should increase our read pointer - if (sol_pos + 2*SOL_BITS >= DAT_BITS) begin - equihash_sol_bram_if_b_l <= equihash_sol_bram_if_b.q; // Latch current output as we might need some bits - equihash_sol_bram_if_b.a <= equihash_sol_bram_if_b.a + 1; + case(chk_state) + STATE_CHK_IDLE: begin + sol_cnt_out <= 0; + o_mask_val <= 0; + o_mask <= 0; + sol_hash_xor <= 0; + if (ram_rd_state == STATE_RD_DATA) + chk_state <= STATE_CHK_DATA; end - - //TODO here we also need to check the ordering, and duplicates? - - end + STATE_CHK_DATA: begin - // When we start getting the hash results, start XORing them - if (blake2b_out_hash.val) begin - sol_hash_xor <= hash_solution(sol_hash_xor, blake2b_out_hash.dat); - sol_cnt_out <= sol_cnt_out + 1; - end - - if (sol_cnt_out == SOL_LIST_LEN - 1) begin - o_mask.XOR_FAIL <= |sol_hash_xor; - o_mask_val <= 1; - sol_cnt_out <= sol_cnt_out; - equihash_sol_bram_if_b.a <= 0; - end + // When we start getting the hash results, start XORing them + if (blake2b_out_hash.val) begin + sol_hash_xor <= hash_solution(sol_hash_xor, blake2b_out_hash.dat); + sol_cnt_out <= sol_cnt_out + 1; + //TODO here we also need to check the ordering, and duplicates? + end + + if (sol_cnt_out == SOL_LIST_LEN - 1) begin + o_mask.XOR_FAIL <= |sol_hash_xor; + o_mask_val <= 1; + chk_state <= STATE_CHK_WAIT; + end + + end + STATE_CHK_WAIT: begin + if (ram_rd_state == STATE_RD_IDLE && ram_wr_state == STATE_WR_IDLE) + chk_state <= STATE_CHK_IDLE; + end + endcase end end - // Constants always_comb begin parameters = {'0, 8'd1, 8'd1, 8'd0, BLAKE2B_DIGEST_BYTS}; parameters[48*8-1 +: 16*8] = POW_TAG; blake2b_in_hash.dat = equihash_gen_in; + for (int i = 0; i < DAT_BYTS; i++) + sol_ram_byte_bit_flip[i*8 +: 8] = flip_bit_byte(equihash_sol_bram_if_b.q[i*8 +: 8]); end // Function to OR the hash output depending on equihash parameters -function hash_solution(input [N-1:0] curr, input [N*INDICIES_PER_HASH-1:0] in); +function [N-1:0] hash_solution(input [N-1:0] curr, input [N*INDICIES_PER_HASH-1:0] in); + hash_solution = curr; for (int i = 0; i < INDICIES_PER_HASH; i++) - curr = curr ^ in[i*N +: N]; - return curr; + hash_solution = hash_solution ^ in[i*N +: N]; +endfunction + +// Function to convert bit order in each byte of the solution list +function [7:0] flip_bit_byte(input [7:0] in); + for (int i = 0; i < 8; i++) + flip_bit_byte[8-1-i] = in[i]; endfunction // Instantiate the Blake2b block - use high performance pipelined version diff --git a/zcash_verif/src/tb/zcash_verif_equihash_tb.sv b/zcash_verif/src/tb/zcash_verif_equihash_tb.sv index 4e93052..664fc1e 100644 --- a/zcash_verif/src/tb/zcash_verif_equihash_tb.sv +++ b/zcash_verif/src/tb/zcash_verif_equihash_tb.sv @@ -89,7 +89,7 @@ begin start_241 = 1; - while(!done_241 && !mask_val) @(posedge clk); + while(!done_241 || !mask_val) @(posedge clk); assert (~(|mask)) else $fatal(1, "%m %t ERROR: test_block_346 mask was non-zero", $time); assert (~mask.XOR_FAIL) else $fatal(1, "%m %t ERROR: test_block_346 failed XOR mask check", $time);