Added code for hash map and checking for duplicated index in the
equihash solution.
This commit is contained in:
parent
3cc1446afc
commit
39892b7732
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue