diff --git a/equihash_verifi/src/rtl/equihash_verifi_top.sv b/equihash_verifi/src/rtl/equihash_verifi_top.sv index e69de29..42ebc9b 100644 --- a/equihash_verifi/src/rtl/equihash_verifi_top.sv +++ b/equihash_verifi/src/rtl/equihash_verifi_top.sv @@ -0,0 +1,51 @@ +/* + * This module is the top system level for the equihash verifier system. It takes in an AXI stream which + * represents block chain data and verifies that it is correct. + */ + +module equihash_verifi_top( + input i_clk, i_rst, + + if_axi_stream.sink i_data, + output logic o_valid +); + +if_axi_stream #(.DAT_BYTS(128)) blake2b_in(clk); +if_axi_stream #(.DAT_BYTS(64)) blake2b_out(clk); + + +always_ff @ (posedge i_clk) begin + i_data.rdy <= blake2b_in.rdy; + blake2b_in.val <= i_data.val; + blake2b_in.sop <= i_data.sop; + blake2b_in.eop <= i_data.eop; + blake2b_in.dat <= i_data.dat; + blake2b_in.err <= 0; + blake2b_in.mod <= 0; + blake2b_in.ctl <= 0; + + blake2b_out.rdy <= 1; + o_valid <= (blake2b_out.val && blake2b_out.dat == {64{1'b1}}); +end + + +// The Blake2 core for generating hashes + +logic [64*8-1:0] blake2_parameters; +always_comb begin + blake2_parameters = {32'd0, 8'd1, 8'd1, 8'd0, 8'd64}; +end + +blake2_top #( + .EQUIHASH( 1 ) +) +blake2_top ( + .i_clk ( i_clk ), + .i_rst ( i_rst ), + .i_byte_len ( 8'd128 ), + .i_parameters ( blake2_parameters ), + .i_block ( blake2b_in ), + .o_hash ( blake2b_out ) +); + +endmodule \ No newline at end of file diff --git a/equihash_verifi/synth/equihash_verifi_top.xdc b/equihash_verifi/synth/equihash_verifi_top.xdc new file mode 100644 index 0000000..b99f66e --- /dev/null +++ b/equihash_verifi/synth/equihash_verifi_top.xdc @@ -0,0 +1 @@ +create_clock -period 5.000 -name i_clk -waveform {0.000 2.500} [get_ports -filter { NAME =~ "*i_clk*" && DIRECTION == "IN" }] diff --git a/ip_cores/blake2b/src/rtl/blake2_pkg.sv b/ip_cores/blake2b/src/rtl/blake2_pkg.sv index 6b87d18..9654b3c 100644 --- a/ip_cores/blake2b/src/rtl/blake2_pkg.sv +++ b/ip_cores/blake2b/src/rtl/blake2_pkg.sv @@ -1,7 +1,4 @@ package blake2_pkg; - // User configurable values - parameter [7:0] NN = 64; // Output hash byte length - // Initial values parameter [7:0][63:0] IV = { @@ -15,6 +12,7 @@ package blake2_pkg; 64'h6a09e667f3bcc908 }; + // Sigma permutations used for G function blocks and input messages parameter [16*10-1:0][31:0] SIGMA = { 0, 13, 12, 3, 14, 9, 11, 15, 5, 1, 6, 7, 4, 8, 2, 10, 5, 10, 4, 1, 7, 13, 2, 12, 8, 0, 3, 11, 9, 14, 15, 6, @@ -28,7 +26,7 @@ package blake2_pkg; 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; - + // Mapping for each G function block to the state vector v parameter [4*8-1:0][31:0] G_MAPPING = { 14, 9, 4, 3, 13, 8, 7, 2, diff --git a/ip_cores/blake2b/src/rtl/blake2_top.sv b/ip_cores/blake2b/src/rtl/blake2_top.sv index 8bc06d0..2422a40 100644 --- a/ip_cores/blake2b/src/rtl/blake2_top.sv +++ b/ip_cores/blake2b/src/rtl/blake2_top.sv @@ -1,142 +1,155 @@ -// Implemented from RFC-7693, The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC) +/* Implemented from RFC-7693, The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC) + * Personalization string in the input parameter should be "ZcashPoW" followed by n and k in + * little endian order. + */ module blake2_top import blake2_pkg::*; -#( -) ( input i_clk, i_rst, - // Parameter block input - input [7:0] i_digest_byte_len, - input [7:0] i_key_byte_len, - - input [128*8-1:0] i_block, - input i_new_block, - input i_final_block, - input i_val, - - output logic[64*8-1:0] o_digest, - output logic o_rdy, - output logic o_val, - output logic o_err, + input [7:0] i_byte_len, // Length of the input message + input [64*8-1:0] i_parameters, // Input parameters used in the inital state. - if_axi_stream o_hash + if_axi_stream.sink i_block, // Input block with valid and ready signals for flow control + if_axi_stream.source o_hash // Output digest with valid and ready signals for flow control ); enum {STATE_IDLE = 0, STATE_ROUNDS = 1, - STATE_NEXT_BLOCK = 2} blake2_state; + STATE_NEXT_BLOCK = 2, + STATE_FINAL_BLOCK = 3} blake2_state; localparam ROUNDS = 12; -logic [64*8-1:0] parameters, parameters_r; -logic [7:0][63:0] h; // The state vector -logic [15:0][63:0] v; // The local work vector -logic [31:0][63:0] g_out; // Outputs of the G mixing function - use 8 here to save on timing +logic [7:0][63:0] h, h_tmp; // The state vector +logic [15:0][63:0] v, v_tmp; // The local work vector and its intermediate value +logic [31:0][63:0] g_out;//, g_out_r; // Outputs of the G mixing function - use 8 here to save on timing logic [127:0] t; // Counter - TODO make this smaller - related to param -logic [$clog2(ROUNDS)-1:0] round_cntr; -logic cnt; +logic [$clog2(ROUNDS)-1:0] round_cntr, round_cntr_msg, round_cntr_fin; logic g_col; -logic [15:0][63:0] block_r; // The message block registered and converted to a 2d array -logic final_block_r; +logic [15:0][63:0] block, block_r; // The message block registered and converted to a 2d array +logic block_eop_l; // Use to latch if this is the final block -always_comb begin - parameters = {32'd0, 8'd1, 8'd1, i_key_byte_len, blake2_pkg::NN}; +// Pipelining logic that has no reset +always_ff @(posedge i_clk) begin + + //g_out_r <= g_out; + + if (blake2_state == STATE_IDLE) begin + block_r <= 0; + if (i_block.val && i_block.rdy) begin + block_r <= i_block.dat; + end + end + + for (int i = 0; i < 16; i++) + if (g_col == 0/* && blake2_state == STATE_ROUNDS*/) // TODO why do I need this qualifier + v_tmp[i] <= g_out[blake2_pkg::G_MAPPING[i]]; + + for (int i = 0; i < 8; i++) + if (blake2_state == STATE_ROUNDS) + h_tmp[i] <= g_out[16 + blake2_pkg::G_MAPPING_DIAG[i]] ^ g_out[16 + blake2_pkg::G_MAPPING_DIAG[i+8]]; //TODO fix + end -// Logic that is for pipelining -always_ff @(posedge i_clk) begin - if (i_val && o_rdy) begin - block_r <= i_block; - final_block_r <= i_final_block; - parameters_r <= parameters; - end +always_comb begin + block = i_block.dat; end // State machine logic for compressing always_ff @(posedge i_clk) begin if (i_rst) begin blake2_state <= STATE_IDLE; - o_val <= 0; - o_rdy <= 0; + i_block.rdy <= 0; h <= 0; v <= 0; t <= 128; g_col <= 0; round_cntr <= 0; - o_err <= 0; - o_digest <= 0; - cnt <= 0; - - o_hash.reset(); - + round_cntr_msg <= 0; + o_hash.reset_source(); + round_cntr_fin <= 0; + block_eop_l <= 0; end else begin - cnt <= cnt + 1; + + if (blake2_state != STATE_NEXT_BLOCK) g_col <= ~g_col; + case (blake2_state) STATE_IDLE: begin - o_val <= 0; - init_state_vector(); + h <= i_parameters ^ blake2_pkg::IV; t <= 128; - o_err <= 0; - o_rdy <= 1; + i_block.rdy <= 1; v <= 0; - o_hash.val = 0; + o_hash.val <= 0; g_col <= 0; round_cntr <= 0; - if (o_rdy && i_val && i_new_block) begin - init_local_work_vector(i_final_block ? i_digest_byte_len : t); + round_cntr_msg <= 0; + round_cntr_fin <= 0; + if (i_block.rdy && i_block.val && i_block.sop) begin + init_local_work_vector(i_byte_len, i_block.eop); blake2_state <= STATE_ROUNDS; - o_rdy <= 0; + g_col <= 0; + i_block.rdy <= 0; + block_eop_l <= i_block.eop; end end // Here we do the compression over 12 rounds, each round can be done in two clock cycles // After we do 12 rounds we increment counter t STATE_ROUNDS: begin - // Update local work vector with output of G function blocks - for (int i = 0; i < 16; i++) - v[i] <= g_col == 0 ? g_out[blake2_pkg::G_MAPPING[i]] : g_out[16 + blake2_pkg::G_MAPPING_DIAG[i]]; - - if (g_col) - round_cntr <= round_cntr + 1; - g_col <= ~g_col; - // Update state vector on the final round - if (round_cntr == ROUNDS-1 && g_col) begin - - for (int i = 0; i < 8; i++) - h[i] <= h[i] ^ - g_out[16 + blake2_pkg::G_MAPPING_DIAG[i]] ^ - g_out[16 + blake2_pkg::G_MAPPING_DIAG[i+8]]; - - blake2_state <= STATE_NEXT_BLOCK; - if (~final_block_r) - o_rdy <= 1; + // Update local work vector with output of G function blocks depending on column or diagonal operation + for (int i = 0; i < 16; i++) begin + v[i] <= g_out[16 + blake2_pkg::G_MAPPING_DIAG[i]]; end + if (g_col) begin + round_cntr <= round_cntr + 1; + end else begin + round_cntr_msg <= (round_cntr_msg + 1) % 10; + end + if (round_cntr == ROUNDS-1) + round_cntr_fin <= 1; + + if (round_cntr_fin) begin + if (block_eop_l) + blake2_state <= STATE_FINAL_BLOCK; + else begin + blake2_state <= STATE_NEXT_BLOCK; + i_block.rdy <= 1; + end + end end STATE_NEXT_BLOCK: begin - if (final_block_r) begin - - o_val <= 1; - o_digest <= h; - t <= 128; - if (~o_hash.val) begin - o_hash.dat <= h; - o_hash.val <= 1; - o_hash.sop <= 1; - o_hash.eop <= 1; - end - if (o_hash.rdy) - blake2_state <= STATE_IDLE; - - end else if (o_rdy && i_val) begin - round_cntr <= 0; - init_local_work_vector(t); + round_cntr <= 0; + round_cntr_msg <= 0; + round_cntr_fin <= 0; + if (i_block.rdy && i_block.val) begin + init_local_work_vector(t, i_block.eop); //TODO this wont work with h_tmp + block_eop_l <= i_block.eop; t <= t + 128; + h <= h ^ h_tmp; blake2_state <= STATE_ROUNDS; end end + STATE_FINAL_BLOCK: begin + t <= 128; + round_cntr <= 0; + round_cntr_fin <= 0; + round_cntr_msg <= 0; + if (~o_hash.val || (o_hash.val && o_hash.rdy)) begin + if (~o_hash.val) begin + o_hash.dat <= h ^ h_tmp; + o_hash.val <= 1; + o_hash.sop <= 1; + o_hash.eop <= 1; + end + if (o_hash.rdy) begin + blake2_state <= STATE_IDLE; + i_block.rdy <= 1; + end + end + end endcase end end @@ -145,38 +158,40 @@ end generate begin genvar gv_g; for (gv_g = 0; gv_g < 8; gv_g++) begin: G_FUNCTION_GEN + + // For each G function we want to pipeline the input message to help timing + logic [63:0] m0, m1; + always_ff @ (posedge i_clk) begin + if(blake2_state == STATE_IDLE) begin + m0 <= block[blake2_pkg::SIGMA[gv_g*2]]; + m1 <= block[blake2_pkg::SIGMA[gv_g*2 + 1]]; + end else begin + m0 <= block_r[blake2_pkg::SIGMA[16*round_cntr_msg + gv_g*2]]; + m1 <= block_r[blake2_pkg::SIGMA[16*round_cntr_msg + gv_g*2 + 1]]; + end + end + blake2_g #(.PIPELINES(0)) blake2_g ( .i_clk(i_clk), - .i_a(v[blake2_pkg::G_MAPPING[(gv_g*4 + 0)]]), - .i_b(v[blake2_pkg::G_MAPPING[(gv_g*4 + 1)]]), - .i_c(v[blake2_pkg::G_MAPPING[(gv_g*4 + 2)]]), - .i_d(v[blake2_pkg::G_MAPPING[(gv_g*4 + 3)]]), - .i_m0(block_r[blake2_pkg::SIGMA[16*(round_cntr % 10) + (gv_g*2)]]), - .i_m1(block_r[blake2_pkg::SIGMA[16*(round_cntr % 10) + (gv_g*2 + 1)]]), + .i_a(gv_g < 4 ? v[blake2_pkg::G_MAPPING[(gv_g*4 + 0)]] : v_tmp[blake2_pkg::G_MAPPING[(gv_g*4 + 0)]]), + .i_b(gv_g < 4 ? v[blake2_pkg::G_MAPPING[(gv_g*4 + 1)]] : v_tmp[blake2_pkg::G_MAPPING[(gv_g*4 + 1)]]), + .i_c(gv_g < 4 ? v[blake2_pkg::G_MAPPING[(gv_g*4 + 2)]] : v_tmp[blake2_pkg::G_MAPPING[(gv_g*4 + 2)]]), + .i_d(gv_g < 4 ? v[blake2_pkg::G_MAPPING[(gv_g*4 + 3)]] : v_tmp[blake2_pkg::G_MAPPING[(gv_g*4 + 3)]]), + .i_m0(m0), + .i_m1(m1), .o_a(g_out[gv_g*4 + 0]), .o_b(g_out[gv_g*4 + 1]), .o_c(g_out[gv_g*4 + 2]), .o_d(g_out[gv_g*4 + 3])); + end end endgenerate - -// Task to initialize the state vector -task init_state_vector(); -begin - for (int i = 0; i < 8; i++) - if (i == 0) - h[i] <= parameters ^ blake2_pkg::IV[i]; - else - h[i] <= blake2_pkg::IV[i]; -end -endtask - // Task to initialize local work vector for the compression function -task init_local_work_vector(input [127:0] cntr); +task init_local_work_vector(input [127:0] cntr, input last_block); begin for (int i = 0; i < 16; i++) case (i) inside @@ -184,7 +199,7 @@ begin 8,9,10,11: v[i] <= blake2_pkg::IV[i%8]; 12: v[i] <= blake2_pkg::IV[i%8] ^ cntr[63:0]; 13: v[i] <= blake2_pkg::IV[i%8] ^ cntr[64 +: 64]; - 14: v[i] <= blake2_pkg::IV[i%8] ^ {64{i_final_block}}; + 14: v[i] <= blake2_pkg::IV[i%8] ^ {64{last_block}}; 15: v[i] <= blake2_pkg::IV[i%8]; endcase end diff --git a/ip_cores/blake2b/src/rtl/blake2b_pipe_top.sv b/ip_cores/blake2b/src/rtl/blake2b_pipe_top.sv new file mode 100644 index 0000000..7120235 --- /dev/null +++ b/ip_cores/blake2b/src/rtl/blake2b_pipe_top.sv @@ -0,0 +1,207 @@ +/* Implemented from RFC-7693, The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC) + * This is a unrolled pipelined version of the hash function required for Equihash. + * A single has takes 25 clock cycles but + */ + +module blake2_pipe_top + import blake2_pkg::*; +#( +) +( + input i_clk, i_rst, + + input [7:0] i_byte_len, // Length of the input message + input [64*8-1:0] i_parameters, // Input parameters used in the inital state + + if_axi_stream.sink i_block, // Input block with valid and ready signals for flow control + if_axi_stream.source o_hash // Output digest with valid and ready signals for flow control +); + +enum {STATE_IDLE = 0, + STATE_ROUNDS = 1, + STATE_NEXT_BLOCK = 2, + STATE_FINAL_BLOCK = 3} blake2_state; + +localparam ROUNDS = 12; + +logic [7:0][63:0] h, h_tmp; // The state vector +logic [15:0][63:0] v, v_tmp; // The local work vector and its intermediate value +logic [31:0][63:0] g_out, g_out_r; // Outputs of the G mixing function - use 8 here to save on timing +logic [127:0] t; // Counter - TODO make this smaller - related to param +logic [$clog2(ROUNDS)-1:0] round_cntr, round_cntr_msg, round_cntr_fin; +logic g_col; +logic [15:0][63:0] block, block_r; // The message block registered and converted to a 2d array +logic block_eop_l; // Use to latch if this is the final block + +// Pipelining logic that has no reset +always_ff @(posedge i_clk) begin + + g_out_r <= g_out; + + if (i_block.val && i_block.rdy) begin + block_r <= i_block.dat; + end + + for (int i = 0; i < 16; i++) + if (g_col == 0) // TODO why do I need this qualifier + v_tmp[i] <= g_out[blake2_pkg::G_MAPPING[i]]; + + for (int i = 0; i < 8; i++) + if (blake2_state == STATE_ROUNDS) + h_tmp[i] <= g_out[16 + blake2_pkg::G_MAPPING_DIAG[i]] ^ g_out[16 + blake2_pkg::G_MAPPING_DIAG[i+8]]; //TODO fix + +end + +always_comb begin + block = i_block.dat; +end + +// State machine logic for compressing +always_ff @(posedge i_clk) begin + if (i_rst) begin + blake2_state <= STATE_IDLE; + i_block.rdy <= 0; + h <= 0; + v <= 0; + t <= 128; + g_col <= 0; + round_cntr <= 0; + round_cntr_msg <= 0; + o_hash.reset_source(); + round_cntr_fin <= 0; + block_eop_l <= 0; + end else begin + + g_col <= ~g_col; + + case (blake2_state) + STATE_IDLE: begin + h <= i_parameters ^ blake2_pkg::IV; + t <= 128; + i_block.rdy <= 1; + v <= 0; + o_hash.val <= 0; + g_col <= 0; + round_cntr <= 0; + round_cntr_msg <= 0; + round_cntr_fin <= 0; + if (i_block.rdy && i_block.val && i_block.sop) begin + init_local_work_vector(i_byte_len, i_block.eop); + blake2_state <= STATE_ROUNDS; + g_col <= 0; + i_block.rdy <= 0; + block_eop_l <= i_block.eop; + end + end + // Here we do the compression over 12 rounds, each round can be done in two clock cycles + // After we do 12 rounds we increment counter t + STATE_ROUNDS: begin + + // Update local work vector with output of G function blocks depending on column or diagonal operation + for (int i = 0; i < 16; i++) begin + v[i] <= g_out[16 + blake2_pkg::G_MAPPING_DIAG[i]]; + end + + if (g_col) begin + round_cntr <= round_cntr + 1; + end else begin + round_cntr_msg <= (round_cntr_msg + 1) % 10; + end + if (round_cntr == ROUNDS-1) + round_cntr_fin <= 1; + + if (round_cntr_fin) begin + if (block_eop_l || EQUIHASH) + blake2_state <= STATE_FINAL_BLOCK; + else begin + blake2_state <= STATE_NEXT_BLOCK; + i_block.rdy <= 1; + end + end + end + STATE_NEXT_BLOCK: begin + round_cntr <= 0; + round_cntr_msg <= 0; + round_cntr_fin <= 0; + if (i_block.rdy && i_block.val) begin + init_local_work_vector(t, i_block.eop); //TODO this wont work with h_tmp + block_eop_l <= i_block.eop; + t <= t + 128; + h <= h ^ h_tmp; + blake2_state <= STATE_ROUNDS; + end + end + STATE_FINAL_BLOCK: begin + t <= 128; + round_cntr <= 0; + round_cntr_fin <= 0; + round_cntr_msg <= 0; + if (~o_hash.val || (o_hash.val && o_hash.rdy)) begin + if (~o_hash.val) begin + o_hash.dat <= h ^ h_tmp; + o_hash.val <= 1; + o_hash.sop <= 1; + o_hash.eop <= 1; + end + if (o_hash.rdy) begin + blake2_state <= STATE_IDLE; + i_block.rdy <= 1; + end + end + end + endcase + end +end + +// 8x G-function blocks. 4 are col and 4 are diagonal +generate begin + genvar gv_g; + for (gv_g = 0; gv_g < 8; gv_g++) begin: G_FUNCTION_GEN + + // For each G function we want to pipeline the input message to help timing + logic [63:0] m0, m1; + always_ff @ (posedge i_clk) begin + if(blake2_state == STATE_IDLE) begin + m0 <= block[blake2_pkg::SIGMA[gv_g*2]]; + m1 <= block[blake2_pkg::SIGMA[gv_g*2 + 1]]; + end else begin + m0 <= block_r[blake2_pkg::SIGMA[16*round_cntr_msg + gv_g*2]]; + m1 <= block_r[blake2_pkg::SIGMA[16*round_cntr_msg + gv_g*2 + 1]]; + end + end + + blake2_g + #(.PIPELINES(0)) + blake2_g ( + .i_clk(i_clk), + .i_a(gv_g < 4 ? v[blake2_pkg::G_MAPPING[(gv_g*4 + 0)]] : v_tmp[blake2_pkg::G_MAPPING[(gv_g*4 + 0)]]), + .i_b(gv_g < 4 ? v[blake2_pkg::G_MAPPING[(gv_g*4 + 1)]] : v_tmp[blake2_pkg::G_MAPPING[(gv_g*4 + 1)]]), + .i_c(gv_g < 4 ? v[blake2_pkg::G_MAPPING[(gv_g*4 + 2)]] : v_tmp[blake2_pkg::G_MAPPING[(gv_g*4 + 2)]]), + .i_d(gv_g < 4 ? v[blake2_pkg::G_MAPPING[(gv_g*4 + 3)]] : v_tmp[blake2_pkg::G_MAPPING[(gv_g*4 + 3)]]), + .i_m0(m0), + .i_m1(m1), + .o_a(g_out[gv_g*4 + 0]), + .o_b(g_out[gv_g*4 + 1]), + .o_c(g_out[gv_g*4 + 2]), + .o_d(g_out[gv_g*4 + 3])); + + end +end +endgenerate + +// Task to initialize local work vector for the compression function +task init_local_work_vector(input [127:0] cntr, input last_block); +begin + for (int i = 0; i < 16; i++) + case (i) inside + 0,1,2,3,4,5,6,7: v[i] <= h[i]; + 8,9,10,11: v[i] <= blake2_pkg::IV[i%8]; + 12: v[i] <= blake2_pkg::IV[i%8] ^ cntr[63:0]; + 13: v[i] <= blake2_pkg::IV[i%8] ^ cntr[64 +: 64]; + 14: v[i] <= blake2_pkg::IV[i%8] ^ {64{last_block}}; + 15: v[i] <= blake2_pkg::IV[i%8]; + endcase +end +endtask + +endmodule \ No newline at end of file diff --git a/ip_cores/blake2b/src/tb/blake2_top_tb.sv b/ip_cores/blake2b/src/tb/blake2_top_tb.sv index e8e9493..578d31e 100644 --- a/ip_cores/blake2b/src/tb/blake2_top_tb.sv +++ b/ip_cores/blake2b/src/tb/blake2_top_tb.sv @@ -1,19 +1,15 @@ module blake2_top_tb(); import blake2_pkg::*; +import common_pkg::*; logic clk, rst; -logic [7:0] digest_byte_len, key_byte_len; -logic [128*8-1:0] i_block; -logic i_new_block; -logic i_final_block; -logic i_val; -logic[64*8-1:0] o_digest; -logic o_rdy; -logic o_val; -logic o_err; +logic [7:0] i_byte_len; +logic [64*8-1:0] parameters; -if_axi_stream #(.DAT_BYTS(blake2_pkg::NN)) out_hash(clk); +logic [64*8-1:0] expected; +if_axi_stream #(.DAT_BYTS(128)) i_block(clk); +if_axi_stream #(.DAT_BYTS(64)) out_hash(clk); initial begin rst = 0; @@ -28,67 +24,69 @@ end blake2_top DUT ( - .i_clk(clk), - .i_rst(rst), - .i_digest_byte_len( digest_byte_len ), - .i_key_byte_len( key_byte_len ), - .i_block(i_block), - .i_new_block(i_new_block), - .i_final_block(i_final_block), - .i_val(i_val), - .o_digest(o_digest), - .o_rdy(o_rdy), - .o_val(o_val), - .o_err(o_err), - - .o_hash(out_hash) + .i_clk ( clk ), + .i_rst ( rst ), + .i_parameters ( parameters ), + .i_byte_len ( i_byte_len ), + .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 - i_val = 0; - @(posedge clk); - while (!o_rdy) @(posedge clk); - - @(negedge clk); - i_val = 1; - i_final_block = 1; - i_new_block = 1; - digest_byte_len = 3; - key_byte_len = 0; - i_block = 'h636261; - - @(negedge clk); - i_val = 0; - - while (!out_hash.val) @(posedge clk); - - assert (out_hash.dat == 'h239900d4ed8623b95a92f1dba88ad31895cc3345ded552c22d79ab2a39c5877dd1a2ffdb6fbb124bb7c45a68142f214ce9f6129fb697276a0d4d1c983fa580ba) else $fatal(0, "%m %t:ERROR, out_hash.dat did not match, was:\n0x%h", $time, out_hash.dat); - assert (out_hash.sop == 1) else $fatal(0, "%m %t:ERROR, out_hash.sop was not high", $time); - assert (out_hash.eop == 1) else $fatal(0, "%m %t:ERROR, out_hash.sop was not high", $time); - + integer signed get_len; + logic [common_pkg::MAX_SIM_BYTS*8-1:0] get_dat; + expected = 'h560c602c9cda1e198190f58e6341131f127367051c64f7df7d343e1b4c32a8bbc0eac1bcae463807dca442ae77d5150df700f6a640949a52cd4341dfc1e1044b; + i_byte_len = 3; + i_block.put_stream("hSV", i_byte_len); + out_hash.get_stream(get_dat, get_len); + common_pkg::compare_and_print(get_dat, expected); $display("rfc_test PASSED"); end endtask +// This is a test for hashing random string of 128 bytes +task test_128_bytes(); +begin + integer signed get_len; + logic [common_pkg::MAX_SIM_BYTS*8-1:0] get_dat; + expected = 'hd2a56bb7bb1ff1fffcf2f151522455e32969ddfeb409b105f45299b8cbd68eb370fd6d45d63981d23cd2686dfd9a76f5b1d134be076f7d08ecc457522042e34a; + i_byte_len = 128; + i_block.put_stream("monek14SFMpNgHz12zMfplMfcHkx6JhKhSWTNwzGiq8UiPa4n4Ehq363oHG92GPDVpvQut4ui5e6XxieeKTn1THLWiMZ0iaOFndxcT6FGPgmHXQ5zJU96X71zfWbvUQs", i_byte_len); + out_hash.get_stream(get_dat, get_len); + common_pkg::compare_and_print(get_dat, expected); + $display("test_128_bytes PASSED"); +end +endtask + +// This is a test for hashing random string of 140 bytes (does two passes which is required for equihash) +task test_140_bytes(); +begin + integer signed get_len; + logic [common_pkg::MAX_SIM_BYTS*8-1:0] get_dat; + expected = 'h429b65332e3b6701a29664f98c247204858479f55a8c18cc9b0ffa321cda4288fd420a5d47d134949f3b858bff7a696a00d91a07c92055cdd597971cf573281c; + i_byte_len = 140; + i_block.put_stream("6RehRZqUdYD2SB3N35QlQhreiU2XEaSgIGUsreLqV49l8Z5r93FbP567Juqc1IUaVyJKv8qFmtQwXYvZdnrMacAs5H9hBhs5JxAfyDibIM3TjKyiVzXC8lfCqiN1j6fW8FSJY131mVpw", i_byte_len); + out_hash.get_stream(get_dat, get_len); + common_pkg::compare_and_print(get_dat, expected); + $display("test_140_bytes PASSED"); +end +endtask + // Main testbench calls initial begin - key_byte_len = 0; - digest_byte_len = 3; - i_block = '0; - i_new_block = '0; - i_final_block = '0; - i_val = '0; + i_block.reset_source(); + i_byte_len = 3; out_hash.rdy = 1; - - + parameters = {32'd0, 8'd1, 8'd1, 8'd0, 8'd64}; #200ns; - rfc_test(); + //test_128_bytes(); + //test_140_bytes(); - #100ns $finish(); + #10us $finish(); end diff --git a/ip_cores/blake2b/synth/blake2g_top.xdc b/ip_cores/blake2b/synth/blake2g_top.xdc index 5d8cffe..933adcc 100644 --- a/ip_cores/blake2b/synth/blake2g_top.xdc +++ b/ip_cores/blake2b/synth/blake2g_top.xdc @@ -1 +1 @@ -create_clock -period 2.000 -name i_clk -waveform {0.000 1.000} [get_ports -filter { NAME =~ "*clk*" && DIRECTION == "IN" }] +create_clock -period 5.000 -name i_clk -waveform {0.000 2.500} [get_ports -filter { NAME =~ "i_clk" && DIRECTION == "IN" }] diff --git a/ip_cores/common/src/rtl/common_if.sv b/ip_cores/common/src/rtl/common_if.sv index fc4c1d1..841cb93 100644 --- a/ip_cores/common/src/rtl/common_if.sv +++ b/ip_cores/common/src/rtl/common_if.sv @@ -4,9 +4,9 @@ interface if_axi_stream # ( parameter DAT_BYTS = 8, parameter CTL_BYTS = 8 )( - input i_clk + input clk ); - + import common_pkg::*; localparam DAT_BITS = DAT_BYTS*8; localparam CTL_BITS = CTL_BYTS*8; @@ -19,7 +19,11 @@ interface if_axi_stream # ( logic [DAT_BITS-1:0] dat; logic [$clog2(DAT_BYTS)-1:0] mod; - task reset(); + modport sink (input val, err, sop, eop, ctl, dat, output rdy); + modport source (output val, err, sop, eop, ctl, dat, input rdy, import task reset_source()); + + // Task to reset a source interface signals to all 0 + task reset_source(); val <= 0; err <= 0; sop <= 0; @@ -29,4 +33,49 @@ interface if_axi_stream # ( mod <= 0; endtask + // Task used in simulation to drive data on a source interface + task automatic put_stream(input logic [common_pkg::MAX_SIM_BYTS*8-1:0] data, input integer signed len); + logic sop_l=0; + + reset_source(); + @(posedge clk); + + while (len > 0) begin + sop = ~sop_l; + eop = len - DAT_BYTS <= 0; + val = 1; + dat = data; + if (eop) mod = len; + data = data >> DAT_BITS; + sop_l = 1; + len = len - DAT_BYTS; + @(posedge clk); // Go to next clock edge + while (!rdy) @(posedge clk); // If not rdy then wait here + end + reset_source(); + endtask + + // Task used in simulation to get data from a sink interface + task automatic get_stream(ref logic [common_pkg::MAX_SIM_BYTS*8-1:0] data, ref integer signed len); + logic sop_l = 0; + rdy = 1; + len = 0; + data = 0; + @(posedge clk); + + while (1) begin + if (val && rdy) begin + sop_l = sop_l || sop; + if (!sop_l) $warning("%m %t:WARNING, get_stream() .val without seeing .sop", $time); + data[len*8 +: DAT_BITS] = dat; + len = len + (eop ? (mod == 0 ? DAT_BYTS : mod) : DAT_BYTS); + if (eop) break; + end + @(posedge clk); + end + + + endtask + + endinterface \ No newline at end of file diff --git a/ip_cores/common/src/rtl/common_pkg.sv b/ip_cores/common/src/rtl/common_pkg.sv new file mode 100644 index 0000000..a397d1c --- /dev/null +++ b/ip_cores/common/src/rtl/common_pkg.sv @@ -0,0 +1,24 @@ +// Common parameter values and tasks +package common_pkg; + parameter MAX_SIM_BYTS = 1024; // 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); + if (data == expected) begin + $display("%m %t INFO: Data matched", $time); + end else begin + $write("exp: 0x"); + while(expected != 0) begin + $write("%x", expected[7:0]); + expected = expected >> 8; + end + $write("\nwas: 0x"); + while(data != 0) begin + $write("%x", data[7:0]); + data = data >> 8; + end + $write("\n"); + $fatal(1, "%m %t ERROR: data did not match", $time); + end + endtask +endpackage \ No newline at end of file