From 361ece0ee33a07b119b7b015b4d0448ea2d26e56 Mon Sep 17 00:00:00 2001 From: bsdevlin Date: Tue, 12 Feb 2019 00:07:18 +0800 Subject: [PATCH] Initial files for the Blake2B core --- .project | 11 ++ ip_cores/blake2b/src/rtl/blake2_g.sv | 58 ++++++++ ip_cores/blake2b/src/rtl/blake2_pkg.sv | 50 +++++++ ip_cores/blake2b/src/rtl/blake2_top.sv | 177 +++++++++++++++++++++++ ip_cores/blake2b/src/tb/blake2_g_tb.sv | 46 ++++++ ip_cores/blake2b/src/tb/blake2_top_tb.sv | 88 +++++++++++ 6 files changed, 430 insertions(+) create mode 100644 .project create mode 100644 ip_cores/blake2b/src/rtl/blake2_g.sv create mode 100644 ip_cores/blake2b/src/rtl/blake2_pkg.sv create mode 100644 ip_cores/blake2b/src/rtl/blake2_top.sv create mode 100644 ip_cores/blake2b/src/tb/blake2_g_tb.sv create mode 100644 ip_cores/blake2b/src/tb/blake2_top_tb.sv diff --git a/.project b/.project new file mode 100644 index 0000000..0ef5a74 --- /dev/null +++ b/.project @@ -0,0 +1,11 @@ + + + zcash-fpga + + + + + + + + diff --git a/ip_cores/blake2b/src/rtl/blake2_g.sv b/ip_cores/blake2b/src/rtl/blake2_g.sv new file mode 100644 index 0000000..5a18bd6 --- /dev/null +++ b/ip_cores/blake2b/src/rtl/blake2_g.sv @@ -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 \ No newline at end of file diff --git a/ip_cores/blake2b/src/rtl/blake2_pkg.sv b/ip_cores/blake2b/src/rtl/blake2_pkg.sv new file mode 100644 index 0000000..614bbbf --- /dev/null +++ b/ip_cores/blake2b/src/rtl/blake2_pkg.sv @@ -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 \ No newline at end of file diff --git a/ip_cores/blake2b/src/rtl/blake2_top.sv b/ip_cores/blake2b/src/rtl/blake2_top.sv new file mode 100644 index 0000000..3cc9426 --- /dev/null +++ b/ip_cores/blake2b/src/rtl/blake2_top.sv @@ -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 \ No newline at end of file diff --git a/ip_cores/blake2b/src/tb/blake2_g_tb.sv b/ip_cores/blake2b/src/tb/blake2_g_tb.sv new file mode 100644 index 0000000..19953c0 --- /dev/null +++ b/ip_cores/blake2b/src/tb/blake2_g_tb.sv @@ -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 \ No newline at end of file diff --git a/ip_cores/blake2b/src/tb/blake2_top_tb.sv b/ip_cores/blake2b/src/tb/blake2_top_tb.sv new file mode 100644 index 0000000..ac0e9a8 --- /dev/null +++ b/ip_cores/blake2b/src/tb/blake2_top_tb.sv @@ -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 \ No newline at end of file