Added code for hash map and checking for duplicated index in the

equihash solution.
This commit is contained in:
bsdevlin 2019-02-25 21:41:50 -05:00
parent 3cc1446afc
commit 39892b7732
6 changed files with 786 additions and 30 deletions

View File

@ -21,12 +21,13 @@
interface if_axi_stream # (
parameter DAT_BYTS = 8,
parameter CTL_BYTS = 1
parameter DAT_BITS = DAT_BYTS*8,
parameter CTL_BYTS = 1,
parameter CTL_BITS = CTL_BYTS*8,
parameter MOD_BITS = $clog2(DAT_BYTS)
)(
input i_clk
);
localparam DAT_BITS = DAT_BYTS*8;
localparam CTL_BITS = CTL_BYTS*8;
logic rdy;
logic val;
@ -35,7 +36,7 @@ interface if_axi_stream # (
logic eop;
logic [CTL_BITS-1:0] ctl;
logic [DAT_BITS-1:0] dat;
logic [$clog2(DAT_BYTS)-1:0] mod;
logic [MOD_BITS-1:0] mod;
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, i_clk, import task reset_source());

View File

@ -20,34 +20,32 @@
*/
module axi_stream_fifo #(
parameter DEPTH,
parameter SIZE,
parameter DAT_BITS,
parameter MOD_BITS = $clog2(DAT_BITS/8),
parameter CTL_BITS
) (
input i_clk, i_rst,
if_axi_stream.sink i_axi,
if_axi_stream.source o_axi
if_axi_stream.source o_axi,
output logic o_full,
output logic o_emp
);
localparam MOD_BITS = $clog2(DAT_BITS/8);
logic [$clog2(SIZE)-1:0] rd_ptr, wr_ptr;
logic [$clog2(DEPTH):0] rd_ptr, wr_ptr;
logic empty, full;
logic [DEPTH-1:0][DAT_BITS + CTL_BITS + MOD_BITS + 3 -1:0] ram;
logic [SIZE-1:0][DAT_BITS + CTL_BITS + MOD_BITS + 3 -1:0] ram;
// Control for full and empty, and assigning outputs from the ram
always_comb begin
empty = (rd_ptr == wr_ptr);
full = (wr_ptr == rd_ptr - 1);
i_axi.rdy = ~full;
i_axi.rdy = ~o_full;
o_axi.dat = ram[rd_ptr][0 +: DAT_BITS];
o_axi.ctl = ram[rd_ptr][DAT_BITS +: CTL_BITS];
o_axi.mod = ram[rd_ptr][CTL_BITS+DAT_BITS +: MOD_BITS];
o_axi.sop = ram[rd_ptr][CTL_BITS+DAT_BITS+MOD_BITS +: 1];
o_axi.eop = ram[rd_ptr][CTL_BITS+DAT_BITS+MOD_BITS+1 +: 1];
o_axi.err = ram[rd_ptr][CTL_BITS+DAT_BITS+MOD_BITS+2 +: 1];
o_axi.val = ~empty;
o_axi.val = ~o_emp;
end
// Logic for writing and reading from ram without reset
@ -62,9 +60,26 @@ always_ff @ (posedge i_clk) begin
if (i_rst) begin
rd_ptr <= 0;
wr_ptr <= 0;
o_emp <= 1;
o_full <= 0;
end else begin
wr_ptr <= i_axi.val && i_axi.rdy ? (wr_ptr + 1) : wr_ptr;
rd_ptr <= o_axi.val && o_axi.rdy ? (rd_ptr + 1) : rd_ptr;
// Write and read
if (i_axi.val && i_axi.rdy && o_axi.val && o_axi.rdy) begin
wr_ptr <= (wr_ptr + 1) % SIZE;
rd_ptr <= (rd_ptr + 1) % SIZE;
// Write
end else if(~o_full && i_axi.val && i_axi.rdy) begin
o_emp <= 0;
wr_ptr <= (wr_ptr + 1) % SIZE;
if ((wr_ptr + 1) % SIZE == rd_ptr) o_full <= 1;
// Read
end else if (~o_emp && o_axi.val && o_axi.rdy) begin
o_full <= 0;
rd_ptr <= (rd_ptr + 1) % SIZE;
if ((rd_ptr + 1) % SIZE == wr_ptr) o_emp <= 1;
end
end
end

View File

@ -0,0 +1,80 @@
/*
This is combinatorial CRC module that generates a function to calculate CRC
based on parameters for input and output length, and polynomial function.
Can optionally be pipelined.
Default parameters are for CRC-32 (0x04C11DB7)
To get uniformly distributed keys it is important to use the upper bits
as the output into the hash function.
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
module crc #(
parameter IN_BITS = 8,
parameter OUT_BITS = 4,
parameter [OUT_BITS-1:0] POLYNOMIAL = 'b1110, // = x^4 + x + 1
parameter PIPELINE = 0
)(
input i_clk, i_rst,
input [IN_BITS-1:0] in,
output logic [OUT_BITS-1:0] out
);
generate
if (PIPELINE) begin: GEN_PIPELINE
always_ff @ (posedge i_clk) begin
if (i_rst) begin
out <= 0;
end else begin
out <= crc(in, {IN_BITS{1'd1}});
end
end
end else begin
always_comb out = crc(in, {IN_BITS{1'd1}});
end
endgenerate
function [OUT_BITS-1:0] crc(input logic [IN_BITS-1:0] in, last_crc);
// For each bit we determine the equation based on input and last_crc
// Each bit gets an equation as we loop through the input bits
// In the end we will have an equation where each out bit is a XOR
// of inputs and outbits
logic [IN_BITS-1:0] d_in;
for (int i = IN_BITS-1; i >= 0; i--) begin
// And then we loop though each of the CRC bits
d_in = in[i] ^ last_crc[OUT_BITS-1];
for (int j = OUT_BITS - 1; j >= 0; j--) begin
if (POLYNOMIAL[j]) begin
last_crc[j] = (j == 0) ? d_in : last_crc[j-1] ^ d_in;
end else begin
last_crc[j] = last_crc[j-1];
end
end
end
crc = last_crc;
endfunction
// Some checks to make sure our parameters are correct:
initial begin
assert (OUT_BITS <= IN_BITS) else $fatal(1, "%m %t ERROR: OUT_BITS must be less than or equal to IN_BITS", $time);
assert (POLYNOMIAL[0]) else $fatal(1, "%m %t ERROR: Bit 0 of polynomial must be 1", $time);
end
endmodule

View File

@ -0,0 +1,466 @@
/*
This is a parameterizable hash map implementation using the a CRC function
as the hash.
Internally we use a main memory for collisions and then a linked list
to store items when they collide.
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
module hash_map #(
parameter KEY_BITS = 32, // How many bits is our key
parameter DAT_BITS = 8, // Each key maps to data
parameter HASH_MEM_SIZE = 8, // The size of the first level memory, this will define how many bits of the output hash function we use
parameter LL_MEM_SIZE = 8 // The size of the linked list memory that is used in case of collisions
)(
input i_clk, i_rst,
input [KEY_BITS-1:0] i_key,
input i_val,
input [DAT_BITS-1:0] i_dat,
input [1:0] i_opcode, // 0 = Lookup, 1 = Add, 2 = Delete
output logic o_rdy,
output logic [DAT_BITS-1:0] o_dat, // Valid after lookup
output logic o_val,
output logic o_fnd, // Will be high if adding a key and it already exists (old data overwritten)
// or for a lookup if key was found.
// To clear memory
input i_cfg_clr,
// When linked list memory is full this will be high
output logic o_cfg_full
);
parameter CRC_IN_BITS = 32;
logic [CRC_IN_BITS-1:0] key;
logic [DAT_BITS-1:0] dat;
logic [$clog2(HASH_MEM_SIZE)-1: 0] hash_out, prev_coll_addr;
logic [$clog2(LL_MEM_SIZE)-1:0] prev_ll_addr;
// Two memories make the hash table, a collision memory and a linked list memory
typedef struct packed {
logic [KEY_BITS-1:0] key;
logic [DAT_BITS-1:0] dat;
logic [$clog2(LL_MEM_SIZE)-1:0] nxt_ptr;
logic used;
} hash_map_node_t;
logic free_mem_fifo_emp, pre_node_coll;
if_axi_stream #(.DAT_BITS($clog2(LL_MEM_SIZE)), .CTL_BITS(1), .MOD_BITS(1)) free_mem_fifo_if_in(i_clk);
if_axi_stream #(.DAT_BITS($clog2(LL_MEM_SIZE)), .CTL_BITS(1), .MOD_BITS(1)) free_mem_fifo_if_out(i_clk);
if_ram #(.RAM_WIDTH($bits(hash_map_node_t)), .RAM_DEPTH(HASH_MEM_SIZE)) coll_bram_if_a (i_clk, i_rst);
if_ram #(.RAM_WIDTH($bits(hash_map_node_t)), .RAM_DEPTH(HASH_MEM_SIZE)) coll_bram_if_b (i_clk, i_rst);
logic [$clog2(HASH_MEM_SIZE)-1:0] coll_bram_if_a_cfg;
if_ram #(.RAM_WIDTH($bits(hash_map_node_t)), .RAM_DEPTH(LL_MEM_SIZE)) ll_bram_if_a (i_clk, i_rst);
if_ram #(.RAM_WIDTH($bits(hash_map_node_t)), .RAM_DEPTH(LL_MEM_SIZE)) ll_bram_if_b (i_clk, i_rst);
hash_map_node_t coll_node_rd, ll_node_rd, coll_node_wr, ll_node_wr;
logic free_mem_fifo_rst, free_mem_loaded, coll_ram_rst;
logic [1:0] ll_ram_wait, coll_ram_wait;
typedef enum
{STATE_IDLE = 0,
STATE_LOOPUP_COL = 1, // Lookup in first collision memory
STATE_LOOPUP_LL = 2, // Lookup in linked list
STATE_MODIFY_LL = 3,
STATE_RESET = 4} hash_state_t;
typedef enum
{OPCODE_LOOKUP = 0,
OPCODE_ADD = 1,
OPCODE_DELETE = 2} opcode_t;
opcode_t opcode;
hash_state_t hash_state, hash_state_prev;
// Counters that can be used for debug
logic [7:0] hash_collisions;
always_ff @ (posedge i_clk) begin
if (i_rst) begin
key <= 0;
dat <= 0;
opcode <= OPCODE_LOOKUP;
o_rdy <= 0;
o_val <= 0;
o_fnd <= 0;
o_dat <= 0;
o_cfg_full <= 0;
free_mem_loaded <= 0;
coll_ram_rst <= 0;
free_mem_fifo_if_in.reset_source();
free_mem_fifo_if_out.rdy <= 0;
free_mem_fifo_rst <= 0;
hash_state <= STATE_RESET; // We start in reset state because we need to load the free ram pointer list
hash_state_prev <= STATE_IDLE;
ll_ram_wait <= 0;
coll_ram_wait <= 0;
coll_bram_if_b.reset_source();
ll_bram_if_b.reset_source();
coll_bram_if_a.we <= 0;
ll_bram_if_a.we <= 0;
coll_bram_if_a.en <= 1;
ll_bram_if_a.en <= 1;
coll_node_wr <= 0;
ll_node_wr <= 0;
coll_bram_if_a_cfg <= 0;
hash_collisions <= 0;
prev_ll_addr <= 0;
prev_coll_addr <= 0;
pre_node_coll <= 0;
end else begin
hash_state_prev <= hash_state;
free_mem_fifo_rst <= 0;
coll_bram_if_a.we <= 0;
ll_bram_if_a.we <= 0;
coll_bram_if_a.re <= 1;
ll_bram_if_a.re <= 1;
o_cfg_full <= free_mem_fifo_emp; // If the LL FIFO is empty it means we have no more free nodes
free_mem_fifo_if_out.rdy <= 0;
free_mem_fifo_if_in.val <= 0;
ll_ram_wait <= ll_ram_wait << 1;
coll_ram_wait <= coll_ram_wait << 1;
o_val <= 0;
case (hash_state)
STATE_IDLE: begin
coll_bram_if_a_cfg <= hash_out;
o_rdy <= 1;
dat <= i_dat;
key <= i_key;
opcode <= opcode_t'(i_opcode);
if (o_rdy && i_val) begin
o_rdy <= 0;
hash_state <= STATE_LOOPUP_COL;
coll_ram_wait[0] <= 1;
end else if (i_cfg_clr) begin
o_rdy <= 0;
hash_state <= STATE_RESET;
end
end
STATE_LOOPUP_COL: begin
o_dat <= coll_node_rd.dat;
ll_bram_if_a.a <= coll_node_rd.nxt_ptr;
if (coll_ram_wait[1]) begin
prev_coll_addr <= coll_bram_if_a.a;
pre_node_coll <= 1;
case(opcode)
// Lookup
OPCODE_LOOKUP: begin
if (coll_node_rd.used == 0) begin
o_val <= 1;
o_rdy <= 1;
o_fnd <= 0;
hash_state <= STATE_IDLE;
end else if (coll_node_rd.used == 1 && coll_node_rd.key == key) begin
o_val <= 1;
o_fnd <= 1;
hash_state <= STATE_IDLE;
end else begin
ll_ram_wait[0] <= 1;
hash_state <= STATE_LOOPUP_LL;
end
end
// Add
OPCODE_ADD: begin
// Not used so can directly add here
if (coll_node_rd.used == 0) begin
coll_node_wr.used <= 1;
coll_node_wr.dat <= dat;
coll_node_wr.nxt_ptr <= 0;
coll_node_wr.key <= key;
coll_bram_if_a.we <= 1;
o_rdy <= 1;
o_val <= 1;
hash_state <= STATE_IDLE;
end else begin
// Need to use free memory location from FIFO - check we have space
if (free_mem_fifo_emp) begin
o_val <= 1;
hash_state <= STATE_IDLE;
end else begin
if (coll_node_rd.nxt_ptr == 0) begin
coll_node_wr <= coll_node_rd;
coll_node_wr.nxt_ptr <= free_mem_fifo_if_out.dat;
coll_bram_if_a.we <= 1;
ll_node_wr.used <= 1;
ll_node_wr.dat <= dat;
ll_node_wr.nxt_ptr <= 0;
ll_node_wr.key <= key;
ll_bram_if_a.a <= free_mem_fifo_if_out.dat;
free_mem_fifo_if_out.rdy <= 1;
ll_bram_if_a.we <= 1;
o_rdy <= 1;
o_val <= 1;
hash_state <= STATE_IDLE;
end else begin
ll_ram_wait[0] <= 1;
hash_state <= STATE_LOOPUP_LL;
hash_collisions <= hash_collisions + 1;
end
end
end
end
// Delete
OPCODE_DELETE: begin
if (coll_node_rd.used == 1 && coll_node_rd.key == key) begin
if (coll_node_rd.nxt_ptr != 0) begin
// Want to free the nxt_ptr and move it into the collision ram
free_mem_fifo_if_in.dat <= coll_node_rd.nxt_ptr;
free_mem_fifo_if_in.val <= 1;
coll_bram_if_a_cfg <= coll_bram_if_a.a;
ll_ram_wait[0] <= 1;
hash_state <= STATE_MODIFY_LL;
end else begin
coll_node_wr <= 0;
o_fnd <= 1;
o_val <= 1;
coll_bram_if_a.we <= 1;
o_rdy <= 1;
hash_state <= STATE_IDLE;
end
end else if (coll_node_rd.used == 0) begin
o_fnd <= 0;
o_val <= 1;
o_rdy <= 1;
hash_state <= STATE_IDLE;
end else begin
ll_ram_wait[0] <= 1;
hash_state <= STATE_LOOPUP_LL;
end
end
endcase
end
end
STATE_LOOPUP_LL: begin
// In this state we keep traversing memory until key to nxt_ptr is zero
o_dat <= ll_node_rd.dat;
ll_bram_if_a.a <= ll_node_rd.nxt_ptr;
if (ll_ram_wait[1]) begin
prev_ll_addr <= ll_bram_if_a.a;
pre_node_coll <= 0;
case(opcode)
// Lookup
OPCODE_LOOKUP: begin
if (ll_node_rd.nxt_ptr == 0 && ll_node_rd.key != key) begin
o_val <= 1;
o_rdy <= 1;
o_fnd <= 0;
hash_state <= STATE_IDLE;
end else if (ll_node_rd.key == key) begin
o_val <= 1;
o_fnd <= 1;
hash_state <= STATE_IDLE;
end else begin
ll_ram_wait[0] <= 1;
end
end
// Add
OPCODE_ADD: begin
// Pop a location from the FIFO and use its memory location as the next element
if (ll_node_rd.nxt_ptr == 0) begin
ll_node_wr.used <= 1;
ll_node_wr.dat <= dat;
ll_node_wr.nxt_ptr <= 0;
ll_node_wr.key <= key;
ll_bram_if_a.a <= free_mem_fifo_if_out.dat;
free_mem_fifo_if_out.rdy <= 1;
ll_bram_if_a.we <= 1;
o_rdy <= 1;
o_val <= 1;
hash_state <= STATE_IDLE;
end else begin
ll_ram_wait[0] <= 1;
end
end
// Delete
OPCODE_DELETE: begin
if (ll_node_rd.key == key) begin
// We need to travese backwards and set the nxt_ptr to this.nxt_ptr,
// and add this address back to the free memory pool
free_mem_fifo_if_in.dat <= ll_bram_if_a.a;
free_mem_fifo_if_in.val <= 1;
if (pre_node_coll)
coll_bram_if_a_cfg <= prev_coll_addr;
else
ll_bram_if_a.a <= prev_ll_addr;
ll_ram_wait[0] <= 1;
pre_node_coll <= pre_node_coll;
hash_state <= STATE_MODIFY_LL;
// Count not find the element
end else if (coll_node_rd.nxt_ptr == 0) begin
o_fnd <= 0;
o_val <= 1;
o_rdy <= 1;
hash_state <= STATE_IDLE;
end else begin
hash_state <= STATE_LOOPUP_LL;
end
end
endcase
end
end
// This state is used when we make a delete in the middle of the LL
STATE_MODIFY_LL: begin
hash_state_prev <= hash_state_prev;
if (ll_ram_wait[1]) begin
if (hash_state_prev == STATE_LOOPUP_COL) begin
coll_node_wr <= ll_node_rd;
coll_bram_if_a.we <= 1;
end else if (pre_node_coll) begin
coll_node_wr <= coll_node_rd;
coll_node_wr.nxt_ptr <= prev_ll_addr;
coll_bram_if_a.we <= 1;
end else begin
ll_node_wr <= ll_node_rd;
ll_node_wr.nxt_ptr <= prev_ll_addr;
ll_bram_if_a.we <= 1;
end
o_fnd <= 1;
o_val <= 1;
o_rdy <= 1;
hash_state <= STATE_IDLE;
end
end
// In this state we clear the free memory FIFO and re-load it
STATE_RESET: begin
o_rdy <= 0;
hash_collisions <= 0;
o_cfg_full <= 0;
// First clock we reset the fifo
if (hash_state_prev != STATE_RESET) begin
free_mem_fifo_rst <= 1;
free_mem_fifo_if_in.dat <= 1; // Need to reserve nxt_ptr = 0 as terminator
free_mem_fifo_if_out.rdy <= 0;
free_mem_loaded <= 0;
coll_ram_rst <= 0;
coll_node_wr.used <= 0;
coll_bram_if_a.we <= 1;
coll_bram_if_a_cfg <= 0;
end else begin
free_mem_fifo_if_in.val <= 0;
// Next we load the fifo with pointers
if (free_mem_fifo_if_in.rdy && ~free_mem_loaded) begin
free_mem_fifo_if_in.val <= 1;
if (free_mem_fifo_if_in.val) begin
free_mem_fifo_if_in.dat <= free_mem_fifo_if_in.dat + 1;
if (free_mem_fifo_if_in.dat == LL_MEM_SIZE-1) begin
free_mem_loaded <= 1;
free_mem_fifo_if_in.val <= 0;
end
end
end
// And clear collision memory
if (~coll_ram_rst) begin
coll_bram_if_a.we <= 1;
coll_bram_if_a_cfg <= coll_bram_if_a_cfg + 1;
coll_bram_if_a.we <= 1;
if (coll_bram_if_a_cfg == HASH_MEM_SIZE-1) begin
coll_ram_rst <= 1;
coll_bram_if_a.we <= 0;
end
end
if (free_mem_loaded && coll_ram_rst)
hash_state <= STATE_IDLE;
end
end
endcase
end
end
always_comb begin
coll_bram_if_a.a = (hash_state == STATE_IDLE) ? hash_out : coll_bram_if_a_cfg;
coll_node_rd = coll_bram_if_a.q;
ll_node_rd = ll_bram_if_a.q;
coll_bram_if_a.d = coll_node_wr;
ll_bram_if_a.d = ll_node_wr;
end
// Use CRC-32 as the hash function
crc #(
.IN_BITS ( CRC_IN_BITS ),
.OUT_BITS ( $clog2(HASH_MEM_SIZE) ),
.POLYNOMIAL ( 32'h04C11DB7 ),
.PIPELINE ( 0 )
)
crc_i (
.i_clk ( i_clk ),
.i_rst ( i_rst ),
.in ( (hash_state != STATE_IDLE
|| coll_bram_if_a.we ) ? key : i_key ),
.out ( hash_out )
);
// We store the free pointers in a FIFO
axi_stream_fifo #(
.SIZE ( LL_MEM_SIZE ),
.DAT_BITS ( $clog2(LL_MEM_SIZE) ),
.MOD_BITS ( 1 ),
.CTL_BITS ( 1 )
)
free_mem_fifo (
.i_clk ( i_clk ),
.i_rst ( free_mem_fifo_rst || i_rst ),
.i_axi ( free_mem_fifo_if_in ),
.o_axi ( free_mem_fifo_if_out ),
.o_full (),
.o_emp ( free_mem_fifo_emp )
);
// RAM to store first level collision
bram #(
.RAM_WIDTH ( $bits(hash_map_node_t) ),
.RAM_DEPTH ( HASH_MEM_SIZE ),
.RAM_PERFORMANCE ( "LOW_LATENCY" )
) coll_bram (
.a ( coll_bram_if_a ),
.b ( coll_bram_if_b )
);
// Spill over linked list memory
bram #(
.RAM_WIDTH ( $bits(hash_map_node_t) ),
.RAM_DEPTH ( LL_MEM_SIZE ),
.RAM_PERFORMANCE ( "LOW_LATENCY" )
) ll_bram (
.a ( ll_bram_if_a ),
.b ( ll_bram_if_b )
);
endmodule

View File

@ -0,0 +1,129 @@
/*
The hash_map testbench.
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
module hash_map_tb();
parameter KEY_BITS = 4;
parameter DAT_BITS = KEY_BITS;
parameter HASH_MEM_SIZE = 4;
parameter LL_MEM_SIZE = 8;
logic clk, rst;
logic [KEY_BITS-1:0] i_key;
logic i_val;
logic [DAT_BITS-1:0] i_dat;
logic o_rdy;
logic [DAT_BITS-1:0] o_dat;
logic o_val;
logic o_fnd;
logic [1:0] i_opcode;
// Configuration
logic i_cfg_clr, o_cfg_full;
initial begin
rst = 0;
#100ns rst = 1;
#100ns rst = 0;
end
initial begin
clk = 0;
forever #10ns clk = ~clk;
end
hash_map #(
.KEY_BITS ( KEY_BITS ),
.DAT_BITS ( DAT_BITS ),
.HASH_MEM_SIZE ( HASH_MEM_SIZE ),
.LL_MEM_SIZE ( LL_MEM_SIZE )
)
DUT (
.i_clk ( clk ),
.i_rst ( rst ),
.i_key ( i_key ),
.i_val ( i_val ),
.i_dat ( i_dat ),
.i_opcode ( i_opcode ),
.o_rdy ( o_rdy ),
.o_dat ( o_dat ),
.o_val ( o_val ),
.o_fnd ( o_fnd ),
.i_cfg_clr ( i_cfg_clr ),
.o_cfg_full ( o_cfg_full )
);
task add(input [KEY_BITS-1:0] key, [DAT_BITS-1:0] dat);
i_val = 0;
@(negedge clk);
i_opcode = 1;
i_key = key;
i_dat = dat;
i_val = 1;
@(posedge clk);
while (1) begin
if (o_rdy) break;
@(posedge clk);
end
@(negedge clk) i_val = 0;
while (!o_val) @(posedge clk);
endtask
task delete(input [KEY_BITS-1:0] key);
i_val = 0;
@(negedge clk);
i_opcode = 2;
i_key = key;
i_val = 1;
@(posedge clk);
while (1) begin
if (o_rdy) break;
@(posedge clk);
end
@(negedge clk) i_val = 0;
while (!o_val) @(posedge clk);
endtask
initial begin
i_opcode = 0;
i_key = 0;
i_val = 0;
i_cfg_clr = 0;
#200ns;
repeat (5) @(posedge clk);
for (int i = 0; i < 11; i++) begin
add(i,i);
@(posedge clk);
end
repeat (10) @(posedge clk);
// Try deleting from col and ll
delete(6);
delete(2);
add(6, 0);
add(2, 0);
end
endmodule

View File

@ -61,9 +61,14 @@ if_ram #(.RAM_WIDTH(EQUIHASH_SOL_BRAM_WIDTH), .RAM_DEPTH(EQUIHASH_SOL_BRAM_DEPTH
logic [DAT_BITS-1:0] equihash_sol_bram_if_b_l;
logic [2*DAT_BITS-1:0] equihash_sol_bram_if_b_l_comb, equihash_sol_bram_if_b_l_comb_flip;
logic [SOL_BITS-1:0] equihash_sol_index;
logic [SOL_BITS-1:0] equihash_sol_index, hash_map_in_dat;
logic [1:0] equihash_sol_bram_read;
logic hash_map_in_val, hash_map_out_rdy, hash_map_out_val, hash_map_out_fnd, hash_map_clr, hash_map_full;
logic sol_index_fifo_if_emp;
if_axi_stream #(.DAT_BITS(SOL_BITS), .CTL_BITS(1), .MOD_BITS(1)) sol_index_fifo_if_in(i_clk);
if_axi_stream #(.DAT_BITS(SOL_BITS), .CTL_BITS(1), .MOD_BITS(1)) sol_index_fifo_if_out(i_clk);
enum {STATE_WR_IDLE = 0,
STATE_WR_DATA = 1,
STATE_WR_WAIT = 2} ram_wr_state;
@ -74,7 +79,8 @@ enum {STATE_RD_IDLE = 0,
enum {STATE_CHK_IDLE = 0,
STATE_CHK_DATA = 1,
STATE_CHK_WAIT = 2} chk_state;
STATE_CHK_WAIT = 2,
STATE_CHK_DONE = 3} chk_state;
// State machine for controlling writing equihash solution into the RAM and registering the header
always_ff @ (posedge i_clk) begin
@ -100,7 +106,7 @@ always_ff @ (posedge i_clk) begin
case (ram_wr_state)
// This state we are waiting for an input block
STATE_WR_IDLE: begin
i_axi.rdy <= 1;
i_axi.rdy <= hash_map_out_rdy;
if (i_axi.val && i_axi.rdy) begin
ram_wr_state <= STATE_WR_DATA;
equihash_sol_bram_if_a.a <= 0;
@ -122,11 +128,11 @@ always_ff @ (posedge i_clk) begin
STATE_WR_WAIT: begin
equihash_sol_bram_if_a.we <= 0;
equihash_sol_bram_if_a.a <= equihash_sol_bram_if_a.a;
if (chk_state == STATE_CHK_WAIT) begin
if (chk_state == STATE_CHK_DONE) begin
ram_wr_state <= STATE_WR_IDLE;
i_axi.rdy <= 1;
cblockheader_val <= 0;
equihash_sol_bram_if_a.a <= 0;
equihash_sol_bram_if_a.a <= 0;
end
end
endcase
@ -164,8 +170,8 @@ always_ff @ (posedge i_clk) begin
sol_cnt_in <= 0;
blake2b_in_hash.val <= 0;
//First case has special state
if (equihash_sol_bram_if_a.a*DAT_BYTS >= ($bits(cblockheader_t)/8) + (DAT_BYTS*2)) begin
// First case has special state
if ( equihash_sol_bram_if_a.a*DAT_BYTS >= ($bits(cblockheader_t)/8) + (DAT_BYTS*2)) begin
if (~|equihash_sol_bram_read) begin
equihash_sol_bram_if_b.a <= equihash_sol_bram_if_b.a + 1;
equihash_sol_bram_read[0] <= 1;
@ -175,6 +181,7 @@ always_ff @ (posedge i_clk) begin
end
end
STATE_RD_DATA: begin
equihash_gen_in <= 0;
equihash_gen_in.bits <= cblockheader.bits;
equihash_gen_in.my_time <= cblockheader.my_time;
@ -193,7 +200,7 @@ always_ff @ (posedge i_clk) begin
equihash_sol_bram_if_b.a <= equihash_sol_bram_if_b.a + 1;
equihash_sol_bram_read[0] <= 1;
end
// Load input into Blake2b block
blake2b_in_hash.val <= 1;
sol_cnt_in <= sol_cnt_in + 1;
@ -205,7 +212,7 @@ always_ff @ (posedge i_clk) begin
end
end
STATE_RD_WAIT: begin
if (chk_state == STATE_CHK_WAIT) begin
if (chk_state == STATE_CHK_DONE) begin
ram_rd_state <= STATE_RD_IDLE;
end
end
@ -221,10 +228,17 @@ always_ff @ (posedge i_clk) begin
blake2b_out_hash.rdy <= 0;
sol_cnt_out <= 0;
chk_state <= STATE_CHK_IDLE;
hash_map_clr <= 0;
end else begin
// Defaults
blake2b_out_hash.rdy <= 1;
hash_map_clr <= 0;
sol_index_fifo_if_in.val <= blake2b_in_hash.val;
sol_index_fifo_if_in.dat <= equihash_sol_index;
if ( (hash_map_out_val && hash_map_out_fnd) || hash_map_full )
o_mask.DUPLICATE_FND <= 1;
case(chk_state)
STATE_CHK_IDLE: begin
@ -258,11 +272,18 @@ always_ff @ (posedge i_clk) begin
end
STATE_CHK_WAIT: begin
o_mask.XOR_NON_ZERO <= |sol_hash_xor;
o_mask.DUPLICATE_FND <= 0; //TODO this with hash
o_mask_val <= 1;
if (ram_rd_state == STATE_RD_IDLE && ram_wr_state == STATE_WR_IDLE)
chk_state <= STATE_CHK_IDLE;
if (ram_rd_state == STATE_RD_WAIT &&
ram_wr_state == STATE_WR_WAIT &&
sol_index_fifo_if_emp ) begin
o_mask_val <= 1;
chk_state <= STATE_CHK_DONE;
hash_map_clr <= 1;
end
end
STATE_CHK_DONE: begin
chk_state <= STATE_CHK_IDLE;
end
endcase
end
@ -288,6 +309,12 @@ always_comb begin
// The SOL_BITS is also bit reversed
for (int i = 0; i < SOL_BITS; i++)
equihash_sol_index[i] = equihash_sol_bram_if_b_l_comb_flip[sol_pos + SOL_BITS-1-i];
// Hash map is fed directly from FIFO
hash_map_in_val = sol_index_fifo_if_out.val;
hash_map_in_dat = sol_index_fifo_if_out.dat;
sol_index_fifo_if_out.rdy = hash_map_out_rdy;
end
// This function checks the ordering of the XORs, so that the number of zeros
@ -336,6 +363,44 @@ bram #(
.b ( equihash_sol_bram_if_b )
);
axi_stream_fifo #(
.SIZE ( SOL_LIST_LEN ),
.DAT_BITS ( SOL_BITS ),
.MOD_BITS ( 1 ),
.CTL_BITS ( 1 )
)
sol_index_fifo (
.i_clk ( i_clk ),
.i_rst ( i_rst ),
.i_axi ( sol_index_fifo_if_in ),
.o_axi ( sol_index_fifo_if_out ),
.o_full (),
.o_emp ( sol_index_fifo_if_emp )
);
// Hash table used to detect duplicate index, fed from FIFO
// Could potentially be run at much higher clock
hash_map #(
.KEY_BITS ( SOL_BITS ),
.DAT_BITS ( 1 ),
.HASH_MEM_SIZE ( 2*SOL_LIST_LEN ),
.LL_MEM_SIZE ( SOL_LIST_LEN )
)
hash_map_i (
.i_clk ( i_clk ),
.i_rst ( i_rst ),
.i_key ( hash_map_in_dat ),
.i_val ( hash_map_in_val ),
.i_dat ( 1'd1 ),
.i_opcode ( 2'd1 ),
.o_rdy ( hash_map_out_rdy ),
.o_dat (),
.o_val ( hash_map_out_val ),
.o_fnd ( hash_map_out_fnd ),
.i_cfg_clr ( hash_map_clr ),
.o_cfg_full ( hash_map_full )
);
// Some checks to make sure our data structures are correct:
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);