Updating files for equihash verifier
This commit is contained in:
parent
cb39d92778
commit
033d31de09
|
@ -72,6 +72,7 @@ generate
|
||||||
o_hash.ctl = ctl[NUM_PIPE-1];
|
o_hash.ctl = ctl[NUM_PIPE-1];
|
||||||
o_hash.sop = 1;
|
o_hash.sop = 1;
|
||||||
o_hash.eop = 1;
|
o_hash.eop = 1;
|
||||||
|
o_hash.err = 0;
|
||||||
o_hash.dat = h[NUM_PIPE-1];
|
o_hash.dat = h[NUM_PIPE-1];
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -150,10 +151,11 @@ generate
|
||||||
end
|
end
|
||||||
// Second stage
|
// Second stage
|
||||||
if (o_hash.rdy) begin
|
if (o_hash.rdy) begin
|
||||||
h[PIPE_G0+1] <= h[PIPE_G0];
|
|
||||||
init_local_work_vector_pipe(PIPE_G0+1, LAST_BLOCK ? byte_len : 128 , LAST_BLOCK);
|
|
||||||
|
|
||||||
// Shift message down either from previous pipeline or from fixed portion
|
// Shift message down either from previous pipeline or from fixed portion
|
||||||
|
if (g0 < (NUM_PASSES - 1)) begin
|
||||||
|
h[PIPE_G0+1] <= h[PIPE_G0];
|
||||||
|
init_local_work_vector_pipe(PIPE_G0+1, LAST_BLOCK ? byte_len : 128 , LAST_BLOCK);
|
||||||
msg[PIPE_G0+1] <= 0;
|
msg[PIPE_G0+1] <= 0;
|
||||||
for (int i = 0; i < 128; i++) begin
|
for (int i = 0; i < 128; i++) begin
|
||||||
if ((g0+1)*128 + i < MSG_VAR_BYTS)
|
if ((g0+1)*128 + i < MSG_VAR_BYTS)
|
||||||
|
@ -161,6 +163,7 @@ generate
|
||||||
end
|
end
|
||||||
ctl[PIPE_G0+1] <= ctl[PIPE_G0];
|
ctl[PIPE_G0+1] <= ctl[PIPE_G0];
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Interface for a AXI stream
|
Commonly used interfaces:
|
||||||
|
- AXI stream
|
||||||
|
- RAM
|
||||||
|
|
||||||
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
|
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
|
||||||
|
|
||||||
|
@ -21,9 +23,8 @@ interface if_axi_stream # (
|
||||||
parameter DAT_BYTS = 8,
|
parameter DAT_BYTS = 8,
|
||||||
parameter CTL_BYTS = 1
|
parameter CTL_BYTS = 1
|
||||||
)(
|
)(
|
||||||
input clk
|
input i_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;
|
||||||
|
|
||||||
|
@ -36,8 +37,8 @@ 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;
|
||||||
|
|
||||||
modport sink (input val, err, sop, eop, ctl, dat, mod, clk, output rdy);
|
modport sink (input val, err, sop, eop, ctl, dat, mod, i_clk, output rdy);
|
||||||
modport source (output val, err, sop, eop, ctl, dat, mod, input rdy, clk, import task reset_source());
|
modport source (output val, err, sop, eop, ctl, dat, mod, input rdy, i_clk, import task reset_source());
|
||||||
|
|
||||||
// Task to reset a source interface signals to all 0
|
// Task to reset a source interface signals to all 0
|
||||||
task reset_source();
|
task reset_source();
|
||||||
|
@ -55,7 +56,7 @@ interface if_axi_stream # (
|
||||||
logic sop_l=0;
|
logic sop_l=0;
|
||||||
|
|
||||||
reset_source();
|
reset_source();
|
||||||
@(posedge clk);
|
@(posedge i_clk);
|
||||||
|
|
||||||
while (len > 0) begin
|
while (len > 0) begin
|
||||||
sop = ~sop_l;
|
sop = ~sop_l;
|
||||||
|
@ -66,8 +67,8 @@ interface if_axi_stream # (
|
||||||
data = data >> DAT_BITS;
|
data = data >> DAT_BITS;
|
||||||
sop_l = 1;
|
sop_l = 1;
|
||||||
len = len - DAT_BYTS;
|
len = len - DAT_BYTS;
|
||||||
@(posedge clk); // Go to next clock edge
|
@(posedge i_clk); // Go to next clock edge
|
||||||
while (!rdy) @(posedge clk); // If not rdy then wait here
|
while (!rdy) @(posedge i_clk); // If not rdy then wait here
|
||||||
end
|
end
|
||||||
reset_source();
|
reset_source();
|
||||||
endtask
|
endtask
|
||||||
|
@ -78,7 +79,7 @@ interface if_axi_stream # (
|
||||||
rdy = 1;
|
rdy = 1;
|
||||||
len = 0;
|
len = 0;
|
||||||
data = 0;
|
data = 0;
|
||||||
@(posedge clk);
|
@(posedge i_clk);
|
||||||
|
|
||||||
while (1) begin
|
while (1) begin
|
||||||
if (val && rdy) begin
|
if (val && rdy) begin
|
||||||
|
@ -88,11 +89,37 @@ interface if_axi_stream # (
|
||||||
len = len + (eop ? (mod == 0 ? DAT_BYTS : mod) : DAT_BYTS);
|
len = len + (eop ? (mod == 0 ? DAT_BYTS : mod) : DAT_BYTS);
|
||||||
if (eop) break;
|
if (eop) break;
|
||||||
end
|
end
|
||||||
@(posedge clk);
|
@(posedge i_clk);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface if_ram # (
|
||||||
|
parameter RAM_WIDTH = 32,
|
||||||
|
parameter RAM_DEPTH = 128
|
||||||
|
)(
|
||||||
|
input i_clk, i_rst
|
||||||
|
);
|
||||||
|
|
||||||
|
logic [$clog2(RAM_DEPTH)-1:0] a;
|
||||||
|
logic en;
|
||||||
|
logic we;
|
||||||
|
logic re;
|
||||||
|
logic [RAM_WIDTH-1:0 ] d, q;
|
||||||
|
|
||||||
|
modport sink (input a, en, re, we, d, i_clk, i_rst, output q);
|
||||||
|
modport source (output a, en, re, we, d, input q, i_clk, i_rst, import task reset_source());
|
||||||
|
|
||||||
|
// Task to reset a source interface signals to all 0
|
||||||
|
task reset_source();
|
||||||
|
a <= 0;
|
||||||
|
en <= 0;
|
||||||
|
we <= 0;
|
||||||
|
re <= 0;
|
||||||
|
d <= 0;
|
||||||
|
endtask
|
||||||
|
|
||||||
endinterface
|
endinterface
|
|
@ -1,12 +1,49 @@
|
||||||
|
|
||||||
// Xilinx True Dual Port RAM, No Change, Dual Clock
|
// Xilinx True Dual Port RAM, No Change, Dual Clock.
|
||||||
|
// Added wrapper to use intefaces.
|
||||||
// This code implements a parameterizable true dual port memory (both ports can read and write).
|
// This code implements a parameterizable true dual port memory (both ports can read and write).
|
||||||
// This is a no change RAM which retains the last read value on the output during writes
|
// This is a no change RAM which retains the last read value on the output during writes
|
||||||
// which is the most power efficient mode.
|
// which is the most power efficient mode.
|
||||||
// If a reset or enable is not necessary, it may be tied off or removed from the code.
|
// If a reset or enable is not necessary, it may be tied off or removed from the code.
|
||||||
|
|
||||||
//module xilinx_true_dual_port_no_change_2_clock_ram #(
|
|
||||||
module bram #(
|
module bram #(
|
||||||
|
parameter RAM_WIDTH = 18,
|
||||||
|
parameter RAM_DEPTH = 1024,
|
||||||
|
parameter RAM_PERFORMANCE = "HIGH_PERFORMANCE",
|
||||||
|
parameter INIT_FILE = ""
|
||||||
|
) (
|
||||||
|
if_ram.sink a,
|
||||||
|
if_ram.sink b
|
||||||
|
);
|
||||||
|
|
||||||
|
xilinx_true_dual_port_no_change_2_clock_ram #(
|
||||||
|
.RAM_WIDTH(RAM_WIDTH), // Specify RAM data width
|
||||||
|
.RAM_DEPTH(RAM_DEPTH), // Specify RAM depth (number of entries)
|
||||||
|
.RAM_PERFORMANCE(RAM_PERFORMANCE), // Select "HIGH_PERFORMANCE" or "LOW_LATENCY"
|
||||||
|
.INIT_FILE(INIT_FILE) // Specify name/location of RAM initialization file if using one (leave blank if not)
|
||||||
|
) your_instance_name (
|
||||||
|
.addra(a.a), // Port A address bus, width determined from RAM_DEPTH
|
||||||
|
.addrb(b.a), // Port B address bus, width determined from RAM_DEPTH
|
||||||
|
.dina(a.d), // Port A RAM input data, width determined from RAM_WIDTH
|
||||||
|
.dinb(b.d), // Port B RAM input data, width determined from RAM_WIDTH
|
||||||
|
.clka(a.i_clk), // Port A clock
|
||||||
|
.clkb(b.i_clk), // Port B clock
|
||||||
|
.wea(a.we), // Port A write enable
|
||||||
|
.web(b.we), // Port B write enable
|
||||||
|
.ena(a.en), // Port A RAM Enable, for additional power savings, disable port when not in use
|
||||||
|
.enb(b.en), // Port B RAM Enable, for additional power savings, disable port when not in use
|
||||||
|
.rsta(a.i_rst), // Port A output reset (does not affect memory contents)
|
||||||
|
.rstb(b.i_rst), // Port B output reset (does not affect memory contents)
|
||||||
|
.regcea(a.re), // Port A output register enable
|
||||||
|
.regceb(b.re), // Port B output register enable
|
||||||
|
.douta(a.q), // Port A RAM output data, width determined from RAM_WIDTH
|
||||||
|
.doutb(b.q) // Port B RAM output data, width determined from RAM_WIDTH
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
|
||||||
|
module xilinx_true_dual_port_no_change_2_clock_ram #(
|
||||||
parameter RAM_WIDTH = 18, // Specify RAM data width
|
parameter RAM_WIDTH = 18, // Specify RAM data width
|
||||||
parameter RAM_DEPTH = 1024, // Specify RAM depth (number of entries)
|
parameter RAM_DEPTH = 1024, // Specify RAM depth (number of entries)
|
||||||
parameter RAM_PERFORMANCE = "HIGH_PERFORMANCE", // Select "HIGH_PERFORMANCE" or "LOW_LATENCY"
|
parameter RAM_PERFORMANCE = "HIGH_PERFORMANCE", // Select "HIGH_PERFORMANCE" or "LOW_LATENCY"
|
||||||
|
@ -103,31 +140,3 @@ module bram #(
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
// The following is an instantiation template for xilinx_true_dual_port_no_change_2_clock_ram
|
|
||||||
/*
|
|
||||||
// Xilinx True Dual Port RAM, No Change, Dual Clock
|
|
||||||
xilinx_true_dual_port_no_change_2_clock_ram #(
|
|
||||||
.RAM_WIDTH(18), // Specify RAM data width
|
|
||||||
.RAM_DEPTH(1024), // Specify RAM depth (number of entries)
|
|
||||||
.RAM_PERFORMANCE("HIGH_PERFORMANCE"), // Select "HIGH_PERFORMANCE" or "LOW_LATENCY"
|
|
||||||
.INIT_FILE("") // Specify name/location of RAM initialization file if using one (leave blank if not)
|
|
||||||
) your_instance_name (
|
|
||||||
.addra(addra), // Port A address bus, width determined from RAM_DEPTH
|
|
||||||
.addrb(addrb), // Port B address bus, width determined from RAM_DEPTH
|
|
||||||
.dina(dina), // Port A RAM input data, width determined from RAM_WIDTH
|
|
||||||
.dinb(dinb), // Port B RAM input data, width determined from RAM_WIDTH
|
|
||||||
.clka(clka), // Port A clock
|
|
||||||
.clkb(clkb), // Port B clock
|
|
||||||
.wea(wea), // Port A write enable
|
|
||||||
.web(web), // Port B write enable
|
|
||||||
.ena(ena), // Port A RAM Enable, for additional power savings, disable port when not in use
|
|
||||||
.enb(enb), // Port B RAM Enable, for additional power savings, disable port when not in use
|
|
||||||
.rsta(rsta), // Port A output reset (does not affect memory contents)
|
|
||||||
.rstb(rstb), // Port B output reset (does not affect memory contents)
|
|
||||||
.regcea(regcea), // Port A output register enable
|
|
||||||
.regceb(regceb), // Port B output register enable
|
|
||||||
.douta(douta), // Port A RAM output data, width determined from RAM_WIDTH
|
|
||||||
.doutb(doutb) // Port B RAM output data, width determined from RAM_WIDTH
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ initial begin
|
||||||
o_done = 0;
|
o_done = 0;
|
||||||
o_axi.reset_source();
|
o_axi.reset_source();
|
||||||
sop_l = 0;
|
sop_l = 0;
|
||||||
while (!i_start) @(posedge o_axi.clk);
|
while (!i_start) @(posedge o_axi.i_clk);
|
||||||
|
|
||||||
fp = $fopen(i_file, BINARY ? "rb" : "r");
|
fp = $fopen(i_file, BINARY ? "rb" : "r");
|
||||||
if (fp==0) $fatal(1, "%m %t ERROR: file_to_axi could not open file %s", $time, i_file);
|
if (fp==0) $fatal(1, "%m %t ERROR: file_to_axi could not open file %s", $time, i_file);
|
||||||
|
@ -60,8 +60,8 @@ initial begin
|
||||||
o_axi.eop = $feof(fp);
|
o_axi.eop = $feof(fp);
|
||||||
o_axi.mod = $feof(fp) ? r : 0;
|
o_axi.mod = $feof(fp) ? r : 0;
|
||||||
|
|
||||||
@(posedge o_axi.clk);
|
@(posedge o_axi.i_clk);
|
||||||
while (!(o_axi.val && o_axi.rdy)) @(posedge o_axi.clk);
|
while (!(o_axi.val && o_axi.rdy)) @(posedge o_axi.i_clk);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,9 @@
|
||||||
|
|
||||||
module zcash_verif_equihash
|
module zcash_verif_equihash
|
||||||
import zcash_verif_pkg::*;
|
import zcash_verif_pkg::*;
|
||||||
(
|
#(
|
||||||
|
parameter DAT_BYTS = 8
|
||||||
|
)(
|
||||||
input i_clk, i_rst,
|
input i_clk, i_rst,
|
||||||
|
|
||||||
if_axi_stream.sink i_axi,
|
if_axi_stream.sink i_axi,
|
||||||
|
@ -32,65 +34,227 @@ module zcash_verif_equihash
|
||||||
output logic o_mask_val
|
output logic o_mask_val
|
||||||
);
|
);
|
||||||
|
|
||||||
|
localparam [7:0] EQUIHASH_GEN_BYTS = $bits(equihash_gen_in_t)/8;
|
||||||
|
localparam DAT_BITS = DAT_BYTS*8;
|
||||||
|
|
||||||
cblockheader_t cblockheader;
|
cblockheader_t cblockheader;
|
||||||
logic [COLLISION_BIT_LEN-1:0] sol_hash_xor;
|
logic cblockheader_val;
|
||||||
|
logic [$clog2($bits(cblockheader_t)/8)-1:0] cblockheader_byts;
|
||||||
|
|
||||||
|
equihash_gen_in_t equihash_gen_in;
|
||||||
|
logic [N-1:0] sol_hash_xor;
|
||||||
|
logic [$clog2(SOL_LIST_LEN)-1:0] sol_cnt_out, sol_cnt_in; // This tracks how many solutions we have XORed
|
||||||
|
logic [$clog2(DAT_BITS)-1:0] sol_pos; // This tracks the pos in our DAT_BITS RAM output
|
||||||
|
logic [SOL_BITS-1:0] ram_out;
|
||||||
|
|
||||||
logic [64*8-1:0] parameters;
|
logic [64*8-1:0] parameters;
|
||||||
logic [7:0] byte_len;
|
logic [7:0] byte_len;
|
||||||
|
logic all_checks_done;
|
||||||
|
|
||||||
if_axi_stream #(.DAT_BYTS(INDICIES_PER_HASH * N), .CTL_BYTS(1)) blake2b_out_hash(clk);
|
|
||||||
if_axi_stream #(.DAT_BYTS(EQUIHASH_BLAKE2B_PIPE == 0 ? 128 : $bits(equihash_gen_in_t)/8 )) blake2b_in_hash(clk);
|
|
||||||
|
|
||||||
|
|
||||||
|
if_axi_stream #(.DAT_BYTS(BLAKE2B_DIGEST_BYTS), .CTL_BYTS(1)) blake2b_out_hash(i_clk);
|
||||||
|
if_axi_stream #(.DAT_BYTS(EQUIHASH_BLAKE2B_PIPE == 0 ? 128 : EQUIHASH_GEN_BYTS )) blake2b_in_hash(i_clk);
|
||||||
|
|
||||||
|
// We write the block into a port as it comes in and then read from the b port
|
||||||
|
if_ram #(.RAM_WIDTH(DAT_BITS), .RAM_DEPTH(SOL_LIST_BYTS/DAT_BYTS)) equihash_sol_bram_if_a (i_clk, i_rst);
|
||||||
|
if_ram #(.RAM_WIDTH(DAT_BITS), .RAM_DEPTH(SOL_LIST_BYTS/DAT_BYTS)) equihash_sol_bram_if_b (i_clk, i_rst);
|
||||||
|
logic [DAT_BITS-1:0] equihash_sol_bram_if_b_l;
|
||||||
|
|
||||||
|
enum {STATE_IDLE = 0,
|
||||||
|
STATE_DATA_WRITE = 1,
|
||||||
|
STATE_FINISH_WAIT = 2} ram_state;
|
||||||
|
|
||||||
|
// State machine for controlling writing equihash solution into the RAM and registering the header
|
||||||
always_ff @ (posedge i_clk) begin
|
always_ff @ (posedge i_clk) begin
|
||||||
if (i_rst) begin
|
if (i_rst) begin
|
||||||
i_axi.rdy <= 0;
|
i_axi.rdy <= 0;
|
||||||
|
equihash_sol_bram_if_a.reset_source();
|
||||||
|
cblockheader <= 0;
|
||||||
|
cblockheader_byts <= 0;
|
||||||
|
cblockheader_val <= 0;
|
||||||
|
ram_state <= STATE_IDLE;
|
||||||
|
end else begin
|
||||||
|
// Defaults
|
||||||
|
equihash_sol_bram_if_a.we <= 1;
|
||||||
|
equihash_sol_bram_if_a.en <= 1;
|
||||||
|
equihash_sol_bram_if_a.d <= i_axi.dat;
|
||||||
|
|
||||||
|
if (i_axi.val && i_axi.rdy && ~cblockheader_val) begin
|
||||||
|
cblockheader <= {cblockheader, i_axi.dat};
|
||||||
|
cblockheader_val <= (cblockheader_byts + DAT_BYTS) >= $bits(cblockheader_t)/8;
|
||||||
|
cblockheader_byts <= cblockheader_byts + DAT_BYTS;
|
||||||
|
end
|
||||||
|
|
||||||
|
case (ram_state)
|
||||||
|
// This state we are waiting for an input block
|
||||||
|
STATE_IDLE: begin
|
||||||
|
i_axi.rdy <= 1;
|
||||||
|
if (i_axi.val && i_axi.rdy) begin
|
||||||
|
ram_state <= STATE_DATA_WRITE;
|
||||||
|
equihash_sol_bram_if_a.a <= equihash_sol_bram_if_a.a + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
// Here we are checking header values as well as populating the RAM
|
||||||
|
STATE_DATA_WRITE: begin
|
||||||
|
if (i_axi.val && i_axi.rdy) begin
|
||||||
|
equihash_sol_bram_if_a.a <= equihash_sol_bram_if_a.a + 1;
|
||||||
|
if (i_axi.eop) begin
|
||||||
|
i_axi.rdy <= 0;
|
||||||
|
ram_state <= STATE_FINISH_WAIT;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
// Here we are have finished populating RAM and waiting for all checks to finish
|
||||||
|
STATE_FINISH_WAIT: begin
|
||||||
|
equihash_sol_bram_if_a.we <= 0;
|
||||||
|
equihash_sol_bram_if_a.a <= equihash_sol_bram_if_a.a;
|
||||||
|
if (all_checks_done) begin
|
||||||
|
ram_state <= STATE_IDLE;
|
||||||
|
i_axi.rdy <= 1;
|
||||||
|
cblockheader_val <= 0;
|
||||||
|
equihash_sol_bram_if_a.a <= 0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// State machine for controlling the hash calculation
|
||||||
|
// and checking the header values
|
||||||
|
always_ff @ (posedge i_clk) begin
|
||||||
|
if (i_rst) begin
|
||||||
o_mask_val <= 0;
|
o_mask_val <= 0;
|
||||||
o_mask <= 0;
|
o_mask <= 0;
|
||||||
sol_hash_xor <= 0;
|
sol_hash_xor <= 0;
|
||||||
blake2b_in_hash.reset_source();
|
blake2b_in_hash.reset_source();
|
||||||
blake2b_out_hash.rdy <= 0;
|
blake2b_out_hash.rdy <= 0;
|
||||||
|
equihash_sol_bram_if_b.reset_source();
|
||||||
|
all_checks_done <= 0;
|
||||||
|
sol_cnt_in <= 0;
|
||||||
|
sol_cnt_out <= 0;
|
||||||
|
sol_pos <= 0;
|
||||||
|
equihash_sol_bram_if_b_l <= 0;
|
||||||
end else begin
|
end else begin
|
||||||
|
// Defaults
|
||||||
|
equihash_sol_bram_if_b.re <= 1;
|
||||||
|
equihash_sol_bram_if_b.en <= 1;
|
||||||
blake2b_out_hash.rdy <= 1;
|
blake2b_out_hash.rdy <= 1;
|
||||||
i_axi.rdy <= 1;
|
blake2b_in_hash.sop <= 1;
|
||||||
|
blake2b_in_hash.eop <= 1;
|
||||||
|
blake2b_in_hash.val <= 0;
|
||||||
|
|
||||||
|
if (ram_state == STATE_IDLE) begin
|
||||||
|
equihash_sol_bram_if_b.a <= $bits(cblockheader_t)/DAT_BITS;
|
||||||
|
sol_pos <= $bits(cblockheader_t) % DAT_BITS;
|
||||||
|
sol_cnt_out <= 0;
|
||||||
|
sol_cnt_in <= 0;
|
||||||
|
blake2b_in_hash.val <= 0;
|
||||||
|
o_mask_val <= 0;
|
||||||
|
o_mask <= 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (cblockheader_val) begin
|
||||||
|
equihash_gen_in.bits <= cblockheader.bits;
|
||||||
|
equihash_gen_in.my_time <= cblockheader.my_time;
|
||||||
|
equihash_gen_in.hash_reserved <= 0;
|
||||||
|
equihash_gen_in.hash_merkle_root <= cblockheader.hash_merkle_root;
|
||||||
|
equihash_gen_in.hash_prev_block <= cblockheader.hash_prev_block;
|
||||||
|
equihash_gen_in.version <= cblockheader.version;
|
||||||
|
equihash_gen_in.nonce <= cblockheader.nonce;
|
||||||
|
for (int i = 0; i < SOL_BITS; i++)
|
||||||
|
if (i + sol_pos >= DAT_BITS)
|
||||||
|
equihash_gen_in.index[i] <= equihash_sol_bram_if_b_l[i + sol_pos - DAT_BITS];
|
||||||
|
else
|
||||||
|
equihash_gen_in.index[i] <= equihash_sol_bram_if_b.q[i+sol_pos];
|
||||||
|
end
|
||||||
|
|
||||||
|
// We can start loading the hash block
|
||||||
|
if((sol_cnt_in < SOL_LIST_LEN - 1) &&
|
||||||
|
blake2b_in_hash.rdy &&
|
||||||
|
(equihash_sol_bram_if_a.a >= $bits(cblockheader_t)/8 + DAT_BYTS)) begin
|
||||||
|
blake2b_in_hash.val <= 1; // TODO control if we take more than one hash per clock
|
||||||
|
sol_cnt_in <= sol_cnt_in + 1;
|
||||||
|
sol_pos <= sol_pos + SOL_BITS;
|
||||||
|
// Calculate if we should increase our read pointer
|
||||||
|
if (sol_pos + 2*SOL_BITS >= DAT_BITS) begin
|
||||||
|
equihash_sol_bram_if_b_l <= equihash_sol_bram_if_b.q; // Latch current output as we might need some bits
|
||||||
|
equihash_sol_bram_if_b.a <= equihash_sol_bram_if_b.a + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
//TODO here we also need to check the ordering, and duplicates?
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// When we start getting the hash results, start XORing them
|
||||||
|
if (blake2b_out_hash.val) begin
|
||||||
|
sol_hash_xor <= hash_solution(sol_hash_xor, blake2b_out_hash.dat);
|
||||||
|
sol_cnt_out <= sol_cnt_out + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (sol_cnt_out == SOL_LIST_LEN - 1) begin
|
||||||
|
o_mask.XOR_FAIL <= |sol_hash_xor;
|
||||||
|
o_mask_val <= 1;
|
||||||
|
sol_cnt_out <= sol_cnt_out;
|
||||||
|
equihash_sol_bram_if_b.a <= 0;
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// Constants that do not change
|
// Constants
|
||||||
always_comb begin
|
always_comb begin
|
||||||
byte_len = $bits(equihash_gen_in_t)/8;
|
parameters = {'0, 8'd1, 8'd1, 8'd0, BLAKE2B_DIGEST_BYTS};
|
||||||
parameters = {'0, 8'd1, 8'd1, 8'd0, byte_len};
|
|
||||||
parameters[48*8-1 +: 16*8] = POW_TAG;
|
parameters[48*8-1 +: 16*8] = POW_TAG;
|
||||||
|
blake2b_in_hash.dat = equihash_gen_in;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// Function to OR the hash output depending on equihash parameters
|
||||||
|
function hash_solution(input [N-1:0] curr, input [N*INDICIES_PER_HASH-1:0] in);
|
||||||
|
for (int i = 0; i < INDICIES_PER_HASH; i++)
|
||||||
|
curr = curr ^ in[i*N +: N];
|
||||||
|
return curr;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
// Instantiate the Blake2b block
|
||||||
generate if ( EQUIHASH_BLAKE2B_PIPE == 0 ) begin: BLAKE2B_GEN
|
generate if ( EQUIHASH_BLAKE2B_PIPE == 0 ) begin: BLAKE2B_GEN
|
||||||
blake2b_top DUT (
|
blake2b_top DUT (
|
||||||
.i_clk ( i_clk ),
|
.i_clk ( i_clk ),
|
||||||
.i_rst ( i_rst ),
|
.i_rst ( i_rst ),
|
||||||
.i_parameters ( parameters ),
|
.i_parameters ( parameters ),
|
||||||
.i_byte_len ( byte_len ),
|
.i_byte_len ( EQUIHASH_GEN_BYTS ),
|
||||||
.i_block ( blake2b_in_hash ),
|
.i_block ( blake2b_in_hash ),
|
||||||
.o_hash ( blake2b_out_hash )
|
.o_hash ( blake2b_out_hash )
|
||||||
);
|
);
|
||||||
end else begin
|
end else begin
|
||||||
blake2b_pipe_top #(
|
blake2b_pipe_top #(
|
||||||
.MSG_LEN ( $bits(equihash_gen_in_t)/8 ),
|
.MSG_LEN ( EQUIHASH_GEN_BYTS ),
|
||||||
|
.MSG_VAR_BYTS ( 4 ), // Only lower 4 bytes of input to hash change
|
||||||
.CTL_BITS ( 8 )
|
.CTL_BITS ( 8 )
|
||||||
)
|
)
|
||||||
DUT (
|
DUT (
|
||||||
.i_clk ( i_clk ),
|
.i_clk ( i_clk ),
|
||||||
.i_rst ( i_rst ),
|
.i_rst ( i_rst ),
|
||||||
.i_parameters ( parameters ),
|
.i_parameters ( parameters ),
|
||||||
.i_byte_len ( byte_len ),
|
.i_byte_len ( EQUIHASH_GEN_BYTS ),
|
||||||
.i_block ( blake2b_in_hash ),
|
.i_block ( blake2b_in_hash ),
|
||||||
.o_hash ( blake2b_out_hash )
|
.o_hash ( blake2b_out_hash )
|
||||||
);
|
);
|
||||||
end
|
end
|
||||||
endgenerate
|
endgenerate
|
||||||
|
|
||||||
// Some checks to make sure our data structures are correct:
|
// Memory to store the equihash solution as it comes in. We use dual port,
|
||||||
|
// one port for writing and one port for reading
|
||||||
|
bram #(
|
||||||
|
.RAM_WIDTH ( DAT_BITS ),
|
||||||
|
.RAM_DEPTH ( SOL_LIST_BYTS/DAT_BYTS ),
|
||||||
|
.RAM_PERFORMANCE ( "LOW_LATENCY" ) // Select "HIGH_PERFORMANCE" or "LOW_LATENCY"
|
||||||
|
) equihash_sol_bram (
|
||||||
|
.a ( equihash_sol_bram_if_a ),
|
||||||
|
.b ( equihash_sol_bram_if_b )
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Some checks to make sure our data structures are correct:
|
||||||
initial begin
|
initial begin
|
||||||
assert ($bits(equihash_gen_in_t)/8 == 144) else $fatal(1, "%m %t ERROR: equihash_gen_in_t is not 144 bytes in size", $time);
|
assert ($bits(equihash_gen_in_t)/8 == 144) else $fatal(1, "%m %t ERROR: equihash_gen_in_t is not 144 bytes in size", $time);
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,9 +22,10 @@ package zcash_verif_pkg;
|
||||||
// Variables used in the equihash PoW
|
// Variables used in the equihash PoW
|
||||||
parameter [31:0] N = 200;
|
parameter [31:0] N = 200;
|
||||||
parameter [31:0] K = 9;
|
parameter [31:0] K = 9;
|
||||||
parameter EQUIHASH_BLAKE2B_PIPE = 0; // Do we use the pipelined (high performance but large area) Blake2b core
|
parameter EQUIHASH_BLAKE2B_PIPE = 1; // Do we use the pipelined (high performance but large area) Blake2b core
|
||||||
parameter INDICIES_PER_HASH = (512/N);
|
parameter INDICIES_PER_HASH = (512/N);
|
||||||
parameter COLLISION_BIT_LEN = N/(K+1);
|
parameter COLLISION_BIT_LEN = N/(K+1);
|
||||||
|
parameter BLAKE2B_DIGEST_BYTS = (N*INDICIES_PER_HASH)/8;
|
||||||
parameter SOL_BITS = COLLISION_BIT_LEN+1;
|
parameter SOL_BITS = COLLISION_BIT_LEN+1;
|
||||||
parameter SOL_LIST_LEN = 1 << K;
|
parameter SOL_LIST_LEN = 1 << K;
|
||||||
parameter SOL_LIST_BYTS = SOL_LIST_LEN*SOL_BITS/8;
|
parameter SOL_LIST_BYTS = SOL_LIST_LEN*SOL_BITS/8;
|
||||||
|
@ -48,10 +49,13 @@ package zcash_verif_pkg;
|
||||||
logic [31:0] version;
|
logic [31:0] version;
|
||||||
} equihash_gen_in_t;
|
} equihash_gen_in_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
logic [SOL_LIST_LEN-1:0][SOL_BITS-1:0] sol;
|
||||||
|
logic [3*8-1:0] size; // Contains size of solution array - should be 1347 for (200,9)
|
||||||
|
} equihash_sol_t;
|
||||||
|
|
||||||
// Header format for block header (CBlockheader)
|
// Header format for block header (CBlockheader)
|
||||||
typedef struct packed {
|
typedef struct packed {
|
||||||
equihash_sol_t equihash_sol_t;
|
|
||||||
logic [255:0] nonce;
|
logic [255:0] nonce;
|
||||||
logic [31:0] bits;
|
logic [31:0] bits;
|
||||||
logic [31:0] my_time;
|
logic [31:0] my_time;
|
||||||
|
@ -61,10 +65,11 @@ package zcash_verif_pkg;
|
||||||
logic [31:0] version;
|
logic [31:0] version;
|
||||||
} cblockheader_t;
|
} cblockheader_t;
|
||||||
|
|
||||||
|
// Header format for block header (CBlockheader) inc. solution
|
||||||
typedef struct packed {
|
typedef struct packed {
|
||||||
logic [SOL_LIST_LEN-1:0][SOL_BITS-1:0] sol;
|
equihash_sol_t equihash_sol;
|
||||||
logic [3*8-1:0] size; // Contains size of solution array - should be 1347 for (200,9)
|
cblockheader_t cblockheader;
|
||||||
} equihash_sol_t;
|
} cblockheader_sol_t;
|
||||||
|
|
||||||
|
|
||||||
endpackage
|
endpackage
|
Loading…
Reference in New Issue