Initial files for the Blake2B core

This commit is contained in:
bsdevlin 2019-02-12 00:07:18 +08:00
parent ada8931711
commit 361ece0ee3
6 changed files with 430 additions and 0 deletions

11
.project Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>zcash-fpga</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
</buildSpec>
<natures>
</natures>
</projectDescription>

View File

@ -0,0 +1,58 @@
module blake2_g
#(
parameter PIPELINES = 1 // Do we want to optionally add pipeline stages
)
(
input i_clk,
input [63:0] i_a, i_b, i_c, i_d, i_m0, i_m1,
output logic [63:0] o_a, o_b, o_c, o_d
);
logic [63:0] a0, b0, c0, d0, a1, b1, c1, d1, b2, d2, b3, d3;
logic [PIPELINES:0][64*4-1:0] pipeline;
// Logic used to implement G function
always_comb begin
a0 = i_a + i_b + i_m0;
d0 = i_d ^ a0;
d1 = {d0[0 +: 32], d0[32 +: 32]};
c0 = i_c + d1;
b0 = i_b ^ c0;
b1 = {b0[0 +: 24], b0[24 +: 40]};
a1 = a0 + b1 + i_m1;
d2 = d1 ^ a1;
d3 = {d2[0 +: 16], d2[16 +: 48]};
c1 = c0 + d3;
b2 = b1 ^ c1;
b3 = {b2[0 +: 63], b2[63]};
end
// Final output assignment
always_comb begin
o_a = pipeline[PIPELINES][0*64 +: 64];
o_b = pipeline[PIPELINES][1*64 +: 64];
o_c = pipeline[PIPELINES][2*64 +: 64];
o_d = pipeline[PIPELINES][3*64 +: 64];
end
// Optional pipelines
generate begin: PIPE_GEN
genvar gv_p;
always_comb begin
pipeline[0][0*64 +: 64] = a1;
pipeline[0][1*64 +: 64] = b3;
pipeline[0][2*64 +: 64] = c1;
pipeline[0][3*64 +: 64] = d3;
end
for (gv_p = 0; gv_p < PIPELINES; gv_p++) begin: PIPE_LOOP_GEN
always_ff @ (posedge i_clk) begin
pipeline[gv_p + 1][0*64 +: 64] <= pipeline[gv_p][0*64 +: 64];
pipeline[gv_p + 1][1*64 +: 64] <= pipeline[gv_p][1*64 +: 64];
pipeline[gv_p + 1][2*64 +: 64] <= pipeline[gv_p][2*64 +: 64];
pipeline[gv_p + 1][3*64 +: 64] <= pipeline[gv_p][3*64 +: 64];
end
end
end
endgenerate
endmodule

View File

@ -0,0 +1,50 @@
package blake2_pkg;
// Initial values
parameter [7:0][63:0] IV = {
64'h5be0cd19137e2179,
64'h1f83d9abfb41bd6b,
64'h9b05688c2b3e6c1f,
64'h510e527fade682d1,
64'ha54ff53a5f1d36f1,
64'h3c6ef372fe94f82b,
64'hbb67ae8584caa73b,
64'h6a09e667f3bcc908
};
parameter [15*10-1:0][31:0] SIGMA = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0
};
parameter [4*8-1:0][31:0] G_MAPPING = {
14, 9, 4, 3,
13, 8, 7, 2,
12, 11, 6, 1,
15, 10, 5, 0,
15, 11, 7, 3,
14, 10, 6, 2,
13, 9, 5, 1,
12, 8, 4, 0
};
// Top 4 bits per entry is the nth G-function unit
// lower 4 bits is the ith output of the G-function
parameter [15:0][5:0] G_FINAL_MAPPING = {
{3'd4,3'd3}, {3'd7,3'd3}, {3'd6,3'd3}, {3'd5,3'd3},
{3'd5,3'd2}, {3'd4,3'd2}, {3'd7,3'd2}, {3'd6,3'd2},
{3'd6,3'd1}, {3'd5,3'd1}, {3'd4,3'd1}, {3'd7,3'd1},
{3'd7,3'd0}, {3'd6,3'd0}, {3'd5,3'd0}, {3'd4, 3'd0}
};
endpackage

View File

@ -0,0 +1,177 @@
// Implemented from RFC-7693, The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)
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
);
enum {STATE_IDLE = 0,
STATE_ROUNDS = 1,
STATE_NEXT_BLOCK = 2} blake2_state;
localparam ROUNDS = 12;
logic [64*8-1:0] parameters;
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 [127:0] t; // Counter - TODO make this smaller - related to param
logic [$clog2(ROUNDS)-1:0] round_cntr;
logic cnt;
logic g_row_col;
logic [15:0][63:0] block_r; // The message block registered and converted to a 2d array
logic final_block_r;
// Logic that is for pipelining
always_ff @(posedge i_clk) begin
parameters <= {32'd0, 8'd1, 8'd1, i_key_byte_len, i_digest_byte_len};
if (i_val && o_rdy) begin
block_r <= i_block;
final_block_r <= i_final_block;
end
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;
h <= 0;
v <= 0;
t <= 0;
g_row_col <= 0;
round_cntr <= 0;
o_err <= 0;
o_digest <= 0;
cnt <= 0;
end else begin
cnt <= cnt + 1;
case (blake2_state)
STATE_IDLE: begin
o_val <= 0;
init_state_vector();
t <= 0;
o_err <= 0;
o_rdy <= 1;
v <= 0;
g_row_col <= 0;
round_cntr <= 0;
if (o_rdy && i_val && i_new_block) begin
init_local_work_vector();
blake2_state <= STATE_ROUNDS;
o_rdy <= 0;
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_out[G_MAPPING[g_row_col*16 + i]];
if (g_row_col)
round_cntr <= round_cntr + 1;
g_row_col <= ~g_row_col;
// Update state vector on the final round
if (round_cntr == ROUNDS-1) begin
for (int i = 0; i < 7; i++)
h[i] <= h[i] ^
g_out[G_FINAL_MAPPING[i][5:3]][G_FINAL_MAPPING[i][2:0]] ^
g_out[G_FINAL_MAPPING[i+8][5:3]][G_FINAL_MAPPING[i][2:0]];
blake2_state <= STATE_NEXT_BLOCK;
if (~final_block_r)
o_rdy <= 1;
end
end
STATE_NEXT_BLOCK: begin
if (final_block_r) begin
blake2_state <= STATE_IDLE;
o_val <= 1;
o_digest <= h;
end else if (o_rdy && i_val) begin
round_cntr <= 0;
init_local_work_vector();
t <= (t+1) * 128;
blake2_state <= STATE_ROUNDS;
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
blake2_g
#(.PIPELINES(0))
blake2_g (
.i_clk(i_clk),
.i_a(v[(gv_g*4 + 0) % 16]),
.i_b(v[(gv_g*4 + 1) % 16]),
.i_c(v[(gv_g*4 + 2) % 16]),
.i_d(v[(gv_g*4 + 3) % 16]),
.i_m0(block_r[blake2_pkg::SIGMA[(round_cntr % 10) + (gv_g*16)]]),
.i_m1(block_r[blake2_pkg::SIGMA[(round_cntr % 10) + ((gv_g+1))*16]]),
.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();
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] ^ t[63:0];
13: v[i] <= blake2_pkg::IV[i%8] ^ t[64 +: 64];
14: v[i] <= blake2_pkg::IV[i%8] ^ {64{i_final_block}};
15: v[i] <= blake2_pkg::IV[i%8];
endcase
end
endtask
endmodule

View File

@ -0,0 +1,46 @@
module blake2_g_tb();
logic clk;
logic [63:0] o_a, o_b, o_c, o_d, i_a, i_b, i_c, i_d, i_m0, i_m1;
localparam PIPELINES = 1;
blake2_g #(.PIPELINES(PIPELINES)) DUT (.i_clk(clk), .o_a(o_a), .o_b(o_b), .o_c(o_c), .o_d(o_d), .i_a(i_a), .i_b(i_b), .i_c(i_c), .i_d(i_d), .i_m0(i_m0), .i_m1(i_m1));
initial begin
clk = 0;
forever #10ns clk = ~clk;
end
task test1();
begin
@(posedge clk)
i_a = 64'h6a09e667f2bdc948;
i_b = 64'h510e527fade682d1;
i_c = 64'h6a09e667f3bcc908;
i_d = 64'h510e527fade68251;
i_m0 = 64'h0000000000000000;
i_m1 = 64'h0000000000000000;
repeat (PIPELINES) @(posedge clk);
#1;
assert (o_a == 64'hf0c9aa0de38b1b89) else $fatal(0, "%m %t:ERROR, o_a did not match", $time);
assert (o_b == 64'hbbdf863401fde49b) else $fatal(0, "%m %t:ERROR, o_b did not match", $time);
assert (o_c == 64'he85eb23c42183d3d) else $fatal(0, "%m %t:ERROR, o_c did not match", $time);
assert (o_d == 64'h7111fd8b6445099d) else $fatal(0, "%m %t:ERROR, o_d did not match", $time);
$display("test1 PASSED");
end
endtask
// Main testbench calls
initial begin
#100ns;
test1();
#100ns $finish();
end
endmodule

View File

@ -0,0 +1,88 @@
module blake2_top_tb();
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;
initial begin
rst = 0;
#100ns rst = 1;
#100ns rst = 0;
end
initial begin
clk = 0;
forever #10ns clk = ~clk;
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)
);
// 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;
i_block = 'h636261;
@(negedge clk);
i_val = 0;
// TODO check rdy goes low
while (!o_val) @(posedge clk);
@(posedge clk);
@(posedge clk);
// TODO verify result
$display("rfc_test PASSED");
end
endtask
// Main testbench calls
initial begin
key_byte_len = 0;
digest_byte_len = 64;
i_block = '0;
i_new_block = '0;
i_final_block = '0;
i_val = '0;
#200ns;
rfc_test();
#100ns $finish();
end
endmodule