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;
|
package blake2_pkg;
|
||||||
// User configurable values
|
|
||||||
parameter [7:0] NN = 64; // Output hash byte length
|
|
||||||
|
|
||||||
|
|
||||||
// Initial values
|
// Initial values
|
||||||
parameter [7:0][63:0] IV = {
|
parameter [7:0][63:0] IV = {
|
||||||
|
@ -15,6 +12,7 @@ package blake2_pkg;
|
||||||
64'h6a09e667f3bcc908
|
64'h6a09e667f3bcc908
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Sigma permutations used for G function blocks and input messages
|
||||||
parameter [16*10-1:0][31:0] SIGMA = {
|
parameter [16*10-1:0][31:0] SIGMA = {
|
||||||
0, 13, 12, 3, 14, 9, 11, 15, 5, 1, 6, 7, 4, 8, 2, 10,
|
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,
|
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
|
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 = {
|
parameter [4*8-1:0][31:0] G_MAPPING = {
|
||||||
14, 9, 4, 3,
|
14, 9, 4, 3,
|
||||||
13, 8, 7, 2,
|
13, 8, 7, 2,
|
||||||
|
|
|
@ -1,140 +1,153 @@
|
||||||
// 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
|
module blake2_top
|
||||||
import blake2_pkg::*;
|
import blake2_pkg::*;
|
||||||
#(
|
|
||||||
)
|
|
||||||
(
|
(
|
||||||
input i_clk, i_rst,
|
input i_clk, i_rst,
|
||||||
|
|
||||||
// Parameter block input
|
input [7:0] i_byte_len, // Length of the input message
|
||||||
input [7:0] i_digest_byte_len,
|
input [64*8-1:0] i_parameters, // Input parameters used in the inital state.
|
||||||
input [7:0] i_key_byte_len,
|
|
||||||
|
|
||||||
input [128*8-1:0] i_block,
|
if_axi_stream.sink i_block, // Input block with valid and ready signals for flow control
|
||||||
input i_new_block,
|
if_axi_stream.source o_hash // Output digest with valid and ready signals for flow control
|
||||||
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
|
|
||||||
);
|
);
|
||||||
|
|
||||||
enum {STATE_IDLE = 0,
|
enum {STATE_IDLE = 0,
|
||||||
STATE_ROUNDS = 1,
|
STATE_ROUNDS = 1,
|
||||||
STATE_NEXT_BLOCK = 2} blake2_state;
|
STATE_NEXT_BLOCK = 2,
|
||||||
|
STATE_FINAL_BLOCK = 3} blake2_state;
|
||||||
|
|
||||||
localparam ROUNDS = 12;
|
localparam ROUNDS = 12;
|
||||||
|
|
||||||
logic [64*8-1:0] parameters, parameters_r;
|
logic [7:0][63:0] h, h_tmp; // The state vector
|
||||||
logic [7:0][63:0] h; // The state vector
|
logic [15:0][63:0] v, v_tmp; // The local work vector and its intermediate value
|
||||||
logic [15:0][63:0] v; // The local work vector
|
logic [31:0][63:0] g_out;//, g_out_r; // Outputs of the G mixing function - use 8 here to save on timing
|
||||||
logic [31:0][63:0] g_out; // 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 [127:0] t; // Counter - TODO make this smaller - related to param
|
||||||
logic [$clog2(ROUNDS)-1:0] round_cntr;
|
logic [$clog2(ROUNDS)-1:0] round_cntr, round_cntr_msg, round_cntr_fin;
|
||||||
logic cnt;
|
|
||||||
logic g_col;
|
logic g_col;
|
||||||
logic [15:0][63:0] block_r; // The message block registered and converted to a 2d array
|
logic [15:0][63:0] block, block_r; // The message block registered and converted to a 2d array
|
||||||
logic final_block_r;
|
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
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
always_comb begin
|
always_comb begin
|
||||||
parameters = {32'd0, 8'd1, 8'd1, i_key_byte_len, blake2_pkg::NN};
|
block = i_block.dat;
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
// State machine logic for compressing
|
// State machine logic for compressing
|
||||||
always_ff @(posedge i_clk) begin
|
always_ff @(posedge i_clk) begin
|
||||||
if (i_rst) begin
|
if (i_rst) begin
|
||||||
blake2_state <= STATE_IDLE;
|
blake2_state <= STATE_IDLE;
|
||||||
o_val <= 0;
|
i_block.rdy <= 0;
|
||||||
o_rdy <= 0;
|
|
||||||
h <= 0;
|
h <= 0;
|
||||||
v <= 0;
|
v <= 0;
|
||||||
t <= 128;
|
t <= 128;
|
||||||
g_col <= 0;
|
g_col <= 0;
|
||||||
round_cntr <= 0;
|
round_cntr <= 0;
|
||||||
o_err <= 0;
|
round_cntr_msg <= 0;
|
||||||
o_digest <= 0;
|
o_hash.reset_source();
|
||||||
cnt <= 0;
|
round_cntr_fin <= 0;
|
||||||
|
block_eop_l <= 0;
|
||||||
o_hash.reset();
|
|
||||||
|
|
||||||
end else begin
|
end else begin
|
||||||
cnt <= cnt + 1;
|
|
||||||
|
if (blake2_state != STATE_NEXT_BLOCK) g_col <= ~g_col;
|
||||||
|
|
||||||
case (blake2_state)
|
case (blake2_state)
|
||||||
STATE_IDLE: begin
|
STATE_IDLE: begin
|
||||||
o_val <= 0;
|
h <= i_parameters ^ blake2_pkg::IV;
|
||||||
init_state_vector();
|
|
||||||
t <= 128;
|
t <= 128;
|
||||||
o_err <= 0;
|
i_block.rdy <= 1;
|
||||||
o_rdy <= 1;
|
|
||||||
v <= 0;
|
v <= 0;
|
||||||
o_hash.val = 0;
|
o_hash.val <= 0;
|
||||||
g_col <= 0;
|
g_col <= 0;
|
||||||
round_cntr <= 0;
|
round_cntr <= 0;
|
||||||
if (o_rdy && i_val && i_new_block) begin
|
round_cntr_msg <= 0;
|
||||||
init_local_work_vector(i_final_block ? i_digest_byte_len : t);
|
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;
|
blake2_state <= STATE_ROUNDS;
|
||||||
o_rdy <= 0;
|
g_col <= 0;
|
||||||
|
i_block.rdy <= 0;
|
||||||
|
block_eop_l <= i_block.eop;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
// Here we do the compression over 12 rounds, each round can be done in two clock cycles
|
// 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
|
// After we do 12 rounds we increment counter t
|
||||||
STATE_ROUNDS: begin
|
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)
|
// Update local work vector with output of G function blocks depending on column or diagonal operation
|
||||||
round_cntr <= round_cntr + 1;
|
for (int i = 0; i < 16; i++) begin
|
||||||
g_col <= ~g_col;
|
v[i] <= g_out[16 + blake2_pkg::G_MAPPING_DIAG[i]];
|
||||||
|
|
||||||
// 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;
|
|
||||||
end
|
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
|
end
|
||||||
STATE_NEXT_BLOCK: begin
|
STATE_NEXT_BLOCK: begin
|
||||||
if (final_block_r) begin
|
round_cntr <= 0;
|
||||||
|
round_cntr_msg <= 0;
|
||||||
o_val <= 1;
|
round_cntr_fin <= 0;
|
||||||
o_digest <= h;
|
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;
|
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
|
if (~o_hash.val) begin
|
||||||
o_hash.dat <= h;
|
o_hash.dat <= h ^ h_tmp;
|
||||||
o_hash.val <= 1;
|
o_hash.val <= 1;
|
||||||
o_hash.sop <= 1;
|
o_hash.sop <= 1;
|
||||||
o_hash.eop <= 1;
|
o_hash.eop <= 1;
|
||||||
end
|
end
|
||||||
if (o_hash.rdy)
|
if (o_hash.rdy) begin
|
||||||
blake2_state <= STATE_IDLE;
|
blake2_state <= STATE_IDLE;
|
||||||
|
i_block.rdy <= 1;
|
||||||
end else if (o_rdy && i_val) begin
|
end
|
||||||
round_cntr <= 0;
|
|
||||||
init_local_work_vector(t);
|
|
||||||
t <= t + 128;
|
|
||||||
blake2_state <= STATE_ROUNDS;
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
endcase
|
endcase
|
||||||
|
@ -145,38 +158,40 @@ end
|
||||||
generate begin
|
generate begin
|
||||||
genvar gv_g;
|
genvar gv_g;
|
||||||
for (gv_g = 0; gv_g < 8; gv_g++) begin: G_FUNCTION_GEN
|
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
|
blake2_g
|
||||||
#(.PIPELINES(0))
|
#(.PIPELINES(0))
|
||||||
blake2_g (
|
blake2_g (
|
||||||
.i_clk(i_clk),
|
.i_clk(i_clk),
|
||||||
.i_a(v[blake2_pkg::G_MAPPING[(gv_g*4 + 0)]]),
|
.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(v[blake2_pkg::G_MAPPING[(gv_g*4 + 1)]]),
|
.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(v[blake2_pkg::G_MAPPING[(gv_g*4 + 2)]]),
|
.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(v[blake2_pkg::G_MAPPING[(gv_g*4 + 3)]]),
|
.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(block_r[blake2_pkg::SIGMA[16*(round_cntr % 10) + (gv_g*2)]]),
|
.i_m0(m0),
|
||||||
.i_m1(block_r[blake2_pkg::SIGMA[16*(round_cntr % 10) + (gv_g*2 + 1)]]),
|
.i_m1(m1),
|
||||||
.o_a(g_out[gv_g*4 + 0]),
|
.o_a(g_out[gv_g*4 + 0]),
|
||||||
.o_b(g_out[gv_g*4 + 1]),
|
.o_b(g_out[gv_g*4 + 1]),
|
||||||
.o_c(g_out[gv_g*4 + 2]),
|
.o_c(g_out[gv_g*4 + 2]),
|
||||||
.o_d(g_out[gv_g*4 + 3]));
|
.o_d(g_out[gv_g*4 + 3]));
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
endgenerate
|
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 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
|
begin
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
case (i) inside
|
case (i) inside
|
||||||
|
@ -184,7 +199,7 @@ begin
|
||||||
8,9,10,11: v[i] <= blake2_pkg::IV[i%8];
|
8,9,10,11: v[i] <= blake2_pkg::IV[i%8];
|
||||||
12: v[i] <= blake2_pkg::IV[i%8] ^ cntr[63:0];
|
12: v[i] <= blake2_pkg::IV[i%8] ^ cntr[63:0];
|
||||||
13: v[i] <= blake2_pkg::IV[i%8] ^ cntr[64 +: 64];
|
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];
|
15: v[i] <= blake2_pkg::IV[i%8];
|
||||||
endcase
|
endcase
|
||||||
end
|
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();
|
module blake2_top_tb();
|
||||||
|
|
||||||
import blake2_pkg::*;
|
import blake2_pkg::*;
|
||||||
|
import common_pkg::*;
|
||||||
|
|
||||||
logic clk, rst;
|
logic clk, rst;
|
||||||
logic [7:0] digest_byte_len, key_byte_len;
|
logic [7:0] i_byte_len;
|
||||||
logic [128*8-1:0] i_block;
|
logic [64*8-1:0] parameters;
|
||||||
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;
|
|
||||||
|
|
||||||
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
|
initial begin
|
||||||
rst = 0;
|
rst = 0;
|
||||||
|
@ -30,65 +26,67 @@ end
|
||||||
blake2_top DUT (
|
blake2_top DUT (
|
||||||
.i_clk ( clk ),
|
.i_clk ( clk ),
|
||||||
.i_rst ( rst ),
|
.i_rst ( rst ),
|
||||||
.i_digest_byte_len( digest_byte_len ),
|
.i_parameters ( parameters ),
|
||||||
.i_key_byte_len( key_byte_len ),
|
.i_byte_len ( i_byte_len ),
|
||||||
.i_block ( i_block ),
|
.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 )
|
.o_hash ( out_hash )
|
||||||
);
|
);
|
||||||
|
|
||||||
// This test runs the hash which is shown in the RFC, for "abc"
|
// This test runs the hash which is shown in the RFC, for "abc"
|
||||||
task rfc_test();
|
task rfc_test();
|
||||||
begin
|
begin
|
||||||
i_val = 0;
|
integer signed get_len;
|
||||||
@(posedge clk);
|
logic [common_pkg::MAX_SIM_BYTS*8-1:0] get_dat;
|
||||||
while (!o_rdy) @(posedge clk);
|
expected = 'h560c602c9cda1e198190f58e6341131f127367051c64f7df7d343e1b4c32a8bbc0eac1bcae463807dca442ae77d5150df700f6a640949a52cd4341dfc1e1044b;
|
||||||
|
i_byte_len = 3;
|
||||||
@(negedge clk);
|
i_block.put_stream("hSV", i_byte_len);
|
||||||
i_val = 1;
|
out_hash.get_stream(get_dat, get_len);
|
||||||
i_final_block = 1;
|
common_pkg::compare_and_print(get_dat, expected);
|
||||||
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);
|
|
||||||
|
|
||||||
$display("rfc_test PASSED");
|
$display("rfc_test PASSED");
|
||||||
end
|
end
|
||||||
endtask
|
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
|
// Main testbench calls
|
||||||
initial begin
|
initial begin
|
||||||
key_byte_len = 0;
|
i_block.reset_source();
|
||||||
digest_byte_len = 3;
|
i_byte_len = 3;
|
||||||
i_block = '0;
|
|
||||||
i_new_block = '0;
|
|
||||||
i_final_block = '0;
|
|
||||||
i_val = '0;
|
|
||||||
out_hash.rdy = 1;
|
out_hash.rdy = 1;
|
||||||
|
parameters = {32'd0, 8'd1, 8'd1, 8'd0, 8'd64};
|
||||||
|
|
||||||
#200ns;
|
#200ns;
|
||||||
|
|
||||||
|
|
||||||
rfc_test();
|
rfc_test();
|
||||||
|
//test_128_bytes();
|
||||||
|
//test_140_bytes();
|
||||||
|
|
||||||
#100ns $finish();
|
#10us $finish();
|
||||||
|
|
||||||
end
|
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 DAT_BYTS = 8,
|
||||||
parameter CTL_BYTS = 8
|
parameter CTL_BYTS = 8
|
||||||
)(
|
)(
|
||||||
input i_clk
|
input clk
|
||||||
);
|
);
|
||||||
|
import common_pkg::*;
|
||||||
localparam DAT_BITS = DAT_BYTS*8;
|
localparam DAT_BITS = DAT_BYTS*8;
|
||||||
localparam CTL_BITS = CTL_BYTS*8;
|
localparam CTL_BITS = CTL_BYTS*8;
|
||||||
|
|
||||||
|
@ -19,7 +19,11 @@ interface if_axi_stream # (
|
||||||
logic [DAT_BITS-1:0] dat;
|
logic [DAT_BITS-1:0] dat;
|
||||||
logic [$clog2(DAT_BYTS)-1:0] mod;
|
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;
|
val <= 0;
|
||||||
err <= 0;
|
err <= 0;
|
||||||
sop <= 0;
|
sop <= 0;
|
||||||
|
@ -29,4 +33,49 @@ interface if_axi_stream # (
|
||||||
mod <= 0;
|
mod <= 0;
|
||||||
endtask
|
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
|
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