Updates to testbench to include helper functions,
updated synthesis file to meet timing (200MHz)
This commit is contained in:
parent
191fbef305
commit
d391cbc992
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
create_clock -period 5.000 -name i_clk -waveform {0.000 2.500} [get_ports -filter { NAME =~ "*i_clk*" && DIRECTION == "IN" }]
|
|
@ -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,
|
||||
|
|
|
@ -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 [7:0] i_byte_len, // Length of the input message
|
||||
input [64*8-1:0] i_parameters, // Input parameters used in the inital state.
|
||||
|
||||
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,
|
||||
|
||||
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
|
||||
|
||||
// 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
|
||||
|
||||
always_comb begin
|
||||
parameters = {32'd0, 8'd1, 8'd1, i_key_byte_len, blake2_pkg::NN};
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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" }]
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue