aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/control/map/cam_srl.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/control/map/cam_srl.v')
-rw-r--r--fpga/usrp3/lib/control/map/cam_srl.v223
1 files changed, 223 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/control/map/cam_srl.v b/fpga/usrp3/lib/control/map/cam_srl.v
new file mode 100644
index 000000000..6bc4146b0
--- /dev/null
+++ b/fpga/usrp3/lib/control/map/cam_srl.v
@@ -0,0 +1,223 @@
+/*
+
+Copyright (c) 2015-2016 Alex Forencich
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+// Language: Verilog 2001
+
+`timescale 1ns / 1ps
+
+/*
+ * Content Addressable Memory (shift register based)
+ */
+module cam_srl #(
+ // search data bus width
+ parameter DATA_WIDTH = 64,
+ // memory size in log2(words)
+ parameter ADDR_WIDTH = 5,
+ // width of data bus slices (4 for SRL16, 5 for SRL32)
+ parameter SLICE_WIDTH = 4
+)
+(
+ input wire clk,
+ input wire rst,
+
+ input wire [ADDR_WIDTH-1:0] write_addr,
+ input wire [DATA_WIDTH-1:0] write_data,
+ input wire write_delete,
+ input wire write_enable,
+ output wire write_busy,
+
+ input wire [DATA_WIDTH-1:0] compare_data,
+ output wire [2**ADDR_WIDTH-1:0] match_many,
+ output wire [2**ADDR_WIDTH-1:0] match_single,
+ output wire [ADDR_WIDTH-1:0] match_addr,
+ output wire match
+);
+
+// total number of slices (enough to cover DATA_WIDTH with address inputs)
+localparam SLICE_COUNT = (DATA_WIDTH + SLICE_WIDTH - 1) / SLICE_WIDTH;
+// depth of RAMs
+localparam RAM_DEPTH = 2**ADDR_WIDTH;
+
+localparam [1:0]
+ STATE_INIT = 2'd0,
+ STATE_IDLE = 2'd1,
+ STATE_WRITE = 2'd2,
+ STATE_DELETE = 2'd3;
+
+reg [1:0] state_reg = STATE_INIT, state_next;
+
+wire [SLICE_COUNT*SLICE_WIDTH-1:0] compare_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, compare_data};
+wire [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, write_data};
+
+reg [SLICE_WIDTH-1:0] count_reg = {SLICE_WIDTH{1'b1}}, count_next;
+
+reg [SLICE_COUNT-1:0] shift_data;
+reg [RAM_DEPTH-1:0] shift_en;
+
+reg [ADDR_WIDTH-1:0] write_addr_reg = {ADDR_WIDTH{1'b0}}, write_addr_next;
+reg [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded_reg = {SLICE_COUNT*SLICE_WIDTH{1'b0}}, write_data_padded_next;
+
+reg write_busy_reg = 1'b1;
+
+assign write_busy = write_busy_reg;
+
+reg [RAM_DEPTH-1:0] match_raw_out[SLICE_COUNT-1:0];
+reg [RAM_DEPTH-1:0] match_many_raw;
+reg [RAM_DEPTH-1:0] match_many_reg = {RAM_DEPTH{1'b0}};
+
+assign match_many = match_many_reg;
+
+integer k;
+
+always @* begin
+ match_many_raw = ~shift_en;
+ for (k = 0; k < SLICE_COUNT; k = k + 1) begin
+ match_many_raw = match_many_raw & match_raw_out[k];
+ end
+end
+
+cam_priority_encoder #(
+ .WIDTH(RAM_DEPTH),
+ .LSB_PRIORITY("HIGH")
+)
+priority_encoder_inst (
+ .input_unencoded(match_many_reg),
+ .output_valid(match),
+ .output_encoded(match_addr),
+ .output_unencoded(match_single)
+);
+
+integer i;
+
+// SRLs
+genvar row_ind, slice_ind;
+generate
+ for (row_ind = 0; row_ind < RAM_DEPTH; row_ind = row_ind + 1) begin : row
+ for (slice_ind = 0; slice_ind < SLICE_COUNT; slice_ind = slice_ind + 1) begin : slice
+ reg [2**SLICE_WIDTH-1:0] srl_mem = {2**SLICE_WIDTH{1'b0}};
+
+ // match
+ always @* begin
+ match_raw_out[slice_ind][row_ind] = srl_mem[compare_data_padded[SLICE_WIDTH * slice_ind +: SLICE_WIDTH]];
+ end
+
+ // write
+ always @(posedge clk) begin
+ if (shift_en[row_ind]) begin
+ srl_mem <= {srl_mem[2**SLICE_WIDTH-2:0], shift_data[slice_ind]};
+ end
+ end
+ end
+ end
+endgenerate
+
+// match
+always @(posedge clk) begin
+ match_many_reg <= match_many_raw;
+end
+
+// write
+always @* begin
+ state_next = STATE_IDLE;
+
+ count_next = count_reg;
+ shift_data = {SLICE_COUNT{1'b0}};
+ shift_en = {RAM_DEPTH{1'b0}};
+
+ write_addr_next = write_addr_reg;
+ write_data_padded_next = write_data_padded_reg;
+
+ case (state_reg)
+ STATE_INIT: begin
+ // zero out shift registers
+ shift_en = {RAM_DEPTH{1'b1}};
+ shift_data = {SLICE_COUNT{1'b0}};
+
+ if (count_reg == 0) begin
+ state_next = STATE_IDLE;
+ end else begin
+ count_next = count_reg - 1;
+ state_next = STATE_INIT;
+ end
+ end
+ STATE_IDLE: begin
+ if (write_enable) begin
+ write_addr_next = write_addr;
+ write_data_padded_next = write_data_padded;
+ count_next = {SLICE_WIDTH{1'b1}};
+ if (write_delete) begin
+ state_next = STATE_DELETE;
+ end else begin
+ state_next = STATE_WRITE;
+ end
+ end else begin
+ state_next = STATE_IDLE;
+ end
+ end
+ STATE_WRITE: begin
+ // write entry
+ shift_en = 1'b1 << write_addr;
+
+ for (i = 0; i < SLICE_COUNT; i = i + 1) begin
+ shift_data[i] = count_reg == write_data_padded_reg[SLICE_WIDTH * i +: SLICE_WIDTH];
+ end
+
+ if (count_reg == 0) begin
+ state_next = STATE_IDLE;
+ end else begin
+ count_next = count_reg - 1;
+ state_next = STATE_WRITE;
+ end
+ end
+ STATE_DELETE: begin
+ // delete entry
+ shift_en = 1'b1 << write_addr;
+ shift_data = {SLICE_COUNT{1'b0}};
+
+ if (count_reg == 0) begin
+ state_next = STATE_IDLE;
+ end else begin
+ count_next = count_reg - 1;
+ state_next = STATE_DELETE;
+ end
+ end
+ endcase
+end
+
+always @(posedge clk) begin
+ if (rst) begin
+ state_reg <= STATE_INIT;
+ count_reg <= {SLICE_WIDTH{1'b1}};
+ write_busy_reg <= 1'b1;
+ end else begin
+ state_reg <= state_next;
+ count_reg <= count_next;
+ write_busy_reg <= state_next != STATE_IDLE;
+ end
+
+ write_addr_reg <= write_addr_next;
+ write_data_padded_reg <= write_data_padded_next;
+end
+
+endmodule