aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/control/axi_forwarding_cam.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/control/axi_forwarding_cam.v')
-rw-r--r--fpga/usrp3/lib/control/axi_forwarding_cam.v232
1 files changed, 232 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/control/axi_forwarding_cam.v b/fpga/usrp3/lib/control/axi_forwarding_cam.v
new file mode 100644
index 000000000..64076bd0a
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_forwarding_cam.v
@@ -0,0 +1,232 @@
+//
+// Copyright 2013 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// This module implements a highly customized content-addressable memory (CAM)
+// that enables forwarding decisions to be made on a 16 bit field from a stream ID (SID) field.
+// The forwarding is generic in the sense that a SID's host destination can map to any endpoint / crossbar port.
+//
+// The 16 bits are allocated by convention as 8 bits of Network address (addresses USRP's / AXI crossbars) and
+// 8 bits of Host address (addresses endpoints / crossbar ports in a USRP).
+//
+// By definition if the destination field in the SID addresses a different
+// USRP / crossbar than this one then we don't care about the Host field, only the Network field.
+// We only look at the Host field when the Network field addresses us.
+// Thus we need a CAM of 256+256 entries with Log2(N) bits, where N is the number of
+// slave(output) ports on the crossbar switch.
+//
+// SID format:
+//
+// |---------|---------|---------|---------|
+// | SRC | SRC | DST | DST |
+// | NETWORK | HOST | NETWORK | HOST |
+// |---------|---------|---------|---------|
+// 8 8 8 8
+
+module axi_forwarding_cam
+ #(
+ parameter BASE = 0, // BASE address for setting registers in this block. (512 addrs used)
+ parameter WIDTH=64, // Bit width of FIFO word.
+ parameter NUM_OUTPUTS=2 // Number of outputs (destinations) in crossbar.
+ )
+ (
+ input clk,
+ input reset,
+ input clear,
+ // Monitored FIFO signals
+ input [WIDTH-1:0] o_tdata,
+ input o_tvalid,
+ input o_tready,
+ input o_tlast,
+ input pkt_present,
+ // Configuration
+ input [7:0] local_addr,
+ // Setting Bus
+ input set_stb,
+ input [15:0] set_addr,
+ input [31:0] set_data,
+
+ output reg [NUM_OUTPUTS-1:0] forward_valid,
+ input [NUM_OUTPUTS-1:0] forward_ack,
+
+ input rb_rd_stb,
+ input [$clog2(NUM_OUTPUTS)-1:0] rb_addr,
+ output [31:0] rb_data
+ );
+
+
+ localparam WAIT_SOF = 0;
+ localparam WAIT_EOF = 1;
+ reg state;
+
+ localparam IDLE = 0;
+ localparam FORWARD = 1;
+ localparam WAIT = 2;
+
+ reg [1:0] demux_state;
+
+ reg [15:0] dst;
+ reg dst_valid, dst_valid_reg;
+ wire local_dst;
+ wire [8:0] read_addr;
+
+ //
+ // Monitor packets leaving FIFO
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ state <= WAIT_SOF;
+ end else
+ case(state)
+ //
+ // After RESET or the EOF of previous packet, the first cycle with
+ // output valid asserted is the SOF and presents the Header word.
+ // The cycle following the concurrent presentation of asserted output
+ // valid and output ready presents the word following the header.
+ //
+ WAIT_SOF:
+ if (o_tvalid && o_tready) begin
+ state <= WAIT_EOF;
+ end else begin
+ state <= WAIT_SOF;
+ end
+ //
+ // EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
+ //
+ WAIT_EOF:
+ if (o_tlast && o_tvalid && o_tready) begin
+ state <= WAIT_SOF;
+ end else begin
+ state <= WAIT_EOF;
+ end
+ endcase // case(in_state)
+
+ //
+ // Extract Destination fields(s) from SID
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ dst <= 0;
+ dst_valid <= 0;
+ dst_valid_reg <= 0;
+ end else if (o_tvalid && (state == WAIT_SOF) && pkt_present) begin
+ // SID will remain valid until o_tready is asserted as this will cause a state transition.
+ dst <= o_tdata[15:0];
+ dst_valid <= 1;
+ dst_valid_reg <= dst_valid;
+ end else begin
+ dst_valid <= 0;
+ dst_valid_reg <= dst_valid;
+ end
+
+ //
+ // Is Network field in DST our local address?
+ //
+ assign local_dst = (dst[15:8] == local_addr) && dst_valid;
+
+
+ //
+ // Mux address to RAM so that it searches CAM for Network field or Host field.
+ // Network addresses are stored in the lower 256 locations, host addresses the upper 256.
+ //
+ assign read_addr = {local_dst,(local_dst ? dst[7:0] : dst[15:8])};
+
+ //
+ // Implement CAM as block RAM here, 512xCeil(Log2(NUM_OUTPUTS))
+ //
+ //synthesis attribute ram_style of mem is block
+ reg [$clog2(NUM_OUTPUTS)-1 : 0] mem [0:511];
+
+ // Initialize the CAM's local address forwarding decisions with sensible defaults by
+ // assuming dst[7:4] = crossbar port, dst[3:0] = block port. Setup a one-to-one mapping
+ // for crossbar ports and always map same crossbar port regardless of block port.
+ // i.e.
+ // dst 8'h00 => forward to crossbar port 0
+ // dst 8'h01 => forward to crossbar port 0
+ // dst 8'h10 => forward to crossbar port 1
+ // etc.
+ integer xbar_port;
+ integer block_port;
+ initial begin
+ for (xbar_port = 0; xbar_port < NUM_OUTPUTS; xbar_port = xbar_port + 1) begin
+ for (block_port = 0; block_port < 16; block_port = block_port + 1) begin
+ mem[256+(xbar_port << 4)+block_port] = xbar_port;
+ end
+ end
+ end
+
+ reg [8:0] read_addr_reg;
+ wire write;
+ wire [$clog2(NUM_OUTPUTS)-1:0] read_data;
+
+ assign write = (set_addr[15:9] == (BASE >>9)) && set_stb; // Addr decode.
+
+ always @(posedge clk)
+ begin
+ read_addr_reg <= read_addr;
+
+ if (write) begin
+ mem[set_addr[8:0]] <= set_data[$clog2(NUM_OUTPUTS)-1:0];
+ end
+
+ end
+
+ assign read_data = mem[read_addr_reg];
+
+
+ //
+ // State machine to manage forwarding flags.
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ forward_valid <= {NUM_OUTPUTS{1'b0}};
+ demux_state <= IDLE;
+ end else
+ case(demux_state)
+
+ // Wait for Valid DST which indicates a new packet lookup in the CAM.
+ IDLE: begin
+ if (dst_valid_reg == 1) begin
+ forward_valid <= 1'b1 << read_data;
+ demux_state <= FORWARD;
+ end
+ end
+ // When Slave/Output thats forwarding ACK's the forward flag, clear request and wait for packet to be transfered
+ FORWARD: begin
+ if ((forward_ack & forward_valid) != 0) begin
+ forward_valid <= {NUM_OUTPUTS{1'b0}};
+ demux_state <= WAIT;
+ end
+ end
+ // When packet transfered go back to idle.
+ WAIT: begin
+ if (forward_ack == 0)
+ demux_state <= IDLE;
+ end
+
+ endcase // case (demux_state)
+
+ //
+ // Compile forwarding statistics
+ // (This uses a lot of registers!)
+ //
+ genvar m;
+ reg [31:0] statistics [0:NUM_OUTPUTS-1];
+
+ generate
+ for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: generate_stats
+ always @(posedge clk) begin
+ if (reset | clear) begin
+ statistics[m] <= 0;
+ end else if (forward_ack[m] & forward_valid[m]) begin
+ statistics[m] <= statistics[m] + 1;
+ end
+ end
+ end
+ endgenerate
+
+ assign rb_data = statistics[rb_addr];
+
+endmodule