Updates to testbench to include helper functions,

updated synthesis file to meet timing (200MHz)
This commit is contained in:
bsdevlin 2019-02-13 10:53:19 -05:00
parent 191fbef305
commit d391cbc992
9 changed files with 513 additions and 170 deletions

View File

@ -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

View File

@ -0,0 +1 @@
create_clock -period 5.000 -name i_clk -waveform {0.000 2.500} [get_ports -filter { NAME =~ "*i_clk*" && DIRECTION == "IN" }]

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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" }]

View File

@ -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

View File

@ -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