1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
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
|