aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/control/map/axis_muxed_kv_map.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/control/map/axis_muxed_kv_map.v')
-rw-r--r--fpga/usrp3/lib/control/map/axis_muxed_kv_map.v206
1 files changed, 206 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/control/map/axis_muxed_kv_map.v b/fpga/usrp3/lib/control/map/axis_muxed_kv_map.v
new file mode 100644
index 000000000..152cee8eb
--- /dev/null
+++ b/fpga/usrp3/lib/control/map/axis_muxed_kv_map.v
@@ -0,0 +1,206 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axis_muxed_kv_map
+//
+// Description:
+//
+// This module implements a memory that stores key and value (KV) pairs such
+// that the value can be looked up using the key (e.g., for a routing table).
+// This implementation uses AXI stream for both inserting key-value pairs and
+// for looking up a value by its key. It also supports multiple find/result
+// AXI streams, which share the same KV map internally.
+//
+// Values are inserted into the KV map using the axis_insert_* AXI stream. A
+// value can be looked up by its key using the axis_find_* AXI stream, in
+// which case the resulting value is output on the axis_result_* AXI stream.
+//
+// Ports:
+//
+// axis_insert_tdest : Key to insert into the KV map
+// axis_insert_tdata : Value to associate with the key in TDEST
+// axis_insert_tvalid : Standard AXI stream TVALID
+// axis_insert_tready : Standard AXI stream TREADY
+//
+// axis_find_tdata : Key to look up in the KV map
+// axis_find_tvalid : Standard AXI stream TVALID
+// axis_find_tready : Standard AXI stream TREADY
+//
+// axis_result_tdata : Value associated with key that was input on axis_find
+// axis_result_tkeep : Indicates if TDATA contains a valid value (i.e.,
+// TKEEP is 0 if the lookup fails to find a match)
+// axis_result_tvalid : Standard AXI stream TVALID
+// axis_result_tready : Standard AXI stream TREADY
+//
+// Parameters:
+//
+// KEY_WIDTH : Width of the key (axis_insert_tdest, axis_find_tdata)
+// VAL_WIDTH : Width of the value (axis_insert_tdata, axis_result_tdata)
+// SIZE : Size of the KV map (i.e., 2**SIZE key-value pairs)
+// NUM_PORTS : Number of AXI-Stream ports for the find and result interfaces
+//
+
+module axis_muxed_kv_map #(
+ parameter KEY_WIDTH = 16,
+ parameter VAL_WIDTH = 32,
+ parameter SIZE = 6,
+ parameter NUM_PORTS = 4
+) (
+ input wire clk,
+ input wire reset,
+
+ input wire [KEY_WIDTH-1:0] axis_insert_tdest,
+ input wire [VAL_WIDTH-1:0] axis_insert_tdata,
+ input wire axis_insert_tvalid,
+ output wire axis_insert_tready,
+
+ input wire [(KEY_WIDTH*NUM_PORTS)-1:0] axis_find_tdata,
+ input wire [NUM_PORTS-1:0] axis_find_tvalid,
+ output wire [NUM_PORTS-1:0] axis_find_tready,
+
+ output wire [(VAL_WIDTH*NUM_PORTS)-1:0] axis_result_tdata,
+ output wire [NUM_PORTS-1:0] axis_result_tkeep,
+ output wire [NUM_PORTS-1:0] axis_result_tvalid,
+ input wire [NUM_PORTS-1:0] axis_result_tready
+);
+
+ localparam MUX_W = $clog2(NUM_PORTS) + KEY_WIDTH;
+ localparam DEMUX_W = $clog2(NUM_PORTS) + VAL_WIDTH + 1;
+ genvar i;
+
+ localparam [1:0] ST_IDLE = 2'd0;
+ localparam [1:0] ST_REQUEST = 2'd1;
+ localparam [1:0] ST_PENDING = 2'd2;
+
+ //---------------------------------------------------------
+ // Demux find ports
+ //---------------------------------------------------------
+ wire [KEY_WIDTH-1:0] find_key, find_key_reg;
+ wire find_key_stb;
+ wire [$clog2(NUM_PORTS)-1:0] find_dest, find_dest_reg;
+ wire find_key_valid, find_key_valid_reg;
+ wire find_ready;
+ reg find_in_progress = 1'b0;
+ wire insert_stb;
+ wire insert_busy;
+ wire find_res_stb;
+ wire [VAL_WIDTH-1:0] find_res_val;
+ wire find_res_match, find_res_ready;
+
+ wire [(MUX_W*NUM_PORTS)-1:0] mux_tdata;
+ generate for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_mux_input
+ assign mux_tdata[(MUX_W*i)+KEY_WIDTH-1:MUX_W*i] = axis_find_tdata[(KEY_WIDTH*i)+:KEY_WIDTH];
+ assign mux_tdata[(MUX_W*(i+1))-1:(MUX_W*i)+KEY_WIDTH] = i;
+ end endgenerate
+
+ axi_mux #(
+ .WIDTH(KEY_WIDTH+$clog2(NUM_PORTS)), .SIZE(NUM_PORTS),
+ .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE($clog2(NUM_PORTS))
+ ) mux_i (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .i_tdata(mux_tdata), .i_tlast({NUM_PORTS{1'b1}}),
+ .i_tvalid(axis_find_tvalid), .i_tready(axis_find_tready),
+ .o_tdata({find_dest_reg, find_key_reg}), .o_tlast(),
+ .o_tvalid(find_key_valid_reg), .o_tready(find_ready)
+ );
+
+ axi_fifo #(
+ .WIDTH(KEY_WIDTH+$clog2(NUM_PORTS)), .SIZE(1)
+ ) mux_reg_i (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .i_tdata({find_dest_reg, find_key_reg}),
+ .i_tvalid(find_key_valid_reg), .i_tready(find_ready),
+ .o_tdata({find_dest, find_key}),
+ .o_tvalid(find_key_valid), .o_tready(find_res_stb),
+ .space(), .occupied()
+ );
+
+ always @(posedge clk) begin
+ if (reset) begin
+ find_in_progress <= 1'b0;
+ end else begin
+ if (find_key_stb) begin
+ find_in_progress <= 1'b1;
+ end else if (find_res_stb) begin
+ find_in_progress <= 1'b0;
+ end
+ end
+ end
+
+ // find_key_stb indicates when to begin a new KV map lookup. We must wait
+ // until the output mux is ready before starting a lookup.
+ assign find_key_stb = find_key_valid & find_res_ready & ~find_in_progress;
+
+ //---------------------------------------------------------
+ // Insert logic
+ //---------------------------------------------------------
+ reg [1:0] ins_state = ST_IDLE;
+ always @(posedge clk) begin
+ if (reset) begin
+ ins_state <= ST_IDLE;
+ end else begin
+ case (ins_state)
+ ST_IDLE:
+ if (axis_insert_tvalid & ~insert_busy)
+ ins_state <= ST_REQUEST;
+ ST_REQUEST:
+ ins_state <= ST_PENDING;
+ ST_PENDING:
+ if (~insert_busy)
+ ins_state <= ST_IDLE;
+ default:
+ ins_state <= ST_IDLE;
+ endcase
+ end
+ end
+
+ assign axis_insert_tready = axis_insert_tvalid & (ins_state == ST_PENDING) & ~insert_busy;
+ assign insert_stb = axis_insert_tvalid & (ins_state == ST_REQUEST);
+
+ //---------------------------------------------------------
+ // KV map instantiation
+ //---------------------------------------------------------
+ kv_map #(
+ .KEY_WIDTH (KEY_WIDTH),
+ .VAL_WIDTH (VAL_WIDTH),
+ .SIZE (SIZE)
+ ) map_i (
+ .clk (clk),
+ .reset (reset),
+ .insert_stb (insert_stb),
+ .insert_key (axis_insert_tdest),
+ .insert_val (axis_insert_tdata),
+ .insert_busy (insert_busy),
+ .find_key_stb (find_key_stb),
+ .find_key (find_key),
+ .find_res_stb (find_res_stb),
+ .find_res_match (find_res_match),
+ .find_res_val (find_res_val),
+ .count (/* unused */)
+ );
+
+ //---------------------------------------------------------
+ // Mux results port
+ //---------------------------------------------------------
+ wire [(DEMUX_W*NUM_PORTS)-1:0] demux_tdata;
+ wire [DEMUX_W-1:0] hdr;
+ axi_demux #(
+ .WIDTH(DEMUX_W), .SIZE(NUM_PORTS),
+ .PRE_FIFO_SIZE(1), .POST_FIFO_SIZE(0)
+ ) demux_i (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .header(hdr), .dest(hdr[DEMUX_W-1:VAL_WIDTH+1]),
+ .i_tdata({find_dest, find_res_match, find_res_val}), .i_tlast(1'b1),
+ .i_tvalid(find_res_stb), .i_tready(find_res_ready),
+ .o_tdata(demux_tdata), .o_tlast(),
+ .o_tvalid(axis_result_tvalid), .o_tready(axis_result_tready)
+ );
+
+ generate for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_result_output
+ assign axis_result_tdata[(VAL_WIDTH*i)+:VAL_WIDTH] = demux_tdata[(DEMUX_W*i)+VAL_WIDTH-1:DEMUX_W*i];
+ assign axis_result_tkeep[i] = demux_tdata[(DEMUX_W*i)+VAL_WIDTH];
+ end endgenerate
+
+endmodule