aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/core
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/core')
-rw-r--r--fpga/usrp3/lib/rfnoc/core/Makefile.srcs39
-rw-r--r--fpga/usrp3/lib/rfnoc/core/axis_ctrl_endpoint.v116
-rw-r--r--fpga/usrp3/lib/rfnoc/core/axis_ctrl_master.v316
-rw-r--r--fpga/usrp3/lib/rfnoc/core/axis_ctrl_slave.v333
-rw-r--r--fpga/usrp3/lib/rfnoc/core/axis_data_mdata_to_chdr.v603
-rw-r--r--fpga/usrp3/lib/rfnoc/core/axis_data_to_chdr.v452
-rw-r--r--fpga/usrp3/lib/rfnoc/core/axis_pyld_ctxt_to_chdr.v463
-rw-r--r--fpga/usrp3/lib/rfnoc/core/backend_iface.v142
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_compute_tkeep.v86
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_data_swapper.v227
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_ingress_fifo.v95
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_mgmt_pkt_handler.v617
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_stream_endpoint.v621
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_stream_input.v569
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_stream_output.v557
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_to_axis_ctrl.v319
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_to_axis_data.v422
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_to_axis_data_mdata.v538
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_to_axis_pyld_ctxt.v458
-rw-r--r--fpga/usrp3/lib/rfnoc/core/chdr_to_chdr_data.v55
-rw-r--r--fpga/usrp3/lib/rfnoc/core/ctrlport.vh26
-rw-r--r--fpga/usrp3/lib/rfnoc/core/ctrlport_endpoint.v284
-rw-r--r--fpga/usrp3/lib/rfnoc/core/rfnoc_axis_ctrl_utils.vh154
-rw-r--r--fpga/usrp3/lib/rfnoc/core/rfnoc_backend_iface.vh52
-rw-r--r--fpga/usrp3/lib/rfnoc/core/rfnoc_chdr_internal_utils.vh452
-rw-r--r--fpga/usrp3/lib/rfnoc/core/rfnoc_chdr_utils.vh200
-rw-r--r--fpga/usrp3/lib/rfnoc/core/rfnoc_core_kernel.v385
27 files changed, 8581 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/core/Makefile.srcs b/fpga/usrp3/lib/rfnoc/core/Makefile.srcs
new file mode 100644
index 000000000..0a646f98b
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/Makefile.srcs
@@ -0,0 +1,39 @@
+#
+# Copyright 2018 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+##################################################
+# RFNoC Core Sources
+##################################################
+RFNOC_CORE_HEADERS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/core/, \
+rfnoc_chdr_utils.vh \
+rfnoc_axis_ctrl_utils.vh \
+rfnoc_chdr_internal_utils.vh \
+ctrlport.vh \
+))
+
+RFNOC_CORE_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/core/, \
+axis_ctrl_endpoint.v \
+axis_ctrl_master.v \
+axis_ctrl_slave.v \
+chdr_compute_tkeep.v \
+chdr_to_chdr_data.v \
+chdr_to_axis_pyld_ctxt.v \
+chdr_to_axis_data_mdata.v \
+chdr_to_axis_data.v \
+axis_pyld_ctxt_to_chdr.v \
+axis_data_mdata_to_chdr.v \
+axis_data_to_chdr.v \
+chdr_ingress_fifo.v \
+chdr_mgmt_pkt_handler.v \
+chdr_data_swapper.v \
+chdr_stream_endpoint.v \
+chdr_stream_input.v \
+chdr_stream_output.v \
+chdr_to_axis_ctrl.v \
+ctrlport_endpoint.v \
+backend_iface.v \
+rfnoc_core_kernel.v \
+))
diff --git a/fpga/usrp3/lib/rfnoc/core/axis_ctrl_endpoint.v b/fpga/usrp3/lib/rfnoc/core/axis_ctrl_endpoint.v
new file mode 100644
index 000000000..e1ded42aa
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/axis_ctrl_endpoint.v
@@ -0,0 +1,116 @@
+//
+// Copyright 2018-2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axis_ctrl_endpoint
+// Description:
+// A bidirectional AXIS-Control to AXIS-Control converter.
+// Use this module in noc_shell to interface between the user
+// logic and the rfnoc infrastructure when both interfaces use
+// AXIS-Control.
+//
+// Parameters:
+// - SYNC_CLKS: Is rfnoc_ctrl_clk and axis_ctrl_clk the same clock?
+// - SLAVE_FIFO_SIZE: The depth of the slave FIFO. Note that the
+// slave FIFO will also buffer master responses.
+//
+// Signals:
+// - *_rfnoc_ctrl_* : Input/output AXIS-Control from/to the framework
+// - *_axis_ctrl_* : Input/output AXIS-Control from/to the user
+
+module axis_ctrl_endpoint #(
+ parameter SYNC_CLKS = 0,
+ parameter SLAVE_FIFO_SIZE = 5
+)(
+ // Clocks, Resets, Misc
+ input wire rfnoc_ctrl_clk,
+ input wire rfnoc_ctrl_rst,
+ input wire axis_ctrl_clk,
+ input wire axis_ctrl_rst,
+ // AXIS-Control Bus (RFNoC infrastructure)
+ input wire [31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+ output wire [31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready,
+ // AXIS-Control Bus (User logic)
+ input wire [31:0] s_axis_ctrl_tdata,
+ input wire s_axis_ctrl_tlast,
+ input wire s_axis_ctrl_tvalid,
+ output wire s_axis_ctrl_tready,
+ output wire [31:0] m_axis_ctrl_tdata,
+ output wire m_axis_ctrl_tlast,
+ output wire m_axis_ctrl_tvalid,
+ input wire m_axis_ctrl_tready
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_axis_ctrl_utils.vh"
+
+ // ---------------------------------------------------
+ // Clock Crossing
+ // ---------------------------------------------------
+
+ wire [31:0] i_ctrl_tdata;
+ wire i_ctrl_tlast, i_ctrl_tvalid, i_ctrl_tready;
+
+ generate
+ if (SYNC_CLKS) begin
+ axi_fifo #(.WIDTH(32+1), .SIZE(SLAVE_FIFO_SIZE)) in_fifo_i (
+ .clk(axis_ctrl_clk), .reset(axis_ctrl_rst), .clear(1'b0),
+ .i_tdata({s_rfnoc_ctrl_tlast, s_rfnoc_ctrl_tdata}),
+ .i_tvalid(s_rfnoc_ctrl_tvalid), .i_tready(s_rfnoc_ctrl_tready),
+ .o_tdata({i_ctrl_tlast, i_ctrl_tdata}),
+ .o_tvalid(i_ctrl_tvalid), .o_tready(i_ctrl_tready),
+ .space(), .occupied()
+ );
+
+ axi_fifo #(.WIDTH(32+1), .SIZE(1)) out_fifo_i (
+ .clk(axis_ctrl_clk), .reset(axis_ctrl_rst), .clear(1'b0),
+ .i_tdata({s_axis_ctrl_tlast, s_axis_ctrl_tdata}),
+ .i_tvalid(s_axis_ctrl_tvalid), .i_tready(s_axis_ctrl_tready),
+ .o_tdata({m_rfnoc_ctrl_tlast, m_rfnoc_ctrl_tdata}),
+ .o_tvalid(m_rfnoc_ctrl_tvalid), .o_tready(m_rfnoc_ctrl_tready),
+ .space(), .occupied()
+ );
+ end else begin
+ axi_fifo_2clk #(.WIDTH(32+1), .SIZE(SLAVE_FIFO_SIZE), .PIPELINE("NONE")) in_fifo_i (
+ .reset(rfnoc_ctrl_rst),
+ .i_aclk(rfnoc_ctrl_clk),
+ .i_tdata({s_rfnoc_ctrl_tlast, s_rfnoc_ctrl_tdata}),
+ .i_tvalid(s_rfnoc_ctrl_tvalid), .i_tready(s_rfnoc_ctrl_tready),
+ .o_aclk(axis_ctrl_clk),
+ .o_tdata({i_ctrl_tlast, i_ctrl_tdata}),
+ .o_tvalid(i_ctrl_tvalid), .o_tready(i_ctrl_tready)
+ );
+
+ axi_fifo_2clk #(.WIDTH(32+1), .SIZE(1), .PIPELINE("NONE")) out_fifo_i (
+ .reset(axis_ctrl_rst),
+ .i_aclk(axis_ctrl_clk),
+ .i_tdata({s_axis_ctrl_tlast, s_axis_ctrl_tdata}),
+ .i_tvalid(s_axis_ctrl_tvalid), .i_tready(s_axis_ctrl_tready),
+ .o_aclk(rfnoc_ctrl_clk),
+ .o_tdata({m_rfnoc_ctrl_tlast, m_rfnoc_ctrl_tdata}),
+ .o_tvalid(m_rfnoc_ctrl_tvalid), .o_tready(m_rfnoc_ctrl_tready)
+ );
+ end
+ endgenerate
+
+ axi_fifo #(.WIDTH(32+1), .SIZE(1)) slv_pipe_i (
+ .clk(axis_ctrl_clk), .reset(axis_ctrl_rst), .clear(1'b0),
+ .i_tdata({i_ctrl_tlast, i_ctrl_tdata}),
+ .i_tvalid(i_ctrl_tvalid), .i_tready(i_ctrl_tready),
+ .o_tdata({m_axis_ctrl_tlast, m_axis_ctrl_tdata}),
+ .o_tvalid(m_axis_ctrl_tvalid), .o_tready(m_axis_ctrl_tready),
+ .space(), .occupied()
+ );
+
+endmodule // axis_ctrl_endpoint
+
diff --git a/fpga/usrp3/lib/rfnoc/core/axis_ctrl_master.v b/fpga/usrp3/lib/rfnoc/core/axis_ctrl_master.v
new file mode 100644
index 000000000..19ae98f52
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/axis_ctrl_master.v
@@ -0,0 +1,316 @@
+//
+// Copyright 2018-2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axis_ctrl_master
+// Description:
+// This module implements an AXIS-Control master (and a Control-Port
+// slave). Requests are accepted on the slave Control-Port, converted
+// to AXIS-Control requests, then sent over the master AXI-Stream port.
+// Responses are received on the AXI-Stream slave port, and converted
+// to Control-Port responses.
+// NOTE: Transactions are not buffered so there is no need for flow
+// control or throttling.
+//
+// Parameters:
+// - THIS_PORTID : The local port-ID of this control port
+//
+// Signals:
+// - s_axis_ctrl_* : Input control stream (AXI-Stream) for responses
+// - m_axis_ctrl_* : Output control stream (AXI-Stream) for requests
+// - ctrlport_req_* : Control-port master request port
+// - ctrlport_resp_* : Control-port master response port
+
+module axis_ctrl_master #(
+ parameter [9:0] THIS_PORTID = 10'd0
+)(
+ // Clock and reset
+ input wire clk,
+ input wire rst,
+ // AXIS-Control Bus (Response)
+ input wire [31:0] s_axis_ctrl_tdata,
+ input wire s_axis_ctrl_tlast,
+ input wire s_axis_ctrl_tvalid,
+ output wire s_axis_ctrl_tready,
+ // AXIS-Control Bus (Request)
+ output reg [31:0] m_axis_ctrl_tdata,
+ output wire m_axis_ctrl_tlast,
+ output wire m_axis_ctrl_tvalid,
+ input wire m_axis_ctrl_tready,
+ // Control Port Endpoint (Request)
+ input wire ctrlport_req_wr,
+ input wire ctrlport_req_rd,
+ input wire [19:0] ctrlport_req_addr,
+ input wire [9:0] ctrlport_req_portid,
+ input wire [15:0] ctrlport_req_rem_epid,
+ input wire [9:0] ctrlport_req_rem_portid,
+ input wire [31:0] ctrlport_req_data,
+ input wire [3:0] ctrlport_req_byte_en,
+ input wire ctrlport_req_has_time,
+ input wire [63:0] ctrlport_req_time,
+ // Control Port Endpoint (Response)
+ output wire ctrlport_resp_ack,
+ output wire [1:0] ctrlport_resp_status,
+ output wire [31:0] ctrlport_resp_data
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_axis_ctrl_utils.vh"
+
+ // ---------------------------------------------------
+ // State Machine
+ // ---------------------------------------------------
+ localparam [3:0] ST_IDLE = 4'd0; // Waiting for a request on slave ctrlport
+ localparam [3:0] ST_REQ_HDR_LO = 4'd1; // Sending AXIS-Control request header (low bits)
+ localparam [3:0] ST_REQ_HDR_HI = 4'd2; // Sending AXIS-Control request header (high bits)
+ localparam [3:0] ST_REQ_TS_LO = 4'd3; // Sending AXIS-Control request timestamp (low bits)
+ localparam [3:0] ST_REQ_TS_HI = 4'd4; // Sending AXIS-Control request timestamp (high bits)
+ localparam [3:0] ST_REQ_OP_WORD = 4'd5; // Sending AXIS-Control request operation word
+ localparam [3:0] ST_REQ_OP_DATA = 4'd6; // Sending AXIS-Control request data word
+ localparam [3:0] ST_RESP_HDR_LO = 4'd7; // Receiving AXIS-Control response header (low bits)
+ localparam [3:0] ST_RESP_HDR_HI = 4'd8; // Receiving AXIS-Control response header (high bits)
+ localparam [3:0] ST_RESP_TS_LO = 4'd9; // Receiving AXIS-Control response timestamp (low bits)
+ localparam [3:0] ST_RESP_TS_HI = 4'd10; // Receiving AXIS-Control response timestamp (high bits)
+ localparam [3:0] ST_RESP_OP_WORD = 4'd11; // Receiving AXIS-Control response operation word
+ localparam [3:0] ST_RESP_OP_DATA = 4'd12; // Receiving AXIS-Control response data word
+ localparam [3:0] ST_SHORT_PKT_ERR = 4'd13; // Response was too short. Send a dummy response on ctrlport
+ localparam [3:0] ST_DROP_LONG_PKT = 4'd14; // Response was too long. Dump the rest of the packet
+
+ // State variables
+ reg [3:0] state = ST_IDLE; // Current state for FSM
+ reg [5:0] seq_num = 6'd0; // Expected seqnum for response
+ // Request state
+ reg [3:0] req_opcode; // Cached opcode for transaction request
+ reg [19:0] req_addr; // Cached address for transaction request
+ reg [9:0] req_portid; // Cached port ID for transaction request
+ reg [15:0] req_rem_epid; // Cached remote endpoint ID for transaction request
+ reg [9:0] req_rem_portid; // Cached remote port ID for transaction request
+ reg [31:0] req_data; // Cached data word for transaction request
+ reg [3:0] req_byte_en; // Cached byte enable for transaction request
+ reg req_has_time; // Cached has_time bit for transaction request
+ reg [63:0] req_time; // Cached timestamp for transaction request
+ // Response state
+ reg resp_has_time; // Does the response have a timestamp?
+ reg [1:0] resp_status; // The status in the response
+ reg resp_seq_err, resp_cmd_err; // Error bits for the response
+
+ always @(posedge clk) begin
+ if (rst) begin
+ state <= ST_IDLE;
+ seq_num <= 6'd0;
+ end else begin
+ case (state)
+
+ // Ready to receive a request on ctrlport
+ // ------------------------------------
+ ST_IDLE: begin
+ if (ctrlport_req_wr | ctrlport_req_rd) begin
+ // A transaction was posted on the slave ctrlport...
+ // Cache the opcode
+ if (ctrlport_req_wr & ctrlport_req_rd)
+ req_opcode <= AXIS_CTRL_OPCODE_WRITE_READ;
+ else if (ctrlport_req_rd)
+ req_opcode <= AXIS_CTRL_OPCODE_READ;
+ else
+ req_opcode <= AXIS_CTRL_OPCODE_WRITE;
+ // Cache transaction info
+ req_addr <= ctrlport_req_addr;
+ req_portid <= ctrlport_req_portid;
+ req_rem_epid <= ctrlport_req_rem_epid;
+ req_rem_portid <= ctrlport_req_rem_portid;
+ req_data <= ctrlport_req_data;
+ req_byte_en <= ctrlport_req_byte_en;
+ req_has_time <= ctrlport_req_has_time;
+ req_time <= ctrlport_req_time;
+ // Start sending out AXIS-Ctrl packet
+ state <= ST_REQ_HDR_LO;
+ end
+ end
+
+ // Send a request AXIS comand
+ // (a state for each stage in the packet)
+ // ------------------------------------
+ ST_REQ_HDR_LO: begin
+ if (m_axis_ctrl_tready)
+ state <= ST_REQ_HDR_HI;
+ end
+ ST_REQ_HDR_HI: begin
+ if (m_axis_ctrl_tready)
+ state <= req_has_time ? ST_REQ_TS_LO : ST_REQ_OP_WORD;
+ end
+ ST_REQ_TS_LO: begin
+ if (m_axis_ctrl_tready)
+ state <= ST_REQ_TS_HI;
+ end
+ ST_REQ_TS_HI: begin
+ if (m_axis_ctrl_tready)
+ state <= ST_REQ_OP_WORD;
+ end
+ ST_REQ_OP_WORD: begin
+ if (m_axis_ctrl_tready)
+ state <= ST_REQ_OP_DATA;
+ end
+ ST_REQ_OP_DATA: begin
+ if (m_axis_ctrl_tready)
+ state <= ST_RESP_HDR_LO;
+ end
+
+ // Receive a response AXIS comand
+ // (a state for each stage in the packet)
+ // ------------------------------------
+ ST_RESP_HDR_LO: begin
+ if (s_axis_ctrl_tvalid) begin
+ // Remeber if the packet is supposed to have a timestamp
+ resp_has_time <= axis_ctrl_get_has_time(s_axis_ctrl_tdata);
+ // Check for a sequence error
+ resp_seq_err <= (axis_ctrl_get_seq_num(s_axis_ctrl_tdata) != seq_num);
+ // Assert a command error if:
+ // - The port ID does not match
+ // - The response was too short (the next check)
+ resp_cmd_err <= (axis_ctrl_get_dst_port(s_axis_ctrl_tdata) != THIS_PORTID);
+ if (!s_axis_ctrl_tlast) begin
+ state <= ST_RESP_HDR_HI;
+ end else begin
+ // Response was too short
+ resp_cmd_err <= 1'b1;
+ state <= ST_SHORT_PKT_ERR;
+ end
+ end
+ end
+ ST_RESP_HDR_HI: begin
+ if (s_axis_ctrl_tvalid) begin
+ if (!s_axis_ctrl_tlast) begin
+ state <= resp_has_time ? ST_RESP_TS_LO : ST_RESP_OP_WORD;
+ end else begin
+ // Response was too short
+ resp_cmd_err <= 1'b1;
+ state <= ST_SHORT_PKT_ERR;
+ end
+ end
+ end
+ ST_RESP_TS_LO: begin
+ if (s_axis_ctrl_tvalid) begin
+ if (!s_axis_ctrl_tlast) begin
+ state <= ST_RESP_TS_HI;
+ end else begin
+ // Response was too short
+ resp_cmd_err <= 1'b1;
+ state <= ST_SHORT_PKT_ERR;
+ end
+ end
+ end
+ ST_RESP_TS_HI: begin
+ if (s_axis_ctrl_tvalid) begin
+ if (!s_axis_ctrl_tlast) begin
+ state <= ST_RESP_OP_WORD;
+ end else begin
+ // Response was too short
+ resp_cmd_err <= 1'b1;
+ state <= ST_SHORT_PKT_ERR;
+ end
+ end
+ end
+ ST_RESP_OP_WORD: begin
+ if (s_axis_ctrl_tvalid) begin
+ if (!s_axis_ctrl_tlast) begin
+ // Assert a command error if opcode and addr in request does not match response
+ resp_cmd_err <= resp_cmd_err ||
+ (axis_ctrl_get_opcode(s_axis_ctrl_tdata) != req_opcode) ||
+ (axis_ctrl_get_address(s_axis_ctrl_tdata) != req_addr);
+ resp_status <= axis_ctrl_get_status(s_axis_ctrl_tdata);
+ state <= ST_RESP_OP_DATA;
+ end else begin
+ // Response was too short
+ resp_cmd_err <= 1'b1;
+ state <= ST_SHORT_PKT_ERR;
+ end
+ end
+ end
+ ST_RESP_OP_DATA: begin
+ if (s_axis_ctrl_tvalid) begin
+ // If the packet was too long then just drop the rest without complaining
+ state <= s_axis_ctrl_tlast ? ST_IDLE : ST_DROP_LONG_PKT;
+ seq_num <= seq_num + 6'd1;
+ end
+ end
+
+ // Error handling states
+ // ------------------------------------
+ ST_SHORT_PKT_ERR: begin
+ state <= ST_IDLE;
+ end
+ ST_DROP_LONG_PKT: begin
+ if (s_axis_ctrl_tvalid && s_axis_ctrl_tlast)
+ state <= ST_IDLE;
+ end
+
+ default: begin
+ // We should never get here
+ state <= ST_IDLE;
+ end
+ endcase
+ end
+ end
+
+ // Logic to drive m_axis_ctrl_*
+ // ------------------------------------
+ always @(*) begin
+ case (state)
+ ST_REQ_HDR_LO: begin
+ m_axis_ctrl_tdata = axis_ctrl_build_hdr_lo(
+ 1'b0 /* is_ack*/, req_has_time, seq_num,
+ 4'd1 /* num_data */, THIS_PORTID, req_portid);
+ end
+ ST_REQ_HDR_HI: begin
+ m_axis_ctrl_tdata = axis_ctrl_build_hdr_hi(
+ req_rem_portid, req_rem_epid);
+ end
+ ST_REQ_TS_LO: begin
+ m_axis_ctrl_tdata = req_time[31:0];
+ end
+ ST_REQ_TS_HI: begin
+ m_axis_ctrl_tdata = req_time[63:32];
+ end
+ ST_REQ_OP_WORD: begin
+ m_axis_ctrl_tdata = axis_ctrl_build_op_word(
+ AXIS_CTRL_STS_OKAY, req_opcode, req_byte_en, req_addr);
+ end
+ ST_REQ_OP_DATA: begin
+ m_axis_ctrl_tdata = req_data;
+ end
+ default: begin
+ m_axis_ctrl_tdata = 32'h0;
+ end
+ endcase
+ end
+ assign m_axis_ctrl_tvalid = (state == ST_REQ_HDR_LO) ||
+ (state == ST_REQ_HDR_HI) ||
+ (state == ST_REQ_TS_LO) ||
+ (state == ST_REQ_TS_HI) ||
+ (state == ST_REQ_OP_WORD) ||
+ (state == ST_REQ_OP_DATA);
+ assign m_axis_ctrl_tlast = (state == ST_REQ_OP_DATA);
+
+ // Logic to backpressure responses
+ // ------------------------------------
+ assign s_axis_ctrl_tready = (state == ST_RESP_HDR_LO) ||
+ (state == ST_RESP_HDR_HI) ||
+ (state == ST_RESP_TS_LO) ||
+ (state == ST_RESP_TS_HI) ||
+ (state == ST_RESP_OP_WORD) ||
+ (state == ST_RESP_OP_DATA) ||
+ (state == ST_DROP_LONG_PKT);
+
+ // Logic to drive Control-port response
+ // ------------------------------------
+ assign ctrlport_resp_ack = (state == ST_RESP_OP_DATA && s_axis_ctrl_tvalid) ||
+ (state == ST_SHORT_PKT_ERR);
+ assign ctrlport_resp_status = resp_cmd_err ? AXIS_CTRL_STS_CMDERR :
+ (resp_seq_err ? AXIS_CTRL_STS_WARNING : resp_status);
+ assign ctrlport_resp_data = (state == ST_SHORT_PKT_ERR) ? 32'h0 : s_axis_ctrl_tdata;
+
+endmodule // axis_ctrl_master
diff --git a/fpga/usrp3/lib/rfnoc/core/axis_ctrl_slave.v b/fpga/usrp3/lib/rfnoc/core/axis_ctrl_slave.v
new file mode 100644
index 000000000..558d21be2
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/axis_ctrl_slave.v
@@ -0,0 +1,333 @@
+//
+// Copyright 2018-2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axis_ctrl_slave
+// Description:
+// This module implements an AXIS-Control slave (and a Control-Port
+// master). Requests are accepted on the slave axis port and responses
+// are sent out on the master axis port. This module implements the
+// following operations: {SLEEP, READ, WRITE}. All other operations
+// will be treated as a nop and the output will throw a CMDERR.
+//
+// Parameters:
+// None
+//
+// Signals:
+// - s_axis_ctrl_* : Input control stream (AXI-Stream) for requests
+// - m_axis_ctrl_* : Output control stream (AXI-Stream) for responses
+// - ctrlport_req_* : Control-port master request port
+// - ctrlport_resp_* : Control-port master response port
+
+module axis_ctrl_slave (
+ // CHDR Bus (master and slave)
+ input wire clk,
+ input wire rst,
+ // AXIS-Control Bus (Request)
+ input wire [31:0] s_axis_ctrl_tdata,
+ input wire s_axis_ctrl_tlast,
+ input wire s_axis_ctrl_tvalid,
+ output wire s_axis_ctrl_tready,
+ // AXIS-Control Bus (Response)
+ output wire [31:0] m_axis_ctrl_tdata,
+ output wire m_axis_ctrl_tlast,
+ output wire m_axis_ctrl_tvalid,
+ input wire m_axis_ctrl_tready,
+ // Control Port Endpoint (Request)
+ output wire ctrlport_req_wr,
+ output wire ctrlport_req_rd,
+ output wire [19:0] ctrlport_req_addr,
+ output wire [31:0] ctrlport_req_data,
+ output wire [3:0] ctrlport_req_byte_en,
+ output wire ctrlport_req_has_time,
+ output wire [63:0] ctrlport_req_time,
+ // Control Port Endpoint (Response)
+ input wire ctrlport_resp_ack,
+ input wire [1:0] ctrlport_resp_status,
+ input wire [31:0] ctrlport_resp_data
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_axis_ctrl_utils.vh"
+
+ // ---------------------------------------------------
+ // Width converters
+ // ---------------------------------------------------
+ // Convert 32-bit messages to 64 bits for ease of handling
+ // and buffering. Convert back to 32 bits.
+
+ wire [63:0] in64_tdata;
+ wire [1:0] in64_tkeep;
+ wire in64_tlast, in64_tvalid;
+ reg in64_tready;
+
+ axis_width_conv #(
+ .WORD_W(32), .IN_WORDS(1), .OUT_WORDS(2),
+ .SYNC_CLKS(1), .PIPELINE("OUT")
+ ) upsizer_i (
+ .s_axis_aclk(clk), .s_axis_rst(rst),
+ .s_axis_tdata(s_axis_ctrl_tdata), .s_axis_tkeep(1'b1),
+ .s_axis_tlast(s_axis_ctrl_tlast),
+ .s_axis_tvalid(s_axis_ctrl_tvalid), .s_axis_tready(s_axis_ctrl_tready),
+ .m_axis_aclk(clk), .m_axis_rst(rst),
+ .m_axis_tdata(in64_tdata), .m_axis_tkeep(in64_tkeep),
+ .m_axis_tlast(in64_tlast),
+ .m_axis_tvalid(in64_tvalid), .m_axis_tready(in64_tready)
+ );
+
+ reg [63:0] out64_tdata;
+ wire [1:0] out64_tkeep;
+ reg out64_tvalid;
+ wire out64_tlast, out64_terror, out64_tready;
+
+ wire [63:0] out64_gt_tdata;
+ wire [1:0] out64_gt_tkeep;
+ wire out64_gt_tlast, out64_gt_tvalid, out64_gt_tready;
+
+ // The header of the response packet is generated
+ // immediately when a request is received but the data
+ // comes much later. The packet gate will smooth out the
+ // outgoing responses.
+
+ axi_packet_gate #(
+ .WIDTH(66), .SIZE(4)
+ ) gate_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata({out64_tkeep, out64_tdata}), .i_tlast(out64_tlast),
+ .i_terror(out64_terror),
+ .i_tvalid(out64_tvalid), .i_tready(out64_tready),
+ .o_tdata({out64_gt_tkeep, out64_gt_tdata}), .o_tlast(out64_gt_tlast),
+ .o_tvalid(out64_gt_tvalid), .o_tready(out64_gt_tready)
+ );
+
+ axis_width_conv #(
+ .WORD_W(32), .IN_WORDS(2), .OUT_WORDS(1),
+ .SYNC_CLKS(1), .PIPELINE("IN")
+ ) downsizer_i (
+ .s_axis_aclk(clk), .s_axis_rst(rst),
+ .s_axis_tdata(out64_gt_tdata), .s_axis_tkeep(out64_gt_tkeep),
+ .s_axis_tlast(out64_gt_tlast),
+ .s_axis_tvalid(out64_gt_tvalid), .s_axis_tready(out64_gt_tready),
+ .m_axis_aclk(clk), .m_axis_rst(rst),
+ .m_axis_tdata(m_axis_ctrl_tdata), .m_axis_tkeep(/*unused*/),
+ .m_axis_tlast(m_axis_ctrl_tlast),
+ .m_axis_tvalid(m_axis_ctrl_tvalid), .m_axis_tready(m_axis_ctrl_tready)
+ );
+
+ // ---------------------------------------------------
+ // Transaction Processor
+ // ---------------------------------------------------
+
+ localparam [2:0] ST_IN_HDR = 3'd0; // Transferring input header to output
+ localparam [2:0] ST_IN_TS = 3'd1; // Transferring input timestamp to output
+ localparam [2:0] ST_IN_OP_WORD = 3'd2; // Processing input control word
+ localparam [2:0] ST_WAIT_FOR_ACK = 3'd3; // Waiting for a ctrlport response
+ localparam [2:0] ST_SLEEP = 3'd4; // Idle state for sleep operation
+ localparam [2:0] ST_OUT_OP_WORD = 3'd5; // Outputing control word after respose receipt
+ localparam [2:0] ST_MORE_DATA = 3'd6; // Control word is too long. Passing extra data forward
+ localparam [2:0] ST_DROP = 3'd7; // Something went wrong. Drop the current packet
+
+ // State variables
+ reg [2:0] state = ST_IN_HDR; // Current state of FSM
+ reg [31:0] sleep_cntr = 32'd0; // Counter to count sleep cycles
+ reg cached_has_time = 1'b0; // Cached "has_time" bit for input transaction request
+ reg [63:0] cached_time; // Cached timestamp for input transaction request
+ reg [1:0] resp_status; // Status for outgoing response
+ reg [31:0] resp_data; // Data for outgoing response
+
+ // Sleep is an internal operation
+ wire ctrlport_req_sleep;
+
+ // Shortcuts (transaction request header)
+ wire is_ack = axis_ctrl_get_is_ack (in64_tdata[31:0] );
+ wire has_time = axis_ctrl_get_has_time (in64_tdata[31:0] );
+ wire [5:0] seq_num = axis_ctrl_get_seq_num (in64_tdata[31:0] );
+ wire [3:0] num_data = axis_ctrl_get_num_data (in64_tdata[31:0] );
+ wire [9:0] src_port = axis_ctrl_get_src_port (in64_tdata[31:0] );
+ wire [9:0] dst_port = axis_ctrl_get_dst_port (in64_tdata[31:0] );
+ wire [9:0] rem_dst_port = axis_ctrl_get_rem_dst_port(in64_tdata[63:32]);
+ wire [15:0] rem_dst_epid = axis_ctrl_get_rem_dst_epid(in64_tdata[63:32]);
+ wire malformed = (is_ack || num_data == 4'd0);
+ // Shortcuts (transaction request op-word)
+ wire [19:0] xact_address = axis_ctrl_get_address(in64_tdata[31:0]);
+ wire [3:0] xact_byte_en = axis_ctrl_get_byte_en(in64_tdata[31:0]);
+ wire [3:0] xact_opcode = axis_ctrl_get_opcode (in64_tdata[31:0]);
+ wire [31:0] xact_data = in64_tdata[63:32];
+
+ always @(posedge clk) begin
+ if (rst) begin
+ state <= ST_IN_HDR;
+ end else begin
+ case (state)
+
+ // Receive an AXIS-Control request
+ // (a state for each stage in the packet)
+ // Except for the OP_WORD stage, the appropriate response
+ // line is also pushed to the output
+ // ------------------------------------
+ ST_IN_HDR: begin
+ if (in64_tvalid && in64_tready) begin
+ cached_has_time <= has_time;
+ if (!in64_tlast) begin
+ if (malformed) // Malformed packet. Drop.
+ state <= ST_DROP;
+ else if (has_time) // Pkt has a timestamp
+ state <= ST_IN_TS;
+ else // Pkt has no timestamp
+ state <= ST_IN_OP_WORD;
+ end else begin
+ // Premature termination
+ // out64_terror will be asserted to cancel the outgoing response
+ state <= ST_IN_HDR;
+ end
+ end
+ end
+ ST_IN_TS: begin
+ if (in64_tvalid && in64_tready) begin
+ cached_time <= in64_tdata;
+ if (!in64_tlast) begin
+ state <= ST_IN_OP_WORD;
+ end else begin
+ // Premature termination
+ // out64_terror will be asserted to cancel the outgoing response
+ state <= ST_IN_HDR;
+ end
+ end
+ end
+ ST_IN_OP_WORD: begin
+ if (in64_tvalid) begin
+ if (ctrlport_req_sleep) begin
+ state <= ST_SLEEP;
+ sleep_cntr <= xact_data;
+ end else if (ctrlport_req_rd | ctrlport_req_wr) begin
+ state <= ST_WAIT_FOR_ACK;
+ end else begin
+ // Treat all other operations as a NOP (1 cycle sleep)
+ state <= ST_SLEEP;
+ sleep_cntr <= 32'd0;
+ resp_status <= AXIS_CTRL_STS_CMDERR;
+ end
+ end
+ end
+
+ // Hold the input bus to implement a sleep
+ // ------------------------------------
+ ST_SLEEP: begin
+ if (sleep_cntr == 32'd0) begin
+ state <= ST_OUT_OP_WORD;
+ resp_data <= xact_data;
+ // We could get to this state for an invalid opcode so
+ // only update the status if this is a legit sleep op
+ if (xact_opcode == AXIS_CTRL_OPCODE_SLEEP)
+ resp_status <= AXIS_CTRL_STS_OKAY;
+ end else begin
+ sleep_cntr <= sleep_cntr - 32'd1;
+ end
+ end
+
+ // Wait for a response on the master ctrlport
+ // ------------------------------------
+ ST_WAIT_FOR_ACK: begin
+ if (ctrlport_resp_ack) begin
+ resp_status <= ctrlport_resp_status;
+ if (xact_opcode == AXIS_CTRL_OPCODE_READ ||
+ xact_opcode == AXIS_CTRL_OPCODE_WRITE_READ)
+ resp_data <= ctrlport_resp_data;
+ else
+ resp_data <= xact_data;
+ state <= ST_OUT_OP_WORD;
+ end
+ end
+
+ // Send the AXIS-Control response data
+ // ------------------------------------
+ ST_OUT_OP_WORD: begin
+ if (in64_tvalid && in64_tready) begin
+ state <= in64_tlast ? ST_IN_HDR : ST_MORE_DATA;
+ end
+ end
+
+ // Framing error handlers
+ // ------------------------------------
+ ST_MORE_DATA: begin
+ if (in64_tvalid && in64_tready && in64_tlast)
+ state <= ST_IN_HDR;
+ end
+ ST_DROP: begin
+ if (in64_tvalid && in64_tready && in64_tlast)
+ state <= ST_IN_HDR;
+ end
+
+ default: begin
+ // We should never get here
+ state <= ST_IN_HDR;
+ end
+ endcase
+ end
+ end
+
+ always @(*) begin
+ case (state)
+ ST_IN_HDR: begin // Swap src/dst and add resp flag when passing header
+ in64_tready = out64_tready;
+ out64_tdata = {
+ axis_ctrl_build_hdr_hi(rem_dst_port, rem_dst_epid),
+ axis_ctrl_build_hdr_lo(1'b1, has_time, seq_num, num_data, dst_port, src_port)
+ };
+ out64_tvalid = in64_tvalid && !malformed;
+ end
+ ST_IN_TS: begin // Pass input to the output without modification
+ in64_tready = out64_tready;
+ out64_tdata = in64_tdata;
+ out64_tvalid = in64_tvalid;
+ end
+ ST_OUT_OP_WORD: begin // Update status and data when passing op-word
+ in64_tready = out64_tready;
+ out64_tdata = {
+ resp_data,
+ axis_ctrl_build_op_word(resp_status, xact_opcode, xact_byte_en, xact_address)
+ };
+ out64_tvalid = in64_tvalid;
+ end
+ ST_MORE_DATA: begin // Pass input to the output without modification
+ in64_tready = out64_tready;
+ out64_tdata = in64_tdata;
+ out64_tvalid = in64_tvalid;
+ end
+ ST_DROP: begin // Consume input but don't produce output
+ in64_tready = 1'b1;
+ out64_tdata = 64'h0;
+ out64_tvalid = 1'b0;
+ end
+ default: begin // State machine is waiting. Don't produce output
+ in64_tready = 1'b0;
+ out64_tdata = 64'h0;
+ out64_tvalid = 1'b0;
+ end
+ endcase
+ end
+
+ assign out64_tlast = in64_tlast;
+ assign out64_tkeep = in64_tkeep;
+ assign out64_terror = (state == ST_IN_HDR || state == ST_IN_TS) && in64_tlast; //Premature termination
+
+ // Control-port request signals
+ assign ctrlport_req_sleep = in64_tvalid && (state == ST_IN_OP_WORD) &&
+ (xact_opcode == AXIS_CTRL_OPCODE_SLEEP);
+ assign ctrlport_req_wr = in64_tvalid && (state == ST_IN_OP_WORD) &&
+ (xact_opcode == AXIS_CTRL_OPCODE_WRITE ||
+ xact_opcode == AXIS_CTRL_OPCODE_WRITE_READ);
+ assign ctrlport_req_rd = in64_tvalid && (state == ST_IN_OP_WORD) &&
+ (xact_opcode == AXIS_CTRL_OPCODE_READ ||
+ xact_opcode == AXIS_CTRL_OPCODE_WRITE_READ);
+ assign ctrlport_req_addr = xact_address;
+ assign ctrlport_req_byte_en = xact_byte_en;
+ assign ctrlport_req_data = xact_data;
+ assign ctrlport_req_has_time = cached_has_time;
+ assign ctrlport_req_time = cached_time;
+
+endmodule // axis_ctrl_slave
diff --git a/fpga/usrp3/lib/rfnoc/core/axis_data_mdata_to_chdr.v b/fpga/usrp3/lib/rfnoc/core/axis_data_mdata_to_chdr.v
new file mode 100644
index 000000000..dbeb35d08
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/axis_data_mdata_to_chdr.v
@@ -0,0 +1,603 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axis_data_mdata_to_chdr
+//
+// Description:
+//
+// A framer module for CHDR data packets. It accepts an input data stream
+// (with sideband information for packet flags and timestamp) and a separate
+// metadata stream. A data packet and a metadata packet are required to be
+// input in order for a single CHDR packet to be generated. If no metadata is
+// associated with the payload, then an empty metadata packet must be input
+// along with the data packet (i.e., input a metadata packet with
+// s_axis_mdata_tkeep set to 0).
+//
+// The sideband information (e.g., timestamp, flags) must be input coincident
+// with the AXI-Stream data input and will be sampled coincident with the
+// last word of data in the packet (i.e., when tlast is asserted).
+//
+// This module also performs an optional clock crossing and data width
+// conversion from a user requested width for the payload bus to CHDR_W.
+//
+// In order to guarantee a gapless CHDR data stream, the metadata packet
+// should be input before the end of the data packet, although this is not
+// required.
+//
+// Parameters:
+//
+// CHDR_W : Width of the input CHDR bus in bits
+// ITEM_W : Width of the output item bus in bits
+// NIPC : The number of output items delivered per cycle
+// SYNC_CLKS : Are the CHDR and data clocks synchronous to each other?
+// MTU : Log2 of the maximum packet size in CHDR words
+// INFO_FIFO_SIZE : Log2 of the info FIFO size. This determines the number of
+// packets that can be simultaneously buffered in the
+// payload FIFO.
+// PYLD_FIFO_SIZE : Log2 of the payload FIFO size. The actual FIFO size will
+// be the maximum of 2**MTU or 2**PYLD_FIFO_SIZE, since the
+// FIFO must be at least one MTU so that we can calculate
+// the packet length in the header.
+//
+// Signals:
+//
+// m_axis_chdr_* : Output CHDR stream
+// s_axis_* : Input data stream (AXI-Stream)
+// s_axis_mdata_* : Input metadata stream (AXI-Stream)
+// flush_* : Signals for flush control and status
+//
+
+module axis_data_mdata_to_chdr #(
+ parameter CHDR_W = 256,
+ parameter ITEM_W = 32,
+ parameter NIPC = 2,
+ parameter SYNC_CLKS = 0,
+ parameter MTU = 10,
+ parameter INFO_FIFO_SIZE = 4,
+ parameter PYLD_FIFO_SIZE = MTU
+)(
+ // Clock, reset and settings
+ input wire axis_chdr_clk,
+ input wire axis_chdr_rst,
+ input wire axis_data_clk,
+ input wire axis_data_rst,
+ // CHDR out (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_chdr_tdata,
+ output wire m_axis_chdr_tlast,
+ output wire m_axis_chdr_tvalid,
+ input wire m_axis_chdr_tready,
+ // Payload data stream in (AXI-Stream)
+ input wire [(ITEM_W*NIPC)-1:0] s_axis_tdata,
+ input wire [NIPC-1:0] s_axis_tkeep,
+ input wire s_axis_tlast,
+ input wire s_axis_tvalid,
+ output wire s_axis_tready,
+ // Payload sideband info
+ input wire [63:0] s_axis_ttimestamp,
+ input wire s_axis_thas_time,
+ input wire s_axis_teov,
+ input wire s_axis_teob,
+ // Metadata stream in (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_mdata_tdata,
+ input wire s_axis_mdata_tlast,
+ input wire s_axis_mdata_tkeep,
+ input wire s_axis_mdata_tvalid,
+ output wire s_axis_mdata_tready,
+ // Flush signals
+ input wire flush_en,
+ input wire [31:0] flush_timeout,
+ output wire flush_active,
+ output wire flush_done
+);
+
+ // Make sure the metadata FIFO is large enough to store an entire packet's
+ // worth of metadata (32 words).
+ localparam MDATA_FIFO_SIZE = 5;
+
+ // Make sure the payload FIFO is large enough to store an entire packet's
+ // worth of payload data. This will ensure that we can buffer the entire
+ // packet to calculate its length.
+ localparam PAYLOAD_FIFO_SIZE = PYLD_FIFO_SIZE > MTU ?
+ PYLD_FIFO_SIZE : MTU;
+
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_axis_ctrl_utils.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Timestamp and Flags Capture
+ //---------------------------------------------------------------------------
+ //
+ // The timestamp and flags that we use for each packet is that of the last
+ // data word. Here, we capture this information at the end of the packet.
+ //
+ //---------------------------------------------------------------------------
+
+ reg [63:0] packet_timestamp;
+ reg packet_has_time;
+ reg packet_eov;
+ reg packet_eob;
+
+ always @(posedge axis_data_clk) begin
+ if (s_axis_tvalid & s_axis_tready & s_axis_tlast) begin
+ packet_timestamp <= s_axis_ttimestamp;
+ packet_has_time <= s_axis_thas_time;
+ packet_eov <= s_axis_teov;
+ packet_eob <= s_axis_teob;
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Length Counters
+ //---------------------------------------------------------------------------
+ //
+ // Here We track the state of the incoming packet to determine the payload
+ // and mdata length.
+ //
+ //---------------------------------------------------------------------------
+
+ localparam HDR_LEN = CHDR_W/8; // Length of CHDR header word in bytes
+
+ reg [15:0] packet_length;
+ reg [15:0] length_count = HDR_LEN;
+ reg in_pkt_info_tvalid = 0;
+ wire in_pkt_info_tready;
+
+ always @(posedge axis_data_clk) begin : pkt_length_counter
+ if (axis_data_rst) begin
+ length_count <= HDR_LEN;
+ in_pkt_info_tvalid <= 1'b0;
+ end else begin : pkt_length_counter_main
+ // Calculate the length of this word in bytes, taking tkeep into account
+ integer i;
+ integer num_bytes;
+ num_bytes = 0;
+ for (i = 0; i < NIPC; i = i + 1) begin
+ num_bytes = num_bytes + (s_axis_tkeep[i]*(ITEM_W/8));
+ end
+
+ // Update the packet length if the word is accepted
+ in_pkt_info_tvalid <= 1'b0;
+ if (s_axis_tvalid && s_axis_tready) begin
+ if (s_axis_tlast) begin
+ length_count <= HDR_LEN;
+ packet_length <= length_count + num_bytes;
+ in_pkt_info_tvalid <= 1'b1;
+ end else begin
+ length_count <= length_count + num_bytes;
+ end
+ end
+ end
+ end
+
+
+ reg [4:0] num_mdata = 0;
+ reg [4:0] mdata_count = 0;
+ reg in_mdata_info_tvalid = 0;
+ wire in_mdata_info_tready;
+
+ always @(posedge axis_data_clk) begin : num_mdata_counter
+ if (axis_data_rst) begin
+ mdata_count <= 0;
+ num_mdata <= 0;
+ in_mdata_info_tvalid <= 1'b0;
+ end else begin : num_mdata_counter_main
+ // Update the mdata length if the word is accepted
+ in_mdata_info_tvalid <= 1'b0;
+ if (s_axis_mdata_tvalid && s_axis_mdata_tready) begin
+ if (s_axis_mdata_tlast) begin
+ mdata_count <= 0;
+ num_mdata <= mdata_count + s_axis_mdata_tkeep;
+ in_mdata_info_tvalid <= 1'b1;
+ end else begin
+ mdata_count <= mdata_count + s_axis_mdata_tkeep;
+ end
+ end
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Data Width Converter (ITEM_W*NIPC => CHDR_W)
+ //---------------------------------------------------------------------------
+
+ wire [CHDR_W-1:0] in_pyld_tdata;
+ wire in_pyld_tlast;
+ wire in_pyld_tvalid;
+ wire in_pyld_tready;
+ wire width_conv_tready;
+
+ assign width_conv_tready = in_pyld_tready & in_pkt_info_tready;
+
+ generate
+ if (NIPC != CHDR_W/ITEM_W) begin : gen_axis_width_conv
+ axis_width_conv #(
+ .WORD_W (ITEM_W),
+ .IN_WORDS (NIPC),
+ .OUT_WORDS (CHDR_W/ITEM_W),
+ .SYNC_CLKS (1),
+ .PIPELINE ("IN")
+ ) payload_width_conv_i (
+ .s_axis_aclk (axis_data_clk),
+ .s_axis_rst (axis_data_rst),
+ .s_axis_tdata (s_axis_tdata),
+ .s_axis_tkeep ({NIPC{1'b1}}),
+ .s_axis_tlast (s_axis_tlast),
+ .s_axis_tvalid (s_axis_tvalid),
+ .s_axis_tready (s_axis_tready),
+ .m_axis_aclk (axis_data_clk),
+ .m_axis_rst (axis_data_rst),
+ .m_axis_tdata (in_pyld_tdata),
+ .m_axis_tkeep (),
+ .m_axis_tlast (in_pyld_tlast),
+ .m_axis_tvalid (in_pyld_tvalid),
+ .m_axis_tready (width_conv_tready)
+ );
+ end else begin : no_gen_axis_width_conv
+ assign in_pyld_tdata = s_axis_tdata;
+ assign in_pyld_tlast = s_axis_tlast;
+ assign in_pyld_tvalid = s_axis_tvalid;
+ assign s_axis_tready = width_conv_tready;
+ end
+ endgenerate
+
+
+ //---------------------------------------------------------------------------
+ // Input FIFOs
+ //---------------------------------------------------------------------------
+ //
+ // Buffer the data, packet info, metadata, and cross it into the CHDR clock
+ // domain, if needed. The payload FIFO is sized to match the MTU so that an
+ // entire packet can be buffered while the length is calculated.
+ //
+ //---------------------------------------------------------------------------
+
+ wire [CHDR_W-1:0] out_mdata_tdata, out_pyld_tdata;
+ wire out_mdata_tlast, out_pyld_tlast;
+ wire out_mdata_tvalid, out_pyld_tvalid;
+ reg out_mdata_tready, out_pyld_tready;
+
+ wire out_pkt_info_tvalid;
+ reg out_pkt_info_tready;
+ wire out_eob, out_eov, out_has_time;
+ wire [63:0] out_timestamp;
+ wire [15:0] out_length;
+
+ wire [4:0] out_num_mdata;
+ reg out_mdata_info_tready;
+ wire out_mdata_info_tvalid;
+
+ wire in_mdata_tready;
+
+
+ assign s_axis_mdata_tready = in_mdata_tready & in_mdata_info_tready;
+
+ generate if (SYNC_CLKS) begin : gen_sync_fifo
+ axi_fifo #(
+ .WIDTH (CHDR_W+1),
+ .SIZE (PAYLOAD_FIFO_SIZE)
+ ) pyld_fifo (
+ .clk (axis_chdr_clk),
+ .reset (axis_chdr_rst),
+ .clear (1'b0),
+ .i_tdata ({in_pyld_tlast, in_pyld_tdata}),
+ .i_tvalid (in_pyld_tvalid),
+ .i_tready (in_pyld_tready),
+ .o_tdata ({out_pyld_tlast, out_pyld_tdata}),
+ .o_tvalid (out_pyld_tvalid),
+ .o_tready (out_pyld_tready),
+ .space (),
+ .occupied ()
+ );
+ axi_fifo #(
+ .WIDTH (CHDR_W + 1),
+ .SIZE (MDATA_FIFO_SIZE)
+ ) mdata_fifo (
+ .clk (axis_chdr_clk),
+ .reset (axis_chdr_rst),
+ .clear (1'b0),
+ .i_tdata ({s_axis_mdata_tlast, s_axis_mdata_tdata}),
+ .i_tvalid (s_axis_mdata_tvalid),
+ .i_tready (in_mdata_tready),
+ .o_tdata ({out_mdata_tlast, out_mdata_tdata}),
+ .o_tvalid (out_mdata_tvalid),
+ .o_tready (out_mdata_tready),
+ .space (),
+ .occupied ()
+ );
+ axi_fifo #(
+ .WIDTH (3 + 64 + 16),
+ .SIZE (INFO_FIFO_SIZE)
+ ) pkt_info_fifo (
+ .clk (axis_chdr_clk),
+ .reset (axis_chdr_rst),
+ .clear (1'b0),
+ .i_tdata ({packet_eob, packet_eov, packet_has_time,packet_timestamp, packet_length}),
+ .i_tvalid (in_pkt_info_tvalid),
+ .i_tready (in_pkt_info_tready),
+ .o_tdata ({out_eob, out_eov, out_has_time, out_timestamp, out_length}),
+ .o_tvalid (out_pkt_info_tvalid),
+ .o_tready (out_pkt_info_tready),
+ .space (),
+ .occupied ()
+ );
+ axi_fifo #(
+ .WIDTH (5),
+ .SIZE (INFO_FIFO_SIZE)
+ ) mdata_info_fifo (
+ .clk (axis_chdr_clk),
+ .reset (axis_chdr_rst),
+ .clear (1'b0),
+ .i_tdata (num_mdata),
+ .i_tvalid (in_mdata_info_tvalid),
+ .i_tready (in_mdata_info_tready),
+ .o_tdata (out_num_mdata),
+ .o_tvalid (out_mdata_info_tvalid),
+ .o_tready (out_mdata_info_tready),
+ .space (),
+ .occupied ()
+ );
+
+ end else begin : gen_async_fifo
+ axi_fifo_2clk #(
+ .WIDTH (CHDR_W + 1),
+ .SIZE (PAYLOAD_FIFO_SIZE)
+ ) pyld_fifo (
+ .reset (axis_data_rst),
+ .i_aclk (axis_data_clk),
+ .i_tdata ({in_pyld_tlast, in_pyld_tdata}),
+ .i_tvalid (in_pyld_tvalid),
+ .i_tready (in_pyld_tready),
+ .o_aclk (axis_chdr_clk),
+ .o_tdata ({out_pyld_tlast, out_pyld_tdata}),
+ .o_tvalid (out_pyld_tvalid),
+ .o_tready (out_pyld_tready)
+ );
+ axi_fifo_2clk #(
+ .WIDTH (CHDR_W + 1),
+ .SIZE (MDATA_FIFO_SIZE)
+ ) mdata_fifo (
+ .reset (axis_data_rst),
+ .i_aclk (axis_data_clk),
+ .i_tdata ({s_axis_mdata_tlast, s_axis_mdata_tdata}),
+ .i_tvalid (s_axis_mdata_tvalid),
+ .i_tready (in_mdata_tready),
+ .o_aclk (axis_chdr_clk),
+ .o_tdata ({out_mdata_tlast, out_mdata_tdata}),
+ .o_tvalid (out_mdata_tvalid),
+ .o_tready (out_mdata_tready)
+ );
+ axi_fifo_2clk #(
+ .WIDTH (3 + 64 + 16),
+ .SIZE (INFO_FIFO_SIZE)
+ ) pkt_info_fifo (
+ .reset (axis_data_rst),
+ .i_aclk (axis_data_clk),
+ .i_tdata ({packet_eob, packet_eov, packet_has_time,packet_timestamp, packet_length}),
+ .i_tvalid (in_pkt_info_tvalid),
+ .i_tready (in_pkt_info_tready),
+ .o_aclk (axis_chdr_clk),
+ .o_tdata ({out_eob, out_eov, out_has_time, out_timestamp, out_length}),
+ .o_tvalid (out_pkt_info_tvalid),
+ .o_tready (out_pkt_info_tready)
+ );
+ axi_fifo_2clk #(
+ .WIDTH (5),
+ .SIZE (INFO_FIFO_SIZE)
+ ) mdata_info_fifo (
+ .reset (axis_data_rst),
+ .i_aclk (axis_data_clk),
+ .i_tdata (num_mdata),
+ .i_tvalid (in_mdata_info_tvalid),
+ .i_tready (in_mdata_info_tready),
+ .o_aclk (axis_chdr_clk),
+ .o_tdata (out_num_mdata),
+ .o_tvalid (out_mdata_info_tvalid),
+ .o_tready (out_mdata_info_tready)
+ );
+ end endgenerate
+
+
+ //---------------------------------------------------------------------------
+ // Output State Machine
+ //---------------------------------------------------------------------------
+
+ reg [CHDR_W-1:0] chdr_pf_tdata;
+ reg chdr_pf_tlast, chdr_pf_tvalid;
+ wire chdr_pf_tready;
+
+ localparam [1:0] ST_HDR = 0; // Processing the output CHDR header
+ localparam [1:0] ST_TS = 1; // Processing the output CHDR timestamp
+ localparam [1:0] ST_MDATA = 2; // Processing the output CHDR metadata word
+ localparam [1:0] ST_PYLD = 3; // Processing the output CHDR payload word
+
+ reg [1:0] state = ST_HDR;
+
+ reg [15:0] seq_num = 0;
+
+ wire [63:0] header;
+ reg [63:0] timestamp;
+ wire [15:0] length;
+ reg has_mdata;
+
+ // Some the payload, metadata, and timestamp lengths (out_length already
+ // includes the header).
+ assign length = (CHDR_W > 64) ?
+ out_length + out_num_mdata * (CHDR_W/8) :
+ out_length + out_num_mdata * (CHDR_W/8) + 8*out_has_time;
+
+ // Build the header word
+ assign header = chdr_build_header(
+ 6'b0, // vc
+ out_eob, // eob
+ out_eov, // eov
+ out_has_time ? CHDR_PKT_TYPE_DATA_TS :
+ CHDR_PKT_TYPE_DATA, // pkt_type
+ out_num_mdata, // num_mdata
+ seq_num, // seq_num
+ length, // length
+ 16'b0 // dst_epid
+ );
+
+ always @(posedge axis_chdr_clk) begin
+ if (axis_chdr_rst) begin
+ state <= ST_HDR;
+ seq_num <= 0;
+ end else begin
+ case (state)
+
+ // ST_HDR: CHDR Header
+ // -------------------
+ ST_HDR: begin
+ timestamp <= out_timestamp;
+ has_mdata <= (out_num_mdata != CHDR_NO_MDATA);
+
+ if (out_pkt_info_tvalid && out_mdata_info_tvalid && chdr_pf_tready) begin
+ if (CHDR_W > 64) begin
+ // When CHDR_W > 64, the timestamp is a part of the header word.
+ // If this is a data packet (with or without a TS), we skip the
+ // timestamp state move directly to metadata/body.
+ if (out_num_mdata == CHDR_NO_MDATA) begin
+ state <= ST_PYLD;
+ end else begin
+ state <= ST_MDATA;
+ end
+ end else begin
+ // When CHDR_W == 64, the timestamp comes after the header. Check
+ // if this is a data packet with a timestamp or metadata to
+ // figure out the next state.
+ if (out_has_time) begin
+ state <= ST_TS;
+ end else if (out_num_mdata != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_PYLD;
+ end
+ end
+ end
+ end
+
+ // ST_TS: Timestamp (CHDR_W == 64 only)
+ // ------------------------------------
+ ST_TS: begin
+ if (chdr_pf_tready) begin
+ state <= has_mdata ? ST_MDATA : ST_PYLD;
+ end
+ end
+
+ // ST_MDATA: Metadata word
+ // -----------------------
+ ST_MDATA: begin
+ if (out_mdata_tvalid && out_mdata_tready && out_mdata_tlast) begin
+ state <= ST_PYLD;
+ end
+ end
+
+ // ST_PYLD: Payload word
+ // ---------------------
+ ST_PYLD: begin
+ if (out_pyld_tvalid && out_pyld_tready && out_pyld_tlast) begin
+ state <= ST_HDR;
+ seq_num <= seq_num + 1;
+ end
+ end
+
+ default: begin
+ // We should never get here
+ state <= ST_HDR;
+ end
+ endcase
+ end
+ end
+
+ always @(*) begin
+ case (state)
+ ST_HDR: begin
+ // Insert header word
+ chdr_pf_tdata = (CHDR_W > 64) ? { out_timestamp, header } : header;
+ chdr_pf_tvalid = out_pkt_info_tvalid & out_mdata_info_tvalid;
+ chdr_pf_tlast = 1'b0;
+ out_mdata_tready = chdr_pf_tready & // Remove empty mdata packet from FIFO
+ (out_num_mdata == CHDR_NO_MDATA);
+ out_mdata_info_tready = chdr_pf_tready; // Remove mdata info word from FIFO
+ out_pyld_tready = 1'b0;
+ out_pkt_info_tready = chdr_pf_tready; // Remove packet info word from FIFO
+ end
+ ST_TS: begin
+ // Insert timestamp
+ chdr_pf_tdata[63:0] = timestamp;
+ chdr_pf_tvalid = 1'b1; // Timestamp register is always valid in this state
+ chdr_pf_tlast = 1'b0;
+ out_mdata_tready = 1'b0;
+ out_mdata_info_tready = 1'b0;
+ out_pyld_tready = 1'b0;
+ out_pkt_info_tready = 1'b0;
+ end
+ ST_MDATA: begin
+ // Insert mdata words
+ chdr_pf_tdata = out_mdata_tdata;
+ chdr_pf_tvalid = out_mdata_tvalid;
+ chdr_pf_tlast = 1'b0;
+ out_mdata_tready = chdr_pf_tready;
+ out_mdata_info_tready = 1'b0;
+ out_pyld_tready = 1'b0;
+ out_pkt_info_tready = 1'b0;
+ end
+ ST_PYLD: begin
+ // Insert payload words
+ chdr_pf_tdata = out_pyld_tdata;
+ chdr_pf_tvalid = out_pyld_tvalid;
+ chdr_pf_tlast = out_pyld_tlast;
+ out_mdata_tready = 1'b0;
+ out_mdata_info_tready = 1'b0;
+ out_pyld_tready = chdr_pf_tready;
+ out_pkt_info_tready = 1'b0;
+ end
+ default: begin
+ chdr_pf_tdata = out_pyld_tdata;
+ chdr_pf_tvalid = 1'b0;
+ chdr_pf_tlast = 1'b0;
+ out_mdata_tready = 1'b0;
+ out_mdata_info_tready = 1'b0;
+ out_pyld_tready = 1'b0;
+ out_pkt_info_tready = 1'b0;
+ end
+ endcase
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Flushing Logic
+ //---------------------------------------------------------------------------
+
+ axis_packet_flush #(
+ .WIDTH (CHDR_W),
+ .FLUSH_PARTIAL_PKTS (0),
+ .TIMEOUT_W (32),
+ .PIPELINE ("IN")
+ ) chdr_flusher_i (
+ .clk (axis_chdr_clk),
+ .reset (axis_chdr_rst),
+ .enable (flush_en),
+ .timeout (flush_timeout),
+ .flushing (flush_active),
+ .done (flush_done),
+ .s_axis_tdata (chdr_pf_tdata),
+ .s_axis_tlast (chdr_pf_tlast),
+ .s_axis_tvalid (chdr_pf_tvalid),
+ .s_axis_tready (chdr_pf_tready),
+ .m_axis_tdata (m_axis_chdr_tdata),
+ .m_axis_tlast (m_axis_chdr_tlast),
+ .m_axis_tvalid (m_axis_chdr_tvalid),
+ .m_axis_tready (m_axis_chdr_tready)
+ );
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/core/axis_data_to_chdr.v b/fpga/usrp3/lib/rfnoc/core/axis_data_to_chdr.v
new file mode 100644
index 000000000..6a5b3ce05
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/axis_data_to_chdr.v
@@ -0,0 +1,452 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axis_data_to_chdr
+//
+// Description:
+//
+// A framer module for CHDR data packets. It accepts an input data stream
+// with sideband information for packet flags and timestamp). A CHDR packet
+// will be generated for each data packet that is input.
+//
+// The sideband information (e.g., timestamp, flags) must be input coincident
+// with the AXI-Stream data input and will be sampled coincident with the
+// last word of data in the packet (i.e., when tlast is asserted).
+//
+// This module also performs an optional clock crossing and data width
+// conversion from a user requested width for the payload bus to CHDR_W.
+//
+// Parameters:
+//
+// CHDR_W : Width of the input CHDR bus in bits
+// ITEM_W : Width of the output item bus in bits
+// NIPC : The number of output items delivered per cycle
+// SYNC_CLKS : Are the CHDR and data clocks synchronous to each other?
+// MTU : Log2 of the maximum packet size in CHDR words
+// INFO_FIFO_SIZE : Log2 of the info FIFO size. This determines the number of
+// packets that can be simultaneously buffered in the
+// payload FIFO.
+// PYLD_FIFO_SIZE : Log2 of the payload FIFO size. The actual FIFO size will
+// be the maximum of 2**MTU or 2**PYLD_FIFO_SIZE, since the
+// FIFO must be at least one MTU so that we can calculate
+// the packet length in the header.
+//
+// Signals:
+//
+// m_axis_chdr_* : Output CHDR stream
+// s_axis_* : Input data stream (AXI-Stream)
+// flush_* : Signals for flush control and status
+//
+
+module axis_data_to_chdr #(
+ parameter CHDR_W = 256,
+ parameter ITEM_W = 32,
+ parameter NIPC = 2,
+ parameter SYNC_CLKS = 0,
+ parameter MTU = 10,
+ parameter INFO_FIFO_SIZE = 5,
+ parameter PYLD_FIFO_SIZE = MTU
+)(
+ // Clock, reset and settings
+ input wire axis_chdr_clk,
+ input wire axis_chdr_rst,
+ input wire axis_data_clk,
+ input wire axis_data_rst,
+ // CHDR out (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_chdr_tdata,
+ output wire m_axis_chdr_tlast,
+ output wire m_axis_chdr_tvalid,
+ input wire m_axis_chdr_tready,
+ // Payload data stream in (AXI-Stream)
+ input wire [(ITEM_W*NIPC)-1:0] s_axis_tdata,
+ input wire [NIPC-1:0] s_axis_tkeep,
+ input wire s_axis_tlast,
+ input wire s_axis_tvalid,
+ output wire s_axis_tready,
+ // Payload sideband info
+ input wire [63:0] s_axis_ttimestamp,
+ input wire s_axis_thas_time,
+ input wire s_axis_teov,
+ input wire s_axis_teob,
+ // Flush signals
+ input wire flush_en,
+ input wire [31:0] flush_timeout,
+ output wire flush_active,
+ output wire flush_done
+);
+
+ // Make sure the payload FIFO is large enough to store an entire packet's
+ // worth of payload data. This will ensure that we can buffer the entire
+ // packet to calculate its length.
+ localparam PAYLOAD_FIFO_SIZE = PYLD_FIFO_SIZE > MTU ?
+ PYLD_FIFO_SIZE : MTU;
+
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_axis_ctrl_utils.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Timestamp and Flags Capture
+ //---------------------------------------------------------------------------
+ //
+ // The timestamp and flags that we use for each packet is that of the last
+ // data word. Here, we capture this information at the end of the packet.
+ //
+ //---------------------------------------------------------------------------
+
+ reg [63:0] packet_timestamp;
+ reg packet_has_time;
+ reg packet_eov;
+ reg packet_eob;
+
+ always @(posedge axis_data_clk) begin
+ if (s_axis_tvalid & s_axis_tready & s_axis_tlast) begin
+ packet_timestamp <= s_axis_ttimestamp;
+ packet_has_time <= s_axis_thas_time;
+ packet_eov <= s_axis_teov;
+ packet_eob <= s_axis_teob;
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Length Counters
+ //---------------------------------------------------------------------------
+ //
+ // Here We track the state of the incoming packet to determine the payload
+ // length.
+ //
+ //---------------------------------------------------------------------------
+
+ localparam HDR_LEN = CHDR_W/8; // Length of CHDR header word in bytes
+
+ reg [15:0] packet_length;
+ reg [15:0] length_count = HDR_LEN;
+ reg in_pkt_info_tvalid = 0;
+ wire in_pkt_info_tready;
+
+ always @(posedge axis_data_clk) begin : pkt_length_counter
+ if (axis_data_rst) begin
+ length_count <= HDR_LEN;
+ in_pkt_info_tvalid <= 1'b0;
+ end else begin : pkt_length_counter_main
+ // Calculate the length of this word in bytes, taking tkeep into account
+ integer i;
+ integer num_bytes;
+ num_bytes = 0;
+ for (i = 0; i < NIPC; i = i + 1) begin
+ num_bytes = num_bytes + (s_axis_tkeep[i]*(ITEM_W/8));
+ end
+
+ // Update the packet length if the word is accepted
+ in_pkt_info_tvalid <= 1'b0;
+ if (s_axis_tvalid && s_axis_tready) begin
+ if (s_axis_tlast) begin
+ length_count <= HDR_LEN;
+ packet_length <= length_count + num_bytes;
+ in_pkt_info_tvalid <= 1'b1;
+ end else begin
+ length_count <= length_count + num_bytes;
+ end
+ end
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Data Width Converter (ITEM_W*NIPC => CHDR_W)
+ //---------------------------------------------------------------------------
+
+ wire [CHDR_W-1:0] in_pyld_tdata;
+ wire in_pyld_tlast;
+ wire in_pyld_tvalid;
+ wire in_pyld_tready;
+ wire width_conv_tready;
+
+ assign width_conv_tready = in_pyld_tready & in_pkt_info_tready;
+
+ generate
+ if (NIPC != CHDR_W/ITEM_W) begin : gen_axis_width_conv
+ axis_width_conv #(
+ .WORD_W (ITEM_W),
+ .IN_WORDS (NIPC),
+ .OUT_WORDS (CHDR_W/ITEM_W),
+ .SYNC_CLKS (1),
+ .PIPELINE ("IN")
+ ) payload_width_conv_i (
+ .s_axis_aclk (axis_data_clk),
+ .s_axis_rst (axis_data_rst),
+ .s_axis_tdata (s_axis_tdata),
+ .s_axis_tkeep ({NIPC{1'b1}}),
+ .s_axis_tlast (s_axis_tlast),
+ .s_axis_tvalid (s_axis_tvalid),
+ .s_axis_tready (s_axis_tready),
+ .m_axis_aclk (axis_data_clk),
+ .m_axis_rst (axis_data_rst),
+ .m_axis_tdata (in_pyld_tdata),
+ .m_axis_tkeep (),
+ .m_axis_tlast (in_pyld_tlast),
+ .m_axis_tvalid (in_pyld_tvalid),
+ .m_axis_tready (width_conv_tready)
+ );
+ end else begin : no_gen_axis_width_conv
+ assign in_pyld_tdata = s_axis_tdata;
+ assign in_pyld_tlast = s_axis_tlast;
+ assign in_pyld_tvalid = s_axis_tvalid;
+ assign s_axis_tready = width_conv_tready;
+ end
+ endgenerate
+
+
+ //---------------------------------------------------------------------------
+ // Input FIFOs
+ //---------------------------------------------------------------------------
+ //
+ // Buffer the data, packet info, metadata, and cross it into the CHDR clock
+ // domain, if needed. The payload FIFO is sized to match the MTU so that an
+ // entire packet can be buffered while the length is calculated.
+ //
+ //---------------------------------------------------------------------------
+
+ wire [CHDR_W-1:0] out_pyld_tdata;
+ wire out_pyld_tlast;
+ wire out_pyld_tvalid;
+ reg out_pyld_tready;
+
+ wire out_pkt_info_tvalid;
+ reg out_pkt_info_tready;
+ wire out_eob, out_eov, out_has_time;
+ wire [63:0] out_timestamp;
+ wire [15:0] out_length;
+
+ generate if (SYNC_CLKS) begin : gen_sync_fifo
+ axi_fifo #(
+ .WIDTH (CHDR_W+1),
+ .SIZE (PAYLOAD_FIFO_SIZE)
+ ) pyld_fifo (
+ .clk (axis_chdr_clk),
+ .reset (axis_chdr_rst),
+ .clear (1'b0),
+ .i_tdata ({in_pyld_tlast, in_pyld_tdata}),
+ .i_tvalid (in_pyld_tvalid),
+ .i_tready (in_pyld_tready),
+ .o_tdata ({out_pyld_tlast, out_pyld_tdata}),
+ .o_tvalid (out_pyld_tvalid),
+ .o_tready (out_pyld_tready),
+ .space (),
+ .occupied ()
+ );
+ axi_fifo #(
+ .WIDTH (3 + 64 + 16),
+ .SIZE (INFO_FIFO_SIZE)
+ ) pkt_info_fifo (
+ .clk (axis_chdr_clk),
+ .reset (axis_chdr_rst),
+ .clear (1'b0),
+ .i_tdata ({packet_eob, packet_eov, packet_has_time,packet_timestamp, packet_length}),
+ .i_tvalid (in_pkt_info_tvalid),
+ .i_tready (in_pkt_info_tready),
+ .o_tdata ({out_eob, out_eov, out_has_time, out_timestamp, out_length}),
+ .o_tvalid (out_pkt_info_tvalid),
+ .o_tready (out_pkt_info_tready),
+ .space (),
+ .occupied ()
+ );
+
+ end else begin : gen_async_fifo
+ axi_fifo_2clk #(
+ .WIDTH (CHDR_W + 1),
+ .SIZE (PAYLOAD_FIFO_SIZE)
+ ) pyld_fifo (
+ .reset (axis_data_rst),
+ .i_aclk (axis_data_clk),
+ .i_tdata ({in_pyld_tlast, in_pyld_tdata}),
+ .i_tvalid (in_pyld_tvalid),
+ .i_tready (in_pyld_tready),
+ .o_aclk (axis_chdr_clk),
+ .o_tdata ({out_pyld_tlast, out_pyld_tdata}),
+ .o_tvalid (out_pyld_tvalid),
+ .o_tready (out_pyld_tready)
+ );
+ axi_fifo_2clk #(
+ .WIDTH (3 + 64 + 16),
+ .SIZE (INFO_FIFO_SIZE)
+ ) pkt_info_fifo (
+ .reset (axis_data_rst),
+ .i_aclk (axis_data_clk),
+ .i_tdata ({packet_eob, packet_eov, packet_has_time,packet_timestamp, packet_length}),
+ .i_tvalid (in_pkt_info_tvalid),
+ .i_tready (in_pkt_info_tready),
+ .o_aclk (axis_chdr_clk),
+ .o_tdata ({out_eob, out_eov, out_has_time, out_timestamp, out_length}),
+ .o_tvalid (out_pkt_info_tvalid),
+ .o_tready (out_pkt_info_tready)
+ );
+ end endgenerate
+
+
+ //---------------------------------------------------------------------------
+ // Output State Machine
+ //---------------------------------------------------------------------------
+
+ reg [CHDR_W-1:0] chdr_pf_tdata;
+ reg chdr_pf_tlast, chdr_pf_tvalid;
+ wire chdr_pf_tready;
+
+ localparam [1:0] ST_HDR = 0; // Processing the output CHDR header
+ localparam [1:0] ST_TS = 1; // Processing the output CHDR timestamp
+ localparam [1:0] ST_PYLD = 2; // Processing the output CHDR payload word
+
+ reg [1:0] state = ST_HDR;
+
+ reg [15:0] seq_num = 0;
+
+ wire [63:0] header;
+ reg [63:0] timestamp;
+ wire [15:0] length;
+
+ // Some the payload, metadata, and timestamp lengths (out_length already
+ // includes the header).
+ assign length = (CHDR_W > 64) ? out_length : out_length + 8*out_has_time;
+
+ // Build the header word
+ assign header = chdr_build_header(
+ 6'b0, // vc
+ out_eob, // eob
+ out_eov, // eov
+ out_has_time ? CHDR_PKT_TYPE_DATA_TS :
+ CHDR_PKT_TYPE_DATA, // pkt_type
+ 0, // num_mdata
+ seq_num, // seq_num
+ length, // length
+ 16'b0 // dst_epid
+ );
+
+ always @(posedge axis_chdr_clk) begin
+ if (axis_chdr_rst) begin
+ state <= ST_HDR;
+ seq_num <= 0;
+ end else begin
+ case (state)
+
+ // ST_HDR: CHDR Header
+ // -------------------
+ ST_HDR: begin
+ timestamp <= out_timestamp;
+
+ if (out_pkt_info_tvalid && chdr_pf_tready) begin
+ seq_num <= seq_num + 1;
+
+ if (CHDR_W > 64) begin
+ // When CHDR_W > 64, the timestamp is a part of the header word.
+ // If this is a data packet (with or without a TS), we skip the
+ // timestamp state move directly to the payload.
+ state <= ST_PYLD;
+ end else begin
+ // When CHDR_W == 64, the timestamp comes after the header. Check
+ // if this is a data packet with a timestamp to figure out the
+ // next state.
+ if (out_has_time) begin
+ state <= ST_TS;
+ end else begin
+ state <= ST_PYLD;
+ end
+ end
+ end
+ end
+
+ // ST_TS: Timestamp (CHDR_W == 64 only)
+ // ------------------------------------
+ ST_TS: begin
+ if (chdr_pf_tready) begin
+ state <= ST_PYLD;
+ end
+ end
+
+ // ST_PYLD: Payload word
+ // ---------------------
+ ST_PYLD: begin
+ if (out_pyld_tvalid && out_pyld_tready && out_pyld_tlast) begin
+ state <= ST_HDR;
+ end
+ end
+
+ default: begin
+ // We should never get here
+ state <= ST_HDR;
+ end
+ endcase
+ end
+ end
+
+ always @(*) begin
+ case (state)
+ ST_HDR: begin
+ // Insert header word
+ chdr_pf_tdata = (CHDR_W > 64) ? { out_timestamp, header } : header;
+ chdr_pf_tvalid = out_pkt_info_tvalid;
+ chdr_pf_tlast = 1'b0;
+ out_pyld_tready = 1'b0;
+ out_pkt_info_tready = chdr_pf_tready; // Remove packet info word from FIFO
+ end
+ ST_TS: begin
+ // Insert timestamp
+ chdr_pf_tdata[63:0] = timestamp;
+ chdr_pf_tvalid = 1'b1; // Timestamp register is always valid in this state
+ chdr_pf_tlast = 1'b0;
+ out_pyld_tready = 1'b0;
+ out_pkt_info_tready = 1'b0;
+ end
+ ST_PYLD: begin
+ // Insert payload words
+ chdr_pf_tdata = out_pyld_tdata;
+ chdr_pf_tvalid = out_pyld_tvalid;
+ chdr_pf_tlast = out_pyld_tlast;
+ out_pyld_tready = chdr_pf_tready;
+ out_pkt_info_tready = 1'b0;
+ end
+ default: begin
+ chdr_pf_tdata = out_pyld_tdata;
+ chdr_pf_tvalid = 1'b0;
+ chdr_pf_tlast = 1'b0;
+ out_pyld_tready = 1'b0;
+ out_pkt_info_tready = 1'b0;
+ end
+ endcase
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Flushing Logic
+ //---------------------------------------------------------------------------
+
+ axis_packet_flush #(
+ .WIDTH (CHDR_W),
+ .FLUSH_PARTIAL_PKTS (0),
+ .TIMEOUT_W (32),
+ .PIPELINE ("IN")
+ ) chdr_flusher_i (
+ .clk (axis_chdr_clk),
+ .reset (axis_chdr_rst),
+ .enable (flush_en),
+ .timeout (flush_timeout),
+ .flushing (flush_active),
+ .done (flush_done),
+ .s_axis_tdata (chdr_pf_tdata),
+ .s_axis_tlast (chdr_pf_tlast),
+ .s_axis_tvalid (chdr_pf_tvalid),
+ .s_axis_tready (chdr_pf_tready),
+ .m_axis_tdata (m_axis_chdr_tdata),
+ .m_axis_tlast (m_axis_chdr_tlast),
+ .m_axis_tvalid (m_axis_chdr_tvalid),
+ .m_axis_tready (m_axis_chdr_tready)
+ );
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/core/axis_pyld_ctxt_to_chdr.v b/fpga/usrp3/lib/rfnoc/core/axis_pyld_ctxt_to_chdr.v
new file mode 100644
index 000000000..c73d7f365
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/axis_pyld_ctxt_to_chdr.v
@@ -0,0 +1,463 @@
+//
+// Copyright 2018-2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axis_pyld_ctxt_to_chdr
+// Description:
+// A header framer module for CHDR data packets.
+// Accepts an input payload and context stream, and produces an
+// output CHDR stream.
+// This module also performs an optional clock crossing and data
+// width convertion from a user requested width for the
+// payload bus to CHDR_W.
+// Context and data packets must be interleaved i.e. a context packet
+// must arrive before its corresponding data packet. However, if
+// context prefetching is enabled, the context for the next packet
+// may arrive before the data for the current packet has been
+// consumed. In the case of a rate reduction, this allows the module
+// to sustain a gapless stream of payload items and a bursty
+// sideband context path.
+//
+// Parameters:
+// - CHDR_W: Width of the input CHDR bus in bits
+// - ITEM_W: Width of the output item bus in bits
+// - NIPC: The number of output items delievered per cycle
+// - SYNC_CLKS: Are the CHDR and data clocks synchronous to each other?
+// - CONTEXT_FIFO_SIZE: FIFO size for the context path
+// - PAYLOAD_FIFO_SIZE: FIFO size for the payload path
+// - MTU: Log2 of the maximum packet size in words
+// - CONTEXT_PREFETCH_EN: Is context prefetching enabled?
+//
+// Signals:
+// - s_axis_payload_* : Input payload stream (AXI-Stream)
+// - s_axis_context_* : Input context stream (AXI-Stream)
+// - s_axis_chdr_* : Output CHDR stream (AXI-Stream)
+// - framer_errors : Number of framer errors (dropped packets)
+// - flush_* : Signals for flush control and status
+//
+
+module axis_pyld_ctxt_to_chdr #(
+ parameter CHDR_W = 256,
+ parameter ITEM_W = 32,
+ parameter NIPC = 2,
+ parameter SYNC_CLKS = 0,
+ parameter CONTEXT_FIFO_SIZE = 1,
+ parameter PAYLOAD_FIFO_SIZE = 1,
+ parameter MTU = 9,
+ parameter CONTEXT_PREFETCH_EN = 1
+)(
+ // Clock, reset and settings
+ input wire axis_chdr_clk,
+ input wire axis_chdr_rst,
+ input wire axis_data_clk,
+ input wire axis_data_rst,
+ // CHDR in (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_chdr_tdata,
+ output wire m_axis_chdr_tlast,
+ output wire m_axis_chdr_tvalid,
+ input wire m_axis_chdr_tready,
+ // Payload stream out (AXI-Stream)
+ input wire [(ITEM_W*NIPC)-1:0] s_axis_payload_tdata,
+ input wire [NIPC-1:0] s_axis_payload_tkeep,
+ input wire s_axis_payload_tlast,
+ input wire s_axis_payload_tvalid,
+ output wire s_axis_payload_tready,
+ // Context stream out (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_context_tdata,
+ input wire [3:0] s_axis_context_tuser,
+ input wire s_axis_context_tlast,
+ input wire s_axis_context_tvalid,
+ output wire s_axis_context_tready,
+ // Status
+ output reg [31:0] framer_errors,
+ // Flush signals
+ input wire flush_en,
+ input wire [31:0] flush_timeout,
+ output wire flush_active,
+ output wire flush_done
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_axis_ctrl_utils.vh"
+
+ // ---------------------------------------------------
+ // Intput State Machine
+ // ---------------------------------------------------
+ reg [2:0] ctxt_pkt_cnt = 3'd0, pyld_pkt_cnt = 3'd0;
+ // A payload packet can pass only if it is preceeded by a context packet
+ wire pass_pyld = ((ctxt_pkt_cnt - pyld_pkt_cnt) > 3'd0);
+ // A context packet has to be blocked if its corresponding payload packet hasn't passed except
+ // when prefetching is enabled. In that case one additional context packet is allowed to pass
+ wire pass_ctxt = ((ctxt_pkt_cnt - pyld_pkt_cnt) < (CONTEXT_PREFETCH_EN == 1 ? 3'd2 : 3'd1));
+
+ always @(posedge axis_data_clk) begin
+ if (axis_data_rst) begin
+ ctxt_pkt_cnt <= 3'd0;
+ pyld_pkt_cnt <= 3'd0;
+ end else begin
+ if (s_axis_context_tvalid && s_axis_context_tready && s_axis_context_tlast)
+ ctxt_pkt_cnt <= ctxt_pkt_cnt + 3'd1;
+ if (s_axis_payload_tvalid && s_axis_payload_tready && s_axis_payload_tlast)
+ pyld_pkt_cnt <= pyld_pkt_cnt + 3'd1;
+ end
+ end
+
+ wire tmp_ctxt_tvalid, tmp_ctxt_tready;
+ wire tmp_pyld_tvalid, tmp_pyld_tready;
+
+ assign tmp_ctxt_tvalid = s_axis_context_tvalid && pass_ctxt;
+ assign tmp_pyld_tvalid = s_axis_payload_tvalid && pass_pyld;
+ assign s_axis_context_tready = tmp_ctxt_tready && pass_ctxt;
+ assign s_axis_payload_tready = tmp_pyld_tready && pass_pyld;
+
+ // ---------------------------------------------------
+ // Data Width Converter: ITEM_W*NIPC => CHDR_W
+ // ---------------------------------------------------
+ wire [CHDR_W-1:0] in_pyld_tdata;
+ wire in_pyld_tlast;
+ wire in_pyld_tvalid;
+ wire in_pyld_tready;
+
+ axis_width_conv #(
+ .WORD_W(ITEM_W), .IN_WORDS(NIPC), .OUT_WORDS(CHDR_W/ITEM_W),
+ .SYNC_CLKS(1), .PIPELINE("IN")
+ ) payload_width_conv_i (
+ .s_axis_aclk(axis_data_clk), .s_axis_rst(axis_data_rst),
+ .s_axis_tdata(s_axis_payload_tdata),
+ .s_axis_tkeep(s_axis_payload_tkeep),
+ .s_axis_tlast(s_axis_payload_tlast),
+ .s_axis_tvalid(tmp_pyld_tvalid),
+ .s_axis_tready(tmp_pyld_tready),
+ .m_axis_aclk(axis_data_clk), .m_axis_rst(axis_data_rst),
+ .m_axis_tdata(in_pyld_tdata),
+ .m_axis_tkeep(/* unused */),
+ .m_axis_tlast(in_pyld_tlast),
+ .m_axis_tvalid(in_pyld_tvalid),
+ .m_axis_tready(in_pyld_tready)
+ );
+
+ // ---------------------------------------------------
+ // Payload and Context FIFOs
+ // ---------------------------------------------------
+ wire [CHDR_W-1:0] out_ctxt_tdata , out_pyld_tdata ;
+ wire [3:0] out_ctxt_tuser;
+ wire out_ctxt_tlast , out_pyld_tlast ;
+ wire out_ctxt_tvalid, out_pyld_tvalid;
+ reg out_ctxt_tready, out_pyld_tready;
+
+ generate if (SYNC_CLKS) begin
+ axi_fifo #(.WIDTH(CHDR_W+4+1), .SIZE(CONTEXT_FIFO_SIZE)) ctxt_fifo_i (
+ .clk(axis_chdr_clk), .reset(axis_chdr_rst), .clear(1'b0),
+ .i_tdata({s_axis_context_tlast, s_axis_context_tuser, s_axis_context_tdata}),
+ .i_tvalid(tmp_ctxt_tvalid), .i_tready(tmp_ctxt_tready),
+ .o_tdata({out_ctxt_tlast, out_ctxt_tuser, out_ctxt_tdata}),
+ .o_tvalid(out_ctxt_tvalid), .o_tready(out_ctxt_tready),
+ .space(), .occupied()
+ );
+ axi_fifo #(.WIDTH(CHDR_W+1), .SIZE(PAYLOAD_FIFO_SIZE)) pyld_fifo_i (
+ .clk(axis_chdr_clk), .reset(axis_chdr_rst), .clear(1'b0),
+ .i_tdata({in_pyld_tlast, in_pyld_tdata}),
+ .i_tvalid(in_pyld_tvalid), .i_tready(in_pyld_tready),
+ .o_tdata({out_pyld_tlast, out_pyld_tdata}),
+ .o_tvalid(out_pyld_tvalid), .o_tready(out_pyld_tready),
+ .space(), .occupied()
+ );
+ end else begin
+ axi_fifo_2clk #(.WIDTH(CHDR_W+4+1), .SIZE(CONTEXT_FIFO_SIZE)) ctxt_fifo_i (
+ .reset(axis_data_rst),
+ .i_aclk(axis_data_clk),
+ .i_tdata({s_axis_context_tlast, s_axis_context_tuser, s_axis_context_tdata}),
+ .i_tvalid(tmp_ctxt_tvalid), .i_tready(tmp_ctxt_tready),
+ .o_aclk(axis_chdr_clk),
+ .o_tdata({out_ctxt_tlast, out_ctxt_tuser, out_ctxt_tdata}),
+ .o_tvalid(out_ctxt_tvalid), .o_tready(out_ctxt_tready)
+ );
+ axi_fifo_2clk #(.WIDTH(CHDR_W+1), .SIZE(PAYLOAD_FIFO_SIZE)) pyld_fifo_i (
+ .reset(axis_data_rst),
+ .i_aclk(axis_data_clk),
+ .i_tdata({in_pyld_tlast, in_pyld_tdata}),
+ .i_tvalid(in_pyld_tvalid), .i_tready(in_pyld_tready),
+ .o_aclk(axis_chdr_clk),
+ .o_tdata({out_pyld_tlast, out_pyld_tdata}),
+ .o_tvalid(out_pyld_tvalid), .o_tready(out_pyld_tready)
+ );
+ end endgenerate
+
+ // ---------------------------------------------------
+ // Output State Machine
+ // ---------------------------------------------------
+ wire [CHDR_W-1:0] chdr_pg_tdata;
+ reg chdr_pg_tlast, chdr_pg_tvalid;
+ wire chdr_pg_terror, chdr_pg_tready;
+
+ localparam [2:0] ST_HDR = 3'd0; // Processing the output CHDR header
+ localparam [2:0] ST_TS = 3'd1; // Processing the output CHDR timestamp
+ localparam [2:0] ST_MDATA = 3'd2; // Processing the output CHDR metadata word
+ localparam [2:0] ST_BODY = 3'd3; // Processing the output CHDR payload word
+ localparam [2:0] ST_DROP_CTXT = 3'd4; // Something went wrong... Dropping context packet
+ localparam [2:0] ST_DROP_PYLD = 3'd5; // Something went wrong... Dropping payload packet
+ localparam [2:0] ST_TERMINATE = 3'd6; // Something went wrong... Rejecting output packet
+
+ reg [2:0] state = ST_HDR;
+ reg [4:0] mdata_pending = 5'd0;
+
+ // Shortcuts: CHDR header
+ wire [2:0] out_pkt_type = chdr_get_pkt_type(out_ctxt_tdata[63:0]);
+ wire [4:0] out_num_mdata = chdr_get_num_mdata(out_ctxt_tdata[63:0]);
+
+ always @(posedge axis_chdr_clk) begin
+ if (axis_chdr_rst) begin
+ state <= ST_HDR;
+ framer_errors <= 32'd0;
+ end else begin
+ case (state)
+
+ // ST_HDR: CHDR Header
+ // -------------------
+ ST_HDR: begin
+ if (out_ctxt_tvalid && out_ctxt_tready) begin
+ mdata_pending <= out_num_mdata;
+ if (CHDR_W > 64) begin
+ // When CHDR_W > 64, the timestamp is a part of the header word.
+ // If this is a data packet (with/without a TS), we skip the TS state
+ // move directly to metadata/body
+ if (out_num_mdata != CHDR_NO_MDATA) begin
+ if (!out_ctxt_tlast)
+ if (out_ctxt_tuser == CONTEXT_FIELD_HDR_TS)
+ state <= ST_MDATA; // tlast should be low. Move to metadata.
+ else
+ state <= ST_DROP_CTXT; // Malformed packet: Wrong tuser. Drop ctxt+pyld
+ else
+ state <= ST_DROP_PYLD; // Premature tlast. Drop pyld
+ end else begin
+ if (out_ctxt_tlast)
+ if (out_ctxt_tuser == CONTEXT_FIELD_HDR_TS)
+ state <= ST_BODY; // tlast should be high. Move to payload.
+ else
+ state <= ST_DROP_PYLD; // Malformed packet: Wrong tuser. Drop pyld
+ else
+ state <= ST_DROP_CTXT; // Malformed packet: extra context lines. Drop ctxt+pyld
+ end
+ end else begin
+ // When CHDR_W == 64, the timestamp comes after the header. Check if this is a data
+ // packet with a TS to figure out the next state. If no TS, then check for metadata
+ // to move to the next state. Drop any non-data packets.
+ if (out_pkt_type == CHDR_PKT_TYPE_DATA_TS) begin
+ if (!out_ctxt_tlast)
+ if (out_ctxt_tuser == CONTEXT_FIELD_HDR)
+ state <= ST_TS; // tlast should be low. Move to timestamp.
+ else
+ state <= ST_DROP_CTXT; // Malformed packet: Wrong tuser. Drop ctxt+pyld
+ else
+ state <= ST_DROP_PYLD; // Premature tlast. Drop pyld
+ end else begin
+ if (out_num_mdata != CHDR_NO_MDATA) begin
+ if (!out_ctxt_tlast)
+ if (out_ctxt_tuser == CONTEXT_FIELD_HDR)
+ state <= ST_MDATA; // tlast should be low. Move to metadata.
+ else
+ state <= ST_DROP_CTXT; // Malformed packet: Wrong tuser. Drop ctxt+pyld
+ else
+ state <= ST_DROP_PYLD; // Premature tlast. Drop pyld
+ end else begin
+ if (out_ctxt_tlast)
+ if (out_ctxt_tuser == CONTEXT_FIELD_HDR)
+ state <= ST_BODY; // tlast should be high. Move to payload.
+ else
+ state <= ST_DROP_PYLD; // Malformed packet: Wrong tuser. Drop pyld
+ else
+ state <= ST_DROP_CTXT; // Malformed packet: extra context lines. Drop ctxt+pyld
+ end
+ end
+ end
+ end
+ end
+
+ // ST_TS: Timestamp (CHDR_W == 64 only)
+ // ------------------------------------
+ ST_TS: begin
+ if (out_ctxt_tvalid && out_ctxt_tready) begin
+ if (mdata_pending != CHDR_NO_MDATA) begin
+ if (!out_ctxt_tlast)
+ if (out_ctxt_tuser == CONTEXT_FIELD_TS)
+ state <= ST_MDATA; // tlast should be low. Move to metadata.
+ else
+ state <= ST_DROP_CTXT; // Malformed packet: Wrong tuser. Drop ctxt+pyld
+ else
+ state <= ST_DROP_PYLD; // Premature tlast. Drop pyld
+ end else begin
+ if (out_ctxt_tlast)
+ if (out_ctxt_tuser == CONTEXT_FIELD_TS)
+ state <= ST_BODY; // tlast should be high. Move to payload.
+ else
+ state <= ST_DROP_PYLD; // Malformed packet: Wrong tuser. Drop pyld
+ else
+ state <= ST_DROP_CTXT; // Malformed packet: extra context lines. Drop ctxt+pyld
+ end
+ end
+ end
+
+ // ST_MDATA: Metadata word
+ // -----------------------
+ ST_MDATA: begin
+ if (out_ctxt_tvalid && out_ctxt_tready) begin
+ if (mdata_pending != 5'd1) begin
+ mdata_pending <= mdata_pending - 'd1;
+ if (!out_ctxt_tlast)
+ if (out_ctxt_tuser == CONTEXT_FIELD_MDATA)
+ state <= ST_MDATA; // tlast should be low. Continue processing metadata.
+ else
+ state <= ST_DROP_CTXT; // Malformed packet: Wrong tuser. Drop ctxt+pyld
+ else
+ state <= ST_DROP_PYLD; // Premature tlast. Drop pyld
+ end else begin
+ if (out_ctxt_tlast)
+ if (out_ctxt_tuser == CONTEXT_FIELD_MDATA)
+ state <= ST_BODY; // tlast should be high. Move to payload.
+ else
+ state <= ST_DROP_PYLD; // Malformed packet: Wrong tuser. Drop pyld
+ else
+ state <= ST_DROP_CTXT; // Malformed packet: extra context lines. Drop ctxt+pyld
+ end
+ end
+ end
+
+ // ST_BODY: Payload word
+ // ---------------------
+ ST_BODY: begin
+ if (out_pyld_tvalid && out_pyld_tready) begin
+ state <= out_pyld_tlast ? ST_HDR : ST_BODY;
+ end
+ end
+
+ // ST_DROP_CTXT: Drop current context packet
+ // -----------------------------------------
+ ST_DROP_CTXT: begin
+ if (out_ctxt_tvalid && out_ctxt_tready) begin
+ state <= out_ctxt_tlast ? ST_DROP_PYLD : ST_DROP_CTXT;
+ end
+ end
+
+ // ST_DROP_PYLD: Drop current payload packet
+ // -----------------------------------------
+ ST_DROP_PYLD: begin
+ if (out_pyld_tvalid && out_pyld_tready) begin
+ state <= out_pyld_tlast ? ST_TERMINATE : ST_DROP_PYLD;
+ end
+ end
+
+ // ST_TERMINATE: Drop partial output packet
+ // ----------------------------------------
+ ST_TERMINATE: begin
+ if (chdr_pg_tready) begin
+ state <= ST_HDR;
+ framer_errors <= framer_errors + 32'd1;
+ end
+ end
+
+ default: begin
+ // We should never get here
+ state <= ST_HDR;
+ end
+ endcase
+ end
+ end
+
+
+ always @(*) begin
+ case (state)
+ ST_HDR: begin
+ // A context word passes fwd to the CHDR output
+ chdr_pg_tvalid = out_ctxt_tvalid;
+ chdr_pg_tlast = 1'b0; // tlast is inherited from the data stream
+ out_ctxt_tready = chdr_pg_tready;
+ out_pyld_tready = 1'b0;
+ end
+ ST_TS: begin
+ // A context word passes fwd to the CHDR output
+ chdr_pg_tvalid = out_ctxt_tvalid;
+ chdr_pg_tlast = 1'b0; // tlast is inherited from the data stream
+ out_ctxt_tready = chdr_pg_tready;
+ out_pyld_tready = 1'b0;
+ end
+ ST_MDATA: begin
+ // A context word passes fwd to the CHDR output
+ chdr_pg_tvalid = out_ctxt_tvalid;
+ chdr_pg_tlast = 1'b0; // tlast is inherited from the data stream
+ out_ctxt_tready = chdr_pg_tready;
+ out_pyld_tready = 1'b0;
+ end
+ ST_BODY: begin
+ // A payload word passes fwd to the CHDR output
+ chdr_pg_tvalid = out_pyld_tvalid;
+ chdr_pg_tlast = out_pyld_tlast;
+ out_ctxt_tready = 1'b0;
+ out_pyld_tready = chdr_pg_tready;
+ end
+ ST_DROP_CTXT: begin
+ // A context word is consumed without passing fwd
+ chdr_pg_tvalid = 1'b0;
+ chdr_pg_tlast = 1'b0;
+ out_ctxt_tready = 1'b1;
+ out_pyld_tready = 1'b0;
+ end
+ ST_DROP_PYLD: begin
+ // A payload word is consumed without passing fwd
+ chdr_pg_tvalid = 1'b0;
+ chdr_pg_tlast = 1'b0;
+ out_ctxt_tready = 1'b0;
+ out_pyld_tready = 1'b1;
+ end
+ ST_TERMINATE: begin
+ // A dummy word with a tlast and terror is passed fwd
+ // to evacuate the current packet from the packet_gate
+ chdr_pg_tvalid = 1'b1;
+ chdr_pg_tlast = 1'b1;
+ out_ctxt_tready = 1'b0;
+ out_pyld_tready = 1'b0;
+ end
+ default: begin
+ chdr_pg_tvalid = 1'b0;
+ chdr_pg_tlast = 1'b0;
+ out_ctxt_tready = 1'b0;
+ out_pyld_tready = 1'b0;
+ end
+ endcase
+ end
+
+ assign chdr_pg_tdata = (state == ST_BODY) ? out_pyld_tdata : out_ctxt_tdata;
+ assign chdr_pg_terror = (state == ST_TERMINATE);
+
+ // ---------------------------------------------------
+ // Packet gate
+ // ---------------------------------------------------
+
+ wire [CHDR_W-1:0] chdr_flush_tdata;
+ wire chdr_flush_tlast, chdr_flush_tvalid;
+ wire chdr_flush_terror, chdr_flush_tready;
+
+ axis_packet_flush #(
+ .WIDTH(CHDR_W+1), .FLUSH_PARTIAL_PKTS(0), .TIMEOUT_W(32), .PIPELINE("IN")
+ ) chdr_flusher_i (
+ .clk(axis_chdr_clk), .reset(axis_chdr_rst),
+ .enable(flush_en), .timeout(flush_timeout),
+ .flushing(flush_active), .done(flush_done),
+ .s_axis_tdata({chdr_pg_terror, chdr_pg_tdata}), .s_axis_tlast(chdr_pg_tlast),
+ .s_axis_tvalid(chdr_pg_tvalid), .s_axis_tready(chdr_pg_tready),
+ .m_axis_tdata({chdr_flush_terror, chdr_flush_tdata}), .m_axis_tlast(chdr_flush_tlast),
+ .m_axis_tvalid(chdr_flush_tvalid), .m_axis_tready(chdr_flush_tready)
+ );
+
+ axi_packet_gate #( .WIDTH(CHDR_W), .SIZE(MTU), .USE_AS_BUFF(0) ) out_gate_i (
+ .clk(axis_chdr_clk), .reset(axis_chdr_rst), .clear(flush_active),
+ .i_tdata(chdr_flush_tdata), .i_tlast(chdr_flush_tlast), .i_terror(chdr_flush_terror),
+ .i_tvalid(chdr_flush_tvalid), .i_tready(chdr_flush_tready),
+ .o_tdata(m_axis_chdr_tdata), .o_tlast(m_axis_chdr_tlast),
+ .o_tvalid(m_axis_chdr_tvalid), .o_tready(m_axis_chdr_tready)
+ );
+
+endmodule // axis_pyld_ctxt_to_chdr
diff --git a/fpga/usrp3/lib/rfnoc/core/backend_iface.v b/fpga/usrp3/lib/rfnoc/core/backend_iface.v
new file mode 100644
index 000000000..59429ea6b
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/backend_iface.v
@@ -0,0 +1,142 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: backend_iface
+// Description:
+// A noc_shell interface to the backend infrastructure
+//
+
+module backend_iface #(
+ parameter [31:0] NOC_ID = 32'h0,
+ parameter [5:0] NUM_DATA_I = 0,
+ parameter [5:0] NUM_DATA_O = 0,
+ parameter [5:0] CTRL_FIFOSIZE = 0,
+ parameter [7:0] CTRL_MAX_ASYNC_MSGS = 0,
+ parameter [5:0] MTU = 0
+)(
+ // Input clock
+ input wire rfnoc_chdr_clk,
+ input wire rfnoc_ctrl_clk,
+ // Backend interface (sync. to rfnoc_ctrl_clk)
+ input wire [511:0] rfnoc_core_config,
+ output wire [511:0] rfnoc_core_status,
+ // Output reset
+ output wire rfnoc_chdr_rst,
+ output wire rfnoc_ctrl_rst,
+ // Flush interface (sync. to rfnoc_chdr_clk)
+ output wire data_i_flush_en,
+ output wire [31:0] data_i_flush_timeout,
+ input wire [63:0] data_i_flush_active,
+ input wire [63:0] data_i_flush_done,
+ output wire data_o_flush_en,
+ output wire [31:0] data_o_flush_timeout,
+ input wire [63:0] data_o_flush_active,
+ input wire [63:0] data_o_flush_done
+);
+ localparam RESET_LENGTH = 32;
+
+ `include "rfnoc_backend_iface.vh"
+
+ // -----------------------------------
+ // CONFIG: Infrastructure => Block
+ // -----------------------------------
+ wire [BEC_TOTAL_WIDTH-1:0] rfnoc_core_config_trim = rfnoc_core_config[BEC_TOTAL_WIDTH-1:0];
+
+ reg [31:0] flush_timeout_ctclk = 32'd0;
+ reg flush_en_ctclk = 1'b0;
+ reg soft_ctrl_rst_ctclk = 1'b0;
+ reg soft_chdr_rst_ctclk = 1'b0;
+
+ // Register logic before synchronizer
+ always @(posedge rfnoc_ctrl_clk) begin
+ flush_timeout_ctclk <= rfnoc_core_config_trim[BEC_FLUSH_TIMEOUT_OFFSET +: BEC_FLUSH_TIMEOUT_WIDTH];
+ flush_en_ctclk <= rfnoc_core_config_trim[BEC_FLUSH_EN_OFFSET +: BEC_FLUSH_EN_WIDTH ];
+ soft_ctrl_rst_ctclk <= rfnoc_core_config_trim[BEC_SOFT_CTRL_RST_OFFSET +: BEC_SOFT_CTRL_RST_WIDTH];
+ soft_chdr_rst_ctclk <= rfnoc_core_config_trim[BEC_SOFT_CHDR_RST_OFFSET +: BEC_SOFT_CHDR_RST_WIDTH];
+ end
+
+ // Synchronizer
+ wire [31:0] flush_timeout_chclk;
+ wire flush_en_chclk;
+
+ // Note: We are using a synchronizer to cross the 32-bit timeout bus
+ // into a different clock domain. Typically we would use a 2clk FIFO
+ // but it's OK to have the bits unsynchronized here because the value
+ // is static and is set from SW long before it is actually used.
+
+ synchronizer #(.WIDTH(33), .INITIAL_VAL(33'd0)) sync_ctrl_i (
+ .clk(rfnoc_chdr_clk), .rst(1'b0),
+ .in({flush_en_ctclk, flush_timeout_ctclk}),
+ .out({flush_en_chclk, flush_timeout_chclk})
+ );
+
+ // Synchronize the reset to the CHDR and CTRL clock domains, and extend the
+ // reset pulse to make it long enough for most IP to reset correctly.
+
+ wire rfnoc_ctrl_rst_pulse;
+ wire rfnoc_chdr_rst_pulse;
+
+ pulse_synchronizer #(.MODE("POSEDGE")) soft_ctrl_rst_sync_i (
+ .clk_a(rfnoc_ctrl_clk), .rst_a(1'b0), .pulse_a(soft_ctrl_rst_ctclk), .busy_a(),
+ .clk_b(rfnoc_ctrl_clk), .pulse_b(rfnoc_ctrl_rst_pulse)
+ );
+
+ pulse_synchronizer #(.MODE("POSEDGE")) soft_chdr_rst_sync_i (
+ .clk_a(rfnoc_ctrl_clk), .rst_a(1'b0), .pulse_a(soft_chdr_rst_ctclk), .busy_a(),
+ .clk_b(rfnoc_chdr_clk), .pulse_b(rfnoc_chdr_rst_pulse)
+ );
+
+ pulse_stretch_min #(.LENGTH(RESET_LENGTH)) soft_ctrl_rst_stretch_i (
+ .clk(rfnoc_ctrl_clk), .rst(1'b0),
+ .pulse_in(rfnoc_ctrl_rst_pulse), .pulse_out(rfnoc_ctrl_rst)
+ );
+
+ pulse_stretch_min #(.LENGTH(RESET_LENGTH)) soft_chdr_rst_stretch_i (
+ .clk(rfnoc_chdr_clk), .rst(1'b0),
+ .pulse_in(rfnoc_chdr_rst_pulse), .pulse_out(rfnoc_chdr_rst)
+ );
+
+ assign data_i_flush_timeout = flush_timeout_chclk;
+ assign data_o_flush_timeout = flush_timeout_chclk;
+ assign data_i_flush_en = flush_en_chclk;
+ assign data_o_flush_en = flush_en_chclk;
+
+ // -----------------------------------
+ // STATUS: Block => Infrastructure
+ // -----------------------------------
+
+ reg flush_active_chclk = 1'b0;
+ reg flush_done_chclk = 1'b0;
+
+ // Register logic before synchronizer
+ wire flush_active_ctclk;
+ wire flush_done_ctclk;
+
+ always @(posedge rfnoc_chdr_clk) begin
+ flush_active_chclk <= (|data_i_flush_active[NUM_DATA_I-1:0]) | (|data_o_flush_active[NUM_DATA_O-1:0]);
+ flush_done_chclk <= (&data_i_flush_done [NUM_DATA_I-1:0]) & (&data_o_flush_done [NUM_DATA_O-1:0]);
+ end
+
+ // Synchronizer
+ synchronizer #(.WIDTH(2), .INITIAL_VAL(2'd0)) sync_status_i (
+ .clk(rfnoc_ctrl_clk), .rst(1'b0),
+ .in({flush_active_chclk, flush_done_chclk}),
+ .out({flush_active_ctclk, flush_done_ctclk})
+ );
+
+ assign rfnoc_core_status[BES_PROTO_VER_OFFSET +:BES_PROTO_VER_WIDTH ] = BACKEND_PROTO_VER;
+ assign rfnoc_core_status[BES_NUM_DATA_I_OFFSET +:BES_NUM_DATA_I_WIDTH ] = NUM_DATA_I;
+ assign rfnoc_core_status[BES_NUM_DATA_O_OFFSET +:BES_NUM_DATA_O_WIDTH ] = NUM_DATA_O;
+ assign rfnoc_core_status[BES_CTRL_FIFOSIZE_OFFSET +:BES_CTRL_FIFOSIZE_WIDTH ] = CTRL_FIFOSIZE;
+ assign rfnoc_core_status[BES_CTRL_MAX_ASYNC_MSGS_OFFSET+:BES_CTRL_MAX_ASYNC_MSGS_WIDTH] = CTRL_MAX_ASYNC_MSGS;
+ assign rfnoc_core_status[BES_NOC_ID_OFFSET +:BES_NOC_ID_WIDTH ] = NOC_ID;
+ assign rfnoc_core_status[BES_FLUSH_ACTIVE_OFFSET +:BES_FLUSH_ACTIVE_WIDTH ] = flush_active_ctclk;
+ assign rfnoc_core_status[BES_FLUSH_DONE_OFFSET +:BES_FLUSH_DONE_WIDTH ] = flush_done_ctclk;
+ assign rfnoc_core_status[BES_DATA_MTU_OFFSET +:BES_DATA_MTU_WIDTH ] = MTU;
+ // Assign the rest to 0
+ assign rfnoc_core_status[511:BES_TOTAL_WIDTH] = {(512-BES_TOTAL_WIDTH){1'b0}};
+
+endmodule // backend_iface
+
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_compute_tkeep.v b/fpga/usrp3/lib/rfnoc/core/chdr_compute_tkeep.v
new file mode 100644
index 000000000..72c5bab13
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_compute_tkeep.v
@@ -0,0 +1,86 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_compute_tkeep
+// Description:
+// This module monitors an AXI-Stream CHDR bus and uses the
+// packet size field in the CHDR header to compute a tkeep
+// trailer signal to indicate the the valid bytes when
+// tlast is asserted.
+//
+// Parameters:
+// - CHDR_W: Width of the CHDR bus in bits
+// - ITEM_W: Width of the item bus in bits (must be a multiple of 8)
+//
+// Signals:
+// - axis_* : AXI-Stream CHDR bus
+
+module chdr_compute_tkeep #(
+ parameter CHDR_W = 256,
+ parameter ITEM_W = 32
+)(
+ input wire clk,
+ input wire rst,
+ input wire [CHDR_W-1:0] axis_tdata,
+ input wire axis_tlast,
+ input wire axis_tvalid,
+ input wire axis_tready,
+ output wire [(CHDR_W/ITEM_W)-1:0] axis_tkeep
+);
+
+ `include "rfnoc_chdr_utils.vh"
+
+ generate if (CHDR_W > ITEM_W) begin
+
+ localparam CHDR_W_BYTES = CHDR_W/8;
+ localparam ITEM_W_BYTES = ITEM_W/8;
+ localparam KEEP_W = CHDR_W_BYTES/ITEM_W_BYTES;
+
+ // Binary to thermometer decoder
+ // 2'd0 => 4'b1111 (special case)
+ // 2'd1 => 4'b0001
+ // 2'd2 => 4'b0011
+ // 2'd3 => 4'b0111
+ function [KEEP_W-1:0] bin2thermo;
+ input [$clog2(KEEP_W)-1:0] bin;
+ bin2thermo = ~((~1)<<((bin-1)%KEEP_W));
+ endfunction
+
+ // Read the packet length and figure out the number
+ // of trailing items
+ wire [15:0] pkt_len = chdr_get_length(axis_tdata[63:0]);
+ wire [KEEP_W-1:0] len_thermo = bin2thermo(pkt_len[$clog2(CHDR_W_BYTES)-1:$clog2(ITEM_W_BYTES)]);
+ reg [KEEP_W-1:0] reg_len_thermo = 'h0;
+ reg is_header = 1'b1;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ is_header <= 1'b1;
+ end else if (axis_tvalid & axis_tready) begin
+ is_header <= axis_tlast;
+ if (is_header) begin
+ reg_len_thermo <= len_thermo;
+ end
+ end
+ end
+
+ // tkeep indicates trailing items, so for lines with tlast == 0,
+ // tkeep is all 1's.
+ assign axis_tkeep = (~axis_tlast) ? {KEEP_W{1'b1}} :
+ (is_header ? len_thermo : reg_len_thermo);
+
+ end else if (CHDR_W == ITEM_W) begin
+
+ // Only one item per CHDR word. So always keep it.
+ assign axis_tkeep = 1'b1;
+
+ end else begin
+
+ // Illegal. A item must be smaller than the CHDR_W
+ illegal_parameter_value item_w_cannot_be_larger_than_chdr_w();
+
+ end endgenerate
+
+endmodule // chdr_compute_tkeep
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_data_swapper.v b/fpga/usrp3/lib/rfnoc/core/chdr_data_swapper.v
new file mode 100644
index 000000000..9b5e96bca
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_data_swapper.v
@@ -0,0 +1,227 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_data_swapper
+// Description:
+// A module to adapt a CHDR stream to correctly sequence in
+// a software buffer of a user-specified type. Here are the
+// the swapping assumptions:
+// - The CHDR header, timestamp and metadata for all packet
+// types must be interpreted as a uint64_t.
+// - All Control, Stream Status/Cmd, Management packet payloads
+// must reside in a uint64_t* buffer.
+// - The buffer type for the data packet payload and metadata
+// is user configurable
+//
+// Parameters:
+// - CHDR_W: Width of the tdata bus in bits
+//
+// Signals:
+// - payload_sw_buff: SW buffer mode for payload (0=u64, 1=u32, 2=u16, 3=u8)
+// - mdata_sw_buff : SW buffer mode for metadata (0=u64, 1=u32, 2=u16, 3=u8)
+// - s_axis_* : The input AXI stream
+// - m_axis_* : The output AXI stream
+//
+
+module chdr_data_swapper #(
+ parameter CHDR_W = 256
+)(
+ // Clock and Reset
+ input wire clk,
+ input wire rst,
+ // Software Buffer Mode
+ input wire [1:0] payload_sw_buff,
+ input wire [1:0] mdata_sw_buff,
+ input wire swap_endianness,
+ // Input AXIS
+ input wire [CHDR_W-1:0] s_axis_tdata,
+ input wire s_axis_tlast,
+ input wire s_axis_tvalid,
+ output wire s_axis_tready,
+ // Output AXIS
+ output wire [CHDR_W-1:0] m_axis_tdata,
+ output wire m_axis_tlast,
+ output wire m_axis_tvalid,
+ input wire m_axis_tready
+);
+
+ `include "../core/rfnoc_chdr_utils.vh"
+
+ // *_sw_buff values
+ localparam [1:0] SW_BUFF_UINT64 = 2'd0;
+ localparam [1:0] SW_BUFF_UINT32 = 2'd1;
+ localparam [1:0] SW_BUFF_UINT16 = 2'd2;
+ localparam [1:0] SW_BUFF_UINT8 = 2'd3;
+
+ localparam SWAP_W = $clog2(CHDR_W);
+
+ // Packet states
+ localparam [2:0] ST_HDR = 3'd0;
+ localparam [2:0] ST_TS = 3'd1;
+ localparam [2:0] ST_MDATA = 3'd2;
+ localparam [2:0] ST_DATA_BODY = 3'd3;
+ localparam [2:0] ST_OTHER = 3'd4;
+
+ reg [2:0] state = ST_HDR;
+ reg [4:0] mdata_pending = CHDR_NO_MDATA;
+ reg [SWAP_W-2:0] pyld_tswap = 'h0, mdata_tswap = 'h0;
+
+ // Shortcuts: CHDR header
+ wire [2:0] pkt_type = chdr_get_pkt_type(s_axis_tdata[63:0]);
+ wire [4:0] num_mdata = chdr_get_num_mdata(s_axis_tdata[63:0]);
+
+ // State machine to determine packet state
+ always @(posedge clk) begin
+ if (rst) begin
+ state <= ST_HDR;
+ end else if (s_axis_tvalid & s_axis_tready) begin
+ case (state)
+ ST_HDR: begin
+ mdata_pending <= num_mdata;
+ if (!s_axis_tlast) begin
+ if (CHDR_W > 64) begin
+ if (pkt_type == CHDR_PKT_TYPE_DATA || pkt_type == CHDR_PKT_TYPE_DATA_TS) begin
+ if (num_mdata != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_DATA_BODY;
+ end
+ end else begin
+ state <= ST_OTHER;
+ end
+ end else begin
+ if (pkt_type == CHDR_PKT_TYPE_DATA_TS) begin
+ state <= ST_TS;
+ end else if (pkt_type == CHDR_PKT_TYPE_DATA) begin
+ if (num_mdata != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_DATA_BODY;
+ end
+ end else begin
+ state <= ST_OTHER;
+ end
+ end
+ end else begin
+ state <= ST_HDR;
+ end
+ end
+ ST_TS: begin
+ if (!s_axis_tlast) begin
+ if (mdata_pending != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_DATA_BODY;
+ end
+ end else begin
+ state <= ST_HDR;
+ end
+ end
+ ST_MDATA: begin
+ if (!s_axis_tlast) begin
+ if (mdata_pending == 5'd1) begin
+ state <= ST_DATA_BODY;
+ end else begin
+ mdata_pending <= mdata_pending - 5'd1;
+ end
+ end else begin
+ state <= ST_HDR;
+ end
+ end
+ ST_DATA_BODY: begin
+ if (s_axis_tlast) begin
+ state <= ST_HDR;
+ end
+ end
+ ST_OTHER: begin
+ if (s_axis_tlast) begin
+ state <= ST_HDR;
+ end
+ end
+ default: begin
+ state <= ST_HDR;
+ end
+ endcase
+ end
+ end
+
+ // Convert SW buff size to swap-lane map
+ always @(posedge clk) begin
+ pyld_tswap <= 'h0;
+ mdata_tswap <= 'h0;
+ case (payload_sw_buff)
+ SW_BUFF_UINT8:
+ pyld_tswap[4:2] <= 3'b111;
+ SW_BUFF_UINT16:
+ pyld_tswap[4:2] <= 3'b110;
+ SW_BUFF_UINT32:
+ pyld_tswap[4:2] <= 3'b100;
+ default:
+ pyld_tswap[4:2] <= 3'b000;
+ endcase
+ case (mdata_sw_buff)
+ SW_BUFF_UINT8:
+ mdata_tswap[4:2] <= 3'b111;
+ SW_BUFF_UINT16:
+ mdata_tswap[4:2] <= 3'b110;
+ SW_BUFF_UINT32:
+ mdata_tswap[4:2] <= 3'b100;
+ default:
+ mdata_tswap[4:2] <= 3'b000;
+ endcase
+ end
+
+ wire [SWAP_W-2:0] s_axis_tswap_dyn =
+ (state == ST_DATA_BODY) ? pyld_tswap : (
+ (state == ST_MDATA) ? mdata_tswap : {(SWAP_W-1){1'b0}}
+ );
+ wire s_axis_tswap_end = swap_endianness &&
+ (state == ST_DATA_BODY || state == ST_MDATA);
+
+ // Swapper that re-aligns items in a buffer for software
+ wire [CHDR_W-1:0] out_swap_tdata, out_swap_tdata_pre;
+ wire out_swap_tswap_end, out_swap_tlast, out_swap_tvalid, out_swap_tready;
+
+ axis_data_swap #(
+ .DATA_W(CHDR_W), .USER_W(1'b1),
+ .STAGES_EN({{(SWAP_W-6){1'b0}}, 6'b111100}), .DYNAMIC(1)
+ ) chdr_dyn_swap_i (
+ .clk (clk ),
+ .rst (rst ),
+ .s_axis_tdata (s_axis_tdata ),
+ .s_axis_tswap (s_axis_tswap_dyn ),
+ .s_axis_tuser (s_axis_tswap_end ),
+ .s_axis_tlast (s_axis_tlast ),
+ .s_axis_tvalid(s_axis_tvalid ),
+ .s_axis_tready(s_axis_tready ),
+ .m_axis_tdata (out_swap_tdata_pre),
+ .m_axis_tuser (out_swap_tswap_end),
+ .m_axis_tlast (out_swap_tlast ),
+ .m_axis_tvalid(out_swap_tvalid ),
+ .m_axis_tready(out_swap_tready )
+ );
+
+ // Swapper that pre-corrects for transport endianness
+ genvar i;
+ generate for (i = 0; i < CHDR_W/8; i=i+1) begin
+ assign out_swap_tdata[i*8 +: 8] = out_swap_tswap_end ?
+ out_swap_tdata_pre[((CHDR_W/8)-i-1)*8 +: 8] : out_swap_tdata_pre[i*8 +: 8];
+ end endgenerate
+
+ axi_fifo_flop2 #(.WIDTH(CHDR_W+1)) out_reg_i (
+ .clk (clk ),
+ .reset (rst ),
+ .clear (1'b0 ),
+ .i_tdata ({out_swap_tlast, out_swap_tdata}),
+ .i_tvalid(out_swap_tvalid ),
+ .i_tready(out_swap_tready ),
+ .o_tdata ({m_axis_tlast, m_axis_tdata} ),
+ .o_tvalid(m_axis_tvalid ),
+ .o_tready(m_axis_tready ),
+ .occupied( ),
+ .space ( )
+ );
+
+endmodule // chdr_data_swapper
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_ingress_fifo.v b/fpga/usrp3/lib/rfnoc/core/chdr_ingress_fifo.v
new file mode 100644
index 000000000..e2660426f
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_ingress_fifo.v
@@ -0,0 +1,95 @@
+//
+// Copyright 2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module chdr_ingress_fifo #(
+ parameter WIDTH = 64,
+ parameter SIZE = 12,
+ parameter DEVICE = "7SERIES"
+) (
+ input clk,
+ input reset,
+ input clear,
+
+ input [WIDTH-1:0] i_tdata,
+ input i_tlast,
+ input i_tvalid,
+ output i_tready,
+
+ output [WIDTH-1:0] o_tdata,
+ output o_tlast,
+ output o_tvalid,
+ input o_tready
+);
+
+ localparam SIZE_THRESHOLD = (
+ (DEVICE == "7SERIES") ? 14 : (
+ (DEVICE == "VIRTEX6") ? 14 : (
+ (DEVICE == "SPARTAN6") ? 12 : (
+ 12
+ ))));
+
+ wire [WIDTH-1:0] i_tdata_pre;
+ wire i_tlast_pre, i_tvalid_pre, i_tready_pre;
+
+ // SRL based FIFO to break timing paths to BRAM resources
+ axi_fifo_flop2 #(.WIDTH(WIDTH+1)) pre_fifo (
+ .clk(clk), .reset(reset), .clear(clear),
+ .i_tdata({i_tlast, i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready),
+ .o_tdata({i_tlast_pre, i_tdata_pre}), .o_tvalid(i_tvalid_pre), .o_tready(i_tready_pre),
+ .space(), .occupied()
+ );
+
+ generate
+ if (SIZE <= SIZE_THRESHOLD) begin
+ wire [WIDTH-1:0] o_tdata_int;
+ wire o_tlast_int, o_tvalid_int, o_tready_int;
+ // Instantiate a single axi_fifo if size is not larger than threshold
+ axi_fifo #(.WIDTH(WIDTH+1), .SIZE(SIZE)) main_fifo (
+ .clk(clk), .reset(reset), .clear(clear),
+ .i_tdata({i_tlast_pre, i_tdata_pre}), .i_tvalid(i_tvalid_pre), .i_tready(i_tready_pre),
+ .o_tdata({o_tlast_int, o_tdata_int}), .o_tvalid(o_tvalid_int), .o_tready(o_tready_int),
+ .space(), .occupied()
+ );
+ axi_fifo_flop2 #(.WIDTH(WIDTH+1)) fifo_flop2 (
+ .clk(clk), .reset(reset), .clear(clear),
+ .i_tdata({o_tlast_int, o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
+ .o_tdata({o_tlast, o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
+ .space(), .occupied()
+ );
+ end else begin
+ // Instantiate a cascade of axi_fifos if size is larger than threshold
+ localparam CDEPTH = 2**(SIZE - SIZE_THRESHOLD); //Cascade Depth
+ wire [WIDTH-1:0] c_tdata[CDEPTH:0], int_tdata[CDEPTH-1:0];
+ wire c_tlast[CDEPTH:0], c_tvalid[CDEPTH:0], c_tready[CDEPTH:0];
+ wire int_tlast[CDEPTH-1:0], int_tvalid[CDEPTH-1:0], int_tready[CDEPTH-1:0];
+
+ //Connect input to first cascade state
+ assign {c_tdata[0], c_tlast[0], c_tvalid[0]} = {i_tdata_pre, i_tlast_pre, i_tvalid_pre};
+ assign i_tready_pre = c_tready[0];
+ //Connect output to last cascade state
+ assign {o_tdata, o_tlast, o_tvalid} = {c_tdata[CDEPTH], c_tlast[CDEPTH], c_tvalid[CDEPTH]};
+ assign c_tready[CDEPTH] = o_tready;
+
+ genvar i;
+ for (i=0; i<CDEPTH; i=i+1) begin: fifo_stages
+ axi_fifo #(.WIDTH(WIDTH+1), .SIZE(SIZE_THRESHOLD)) main_fifo (
+ .clk(clk), .reset(reset), .clear(clear),
+ .i_tdata({c_tlast[i], c_tdata[i]}), .i_tvalid(c_tvalid[i]), .i_tready(c_tready[i]),
+ .o_tdata({int_tlast[i], int_tdata[i]}), .o_tvalid(int_tvalid[i]), .o_tready(int_tready[i]),
+ .space(), .occupied()
+ );
+ axi_fifo_flop2 #(.WIDTH(WIDTH+1)) fifo_flop2 (
+ .clk(clk), .reset(reset), .clear(clear),
+ .i_tdata({int_tlast[i], int_tdata[i]}), .i_tvalid(int_tvalid[i]), .i_tready(int_tready[i]),
+ .o_tdata({c_tlast[i+1], c_tdata[i+1]}), .o_tvalid(c_tvalid[i+1]), .o_tready(c_tready[i+1]),
+ .space(), .occupied()
+ );
+ end
+ end
+ endgenerate
+
+endmodule // axi_fifo_large
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_mgmt_pkt_handler.v b/fpga/usrp3/lib/rfnoc/core/chdr_mgmt_pkt_handler.v
new file mode 100644
index 000000000..f9c56a6e1
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_mgmt_pkt_handler.v
@@ -0,0 +1,617 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_mgmt_pkt_handler
+// Description:
+// This module sits inline on a CHDR stream and adds a management
+// node that is discoverable and configurable by software. As a
+// management node, a control-port master to configure any slave.
+// The output CHDR stream has an additional tdest and tid which can
+// be used to make routing decisions for management packets only.
+// tid will be CHDR_MGMT_ROUTE_TDEST when tdest should be used.
+//
+// Parameters:
+// - PROTOVER: RFNoC protocol version {8'd<major>, 8'd<minor>}
+// - CHDR_W: Width of the CHDR bus in bits
+// - USER_W: Width of the user/data bits that accompany an advertisement op
+// - RESP_FIFO_SIZE: Log2 of the depth of the response FIFO
+// Maximum value = 8
+//
+// Signals:
+// - s_axis_chdr_* : Input CHDR stream (AXI-Stream)
+// - m_axis_chdr_* : Output CHDR stream (AXI-Stream)
+// - node_info: Info about the node that contains this management slave
+// - ctrlport_* : Control-port master for management peripheral
+// - op_*: Strobe and info signals for a mgmt advertisement
+
+module chdr_mgmt_pkt_handler #(
+ parameter [15:0] PROTOVER = {8'd1, 8'd0},
+ parameter CHDR_W = 256,
+ parameter USER_W = 1,
+ parameter [0:0] MGMT_ONLY = 0,
+ parameter RESP_FIFO_SIZE = 5
+)(
+ // Clock, reset and settings
+ input wire clk,
+ input wire rst,
+ // Node Info
+ input wire [47:0] node_info,
+ // CHDR Data In (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_chdr_tdata,
+ input wire s_axis_chdr_tlast,
+ input wire s_axis_chdr_tvalid,
+ output wire s_axis_chdr_tready,
+ input wire [USER_W-1:0] s_axis_chdr_tuser,
+ // CHDR Data Out (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_chdr_tdata,
+ output wire [1:0] m_axis_chdr_tid, // Routing mode. Values defined in rfnoc_chdr_internal_utils.vh
+ output wire [9:0] m_axis_chdr_tdest, // Manual routing destination (only valid for tid = CHDR_MGMT_ROUTE_TDEST)
+ output wire m_axis_chdr_tlast,
+ output wire m_axis_chdr_tvalid,
+ input wire m_axis_chdr_tready,
+ // Control port endpoint
+ output reg ctrlport_req_wr,
+ output reg ctrlport_req_rd,
+ output reg [15:0] ctrlport_req_addr,
+ output reg [31:0] ctrlport_req_data,
+ input wire ctrlport_resp_ack,
+ input wire [31:0] ctrlport_resp_data,
+ // Mgmt packet advertisement strobe
+ output wire [USER_W-1:0] op_data,
+ output wire op_stb,
+ output wire [15:0] op_dst_epid,
+ output wire [15:0] op_src_epid
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_chdr_internal_utils.vh"
+
+ // ---------------------------------------------------
+ // Instantiate input demux and output mux to allow
+ // non-management packets to be bypassed
+ // ---------------------------------------------------
+
+ localparam CHDR_W_BYTES = CHDR_W / 8;
+ localparam LOG2_CHDR_W_BYTES = $clog2(CHDR_W_BYTES);
+
+ wire [CHDR_W-1:0] s_mgmt_tdata, m_mgmt_tdata;
+ wire [USER_W-1:0] s_mgmt_tuser;
+ wire [9:0] m_mgmt_tdest;
+ wire [1:0] m_mgmt_tid;
+ wire s_mgmt_tlast, s_mgmt_tvalid, s_mgmt_tready;
+ wire m_mgmt_tlast, m_mgmt_tvalid, m_mgmt_tready;
+
+ generate if (!MGMT_ONLY) begin
+ // Instantiate MUX and DEMUX to segregate management and non-management packets.
+ // Management packets go to the main state machine, all others get bypassed to
+ // the output.
+ wire [CHDR_W-1:0] bypass_tdata;
+ wire [9:0] bypass_tdest;
+ wire [1:0] bypass_tid;
+ wire bypass_tlast, bypass_tvalid, bypass_tready;
+ wire [CHDR_W-1:0] s_header;
+
+ // We consume the management packet only if it is actually a management packet and we
+ // don't know where it's going. If the packet has a valid EPID, it is a response that
+ // is capable of being routed.
+ wire consume_mgmt_pkt = (chdr_get_pkt_type(s_header[63:0]) == CHDR_PKT_TYPE_MGMT) &&
+ (chdr_get_dst_epid(s_header[63:0]) == NULL_EPID);
+
+ axi_demux #(
+ .WIDTH(CHDR_W), .SIZE(2), .PRE_FIFO_SIZE(1), .POST_FIFO_SIZE(0)
+ ) mgmt_demux_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .header(s_header), .dest(consume_mgmt_pkt ? 1'b1 : 1'b0),
+ .i_tdata(s_axis_chdr_tdata), .i_tlast(s_axis_chdr_tlast),
+ .i_tvalid(s_axis_chdr_tvalid), .i_tready(s_axis_chdr_tready),
+ .o_tdata({s_mgmt_tdata, bypass_tdata}), .o_tlast({s_mgmt_tlast, bypass_tlast}),
+ .o_tvalid({s_mgmt_tvalid, bypass_tvalid}), .o_tready({s_mgmt_tready, bypass_tready})
+ );
+
+ // Only one cycle of delay, so can skip past the demux with the tuser bits
+ // Packets are longer than the latency through the axi_demux
+ assign s_mgmt_tuser = s_axis_chdr_tuser;
+
+ assign {bypass_tid, bypass_tdest} = {CHDR_MGMT_ROUTE_EPID, 10'h0};
+
+ axi_mux #(
+ .WIDTH(CHDR_W+10+2), .SIZE(2), .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1)
+ ) mgmt_mux_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata({m_mgmt_tid, m_mgmt_tdest, m_mgmt_tdata, bypass_tid, bypass_tdest, bypass_tdata}),
+ .i_tlast({m_mgmt_tlast, bypass_tlast}),
+ .i_tvalid({m_mgmt_tvalid, bypass_tvalid}), .i_tready({m_mgmt_tready, bypass_tready}),
+ .o_tdata({m_axis_chdr_tid, m_axis_chdr_tdest, m_axis_chdr_tdata}),
+ .o_tlast(m_axis_chdr_tlast),
+ .o_tvalid(m_axis_chdr_tvalid), .o_tready(m_axis_chdr_tready)
+ );
+ end else begin
+ // We are assuming that only management packets come into this module so we don't
+ // instantiate a bypass path to save resources.
+ assign s_mgmt_tdata = s_axis_chdr_tdata;
+ assign s_mgmt_tlast = s_axis_chdr_tlast;
+ assign s_mgmt_tvalid = s_axis_chdr_tvalid;
+ assign s_mgmt_tuser = s_axis_chdr_tuser;
+ assign s_axis_chdr_tready = s_mgmt_tready;
+
+ assign m_axis_chdr_tdata = m_mgmt_tdata;
+ assign m_axis_chdr_tdest = m_mgmt_tdest;
+ assign m_axis_chdr_tid = m_mgmt_tid;
+ assign m_axis_chdr_tlast = m_mgmt_tlast;
+ assign m_axis_chdr_tvalid = m_mgmt_tvalid;
+ assign m_mgmt_tready = m_axis_chdr_tready;
+ end endgenerate
+
+ // ---------------------------------------------------
+ // Convert management packets to 64-bit
+ // For CHDR_W > 64, only the bottom 64 bits are used
+ // ---------------------------------------------------
+ wire [63:0] i64_tdata;
+ wire [USER_W-1:0] i64_tuser;
+ wire i64_tlast, i64_tvalid;
+ reg i64_tready;
+ reg [63:0] o64_tdata;
+ reg [9:0] o64_tdest;
+ reg [1:0] o64_tid;
+ reg o64_tlast, o64_tvalid;
+ wire o64_tready;
+
+ axi_fifo #(.WIDTH(USER_W+65), .SIZE(1)) in_flop_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata({s_mgmt_tuser, s_mgmt_tlast, s_mgmt_tdata[63:0]}),
+ .i_tvalid(s_mgmt_tvalid), .i_tready(s_mgmt_tready),
+ .o_tdata({i64_tuser, i64_tlast, i64_tdata}),
+ .o_tvalid(i64_tvalid), .o_tready(i64_tready),
+ .space(), .occupied()
+ );
+
+ axi_fifo #(.WIDTH(64+10+2+1), .SIZE(1)) out_flop_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata({o64_tlast, o64_tdest, o64_tid, o64_tdata}),
+ .i_tvalid(o64_tvalid), .i_tready(o64_tready),
+ .o_tdata({m_mgmt_tlast, m_mgmt_tdest, m_mgmt_tid, m_mgmt_tdata[63:0]}),
+ .o_tvalid(m_mgmt_tvalid), .o_tready(m_mgmt_tready),
+ .space(), .occupied()
+ );
+
+ generate
+ if (CHDR_W > 64)
+ assign m_mgmt_tdata[CHDR_W-1:CHDR_W-64] = 'h0;
+ endgenerate
+
+ // ---------------------------------------------------
+ // Parse management packet
+ // ---------------------------------------------------
+ localparam [3:0] ST_CHDR_IN_HDR = 4'd0; // Consuming input CHDR header
+ localparam [3:0] ST_CHDR_IN_MDATA = 4'd1; // Discarding input CHDR metadata
+ localparam [3:0] ST_MGMT_IN_HDR = 4'd2; // Consuming input management header
+ localparam [3:0] ST_MGMT_OP_EXEC = 4'd3; // Management operation started
+ localparam [3:0] ST_MGMT_OP_WAIT = 4'd4; // Waiting for management op to finish
+ localparam [3:0] ST_MGMT_OP_DONE = 4'd5; // Consuming management op line
+ localparam [3:0] ST_CHDR_OUT_HDR = 4'd6; // Outputing a CHDR header
+ localparam [3:0] ST_MGMT_OUT_HDR = 4'd7; // Outputing a managment header
+ localparam [3:0] ST_PASS_PAYLOAD = 4'd8; // Passing payload for downstream hops
+ localparam [3:0] ST_MOD_LAST_HOP = 4'd9; // Processing last hop
+ localparam [3:0] ST_POP_RESPONSE = 4'd10; // Popping response from response FIFO
+ localparam [3:0] ST_APPEND_LAST_HOP = 4'd11; // Appending response to last hop
+ localparam [3:0] ST_FAILSAFE_DROP = 4'd12; // Something went wrong. Flushing input.
+
+ // Pieces of state maintained by this state machine
+ reg [3:0] pkt_state = ST_CHDR_IN_HDR; // The state variable
+ reg [4:0] num_mdata; // Number of metadata lines in packet
+ reg [63:0] cached_chdr_hdr, cached_mgmt_hdr; // Cached copies of the CHDR and mgmt headers
+ reg [15:0] stripped_len; // The new CHDR length after ops are stripped
+ reg [9:0] hops_remaining; // Number of hops remaining until pkt is consumed
+ reg [7:0] resp_op_code; // Opcode for the response
+ reg [47:0] resp_op_payload; // Payload for the response
+ reg [USER_W-1:0] cached_tuser; // Cached copy of the tuser bits (for the advertise op)
+
+ // Shortcuts
+ wire [7:0] op_code = chdr_mgmt_get_op_code(i64_tdata);
+ wire [47:0] op_payload = chdr_mgmt_get_op_payload(i64_tdata);
+
+ // Inputs and outputs for the response FIFO
+ wire [55:0] resp_i_tdata, resp_o_tdata;
+ wire resp_i_tvalid, resp_o_tvalid;
+ wire [7:0] num_resp_pending;
+
+ // The massive state machine
+ // -------------------------
+ always @(posedge clk) begin
+ if (rst) begin
+ // We just need to initialize pkt_state here.
+ // All other registers are initialized in states before their usage
+ pkt_state <= ST_CHDR_IN_HDR;
+ end else begin
+ case (pkt_state)
+
+ // ST_CHDR_IN_HDR
+ // ------------------
+ // - Cache and consume the CHDR header. It will be modified
+ // later before the packet is sent out.
+ // - Initialize CHDR specific state
+ ST_CHDR_IN_HDR: begin
+ if (i64_tvalid && i64_tready) begin
+ cached_chdr_hdr <= i64_tdata;
+ cached_tuser <= i64_tuser;
+ stripped_len <= chdr_get_length(i64_tdata);
+ num_mdata <= chdr_get_num_mdata(i64_tdata) - 5'd1;
+ if (!i64_tlast) begin
+ if (chdr_get_pkt_type(i64_tdata) != CHDR_PKT_TYPE_MGMT)
+ pkt_state <= ST_FAILSAFE_DROP; // Drop non-mgmt packets
+ else if (chdr_get_num_mdata(i64_tdata) != CHDR_NO_MDATA)
+ pkt_state <= ST_CHDR_IN_MDATA; // Skip over metadata
+ else
+ pkt_state <= ST_MGMT_IN_HDR; // Start processing packet
+ end else begin
+ pkt_state <= ST_CHDR_IN_HDR; // Premature termination
+ end
+ end
+ end
+
+ // ST_CHDR_IN_MDATA
+ // ------------------
+ // - Discard incoming CHDR metadata
+ ST_CHDR_IN_MDATA: begin
+ if (i64_tvalid && i64_tready) begin
+ num_mdata <= num_mdata - 5'd1;
+ if (!i64_tlast)
+ pkt_state <= (num_mdata == CHDR_NO_MDATA) ? ST_MGMT_IN_HDR : ST_CHDR_IN_MDATA;
+ else
+ pkt_state <= ST_CHDR_IN_HDR; // Premature termination
+ end
+ end
+
+ // ST_MGMT_IN_HDR
+ // ------------------
+ // - Cache and consume the managment header. It will be modified
+ // later before the packet is sent out.
+ // - Initialize management specific state
+ ST_MGMT_IN_HDR: begin
+ if (i64_tvalid && i64_tready) begin
+ cached_mgmt_hdr <= i64_tdata;
+ hops_remaining <= chdr_mgmt_get_num_hops(i64_tdata);
+ pkt_state <= (!i64_tlast) ? ST_MGMT_OP_EXEC : ST_CHDR_IN_HDR;
+ end
+ end
+
+ // ST_MGMT_OP_EXEC
+ // ------------------
+ // - We are processing a management operation for this hop
+ // - Launch the requested action be looking at the op_code
+ ST_MGMT_OP_EXEC: begin
+ if (i64_tvalid) begin
+ // Assume that the packet is getting routed normally
+ // unless some operation changes that
+ o64_tid <= CHDR_MGMT_ROUTE_EPID;
+ o64_tdest <= 10'd0;
+ case (op_code)
+ // Operation: Do nothing
+ CHDR_MGMT_OP_NOP: begin
+ // No-op. Jump to the finish state
+ pkt_state <= ST_MGMT_OP_DONE;
+ end
+ // Operation: Advertise this management packet to outside logic
+ CHDR_MGMT_OP_ADVERTISE: begin
+ // Pretty much a no-op. Jump to the finish state
+ pkt_state <= ST_MGMT_OP_DONE;
+ end
+ // Operation: Select a destination (tdest and tid) for the output CHDR stream
+ CHDR_MGMT_OP_SEL_DEST: begin
+ o64_tid <= CHDR_MGMT_ROUTE_TDEST;
+ o64_tdest <= chdr_mgmt_sel_dest_get_tdest(op_payload);
+ pkt_state <= ST_MGMT_OP_DONE; // Single cycle op
+ end
+ // Operation: Return the packet to source (turn it around)
+ CHDR_MGMT_OP_RETURN: begin
+ o64_tid <= CHDR_MGMT_RETURN_TO_SRC;
+ pkt_state <= ST_MGMT_OP_DONE; // Single cycle op
+ end
+ // Operation: Handle a node information request.
+ // Send the info as a response
+ CHDR_MGMT_OP_INFO_REQ: begin
+ pkt_state <= ST_MGMT_OP_DONE; // Single cycle op
+ end
+ // Operation: Handle a node information response.
+ // Treat as a no-op because this is a slave
+ CHDR_MGMT_OP_INFO_RESP: begin
+ pkt_state <= ST_MGMT_OP_DONE;
+ end
+ // Operation: Post a write on the outgoing control-port
+ CHDR_MGMT_OP_CFG_WR_REQ: begin
+ // ctrlport_req_* signals are assigned below
+ pkt_state <= ST_MGMT_OP_WAIT; // Wait until ACKed
+ end
+ // Operation: Post a read on the outgoing control-port
+ CHDR_MGMT_OP_CFG_RD_REQ: begin
+ // ctrlport_req_* signals are assigned below
+ pkt_state <= ST_MGMT_OP_WAIT; // Wait until ACKed
+ end
+ // Operation: Handle a read response.
+ // Treat as a no-op because this is a slave
+ CHDR_MGMT_OP_CFG_RD_RESP: begin
+ pkt_state <= ST_MGMT_OP_DONE;
+ end
+ default: begin
+ // We should never get here
+ pkt_state <= ST_CHDR_IN_HDR;
+ end
+ endcase
+ end
+ end
+
+ // ST_MGMT_OP_WAIT
+ // ------------------
+ // - A management operation has started. We are waiting for it to finish
+ ST_MGMT_OP_WAIT: begin
+ if (i64_tvalid) begin
+ if (op_code == CHDR_MGMT_OP_CFG_WR_REQ ||
+ op_code == CHDR_MGMT_OP_CFG_RD_REQ) begin
+ // Wait for an control-port transaction to finish
+ if (ctrlport_resp_ack) begin
+ pkt_state <= ST_MGMT_OP_DONE;
+ end
+ end else begin
+ // All other operations should not get here
+ pkt_state <= ST_MGMT_OP_DONE;
+ end
+ end
+ end
+
+ // ST_MGMT_OP_DONE
+ // ------------------
+ // - The management operation has finished
+ // - Consume a word on the input CHDR stream and update interal state
+ ST_MGMT_OP_DONE: begin
+ if (i64_tvalid && i64_tready) begin
+ if (!i64_tlast) begin
+ // We just consumed 8-bytes from the incoming packet
+ stripped_len <= stripped_len - CHDR_W_BYTES;
+ // Check if this was the last op for this hop. If so start
+ // to output a packet, otherwise start the next op.
+ if (chdr_mgmt_get_ops_pending(i64_tdata) == 8'd0) begin
+ hops_remaining <= hops_remaining - 10'd1;
+ pkt_state <= ST_CHDR_OUT_HDR;
+ end else begin
+ pkt_state <= ST_MGMT_OP_EXEC;
+ end
+ end else begin
+ // Premature termination or this is the last operation
+ // Either way, move back to the beginning of the next pkt
+ pkt_state <= ST_CHDR_IN_HDR;
+ end
+ end
+ end
+
+ // ST_CHDR_OUT_HDR
+ // ------------------
+ // - We are outputing the CHDR header
+ ST_CHDR_OUT_HDR: begin
+ if (o64_tvalid && o64_tready)
+ pkt_state <= ST_MGMT_OUT_HDR;
+ end
+
+ // ST_CHDR_OUT_HDR
+ // ------------------
+ // - We are outputing the management header
+ ST_MGMT_OUT_HDR: begin
+ if (o64_tvalid && o64_tready)
+ if (resp_o_tvalid && (hops_remaining == 10'd1))
+ pkt_state <= ST_MOD_LAST_HOP; // Special state to append responses to last hod
+ else
+ pkt_state <= ST_PASS_PAYLOAD; // Just pass the data as-is
+ end
+
+ // ST_PASS_PAYLOAD
+ // ------------------
+ // - We are passing the payload for the downstream hops as-is
+ ST_PASS_PAYLOAD: begin
+ if (o64_tvalid && o64_tready) begin
+ if (!i64_tlast) begin
+ // Check if this was the last op for this hop. If so update
+ // the hop count. If this is the last hop then enter the next
+ // state to process it. We might need to append responses for our
+ // management operations.
+ if (chdr_mgmt_get_ops_pending(i64_tdata) == 8'd0) begin
+ hops_remaining <= hops_remaining - 10'd1;
+ if (resp_o_tvalid && (hops_remaining == 10'd1))
+ pkt_state <= ST_MOD_LAST_HOP; // Special state to append responses to last hod
+ else
+ pkt_state <= ST_PASS_PAYLOAD; // Just pass the data as-is
+ end else begin
+ pkt_state <= ST_PASS_PAYLOAD;
+ end
+ end else begin
+ pkt_state <= ST_CHDR_IN_HDR;
+ end
+ end
+ end
+
+ // ST_MOD_LAST_HOP
+ // ------------------
+ // - We are processing the last hop. We need a special state because we
+ // need to update the "ops_pending" field if we have responses to tack
+ // on to the end of the hop.
+ // - We continue to pass the input to the output while modifying ops_pending
+ // - For the last op, we move to the APPEND state if we need to add responses
+ ST_MOD_LAST_HOP: begin
+ if (o64_tvalid && o64_tready) begin
+ // Check if this was the last op for this hop.
+ if (chdr_mgmt_get_ops_pending(i64_tdata) == 8'd0) begin
+ if (resp_o_tvalid)
+ pkt_state <= ST_POP_RESPONSE; // We have pending responses
+ else
+ pkt_state <= i64_tlast ? ST_CHDR_IN_HDR : ST_FAILSAFE_DROP;
+ end
+ end
+ end
+
+ // ST_POP_RESPONSE
+ // ------------------
+ // - Pop a response word from the FIFO
+ ST_POP_RESPONSE: begin
+ if (resp_o_tvalid) begin
+ resp_op_code <= resp_o_tdata[7:0];
+ resp_op_payload <= resp_o_tdata[55:8];
+ pkt_state <= ST_APPEND_LAST_HOP;
+ end
+ end
+
+ // ST_APPEND_LAST_HOP
+ // ------------------
+ // - Append the popped response to the output packet here
+ // - Keep doing so until the response FIFO is empty
+ ST_APPEND_LAST_HOP: begin
+ if (o64_tvalid && o64_tready)
+ pkt_state <= resp_o_tvalid ? ST_POP_RESPONSE : ST_CHDR_IN_HDR;
+ end
+
+ // ST_FAILSAFE_DROP
+ // ------------------
+ // - Something went wrong. Discard the packet and re-arm the state machine
+ ST_FAILSAFE_DROP: begin
+ if (i64_tvalid && i64_tready)
+ pkt_state <= i64_tlast ? ST_CHDR_IN_HDR : ST_FAILSAFE_DROP;
+ end
+
+ default: begin
+ // We should never get here
+ pkt_state <= ST_CHDR_IN_HDR;
+ end
+ endcase
+ end
+ end
+
+ // Logic to determine when to consume a word from the input CHDR stream
+ always @(*) begin
+ case (pkt_state)
+ ST_CHDR_IN_HDR:
+ i64_tready = 1'b1; // Unconditionally consume header
+ ST_CHDR_IN_MDATA:
+ i64_tready = 1'b1; // Unconditionally discard header
+ ST_MGMT_IN_HDR:
+ i64_tready = 1'b1; // Unconditionally consume header
+ ST_MGMT_OP_DONE:
+ i64_tready = 1'b1; // Operation is done. Consume op-word
+ ST_PASS_PAYLOAD:
+ i64_tready = o64_tready; // We are passing input -> output
+ ST_MOD_LAST_HOP:
+ i64_tready = o64_tready; // We are passing input -> output
+ ST_FAILSAFE_DROP:
+ i64_tready = 1'b1; // Unconditionally consume to drop
+ default:
+ i64_tready = 1'b0; // Hold the input. We are processing it
+ endcase
+ end
+
+ // Swap src/dst EPIDs if returning packet to source
+ wire [15:0] o64_dst_epid = (o64_tid == CHDR_MGMT_RETURN_TO_SRC) ?
+ chdr_mgmt_get_src_epid(cached_mgmt_hdr) : chdr_get_dst_epid(cached_chdr_hdr);
+ wire [15:0] o64_src_epid = (o64_tid == CHDR_MGMT_RETURN_TO_SRC) ?
+ chdr_get_dst_epid(cached_chdr_hdr) : chdr_mgmt_get_src_epid(cached_mgmt_hdr);
+
+ // Logic to drive the output CHDR stream
+ always @(*) begin
+ case (pkt_state)
+ ST_CHDR_OUT_HDR: begin
+ // We are generating new data using cached values.
+ // Output header = Input header with new length
+ o64_tdata = chdr_set_length(
+ chdr_set_dst_epid(cached_chdr_hdr, o64_dst_epid),
+ (stripped_len + (num_resp_pending << LOG2_CHDR_W_BYTES)));
+ o64_tvalid = 1'b1;
+ o64_tlast = 1'b0;
+ end
+ ST_MGMT_OUT_HDR: begin
+ // We are generating new data using cached values.
+ // Output header = Input header with new num_hops and some protocol info
+ o64_tdata = chdr_mgmt_build_hdr(PROTOVER, chdr_w_to_enum(CHDR_W),
+ chdr_mgmt_get_num_hops(cached_mgmt_hdr) - 10'd1, o64_src_epid);
+ o64_tvalid = 1'b1;
+ o64_tlast = 1'b0;
+ end
+ ST_PASS_PAYLOAD: begin
+ // Input -> Output without modification
+ o64_tdata = i64_tdata;
+ o64_tvalid = i64_tvalid;
+ o64_tlast = i64_tlast;
+ end
+ ST_MOD_LAST_HOP: begin
+ // Input -> Output but update the ops_pending field
+ o64_tdata = chdr_mgmt_build_op(chdr_mgmt_get_op_payload(i64_tdata),
+ chdr_mgmt_get_op_code(i64_tdata),
+ chdr_mgmt_get_ops_pending(i64_tdata) + num_resp_pending);
+ o64_tvalid = i64_tvalid;
+ o64_tlast = i64_tlast && !resp_o_tvalid;
+ end
+ ST_APPEND_LAST_HOP: begin
+ // We are generating new data using cached values.
+ o64_tdata = chdr_mgmt_build_op(resp_op_payload, resp_op_code, num_resp_pending);
+ o64_tvalid = 1'b1;
+ o64_tlast = !resp_o_tvalid;
+ end
+ default: begin
+ // We are processing something. Don't output
+ o64_tdata = 64'h0;
+ o64_tvalid = 1'b0;
+ o64_tlast = 1'b0;
+ end
+ endcase
+ end
+
+ // CHDR_MGMT_OP_ADVERTISE
+ // ----------------------
+ assign op_stb = i64_tvalid && (pkt_state == ST_MGMT_OP_DONE) &&
+ (op_code == CHDR_MGMT_OP_ADVERTISE);
+ assign op_dst_epid = chdr_get_dst_epid(cached_chdr_hdr);
+ assign op_src_epid = chdr_mgmt_get_src_epid(cached_mgmt_hdr);
+ assign op_data = cached_tuser;
+
+ // CHDR_MGMT_OP_CFG_WR_REQ
+ // CHDR_MGMT_OP_CFG_RD_REQ
+ // -----------------------
+ // The request is sent out in the ST_MGMT_OP_EXEC state and we wait for a response
+ // in the ST_MGMT_OP_WAIT state
+ always @(posedge clk) begin
+ if (rst) begin
+ ctrlport_req_wr <= 1'b0;
+ ctrlport_req_rd <= 1'b0;
+ end else begin
+ ctrlport_req_wr <= i64_tvalid && (pkt_state == ST_MGMT_OP_EXEC) &&
+ (op_code == CHDR_MGMT_OP_CFG_WR_REQ);
+ ctrlport_req_rd <= i64_tvalid && (pkt_state == ST_MGMT_OP_EXEC) &&
+ (op_code == CHDR_MGMT_OP_CFG_RD_REQ);
+ ctrlport_req_addr <= chdr_mgmt_cfg_reg_get_addr(op_payload);
+ ctrlport_req_data <= chdr_mgmt_cfg_reg_get_data(op_payload);
+ end
+ end
+
+ // CHDR_MGMT_OP_CFG_RD_REQ
+ // CHDR_MGMT_OP_INFO_REQ
+ // -----------------------
+ // Collect the response for these operations and push to the response FIFO
+ assign resp_i_tvalid = i64_tvalid && (
+ ((pkt_state == ST_MGMT_OP_WAIT) && (op_code == CHDR_MGMT_OP_CFG_RD_REQ) && ctrlport_resp_ack) ||
+ ((pkt_state == ST_MGMT_OP_DONE) && (op_code == CHDR_MGMT_OP_INFO_REQ)));
+ assign resp_i_tdata = (op_code == CHDR_MGMT_OP_CFG_RD_REQ) ?
+ {ctrlport_resp_data, ctrlport_req_addr, CHDR_MGMT_OP_CFG_RD_RESP} : // Ctrlport response
+ {node_info, CHDR_MGMT_OP_INFO_RESP}; // NodeInfo
+
+ // The response FIFO should be deep enough to store all the responses
+ wire [15:0] resp_fifo_occ;
+ axi_fifo #(.WIDTH(56), .SIZE(RESP_FIFO_SIZE)) resp_fifo_i (
+ .clk(clk), .reset(rst), .clear(pkt_state == ST_CHDR_IN_HDR),
+ .i_tdata(resp_i_tdata), .i_tvalid(resp_i_tvalid),
+ .i_tready(/* Must be high. Responses will be dropped if FIFO is full */),
+ .o_tdata(resp_o_tdata), .o_tvalid(resp_o_tvalid),
+ .o_tready(resp_o_tvalid && (pkt_state == ST_POP_RESPONSE)),
+ .space(), .occupied(resp_fifo_occ)
+ );
+ assign num_resp_pending = resp_fifo_occ[7:0];
+
+endmodule // chdr_mgmt_pkt_handler
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_stream_endpoint.v b/fpga/usrp3/lib/rfnoc/core/chdr_stream_endpoint.v
new file mode 100644
index 000000000..9c824a0af
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_stream_endpoint.v
@@ -0,0 +1,621 @@
+//
+// Copyright 2018-2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_stream_endpoint
+// Description:
+// The implementation of a stream endpoint. This module serves as
+// an endpoint for a bidirectional stream. It implement a control
+// and a data path, both of which can be individually enabled using
+// parameters. The control path contains a bidirectional CHDR to
+// AXIS-Control converter. The data path has a stream input and
+// output port.
+//
+// Parameters:
+// - PROTOVER: RFNoC protocol version {8'd<major>, 8'd<minor>}
+// - CHDR_W: Width of the CHDR bus in bits
+// - INST_NUM: The instance number of this module
+// - CTRL_XBAR_PORT: The port index on the control crossbar that
+// this module's control path will connect to
+// - AXIS_CTRL_EN: Enable control traffic (axis_ctrl port)
+// - AXIS_DATA_EN: Enable data traffic (axis_data port)
+// - NUM_DATA_I: Number of AXIS data slave ports
+// - NUM_DATA_O: Number of AXIS data master ports
+// - INGRESS_BUFF_SIZE: Buffer size in log2 of the number of words
+// in the ingress buffer for the stream
+// - MTU: Log2 of the maximum packet size in words
+// - REPORT_STRM_ERRS: Report data stream errors upstream
+// - SIM_SPEEDUP: Set to 1 in simultion, and 0 otherwise
+//
+// Signals:
+// - device_id : The ID of the device that has instantiated this module
+// - *_axis_chdr_* : Input/output CHDR stream (AXI-Stream)
+// - *_axis_ctrl_* : Input/output AXIS-Control streams (AXI-Stream)
+// - *_axis_data_* : Input/output CHDR Data streams (AXI-Stream)
+// - strm_*_err_stb: The stream encountered an error
+// - signal_*_err : Notify upstream that we encountered an error
+
+module chdr_stream_endpoint #(
+ parameter [15:0] PROTOVER = {8'd1, 8'd0},
+ parameter CHDR_W = 64,
+ parameter [9:0] INST_NUM = 0,
+ parameter [9:0] CTRL_XBAR_PORT = 0,
+ parameter [0:0] AXIS_CTRL_EN = 1,
+ parameter [0:0] AXIS_DATA_EN = 1,
+ parameter [5:0] NUM_DATA_I = 1,
+ parameter [5:0] NUM_DATA_O = 1,
+ parameter [5:0] INGRESS_BUFF_SIZE = 12,
+ parameter [5:0] MTU = 10,
+ parameter [0:0] REPORT_STRM_ERRS = 1,
+ parameter [0:0] SIM_SPEEDUP = 0
+)(
+ // Clock, reset and settings
+ input wire rfnoc_chdr_clk,
+ input wire rfnoc_chdr_rst,
+ input wire rfnoc_ctrl_clk,
+ input wire rfnoc_ctrl_rst,
+ // Device info
+ input wire [15:0] device_id,
+ // CHDR in (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_chdr_tdata,
+ input wire s_axis_chdr_tlast,
+ input wire s_axis_chdr_tvalid,
+ output wire s_axis_chdr_tready,
+ // CHDR out (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_chdr_tdata,
+ output wire m_axis_chdr_tlast,
+ output wire m_axis_chdr_tvalid,
+ input wire m_axis_chdr_tready,
+ // Flow controlled data in (AXI-Stream)
+ input wire [(CHDR_W*NUM_DATA_I)-1:0] s_axis_data_tdata,
+ input wire [NUM_DATA_I-1:0] s_axis_data_tlast,
+ input wire [NUM_DATA_I-1:0] s_axis_data_tvalid,
+ output wire [NUM_DATA_I-1:0] s_axis_data_tready,
+ // Flow controlled data out (AXI-Stream)
+ output wire [(CHDR_W*NUM_DATA_O)-1:0] m_axis_data_tdata,
+ output wire [NUM_DATA_O-1:0] m_axis_data_tlast,
+ output wire [NUM_DATA_O-1:0] m_axis_data_tvalid,
+ input wire [NUM_DATA_O-1:0] m_axis_data_tready,
+ // Control in (AXI-Stream)
+ input wire [31:0] s_axis_ctrl_tdata,
+ input wire s_axis_ctrl_tlast,
+ input wire s_axis_ctrl_tvalid,
+ output wire s_axis_ctrl_tready,
+ // Control out (AXI-Stream)
+ output wire [31:0] m_axis_ctrl_tdata,
+ output wire m_axis_ctrl_tlast,
+ output wire m_axis_ctrl_tvalid,
+ input wire m_axis_ctrl_tready,
+ // Stream status specfic
+ output wire strm_seq_err_stb,
+ output wire strm_data_err_stb,
+ output wire strm_route_err_stb,
+ input wire signal_data_err
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_chdr_internal_utils.vh"
+
+ // ---------------------------------------------------
+ // Filter packets by type
+ // ---------------------------------------------------
+ wire [CHDR_W-1:0] ctrl_i_tdata, ctrl_o_tdata;
+ wire ctrl_i_tlast, ctrl_o_tlast;
+ wire ctrl_i_tvalid, ctrl_o_tvalid;
+ wire ctrl_i_tready, ctrl_o_tready;
+
+ wire [CHDR_W-1:0] data_i_tdata, data_o_tdata;
+ wire data_i_tlast, data_o_tlast;
+ wire data_i_tvalid, data_o_tvalid;
+ wire data_i_tready, data_o_tready;
+
+ wire [CHDR_W-1:0] strs_i_tdata, strs_o_tdata;
+ wire strs_i_tlast, strs_o_tlast;
+ wire strs_i_tvalid, strs_o_tvalid;
+ wire strs_i_tready, strs_o_tready;
+
+ wire [CHDR_W-1:0] mgmt_i_tdata, mgmt_o_tdata;
+ wire mgmt_i_tlast, mgmt_o_tlast;
+ wire mgmt_i_tvalid, mgmt_o_tvalid;
+ wire mgmt_i_tready, mgmt_o_tready;
+
+ function [1:0] compute_demux_dest;
+ input [63:0] hdr;
+ if (chdr_get_pkt_type(hdr) == CHDR_PKT_TYPE_CTRL)
+ // Control
+ compute_demux_dest = 2'd2;
+ else if (chdr_get_pkt_type(hdr) == CHDR_PKT_TYPE_STRC ||
+ chdr_get_pkt_type(hdr) == CHDR_PKT_TYPE_DATA ||
+ chdr_get_pkt_type(hdr) == CHDR_PKT_TYPE_DATA_TS)
+ // Data and stream command
+ compute_demux_dest = 2'd1;
+ else if (chdr_get_pkt_type(hdr) == CHDR_PKT_TYPE_STRS)
+ // Stream status
+ compute_demux_dest = 2'd0;
+ else
+ // Management (all packets must return to sender)
+ compute_demux_dest = 2'd3;
+ endfunction
+
+ // We give the demux a FIFO large enough to buffer short packets
+ // Flow control will ensure that data does not back up through
+ // this demux but we might have the other packet types block
+ // each other.
+ localparam DEMUX_FIFO_SIZE = 5;
+
+ wire [CHDR_W-1:0] chdr_header;
+ axi_demux #(
+ .WIDTH(CHDR_W), .SIZE(4), .PRE_FIFO_SIZE(DEMUX_FIFO_SIZE), .POST_FIFO_SIZE(1)
+ ) mgmt_demux_i (
+ .clk(rfnoc_chdr_clk), .reset(rfnoc_chdr_rst), .clear(1'b0),
+ .header(chdr_header), .dest(compute_demux_dest(chdr_header[63:0])),
+ .i_tdata (s_axis_chdr_tdata ),
+ .i_tlast (s_axis_chdr_tlast ),
+ .i_tvalid(s_axis_chdr_tvalid),
+ .i_tready(s_axis_chdr_tready),
+ .o_tdata ({mgmt_i_tdata, ctrl_i_tdata, data_i_tdata, strs_i_tdata }),
+ .o_tlast ({mgmt_i_tlast, ctrl_i_tlast, data_i_tlast, strs_i_tlast }),
+ .o_tvalid({mgmt_i_tvalid, ctrl_i_tvalid, data_i_tvalid, strs_i_tvalid}),
+ .o_tready({mgmt_i_tready, ctrl_i_tready, data_i_tready, strs_i_tready})
+ );
+
+ axi_mux #(
+ .WIDTH(CHDR_W), .SIZE(4), .PRIO(1), .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1)
+ ) mgmt_mux_i (
+ .clk(rfnoc_chdr_clk), .reset(rfnoc_chdr_rst), .clear(1'b0),
+ .i_tdata ({mgmt_o_tdata, data_o_tdata, strs_o_tdata, ctrl_o_tdata }),
+ .i_tlast ({mgmt_o_tlast, data_o_tlast, strs_o_tlast, ctrl_o_tlast }),
+ .i_tvalid({mgmt_o_tvalid, data_o_tvalid, strs_o_tvalid, ctrl_o_tvalid}),
+ .i_tready({mgmt_o_tready, data_o_tready, strs_o_tready, ctrl_o_tready}),
+ .o_tdata (m_axis_chdr_tdata ),
+ .o_tlast (m_axis_chdr_tlast ),
+ .o_tvalid(m_axis_chdr_tvalid),
+ .o_tready(m_axis_chdr_tready)
+ );
+
+ // ---------------------------------------------------
+ // Management Path
+ // ---------------------------------------------------
+ wire ctrlport_req_wr, ctrlport_req_rd;
+ reg ctrlport_resp_ack = 1'b0;
+ wire [15:0] ctrlport_req_addr;
+ wire [31:0] ctrlport_req_data;
+ reg [31:0] ctrlport_resp_data;
+
+ localparam [17:0] EXTENDED_INFO = {
+ 3'b0, REPORT_STRM_ERRS, NUM_DATA_O, NUM_DATA_I, AXIS_DATA_EN, AXIS_CTRL_EN};
+
+ // Handle management packets here
+ chdr_mgmt_pkt_handler #(
+ .PROTOVER(PROTOVER), .CHDR_W(CHDR_W), .MGMT_ONLY(1)
+ ) mgmt_ep_i (
+ .clk(rfnoc_chdr_clk), .rst(rfnoc_chdr_rst),
+ .node_info(chdr_mgmt_build_node_info(EXTENDED_INFO, INST_NUM, NODE_TYPE_STREAM_EP, device_id)),
+ .s_axis_chdr_tdata(mgmt_i_tdata), .s_axis_chdr_tlast(mgmt_i_tlast),
+ .s_axis_chdr_tvalid(mgmt_i_tvalid), .s_axis_chdr_tready(mgmt_i_tready),
+ .s_axis_chdr_tuser('d0),
+ .m_axis_chdr_tdata(mgmt_o_tdata), .m_axis_chdr_tlast(mgmt_o_tlast),
+ .m_axis_chdr_tdest(/* unused */), .m_axis_chdr_tid(/* unused */),
+ .m_axis_chdr_tvalid(mgmt_o_tvalid), .m_axis_chdr_tready(mgmt_o_tready),
+ .ctrlport_req_wr(ctrlport_req_wr), .ctrlport_req_rd(ctrlport_req_rd),
+ .ctrlport_req_addr(ctrlport_req_addr), .ctrlport_req_data(ctrlport_req_data),
+ .ctrlport_resp_ack(ctrlport_resp_ack), .ctrlport_resp_data(ctrlport_resp_data),
+ .op_stb(/* unused */), .op_dst_epid(/* unused */), .op_src_epid(/* unused */),
+ .op_data(/* unused */)
+ );
+
+ // ============================== REGISTERS ==============================
+ // * REG_EPID_SELF (Read-Write):
+ // The endpoint ID of this stream endpoint
+ // - [15:0]: Endpoint ID
+ // * REG_RESET_AND_FLUSH (Write-Only):
+ // Reset and flush register
+ // - [0]: Flush data path
+ // - [1]: Flush control path
+ // * REG_OSTRM_CTRL_STATUS (Read-Write):
+ // Control and status register for the output stream
+ // - [0] : Configuration start (strobe)
+ // - [1] : Is this transport lossy?
+ // - [3:2]: Payload SW buff (0=u64, 1=u32, 2=u16, 3=u8)
+ // - [5:4]: Metadata SW buff (0=u64, 1=u32, 2=u16, 3=u8)
+ // - [6] : Swap endianness
+ // * REG_OSTRM_DST_EPID (Write-Only):
+ // The endpoint ID of a downstream stream endpoint
+ // - [15:0]: Endpoint ID
+ // * REG_OSTRM_FC_FREQ_BYTES_LO, REG_OSTRM_FC_FREQ_BYTES_HI (Write-Only):
+ // Number of bytes between flow control status messages
+ // * REG_OSTRM_FC_FREQ_PKTS (Write-Only):
+ // Number of packets between flow control status messages
+ // * REG_OSTRM_FC_HEADROOM (Write-Only):
+ // Flow control headroom register
+ // - [15:0]: Bytes of headroom
+ // - [23:16]: Packets of headroom
+ // * REG_OSTRM_BUFF_CAP_BYTES_LO, REG_OSTRM_BUFF_CAP_BYTES_HI (Read-Only):
+ // Number of bytes in the downstream buffer
+ // * REG_OSTRM_BUFF_CAP_PKTS (Read-Only):
+ // Number of packets in the downstream buffer
+ // * REG_OSTRM_SEQ_ERR_CNT (Read-Only):
+ // Number of sequence errors since initialization
+ // * REG_OSTRM_DATA_ERR_CNT (Read-Only):
+ // Number of data integrity errors since initialization
+ // * REG_OSTRM_ROUTE_ERR_CNT (Read-Only):
+ // Number of routing errors since initialization
+ // * REG_ISTRM_CTRL_STATUS (Read-Write):
+ // Control and status register for the input stream
+ // - [0] : Reserved
+ // - [1] : Reserved
+ // - [3:2]: Payload SW buff (0=u64, 1=u32, 2=u16, 3=u8)
+ // - [5:4]: Metadata SW buff (0=u64, 1=u32, 2=u16, 3=u8)
+ // - [6] : Swap endianness
+ // =======================================================================
+
+ localparam [15:0] REG_EPID_SELF = 16'h00; //RW
+ localparam [15:0] REG_RESET_AND_FLUSH = 16'h04; //W
+ localparam [15:0] REG_OSTRM_CTRL_STATUS = 16'h08; //RW
+ localparam [15:0] REG_OSTRM_DST_EPID = 16'h0C; //W
+ localparam [15:0] REG_OSTRM_FC_FREQ_BYTES_LO = 16'h10; //W
+ localparam [15:0] REG_OSTRM_FC_FREQ_BYTES_HI = 16'h14; //W
+ localparam [15:0] REG_OSTRM_FC_FREQ_PKTS = 16'h18; //W
+ localparam [15:0] REG_OSTRM_FC_HEADROOM = 16'h1C; //W
+ localparam [15:0] REG_OSTRM_BUFF_CAP_BYTES_LO = 16'h20; //R
+ localparam [15:0] REG_OSTRM_BUFF_CAP_BYTES_HI = 16'h24; //R
+ localparam [15:0] REG_OSTRM_BUFF_CAP_PKTS = 16'h28; //R
+ localparam [15:0] REG_OSTRM_SEQ_ERR_CNT = 16'h2C; //R
+ localparam [15:0] REG_OSTRM_DATA_ERR_CNT = 16'h30; //R
+ localparam [15:0] REG_OSTRM_ROUTE_ERR_CNT = 16'h34; //R
+ localparam [15:0] REG_ISTRM_CTRL_STATUS = 16'h38; //RW
+
+ // Configurable registers
+ reg [15:0] reg_epid_self = 16'h0;
+ reg reg_ctrl_reset = 1'b0;
+ reg reg_istrm_reset = 1'b0;
+ reg reg_ostrm_reset = 1'b0;
+ reg reg_ostrm_cfg_start = 1'b0;
+ wire reg_ostrm_cfg_pending;
+ wire reg_ostrm_cfg_failed;
+ reg reg_ostrm_cfg_lossy_xport = 1'b0;
+ reg [1:0] reg_ostrm_cfg_pyld_sw_buff = 2'd0;
+ reg [1:0] reg_ostrm_cfg_mdata_sw_buff = 2'd0;
+ reg reg_ostrm_cfg_swap_endian = 1'b0;
+ reg [15:0] reg_ostrm_dst_epid = 16'h0;
+ reg [39:0] reg_fc_freq_bytes = 40'h0;
+ reg [23:0] reg_fc_freq_pkts = 24'h0;
+ reg [15:0] reg_fc_headroom_bytes = 16'd0;
+ reg [7:0] reg_fc_headroom_pkts = 8'd0;
+ reg [1:0] reg_istrm_cfg_pyld_sw_buff = 2'd0;
+ reg [1:0] reg_istrm_cfg_mdata_sw_buff = 2'd0;
+ reg reg_istrm_cfg_swap_endian = 1'b0;
+ wire reg_fc_enabled;
+ wire [39:0] reg_buff_cap_bytes;
+ wire [23:0] reg_buff_cap_pkts;
+ wire [31:0] reg_seq_err_cnt;
+ wire [31:0] reg_data_err_cnt;
+ wire [31:0] reg_route_err_cnt;
+
+ always @(posedge rfnoc_chdr_clk) begin
+ if (rfnoc_chdr_rst) begin
+ ctrlport_resp_ack <= 1'b0;
+ end else begin
+ // All transactions finish in 1 cycle
+ ctrlport_resp_ack <= ctrlport_req_wr | ctrlport_req_rd;
+ // Handle register writes
+ if (ctrlport_req_wr) begin
+ case(ctrlport_req_addr)
+ REG_EPID_SELF:
+ reg_epid_self <= ctrlport_req_data[15:0];
+ REG_RESET_AND_FLUSH:
+ {reg_ctrl_reset, reg_istrm_reset, reg_ostrm_reset} <= ctrlport_req_data[2:0];
+ REG_OSTRM_CTRL_STATUS:
+ {reg_ostrm_cfg_swap_endian, reg_ostrm_cfg_mdata_sw_buff, reg_ostrm_cfg_pyld_sw_buff,
+ reg_ostrm_cfg_lossy_xport, reg_ostrm_cfg_start} <= ctrlport_req_data[6:0];
+ REG_OSTRM_DST_EPID:
+ reg_ostrm_dst_epid <= ctrlport_req_data[15:0];
+ REG_OSTRM_FC_FREQ_BYTES_LO:
+ reg_fc_freq_bytes[31:0] <= ctrlport_req_data[31:0];
+ REG_OSTRM_FC_FREQ_BYTES_HI:
+ reg_fc_freq_bytes[39:32] <= ctrlport_req_data[7:0];
+ REG_OSTRM_FC_FREQ_PKTS:
+ reg_fc_freq_pkts <= ctrlport_req_data[23:0];
+ REG_OSTRM_FC_HEADROOM:
+ {reg_fc_headroom_pkts, reg_fc_headroom_bytes} <= ctrlport_req_data[23:0];
+ REG_ISTRM_CTRL_STATUS:
+ {reg_istrm_cfg_swap_endian, reg_istrm_cfg_mdata_sw_buff, reg_istrm_cfg_pyld_sw_buff}
+ <= ctrlport_req_data[6:2];
+ endcase
+ end else begin
+ // Strobed registers
+ reg_ostrm_cfg_start <= 1'b0;
+ reg_ctrl_reset <= 1'b0;
+ reg_ostrm_reset <= 1'b0;
+ reg_istrm_reset <= 1'b0;
+ end
+ // Handle register reads
+ if (ctrlport_req_rd) begin
+ case(ctrlport_req_addr)
+ REG_EPID_SELF:
+ ctrlport_resp_data <= {16'h0, reg_epid_self};
+ REG_OSTRM_CTRL_STATUS:
+ ctrlport_resp_data <= {
+ reg_fc_enabled, reg_ostrm_cfg_failed, reg_ostrm_cfg_pending, 23'h0,
+ reg_ostrm_cfg_mdata_sw_buff, reg_ostrm_cfg_pyld_sw_buff,
+ reg_ostrm_cfg_lossy_xport, 1'b0};
+ REG_OSTRM_BUFF_CAP_BYTES_LO:
+ ctrlport_resp_data <= reg_buff_cap_bytes[31:0];
+ REG_OSTRM_BUFF_CAP_BYTES_HI:
+ ctrlport_resp_data <= {24'h0, reg_buff_cap_bytes[39:32]};
+ REG_OSTRM_BUFF_CAP_PKTS:
+ ctrlport_resp_data <= {8'h0, reg_buff_cap_pkts};
+ REG_OSTRM_SEQ_ERR_CNT:
+ ctrlport_resp_data <= reg_seq_err_cnt;
+ REG_OSTRM_DATA_ERR_CNT:
+ ctrlport_resp_data <= reg_data_err_cnt;
+ REG_OSTRM_ROUTE_ERR_CNT:
+ ctrlport_resp_data <= reg_route_err_cnt;
+ REG_ISTRM_CTRL_STATUS:
+ ctrlport_resp_data <= {26'h0,
+ reg_istrm_cfg_mdata_sw_buff, reg_istrm_cfg_pyld_sw_buff, 2'b0};
+ default:
+ ctrlport_resp_data <= 32'h0;
+ endcase
+ end
+ end
+ end
+
+ // ---------------------------------------------------
+ // Data and Flow Control Path
+ // ---------------------------------------------------
+ genvar i;
+ generate if (AXIS_DATA_EN) begin: datapath
+ localparam INPUT_FLUSH_TIMEOUT_W = SIM_SPEEDUP ? 6 : 14;
+
+ // Data => CHDR
+ //-------------
+ wire [CHDR_W-1:0] axis_di_tdata, axis_dis_tdata, axis_di_tdata_pre;
+ wire [5:0] axis_di_tdest;
+ wire axis_di_tlast, axis_dis_tlast;
+ wire axis_di_tvalid, axis_dis_tvalid;
+ wire axis_di_tready, axis_dis_tready;
+
+ // Optional MUX to combine multiple input data ports into a single one
+ if (NUM_DATA_I == 6'd1) begin
+ axi_fifo #(.WIDTH(CHDR_W+1), .SIZE(1)) axis_s_reg_i (
+ .clk(rfnoc_chdr_clk), .reset(rfnoc_chdr_rst | reg_ostrm_reset), .clear(1'b0),
+ .i_tdata({s_axis_data_tlast, s_axis_data_tdata}),
+ .i_tvalid(s_axis_data_tvalid), .i_tready(s_axis_data_tready),
+ .o_tdata({axis_di_tlast, axis_di_tdata_pre}),
+ .o_tvalid(axis_di_tvalid), .o_tready(axis_di_tready),
+ .space(), .occupied()
+ );
+ assign axis_di_tdest = 6'd0;
+ end else begin
+ wire [((CHDR_W+6)*NUM_DATA_I)-1:0] s_axis_data_tdata_tmp;
+ for (i = 0; i < NUM_DATA_I; i=i+1) begin
+ assign s_axis_data_tdata_tmp[(i*(CHDR_W+6))+:(CHDR_W+6)] = {i[5:0], s_axis_data_tdata[(i*CHDR_W)+:CHDR_W]};
+ end
+
+ axi_mux #(
+ .WIDTH(CHDR_W+6), .SIZE(NUM_DATA_I), .PRIO(0), .PRE_FIFO_SIZE(1), .POST_FIFO_SIZE(1)
+ ) axis_s_mux_i (
+ .clk(rfnoc_chdr_clk), .reset(rfnoc_chdr_rst | reg_ostrm_reset), .clear(1'b0),
+ .i_tdata(s_axis_data_tdata_tmp), .i_tlast(s_axis_data_tlast),
+ .i_tvalid(s_axis_data_tvalid), .i_tready(s_axis_data_tready),
+ .o_tdata({axis_di_tdest, axis_di_tdata_pre}), .o_tlast(axis_di_tlast),
+ .o_tvalid(axis_di_tvalid), .o_tready(axis_di_tready)
+ );
+ end
+
+ // Logic to correctly fill in the VC field in the CHDR header
+ reg axis_di_hdr = 1'b1;
+ always @(posedge rfnoc_chdr_clk) begin
+ if (rfnoc_chdr_rst | reg_ostrm_reset)
+ axis_di_hdr <= 1'b1;
+ else if (axis_di_tvalid && axis_di_tready)
+ axis_di_hdr <= axis_di_tlast;
+ end
+ assign axis_di_tdata[63:0] = axis_di_hdr ? chdr_set_vc(axis_di_tdata_pre[63:0], axis_di_tdest) :
+ axis_di_tdata_pre[63:0];
+ if (CHDR_W > 64) begin
+ assign axis_di_tdata[CHDR_W-1:64] = axis_di_tdata_pre[CHDR_W-1:64];
+ end
+
+ // Module to swap words in the payload and metadata depending on SW settings
+ chdr_data_swapper #( .CHDR_W(CHDR_W)) di_swap_i (
+ .clk (rfnoc_chdr_clk),
+ .rst (rfnoc_chdr_rst | reg_ostrm_reset),
+ .payload_sw_buff(reg_ostrm_cfg_pyld_sw_buff),
+ .mdata_sw_buff (reg_ostrm_cfg_mdata_sw_buff),
+ .swap_endianness(reg_ostrm_cfg_swap_endian),
+ .s_axis_tdata (axis_di_tdata),
+ .s_axis_tlast (axis_di_tlast),
+ .s_axis_tvalid (axis_di_tvalid),
+ .s_axis_tready (axis_di_tready),
+ .m_axis_tdata (axis_dis_tdata),
+ .m_axis_tlast (axis_dis_tlast),
+ .m_axis_tvalid (axis_dis_tvalid),
+ .m_axis_tready (axis_dis_tready)
+ );
+
+ // Stream endpoint flow-control output module
+ chdr_stream_output #(
+ .CHDR_W(CHDR_W), .MTU(MTU)
+ ) strm_output_i (
+ .clk (rfnoc_chdr_clk),
+ .rst (rfnoc_chdr_rst | reg_ostrm_reset),
+ .m_axis_chdr_tdata (data_o_tdata),
+ .m_axis_chdr_tlast (data_o_tlast),
+ .m_axis_chdr_tvalid (data_o_tvalid),
+ .m_axis_chdr_tready (data_o_tready),
+ .s_axis_data_tdata (axis_dis_tdata),
+ .s_axis_data_tlast (axis_dis_tlast),
+ .s_axis_data_tvalid (axis_dis_tvalid),
+ .s_axis_data_tready (axis_dis_tready),
+ .s_axis_strs_tdata (strs_i_tdata),
+ .s_axis_strs_tlast (strs_i_tlast),
+ .s_axis_strs_tvalid (strs_i_tvalid),
+ .s_axis_strs_tready (strs_i_tready),
+ .cfg_start (reg_ostrm_cfg_start),
+ .cfg_pending (reg_ostrm_cfg_pending),
+ .cfg_failed (reg_ostrm_cfg_failed),
+ .cfg_lossy_xport (reg_ostrm_cfg_lossy_xport),
+ .cfg_dst_epid (reg_ostrm_dst_epid),
+ .cfg_this_epid (reg_epid_self),
+ .cfg_fc_freq_bytes (reg_fc_freq_bytes),
+ .cfg_fc_freq_pkts (reg_fc_freq_pkts),
+ .cfg_fc_headroom_bytes(reg_fc_headroom_bytes),
+ .cfg_fc_headroom_pkts (reg_fc_headroom_pkts),
+ .fc_enabled (reg_fc_enabled),
+ .capacity_bytes (reg_buff_cap_bytes),
+ .capacity_pkts (reg_buff_cap_pkts),
+ .seq_err_stb (strm_seq_err_stb),
+ .seq_err_cnt (reg_seq_err_cnt),
+ .data_err_stb (strm_data_err_stb),
+ .data_err_cnt (reg_data_err_cnt),
+ .route_err_stb (strm_route_err_stb),
+ .route_err_cnt (reg_route_err_cnt)
+ );
+
+ // CHDR => Data
+ //-------------
+ wire [CHDR_W-1:0] axis_do_tdata, axis_dos_tdata;
+ wire axis_do_tlast, axis_dos_tlast;
+ wire axis_do_tvalid, axis_dos_tvalid;
+ wire axis_do_tready, axis_dos_tready;
+
+ // Stream endpoint flow-control input module
+ chdr_stream_input #(
+ .CHDR_W(CHDR_W), .BUFF_SIZE(INGRESS_BUFF_SIZE),
+ .FLUSH_TIMEOUT_W(INPUT_FLUSH_TIMEOUT_W),
+ .MONITOR_EN(0), .SIGNAL_ERRS(REPORT_STRM_ERRS)
+ ) strm_input_i (
+ .clk (rfnoc_chdr_clk),
+ .rst (rfnoc_chdr_rst | reg_istrm_reset),
+ .s_axis_chdr_tdata (data_i_tdata),
+ .s_axis_chdr_tlast (data_i_tlast),
+ .s_axis_chdr_tvalid(data_i_tvalid),
+ .s_axis_chdr_tready(data_i_tready),
+ .m_axis_data_tdata (axis_do_tdata),
+ .m_axis_data_tlast (axis_do_tlast),
+ .m_axis_data_tvalid(axis_do_tvalid),
+ .m_axis_data_tready(axis_do_tready),
+ .m_axis_strs_tdata (strs_o_tdata),
+ .m_axis_strs_tlast (strs_o_tlast),
+ .m_axis_strs_tvalid(strs_o_tvalid),
+ .m_axis_strs_tready(strs_o_tready),
+ .data_err_stb (signal_data_err)
+ );
+
+ // Module to swap words in the payload and metadata depending on SW settings
+ chdr_data_swapper #( .CHDR_W(CHDR_W)) do_swap_i (
+ .clk (rfnoc_chdr_clk),
+ .rst (rfnoc_chdr_rst | reg_istrm_reset),
+ .payload_sw_buff(reg_istrm_cfg_pyld_sw_buff),
+ .mdata_sw_buff (reg_istrm_cfg_mdata_sw_buff),
+ .swap_endianness(reg_istrm_cfg_swap_endian),
+ .s_axis_tdata (axis_do_tdata),
+ .s_axis_tlast (axis_do_tlast),
+ .s_axis_tvalid (axis_do_tvalid),
+ .s_axis_tready (axis_do_tready),
+ .m_axis_tdata (axis_dos_tdata),
+ .m_axis_tlast (axis_dos_tlast),
+ .m_axis_tvalid (axis_dos_tvalid),
+ .m_axis_tready (axis_dos_tready)
+ );
+
+ // Optional DEMUX to split multiple single stream into multiple outputs
+ // Packets with an invalid (out of bounds) VC goes to port 0
+ if (NUM_DATA_O == 6'd1) begin
+ axi_fifo #(.WIDTH(CHDR_W+1), .SIZE(1)) axis_m_reg_i (
+ .clk(rfnoc_chdr_clk), .reset(rfnoc_chdr_rst | reg_istrm_reset), .clear(1'b0),
+ .i_tdata({axis_dos_tlast, axis_dos_tdata}),
+ .i_tvalid(axis_dos_tvalid), .i_tready(axis_dos_tready),
+ .o_tdata({m_axis_data_tlast, m_axis_data_tdata}),
+ .o_tvalid(m_axis_data_tvalid), .o_tready(m_axis_data_tready),
+ .space(), .occupied()
+ );
+ end else begin
+ wire [CHDR_W-1:0] data_header;
+ wire [5:0] data_vc = chdr_get_vc(data_header[63:0]);
+ axi_demux #(
+ .WIDTH(CHDR_W), .SIZE(NUM_DATA_O), .PRE_FIFO_SIZE(1), .POST_FIFO_SIZE(1)
+ ) axis_m_demux_i (
+ .clk(rfnoc_chdr_clk), .reset(rfnoc_chdr_rst | reg_istrm_reset), .clear(1'b0),
+ .header(data_header),
+ .dest((data_vc < NUM_DATA_O) ? data_vc[$clog2(NUM_DATA_O)-1:0] : {$clog2(NUM_DATA_O){1'b0}}),
+ .i_tdata(axis_dos_tdata), .i_tlast(axis_dos_tlast),
+ .i_tvalid(axis_dos_tvalid), .i_tready(axis_dos_tready),
+ .o_tdata(m_axis_data_tdata), .o_tlast(m_axis_data_tlast),
+ .o_tvalid(m_axis_data_tvalid), .o_tready(m_axis_data_tready)
+ );
+ end
+
+ end else begin // if (AXIS_DATA_EN)
+
+ assign data_i_tready = 1'b1;
+ assign data_o_tdata = {CHDR_W{1'b0}};
+ assign data_o_tlast = 1'b0;
+ assign data_o_tvalid = 1'b0;
+
+ assign strs_i_tready = 1'b1;
+ assign strs_o_tdata = {CHDR_W{1'b0}};
+ assign strs_o_tlast = 1'b0;
+ assign strs_o_tvalid = 1'b0;
+
+ assign s_axis_data_tready = {NUM_DATA_I{1'b0}};
+ assign m_axis_data_tdata = {(CHDR_W*NUM_DATA_O){1'b0}};
+ assign m_axis_data_tlast = {NUM_DATA_O{1'b0}};
+ assign m_axis_data_tvalid = {NUM_DATA_O{1'b0}};
+
+ end endgenerate
+
+ // ---------------------------------------------------
+ // Control Path
+ // ---------------------------------------------------
+ generate if (AXIS_CTRL_EN) begin: ctrlpath
+
+ // Convert from a CHDR control packet to an AXIS control packet
+ chdr_to_axis_ctrl #(
+ .CHDR_W(CHDR_W), .THIS_PORTID(CTRL_XBAR_PORT)
+ ) chdr_ctrl_adapter_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst | reg_ctrl_reset),
+ .this_epid (reg_epid_self),
+ .s_rfnoc_chdr_tdata (ctrl_i_tdata),
+ .s_rfnoc_chdr_tlast (ctrl_i_tlast),
+ .s_rfnoc_chdr_tvalid(ctrl_i_tvalid),
+ .s_rfnoc_chdr_tready(ctrl_i_tready),
+ .m_rfnoc_chdr_tdata (ctrl_o_tdata),
+ .m_rfnoc_chdr_tlast (ctrl_o_tlast),
+ .m_rfnoc_chdr_tvalid(ctrl_o_tvalid),
+ .m_rfnoc_chdr_tready(ctrl_o_tready),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst),
+ .s_rfnoc_ctrl_tdata (s_axis_ctrl_tdata),
+ .s_rfnoc_ctrl_tlast (s_axis_ctrl_tlast),
+ .s_rfnoc_ctrl_tvalid(s_axis_ctrl_tvalid),
+ .s_rfnoc_ctrl_tready(s_axis_ctrl_tready),
+ .m_rfnoc_ctrl_tdata (m_axis_ctrl_tdata),
+ .m_rfnoc_ctrl_tlast (m_axis_ctrl_tlast),
+ .m_rfnoc_ctrl_tvalid(m_axis_ctrl_tvalid),
+ .m_rfnoc_ctrl_tready(m_axis_ctrl_tready)
+ );
+
+ end else begin // if (AXIS_CTRL_EN)
+
+ assign ctrl_i_tready = 1'b1;
+ assign ctrl_o_tdata = {CHDR_W{1'b0}};
+ assign ctrl_o_tlast = 1'b0;
+ assign ctrl_o_tvalid = 1'b0;
+
+ assign s_axis_ctrl_tready = 1'b1;
+ assign m_axis_ctrl_tdata = 32'h0;
+ assign m_axis_ctrl_tlast = 1'b0;
+ assign m_axis_ctrl_tvalid = 1'b0;
+
+ end endgenerate
+
+endmodule // chdr_stream_endpoint
+
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_stream_input.v b/fpga/usrp3/lib/rfnoc/core/chdr_stream_input.v
new file mode 100644
index 000000000..2a8a9c628
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_stream_input.v
@@ -0,0 +1,569 @@
+//
+// Copyright 2018-2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_stream_input
+// Description:
+// Implements the CHDR input port for a stream endpoint.
+// The module accepts stream command and data packets and
+// emits stream status packets. Flow control and error state
+// is communicated using stream status packets. There are no
+// external config interfaces because all configuration is done
+// using stream command packets.
+//
+// Parameters:
+// - CHDR_W: Width of the CHDR bus in bits
+// - BUFF_SIZE: Buffer size in log2 of the number of words in the
+// ingress buffer for the stream
+// - FLUSH_TIMEOUT_W: log2 of the number of cycles to wait in order
+// to flush the input stream
+// - SIGNAL_ERRS: If set to 1 then all stream errors will be notified
+// upstream, otherwise ALL errors are ignored
+//
+// Signals:
+// - s_axis_chdr_* : Input CHDR stream (AXI-Stream)
+// - m_axis_chdr_* : Output flow-controlled CHDR stream (AXI-Stream)
+// - m_axis_strs_* : Output stream status (AXI-Stream)
+// - data_err_stb : If asserted, a data error notification is sent upstream
+//
+
+module chdr_stream_input #(
+ parameter CHDR_W = 256,
+ parameter BUFF_SIZE = 14,
+ parameter FLUSH_TIMEOUT_W = 14,
+ parameter MONITOR_EN = 1,
+ parameter SIGNAL_ERRS = 1
+)(
+ // Clock, reset and settings
+ input wire clk,
+ input wire rst,
+ // CHDR in (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_chdr_tdata,
+ input wire s_axis_chdr_tlast,
+ input wire s_axis_chdr_tvalid,
+ output wire s_axis_chdr_tready,
+ // Flow controlled data out (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_data_tdata,
+ output wire m_axis_data_tlast,
+ output wire m_axis_data_tvalid,
+ input wire m_axis_data_tready,
+ // Stream status out (AXI-Stream)
+ output reg [CHDR_W-1:0] m_axis_strs_tdata,
+ output wire m_axis_strs_tlast,
+ output wire m_axis_strs_tvalid,
+ input wire m_axis_strs_tready,
+ // External stream error signal
+ input wire data_err_stb
+);
+
+ // The buffer size depends on the BUFF_SIZE parameter
+ localparam [40:0] BUFF_SIZE_BYTES = ((41'h1 << BUFF_SIZE) * (CHDR_W / 8)) - 41'h1;
+ // This is a flit-buffer. No packet limits
+ localparam [23:0] BUFF_SIZE_PKTS = 24'hFFFFFF;
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_chdr_internal_utils.vh"
+
+ // ---------------------------------------------------
+ // Ingress Buffer and Flow Control Logic
+ // ---------------------------------------------------
+ wire [CHDR_W-1:0] buff_tdata;
+ wire buff_tlast, buff_tvalid;
+ reg buff_tready;
+ wire [15:0] buff_info;
+
+ chdr_ingress_fifo #(
+ .WIDTH(CHDR_W), .SIZE(BUFF_SIZE)
+ ) ingress_fifo_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata(s_axis_chdr_tdata), .i_tlast(s_axis_chdr_tlast),
+ .i_tvalid(s_axis_chdr_tvalid), .i_tready(s_axis_chdr_tready),
+ .o_tdata(buff_tdata), .o_tlast(buff_tlast),
+ .o_tvalid(buff_tvalid), .o_tready(buff_tready)
+ );
+
+ generate if (MONITOR_EN) begin
+ wire [BUFF_SIZE:0] occ_lines;
+ axis_fifo_monitor #( .COUNT_W(BUFF_SIZE+1) ) fifo_mon_i (
+ .clk(clk), .reset(rst),
+ .i_tlast(s_axis_chdr_tlast), .i_tvalid(s_axis_chdr_tvalid), .i_tready(s_axis_chdr_tready),
+ .o_tlast(buff_tlast), .o_tvalid(buff_tvalid), .o_tready(buff_tready),
+ .i_sop(), .i_eop(), .o_sop(), .o_eop(),
+ .occupied(occ_lines), .occupied_pkts()
+ );
+ // buff_info represents a fraction of the fullness of the buffer
+ // fullness percentage = (buff_info / 32768) * 100
+ if (BUFF_SIZE + 1 >= 16)
+ assign buff_info = occ_lines[BUFF_SIZE:(BUFF_SIZE-15)];
+ else
+ assign buff_info = {occ_lines, {(15-BUFF_SIZE){1'b0}}};
+ end else begin
+ assign buff_info = 16'd0;
+ end endgenerate
+
+ // Flow Control State
+ // xfer_cnt: Total transfer count since fc_enabled = 1
+ // accum: Transfer count since last FC response
+ // fc_freq: The threshold for sending an FC response
+ reg [63:0] xfer_cnt_bytes = 64'd0;
+ reg [39:0] xfer_cnt_pkts = 40'd0;
+ reg [63:0] accum_bytes = 64'd0;
+ reg [39:0] accum_pkts = 40'd0;
+ reg [63:0] fc_freq_bytes = 64'd0;
+ reg [39:0] fc_freq_pkts = 40'd0;
+
+ // State machine transition signals info
+ reg fc_enabled = 1'b0; // Is flow control enabled?
+ wire fc_ping; // A flow control response was requested
+ wire fc_first_resp; // Send the first flow control response
+ wire fc_refresh; // Refresh accumulated values
+ wire fc_override; // Override total xfer counts
+ reg fc_override_del = 1'b0;
+ reg [3:0] fc_due_shreg = 4'h0; // Is a response due? (shift register)
+
+ // Endpoint IDs of this endpoint and the stream source
+ reg [15:0] this_epid = 16'd0, return_epid = 16'd0;
+
+ // Cached values from a stream command
+ reg [63:0] strc_num_bytes;
+ reg [39:0] strc_num_pkts;
+ reg [3:0] strc_op_data; // Unused for now
+ reg [3:0] strc_op_code;
+
+ // Total transfer count updater
+ always @(posedge clk) begin
+ if (rst || !fc_enabled) begin
+ // Reset
+ xfer_cnt_bytes <= 64'd0;
+ xfer_cnt_pkts <= 40'd0;
+ end else if (fc_override) begin
+ // Override
+ xfer_cnt_bytes <= strc_num_bytes;
+ xfer_cnt_pkts <= strc_num_pkts;
+ end else if (buff_tvalid && buff_tready) begin
+ // Count
+ xfer_cnt_bytes <= xfer_cnt_bytes + (CHDR_W/8);
+ if (buff_tlast)
+ xfer_cnt_pkts <= xfer_cnt_pkts + 40'd1;
+ end
+ end
+
+ // Accumulated transfer count updater
+ always @(posedge clk) begin
+ if (rst || !fc_enabled || fc_refresh) begin
+ // Reset
+ accum_bytes <= 64'd0;
+ accum_pkts <= 40'd0;
+ end else if (buff_tvalid && buff_tready) begin
+ // Count
+ accum_bytes <= accum_bytes + (CHDR_W/8);
+ if (buff_tlast)
+ accum_pkts <= accum_pkts + 40'd1;
+ end
+ end
+
+ // Flow control trigger
+ // Why a shift-register here?
+ // 1. For edge detection
+ // 2. To allow the tools to re-time the wide comparators.
+ // We don't care about the latency here because stream
+ // status messages are asynchronous wrt the input.
+ always @(posedge clk) begin
+ if (rst || !fc_enabled) begin
+ fc_due_shreg <= 4'h0;
+ end else begin
+ fc_due_shreg <= {
+ fc_due_shreg[2:0],
+ (accum_bytes >= fc_freq_bytes) || (accum_pkts >= fc_freq_pkts)
+ };
+ end
+ end
+ wire fc_resp_due = fc_due_shreg[2] && !fc_due_shreg[3];
+
+ // ---------------------------------------------------
+ // Stream Command Handler
+ // ---------------------------------------------------
+ localparam [2:0] ST_IN_HDR = 3'd0; // The CHDR header of an input pkt
+ localparam [2:0] ST_IN_DATA = 3'd1; // The CHDR body (incl. mdata) of an input pkt
+ localparam [2:0] ST_STRC_W0 = 3'd2; // The first word of a stream command
+ localparam [2:0] ST_STRC_W1 = 3'd3; // The second word of a stream command
+ localparam [2:0] ST_STRC_EXEC = 3'd4; // A stream command is executing
+ localparam [2:0] ST_FLUSH = 3'd5; // Input is flushing
+ localparam [2:0] ST_DROP = 3'd6; // Current packet is being dropped
+
+ reg [2:0] state = ST_IN_HDR; // State of the input state machine
+ reg pkt_too_long = 1'b0; // Error case. Packet is too long
+ reg is_first_data_pkt = 1'b1; // Is this the first data pkt after fc_enabled = 1?
+ reg is_first_strc_pkt = 1'b1; // Is this the strm cmd data pkt after fc_enabled = 1?
+ reg [15:0] exp_data_seq_num = 16'd0; // Expected sequence number for the next data pkt
+ reg [15:0] exp_strc_seq_num = 16'd0; // Expected sequence number for the next stream cmd pkt
+ reg [15:0] strc_dst_epid = 16'd0; // EPID in CHDR header of STRC packet
+
+ reg [FLUSH_TIMEOUT_W-1:0] flush_counter = {FLUSH_TIMEOUT_W{1'b0}};
+
+ // Shortcuts
+ wire is_data_pkt =
+ chdr_get_pkt_type(buff_tdata[63:0]) == CHDR_PKT_TYPE_DATA ||
+ chdr_get_pkt_type(buff_tdata[63:0]) == CHDR_PKT_TYPE_DATA_TS;
+ wire is_strc_pkt =
+ chdr_get_pkt_type(buff_tdata[63:0]) == CHDR_PKT_TYPE_STRC;
+
+ // Error Logic
+ wire data_seq_err_stb = (state == ST_IN_HDR) && is_data_pkt && !is_first_data_pkt &&
+ (chdr_get_seq_num(buff_tdata[63:0]) != exp_data_seq_num);
+ wire strc_seq_err_stb = (state == ST_IN_HDR) && is_strc_pkt && !is_first_strc_pkt &&
+ (chdr_get_seq_num(buff_tdata[63:0]) != exp_strc_seq_num);
+ wire seq_err_stb = (data_seq_err_stb || strc_seq_err_stb) && buff_tvalid && buff_tready;
+
+ wire route_err_stb = buff_tvalid && buff_tready && (state == ST_IN_HDR) &&
+ (chdr_get_dst_epid(buff_tdata[63:0]) != this_epid);
+
+ // Break critical paths to response FIFO
+ reg [47:0] stream_err_info = 48'h0;
+ reg stream_err_stb = 1'b0;
+ reg [3:0] stream_err_status = CHDR_STRS_STATUS_OKAY;
+
+ always @(posedge clk) begin
+ if (rst || (SIGNAL_ERRS == 0)) begin
+ stream_err_stb <= 1'b0;
+ end else begin
+ stream_err_stb <= seq_err_stb | route_err_stb | data_err_stb;
+ if (seq_err_stb) begin
+ stream_err_status <= CHDR_STRS_STATUS_SEQERR;
+ // The extended info has the packet type (to detect which stream
+ // had an error), the expected and actual sequence number.
+ stream_err_info <= {13'h0, chdr_get_pkt_type(buff_tdata[63:0]),
+ data_seq_err_stb ? exp_data_seq_num : exp_strc_seq_num,
+ chdr_get_seq_num(buff_tdata[63:0])};
+ end else if (route_err_stb) begin
+ stream_err_status <= CHDR_STRS_STATUS_RTERR;
+ // The extended info has the expected and actual destination EPID.
+ stream_err_info <= {16'd0, this_epid, chdr_get_dst_epid(buff_tdata[63:0])};
+ end else begin
+ stream_err_status <= CHDR_STRS_STATUS_DATAERR;
+ // The extended info has the expected and actual destination EPID.
+ stream_err_info <= {16'd0, this_epid, chdr_get_dst_epid(buff_tdata[63:0])};
+ end
+ end
+ end
+
+ // Input State Machine
+ // - Pass data packets forward
+ // - Consume stream cmd packets
+ always @(posedge clk) begin
+ if (rst) begin
+ state <= ST_IN_HDR;
+ pkt_too_long <= 1'b0;
+ fc_enabled <= 1'b0;
+ end else begin
+ case (state)
+ ST_IN_HDR: begin
+ if (buff_tvalid && buff_tready) begin
+ if (!buff_tlast) begin
+ // Classify packet and...
+ if (is_strc_pkt) begin
+ // ...consume if it is a stream command or...
+ state <= ST_STRC_W0;
+ end else if (is_data_pkt) begin
+ // ...pass to output if it is a data packet...
+ state <= ST_IN_DATA;
+ end else begin
+ // ... otherwise drop.
+ state <= ST_DROP;
+ end
+ end
+ // Update other state vars
+ pkt_too_long <= 1'b0;
+ if (is_strc_pkt) begin
+ is_first_strc_pkt <= 1'b0;
+ strc_dst_epid <= chdr_get_dst_epid(buff_tdata[63:0]);
+ exp_strc_seq_num <= chdr_get_seq_num(buff_tdata[63:0]) + 16'd1;
+ end else if (is_data_pkt) begin
+ is_first_data_pkt <= 1'b0;
+ exp_data_seq_num <= chdr_get_seq_num(buff_tdata[63:0]) + 16'd1;
+ end
+ end
+ end
+ ST_IN_DATA: begin
+ // Pass the data packet forward
+ if (buff_tvalid && buff_tready && buff_tlast)
+ state <= ST_IN_HDR;
+ end
+ ST_STRC_W0: begin
+ if (buff_tvalid && buff_tready) begin
+ // Consume the first word of a stream command packet
+ if (CHDR_W > 64) begin
+ strc_num_bytes <= chdr128_strc_get_num_bytes(buff_tdata[127:0]);
+ strc_num_pkts <= chdr128_strc_get_num_pkts (buff_tdata[127:0]);
+ strc_op_data <= chdr128_strc_get_op_data (buff_tdata[127:0]);
+ strc_op_code <= chdr128_strc_get_op_code (buff_tdata[127:0]);
+ return_epid <= chdr128_strs_get_src_epid (buff_tdata[127:0]);
+ state <= ST_STRC_EXEC;
+ pkt_too_long <= ~buff_tlast;
+ end else begin
+ strc_num_pkts <= chdr64_strc_get_num_pkts(buff_tdata[63:0]);
+ strc_op_data <= chdr64_strc_get_op_data (buff_tdata[63:0]);
+ strc_op_code <= chdr64_strc_get_op_code (buff_tdata[63:0]);
+ return_epid <= chdr64_strs_get_src_epid(buff_tdata[63:0]);
+ state <= ST_STRC_W1;
+ end
+ end
+ end
+ ST_STRC_W1: begin
+ if (buff_tvalid && buff_tready) begin
+ // Consume the second word of a stream command packet
+ strc_num_bytes <= chdr64_strc_get_num_bytes(buff_tdata[63:0]);
+ state <= ST_STRC_EXEC;
+ pkt_too_long <= ~buff_tlast;
+ end
+ end
+ ST_STRC_EXEC: begin
+ case (strc_op_code)
+ CHDR_STRC_OPCODE_INIT: begin
+ // Configure FC but disable it temporarily
+ fc_freq_bytes <= strc_num_bytes;
+ fc_freq_pkts <= strc_num_pkts;
+ this_epid <= strc_dst_epid;
+ fc_enabled <= 1'b0;
+ // Flush the input
+ state <= ST_FLUSH;
+ flush_counter <= {FLUSH_TIMEOUT_W{1'b1}};
+ end
+ CHDR_STRC_OPCODE_PING: begin
+ // Ping can complete in 1 cycle
+ state <= pkt_too_long ? ST_DROP : ST_IN_HDR;
+ end
+ CHDR_STRC_OPCODE_RESYNC: begin
+ // Resync can complete in 1 cycle
+ state <= pkt_too_long ? ST_DROP : ST_IN_HDR;
+ end
+ default: begin
+ state <= pkt_too_long ? ST_DROP : ST_IN_HDR;
+ end
+ endcase
+ end
+ ST_FLUSH: begin
+ // Drop until the next packet arrives
+ if (buff_tvalid && buff_tready) begin
+ flush_counter <= {FLUSH_TIMEOUT_W{1'b1}};
+ end else begin
+ flush_counter <= flush_counter - 'd1;
+ if (flush_counter == {FLUSH_TIMEOUT_W{1'b0}}) begin
+ // Done flushing. Re-arm flow control and reset packet
+ // sequence check info.
+ fc_enabled <= 1'b1;
+ is_first_data_pkt <= 1'b1;
+ is_first_strc_pkt <= 1'b1;
+ state <= ST_IN_HDR;
+ end
+ end
+ end
+ ST_DROP: begin
+ // Drop until the next packet arrives
+ if (buff_tvalid && buff_tready && buff_tlast)
+ state <= ST_IN_HDR;
+ end
+ default: begin
+ // We should never get here
+ state <= ST_IN_HDR;
+ end
+ endcase
+ end
+ end
+
+ always @(*) begin
+ case (state)
+ ST_IN_HDR:
+ buff_tready = m_axis_data_tready || !is_data_pkt;
+ ST_IN_DATA:
+ buff_tready = m_axis_data_tready;
+ ST_STRC_W0:
+ buff_tready = 1'b1;
+ ST_STRC_W1:
+ buff_tready = 1'b1;
+ ST_FLUSH:
+ buff_tready = 1'b1;
+ ST_DROP:
+ buff_tready = 1'b1;
+ default:
+ buff_tready = 1'b0;
+ endcase
+ end
+
+ // Logic to drive output port
+ assign m_axis_data_tdata = buff_tdata;
+ assign m_axis_data_tlast = buff_tlast;
+ assign m_axis_data_tvalid = buff_tvalid &&
+ ((state == ST_IN_HDR && is_data_pkt) || state == ST_IN_DATA);
+
+ // Logic to drive triggers
+ assign fc_ping = (state == ST_STRC_EXEC) && (strc_op_code == CHDR_STRC_OPCODE_PING);
+ assign fc_first_resp = (state == ST_FLUSH) && (flush_counter == {FLUSH_TIMEOUT_W{1'b0}});
+ assign fc_override = (state == ST_STRC_EXEC) && (strc_op_code == CHDR_STRC_OPCODE_RESYNC);
+ always @(posedge clk) fc_override_del <= fc_override;
+
+ wire [51:0] resp_o_tdata;
+ wire resp_o_tvalid;
+ reg [51:0] resp_i_tdata;
+ reg resp_i_tvalid = 1'b0;
+
+ // Send a stream status packet for the following cases:
+ // - Immediately after initialization
+ // - If a response is explicitly requested (ping)
+ // - If a response is due i.e. we have exceeded the frequency
+ // - If FC is resynchronized via a stream cmd
+ // - If an error is detected in the stream
+ always @(posedge clk) begin
+ if (rst) begin
+ resp_i_tvalid <= 1'b0;
+ resp_i_tdata <= 52'h0;
+ end else begin
+ resp_i_tvalid <= fc_first_resp || fc_ping || fc_resp_due || fc_override_del || stream_err_stb;
+ resp_i_tdata <= stream_err_stb ? {stream_err_info, stream_err_status} : {48'h0, CHDR_STRS_STATUS_OKAY};
+ end
+ end
+
+ // ---------------------------------------------------
+ // Stream Status Responder
+ // ---------------------------------------------------
+ localparam [2:0] ST_STRS_IDLE = 3'd0; // Waiting for response to post
+ localparam [2:0] ST_STRS_HDR = 3'd1; // Sending response CHDR header
+ localparam [2:0] ST_STRS_W0 = 3'd2; // Sending first response word
+ localparam [2:0] ST_STRS_W1 = 3'd3; // Sending second response word
+ localparam [2:0] ST_STRS_W2 = 3'd4; // Sending third response word
+ localparam [2:0] ST_STRS_W3 = 3'd5; // Sending fourth response word
+ localparam [2:0] ST_STRS_DONE = 3'd6; // Consuming response
+
+ reg [2:0] resp_state = ST_STRS_IDLE; // State of the responder
+ reg [15:0] resp_seq_num = 16'd0; // Current sequence number of response
+
+ assign fc_refresh = (resp_state == ST_STRS_DONE);
+
+ // A FIFO that holds up to 32 posted responses and status information
+ // NOTE: This is a lossy FIFO. If the downstream response port is clogged
+ // then we will drop responses. That should never happen in a normal operating
+ // scenario.
+ axi_fifo #(.WIDTH(48 + 4), .SIZE(5)) resp_fifo_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata(resp_i_tdata), .i_tvalid(resp_i_tvalid), .i_tready(/* Lossy FIFO */),
+ .o_tdata(resp_o_tdata), .o_tvalid(resp_o_tvalid), .o_tready(resp_state == ST_STRS_DONE || !fc_enabled),
+ .space(), .occupied()
+ );
+
+ // Responder State Machine
+ // - Wait for response to appear in FIFO
+ // - Output a full packet (different # of xfers depending on CHDR_W)
+ always @(posedge clk) begin
+ if (rst || !fc_enabled) begin
+ resp_state <= ST_STRS_IDLE;
+ resp_seq_num <= 16'd0;
+ end else begin
+ case (resp_state)
+ ST_STRS_IDLE: begin
+ if (resp_o_tvalid)
+ resp_state <= ST_STRS_HDR;
+ end
+ ST_STRS_HDR: begin
+ if (m_axis_strs_tready)
+ resp_state <= ST_STRS_W0;
+ end
+ ST_STRS_W0: begin
+ if (m_axis_strs_tready)
+ if (CHDR_W < 256)
+ resp_state <= ST_STRS_W1;
+ else
+ resp_state <= ST_STRS_DONE;
+ end
+ ST_STRS_W1: begin
+ if (m_axis_strs_tready)
+ if (CHDR_W < 128)
+ resp_state <= ST_STRS_W2;
+ else
+ resp_state <= ST_STRS_DONE;
+ end
+ ST_STRS_W2: begin
+ if (m_axis_strs_tready)
+ resp_state <= ST_STRS_W3;
+ end
+ ST_STRS_W3: begin
+ if (m_axis_strs_tready)
+ resp_state <= ST_STRS_DONE;
+ end
+ ST_STRS_DONE: begin
+ resp_state <= ST_STRS_IDLE;
+ resp_seq_num <= resp_seq_num + 16'd1;
+ end
+ default: begin
+ // We should never get here
+ resp_state <= ST_STRS_IDLE;
+ end
+ endcase
+ end
+ end
+
+ // Output data. Header and Payload
+ wire [63:0] strs_header = chdr_build_header(
+ /*VC*/ 6'd0, /*eob*/ 1'b0, /*eov*/ 1'b0, CHDR_PKT_TYPE_STRS, CHDR_NO_MDATA,
+ resp_seq_num, 16'd32+(CHDR_W/8), return_epid);
+ wire [255:0] strs_payload = chdr256_strs_build(
+ /*statusinfo*/ resp_o_tdata[51:4], buff_info,
+ xfer_cnt_bytes, xfer_cnt_pkts,
+ BUFF_SIZE_PKTS[23:0], BUFF_SIZE_BYTES[39:0],
+ resp_o_tdata[3:0], this_epid);
+
+ // m_axis_strs_* signal values depend on CHDR_W
+ generate
+ if (CHDR_W == 64) begin
+ // Response spans 5 transfers (header + 4 words)
+ assign m_axis_strs_tlast = (resp_state == ST_STRS_W3);
+ always @(*) begin
+ case (resp_state)
+ ST_STRS_W0:
+ m_axis_strs_tdata = strs_payload[63:0];
+ ST_STRS_W1:
+ m_axis_strs_tdata = strs_payload[127:64];
+ ST_STRS_W2:
+ m_axis_strs_tdata = strs_payload[191:128];
+ ST_STRS_W3:
+ m_axis_strs_tdata = strs_payload[255:192];
+ default:
+ m_axis_strs_tdata = strs_header;
+ endcase
+ end
+ end else if (CHDR_W == 128) begin
+ // Response spans 3 transfers (header + 2 words)
+ assign m_axis_strs_tlast = (resp_state == ST_STRS_W1);
+ always @(*) begin
+ case (resp_state)
+ ST_STRS_W0:
+ m_axis_strs_tdata = strs_payload[127:0];
+ ST_STRS_W1:
+ m_axis_strs_tdata = strs_payload[255:128];
+ default:
+ m_axis_strs_tdata = {64'h0, strs_header};
+ endcase
+ end
+ end else begin
+ // Response spans 2 transfers (header + word)
+ assign m_axis_strs_tlast = (resp_state == ST_STRS_W0);
+ always @(*) begin
+ case (resp_state)
+ ST_STRS_W0:
+ m_axis_strs_tdata[255:0] = strs_payload;
+ default:
+ m_axis_strs_tdata[255:0] = {192'h0, strs_header};
+ endcase
+ if (CHDR_W > 256) begin
+ m_axis_strs_tdata[CHDR_W-1:256] = 'h0;
+ end
+ end
+ end
+ endgenerate
+
+ assign m_axis_strs_tvalid = (resp_state != ST_STRS_IDLE) && (resp_state != ST_STRS_DONE);
+
+endmodule // chdr_stream_input
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_stream_output.v b/fpga/usrp3/lib/rfnoc/core/chdr_stream_output.v
new file mode 100644
index 000000000..271c7fccc
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_stream_output.v
@@ -0,0 +1,557 @@
+//
+// Copyright 2018-2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_stream_output
+// Description:
+// Implements the CHDR output port for a stream endpoint.
+// The module generates stream command packets to setup
+// a downstream endpoint module (chdr_stream_input). Once
+// a stream is setup, the CHDR data on the axis_data port
+// can be sent downstream with full flow control. Stream
+// status messages are recieved from the downstream node
+// to update flow control state. This module has an external
+// configuration bus to initiate stream creation.
+//
+// Parameters:
+// - CHDR_W: Width of the CHDR bus in bits
+// - MTU: Log2 of the maximum number of lines in a packet
+//
+// Signals:
+// - m_axis_chdr_* : Output CHDR stream (AXI-Stream)
+// - s_axis_data_* : Input CHDR Data stream (AXI-Stream) before flow control
+// - s_axis_strs_* : Input stream status (AXI-Stream)
+
+module chdr_stream_output #(
+ parameter CHDR_W = 256,
+ parameter MTU = 10
+)(
+ // Clock, reset and settings
+ input wire clk,
+ input wire rst,
+ // CHDR out (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_chdr_tdata,
+ output wire m_axis_chdr_tlast,
+ output wire m_axis_chdr_tvalid,
+ input wire m_axis_chdr_tready,
+ // Data packets in (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_data_tdata,
+ input wire s_axis_data_tlast,
+ input wire s_axis_data_tvalid,
+ output wire s_axis_data_tready,
+ // Stream status in (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_strs_tdata,
+ input wire s_axis_strs_tlast,
+ input wire s_axis_strs_tvalid,
+ output wire s_axis_strs_tready,
+ // Configuration port
+ input wire cfg_start,
+ output reg cfg_pending = 1'b0,
+ output reg cfg_failed = 1'b0,
+ input wire cfg_lossy_xport,
+ input wire [15:0] cfg_dst_epid,
+ input wire [15:0] cfg_this_epid,
+ input wire [39:0] cfg_fc_freq_bytes,
+ input wire [23:0] cfg_fc_freq_pkts,
+ input wire [15:0] cfg_fc_headroom_bytes,
+ input wire [7:0] cfg_fc_headroom_pkts,
+ // Flow control status
+ output reg fc_enabled = 1'b0,
+ output reg [39:0] capacity_bytes = 40'd0,
+ output reg [23:0] capacity_pkts = 24'd0,
+ // Stream status
+ output wire seq_err_stb,
+ output reg [31:0] seq_err_cnt = 32'd0,
+ output wire data_err_stb,
+ output reg [31:0] data_err_cnt = 32'd0,
+ output wire route_err_stb,
+ output reg [31:0] route_err_cnt = 32'd0
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_chdr_internal_utils.vh"
+
+ localparam CHDR_W_LOG2 = $clog2(CHDR_W);
+
+ // ---------------------------------------------------
+ // Output packet gate
+ // ---------------------------------------------------
+ reg [CHDR_W-1:0] chdr_out_tdata;
+ reg chdr_out_tlast, chdr_out_tvalid;
+ wire chdr_out_tready;
+
+ axi_packet_gate #(
+ .WIDTH(CHDR_W), .SIZE(MTU), .USE_AS_BUFF(0)
+ ) chdr_pkt_gate_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata(chdr_out_tdata), .i_tlast(chdr_out_tlast), .i_terror(1'b0),
+ .i_tvalid(chdr_out_tvalid), .i_tready(chdr_out_tready),
+ .o_tdata(m_axis_chdr_tdata), .o_tlast(m_axis_chdr_tlast),
+ .o_tvalid(m_axis_chdr_tvalid), .o_tready(m_axis_chdr_tready)
+ );
+
+ // ---------------------------------------------------
+ // Flow Control State
+ // ---------------------------------------------------
+
+ // send_cnt: Total transfer count at the sender (here)
+ // recv_cnt: Total transfer count at the receiver
+ // accum: Transfer count since last FC resynchronization request
+ // headroom: Total headroom to keep in the downstream buffer
+ // adj_cap: The adjusted capacity (after headroom) of the downstream buffer
+ // strc_cnt: Saved count for the STRC packet (prevents mid-packet updates)
+ reg [63:0] send_cnt_bytes = 64'd0;
+ reg [39:0] send_cnt_pkts = 40'd0;
+ reg [63:0] recv_cnt_bytes = 64'd0;
+ reg [39:0] recv_cnt_pkts = 40'd0;
+ reg [39:0] accum_bytes = 40'd0;
+ reg [23:0] accum_pkts = 24'd0;
+ reg [15:0] headroom_bytes = 16'd0;
+ reg [ 7:0] headroom_pkts = 8'd0;
+ reg [39:0] adj_cap_bytes = 40'd0;
+ reg [23:0] adj_cap_pkts = 24'd0;
+ reg [63:0] strc_cnt_bytes = 64'd0;
+
+ // Output transfer count
+ always @(posedge clk) begin
+ if (rst || !fc_enabled) begin
+ send_cnt_bytes <= 64'd0;
+ send_cnt_pkts <= 40'd0;
+ end else if (chdr_out_tvalid && chdr_out_tready) begin
+ send_cnt_bytes <= send_cnt_bytes + (CHDR_W/8);
+ if (chdr_out_tlast)
+ send_cnt_pkts <= send_cnt_pkts + 40'd1;
+ end
+ end
+
+ // Buffer occupied counts
+ // TODO: Need better overflow handling
+ wire signed [64:0] occupied_bytes =
+ $signed({1'b0, send_cnt_bytes}) - $signed({1'b0, recv_cnt_bytes});
+ wire signed [40:0] occupied_pkts =
+ $signed({1'b0, send_cnt_pkts}) - $signed({1'b0, recv_cnt_pkts});
+
+ // OK-to-Send shift register
+ // - Why a shift-register here?
+ // To allow the tools to re-time the wide comparators.
+ // - We don't care about the latency here because stream
+ // status messages are asynchronous wrt the data
+ reg [3:0] ok_shreg = 4'b1111; // OK to send? (shift register)
+ always @(posedge clk) begin
+ if (rst || !fc_enabled) begin
+ ok_shreg <= 4'b1111;
+ end else begin
+ ok_shreg <= {ok_shreg[2:0], (
+ (occupied_bytes[40:0] < $signed({1'b0, adj_cap_bytes})) &&
+ (occupied_pkts [24:0] < $signed({1'b0, adj_cap_pkts }))
+ )};
+ end
+ end
+ wire ok_to_send = ok_shreg[3];
+
+ // Accumulated transfer count updater for FC resync
+ reg lossy_xport = 1'b0;
+ reg [3:0] fc_resync_req_shreg = 4'h0;
+ wire fc_resync_req, fc_resync_ack;
+
+ always @(posedge clk) begin
+ if (rst || !fc_enabled || !lossy_xport || fc_resync_ack) begin
+ // Reset
+ accum_bytes <= 40'd0;
+ accum_pkts <= 24'd0;
+ fc_resync_req_shreg <= 4'b0000;
+ end else begin
+ if (chdr_out_tvalid && chdr_out_tready) begin
+ // Count
+ accum_bytes <= accum_bytes + (CHDR_W/8);
+ if (chdr_out_tlast)
+ accum_pkts <= accum_pkts + 24'd1;
+ end
+ // FC resync request
+ fc_resync_req_shreg <= {fc_resync_req_shreg[2:0],
+ (accum_bytes > capacity_bytes) || (accum_pkts > capacity_pkts)};
+ end
+ end
+ assign fc_resync_req = fc_resync_req_shreg[3];
+
+ // ---------------------------------------------------
+ // Stream Status Parser
+ // ---------------------------------------------------
+
+ wire [3:0] msg_i_tdata, msg_o_tdata;
+ wire msg_i_tvalid, msg_o_tvalid;
+ wire msg_i_tready, msg_o_tready;
+
+ axi_fifo #(.WIDTH(4), .SIZE(1)) msg_fifo_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata(msg_i_tdata), .i_tvalid(msg_i_tvalid), .i_tready(msg_i_tready),
+ .o_tdata(msg_o_tdata), .o_tvalid(msg_o_tvalid), .o_tready(msg_o_tready),
+ .space(), .occupied()
+ );
+
+ localparam [2:0] ST_STRS_HDR = 3'd0; // Receiving the CHDR header of a stream status msg
+ localparam [2:0] ST_STRS_W0 = 3'd1; // Receiving the first word of a stream status msg
+ localparam [2:0] ST_STRS_W1 = 3'd2; // Receiving the second word of a stream status msg
+ localparam [2:0] ST_STRS_W2 = 3'd3; // Receiving the third word of a stream status msg
+ localparam [2:0] ST_STRS_W3 = 3'd4; // Receiving the fourth word of a stream status msg
+ localparam [2:0] ST_STRS_LATCH = 3'd5; // Atomically updating and posting the status msg
+ localparam [2:0] ST_STRS_DROP = 3'd6; // Something went wrong dropping current packet
+
+ reg [2:0] strs_state = ST_STRS_HDR;
+ reg strs_too_long = 1'b0;
+ reg [15:0] cached_dst_epid = 16'd0;
+ reg [255:0] cached_strs_msg;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ strs_state <= ST_STRS_HDR;
+ strs_too_long <= 1'b0;
+ end else begin
+ case (strs_state)
+
+ // ST_STRS_HDR
+ // ------------------
+ ST_STRS_HDR: begin
+ if (s_axis_strs_tvalid) begin
+ // Only accept stream status packets. Drop everything else
+ if (chdr_get_pkt_type(s_axis_strs_tdata[63:0]) == CHDR_PKT_TYPE_STRS)
+ strs_state <= ST_STRS_W0;
+ else
+ strs_state <= ST_STRS_DROP;
+ strs_too_long <= 1'b0;
+ end
+ end
+
+ // ST_STRS_W0
+ // ------------------
+ // - Cache the first word of the stream status
+ // - For CHDR_W == 64, this is one of 4 words.
+ // - For CHDR_W == 128, this is one of 2 words.
+ // - For CHDR_W >= 256, this is the only word.
+ ST_STRS_W0: begin
+ if (s_axis_strs_tvalid) begin
+ if (CHDR_W == 64) begin
+ cached_strs_msg[63:0] <= s_axis_strs_tdata[63:0];
+ strs_state <= !s_axis_strs_tlast ? ST_STRS_W1 : ST_STRS_HDR;
+ end else if (CHDR_W == 128) begin
+ cached_strs_msg[127:0] <= s_axis_strs_tdata[127:0];
+ strs_state <= !s_axis_strs_tlast ? ST_STRS_W1 : ST_STRS_HDR;
+ end else begin //CHDR_W >= 256
+ cached_strs_msg[255:0] <= s_axis_strs_tdata[255:0];
+ strs_state <= ST_STRS_LATCH;
+ strs_too_long <= !s_axis_strs_tlast;
+ end
+ end
+ end
+
+ // ST_STRS_W1
+ // ------------------
+ // - Cache the second word of the stream status
+ ST_STRS_W1: begin
+ if (s_axis_strs_tvalid) begin
+ if (CHDR_W == 64) begin
+ cached_strs_msg[127:64] <= s_axis_strs_tdata[63:0];
+ strs_state <= !s_axis_strs_tlast ? ST_STRS_W2 : ST_STRS_HDR;
+ end else begin //CHDR_W >= 128
+ cached_strs_msg[255:128] <= s_axis_strs_tdata[127:0];
+ strs_state <= ST_STRS_LATCH;
+ strs_too_long <= !s_axis_strs_tlast;
+ end
+ end
+ end
+
+ // ST_STRS_W2
+ // ------------------
+ // - Cache the third word of the stream status
+ ST_STRS_W2: begin
+ if (s_axis_strs_tvalid) begin
+ cached_strs_msg[191:128] <= s_axis_strs_tdata[63:0];
+ strs_state <= !s_axis_strs_tlast ? ST_STRS_W3 : ST_STRS_HDR;
+ end
+ end
+
+ // ST_STRS_W3
+ // ------------------
+ // - Cache the fourth word of the stream status
+ ST_STRS_W3: begin
+ if (s_axis_strs_tvalid) begin
+ cached_strs_msg[255:192] <= s_axis_strs_tdata[63:0];
+ strs_state <= ST_STRS_LATCH;
+ strs_too_long <= !s_axis_strs_tlast;
+ end
+ end
+
+ // ST_STRS_LATCH
+ // ------------------
+ // - Act on the received stream status
+ ST_STRS_LATCH: begin
+ capacity_bytes <= chdr256_strs_get_capacity_bytes(cached_strs_msg);
+ capacity_pkts <= chdr256_strs_get_capacity_pkts(cached_strs_msg);
+ recv_cnt_bytes <= chdr256_strs_get_xfercnt_bytes(cached_strs_msg);
+ recv_cnt_pkts <= chdr256_strs_get_xfercnt_pkts(cached_strs_msg);
+ adj_cap_bytes <= chdr256_strs_get_capacity_bytes(cached_strs_msg) -
+ {24'd0, headroom_bytes[15:(CHDR_W_LOG2-3)], {(CHDR_W_LOG2-3){1'b0}}};
+ adj_cap_pkts <= chdr256_strs_get_capacity_pkts(cached_strs_msg) -
+ {16'd0, headroom_pkts};
+ if (msg_i_tready) begin
+ strs_state <= strs_too_long ? ST_STRS_DROP : ST_STRS_HDR;
+ end
+ end
+
+ // ST_STRS_DROP
+ // ------------------
+ ST_STRS_DROP: begin
+ if (s_axis_strs_tvalid && s_axis_strs_tlast)
+ strs_state <= ST_STRS_HDR;
+ end
+ default: begin
+ // We should never get here
+ strs_state <= ST_STRS_HDR;
+ end
+ endcase
+ end
+ end
+
+ assign s_axis_strs_tready = (strs_state != ST_STRS_LATCH);
+
+ assign msg_i_tvalid = (strs_state == ST_STRS_LATCH);
+ assign msg_i_tdata = (chdr256_strs_get_src_epid(cached_strs_msg) != cached_dst_epid) ?
+ CHDR_STRS_STATUS_CMDERR : chdr256_strs_get_status(cached_strs_msg);
+
+
+ // ---------------------------------------------------
+ // Main State Machine
+ // ---------------------------------------------------
+
+ localparam [2:0] ST_PASS_DATA = 3'd0; // Passing input axis_data out
+ localparam [2:0] ST_STRC_HDR = 3'd1; // Sending CHDR header for stream cmd
+ localparam [2:0] ST_STRC_W0 = 3'd2; // Sending first word of stream cmd
+ localparam [2:0] ST_STRC_W1 = 3'd3; // Sending second word of stream cmd
+ localparam [2:0] ST_STRC_WAIT = 3'd4; // Waiting for response (stream status)
+ localparam [2:0] ST_INIT_DLY = 3'd5; // Finishing command execution
+
+ reg [2:0] state = ST_PASS_DATA;
+ reg mid_pkt = 1'b0;
+ reg [15:0] data_seq_num = 16'd0;
+ reg [15:0] strc_seq_num = 16'd0;
+ reg [2:0] cfg_delay = 3'd0;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ state <= ST_PASS_DATA;
+ mid_pkt <= 1'b0;
+ data_seq_num <= 16'd0;
+ strc_seq_num <= 16'd0;
+ cfg_pending <= 1'b0;
+ cfg_failed <= 1'b0;
+ end else begin
+ case (state)
+
+ // ST_PASS_DATA
+ // ------------------
+ // This is the default state where input data is passed to the
+ // output port. Flow control is enforced in this state.
+ // This state also serves as the launch state for a configuration
+ // operation (using cfg_start)
+ ST_PASS_DATA: begin
+ // Update the mid_pkt flag and sequence number
+ if (chdr_out_tvalid && chdr_out_tready) begin
+ mid_pkt <= !chdr_out_tlast;
+ if (chdr_out_tlast)
+ data_seq_num <= data_seq_num + 16'd1;
+ end
+ // Launch a configuration operation
+ if (cfg_start) begin
+ // Latch cfg command
+ cfg_pending <= 1'b1;
+ cfg_failed <= 1'b0;
+ // Disable flow control
+ fc_enabled <= 1'b0;
+ // Cache relevant data from the cfg cmd
+ lossy_xport <= cfg_lossy_xport;
+ cached_dst_epid <= cfg_dst_epid;
+ headroom_bytes <= cfg_fc_headroom_bytes;
+ headroom_pkts <= cfg_fc_headroom_pkts;
+ end
+ // Wait for current packet to transfer then begin the
+ // configuration process or stream command
+ if (cfg_start || cfg_pending || fc_resync_req) begin
+ if (mid_pkt) begin
+ if (chdr_out_tvalid && chdr_out_tready && chdr_out_tlast)
+ state <= ST_STRC_HDR;
+ end else begin
+ if (!(chdr_out_tvalid && chdr_out_tready))
+ state <= ST_STRC_HDR;
+ end
+ end
+ end
+
+ // ST_STRC_HDR
+ // ------------------
+ // Send the CHDR header for a stream command
+ ST_STRC_HDR: begin
+ if (chdr_out_tvalid && chdr_out_tready) begin
+ state <= ST_STRC_W0;
+ // Update seqnum for the next packet
+ strc_seq_num <= strc_seq_num + 16'd1;
+ end
+ // Update byte count for stream command
+ strc_cnt_bytes <= send_cnt_bytes;
+ end
+
+ // ST_STRC_W0
+ // ------------------
+ // Send the first line of a stream command
+ ST_STRC_W0: begin
+ if (chdr_out_tvalid && chdr_out_tready)
+ if (CHDR_W < 128)
+ state <= ST_STRC_W1;
+ else
+ state <= ST_STRC_WAIT;
+ end
+
+ // ST_STRC_W1
+ // ------------------
+ // Send the second line of a stream command
+ ST_STRC_W1: begin
+ if (chdr_out_tvalid && chdr_out_tready)
+ state <= fc_resync_req ? ST_PASS_DATA : ST_STRC_WAIT;
+ end
+
+ // ST_STRC_WAIT
+ // ------------------
+ // Done sending stream command. Wait for a response
+ ST_STRC_WAIT: begin
+ // Wait for a new response to arrive
+ if (msg_o_tvalid) begin
+ if (msg_o_tdata == CHDR_STRS_STATUS_OKAY) begin
+ state <= ST_INIT_DLY;
+ cfg_delay <= 3'd4;
+ fc_enabled <= 1'b1;
+ data_seq_num <= 16'd0;
+ strc_seq_num <= 16'd0;
+ end else begin
+ state <= ST_PASS_DATA;
+ cfg_failed <= 1'b1;
+ cfg_pending <= 1'b0;
+ end
+ end
+ end
+
+ // ST_INIT_DLY
+ // ------------------
+ // Delay matching state for ok_shreg
+ ST_INIT_DLY: begin
+ if (cfg_delay == 3'd0) begin
+ state <= ST_PASS_DATA;
+ cfg_pending <= 1'b0;
+ end else begin
+ cfg_delay <= cfg_delay - 3'd1;
+ end
+ end
+
+ // We should never get here
+ default: begin
+ state <= ST_PASS_DATA;
+ end
+ endcase
+ end
+ end
+
+ // Header for output CHDR data
+ wire [CHDR_W-1:0] data_header;
+ assign data_header[63:0] = chdr_set_seq_num(
+ chdr_set_dst_epid(s_axis_data_tdata[63:0], cached_dst_epid),
+ data_seq_num);
+ generate if (CHDR_W > 64)
+ assign data_header[CHDR_W-1:64] = s_axis_data_tdata[CHDR_W-1:64];
+ endgenerate
+
+ // Header for stream command
+ wire [CHDR_W-1:0] strc_header;
+ assign strc_header[63:0] = chdr_build_header(
+ /*VC*/ 6'd0, /*eob*/ 1'b0, /*eov*/ 1'b0, CHDR_PKT_TYPE_STRC, CHDR_NO_MDATA,
+ strc_seq_num, 16'd16+(CHDR_W/8), cached_dst_epid);
+ generate if (CHDR_W > 64)
+ assign strc_header[CHDR_W-1:64] = {(CHDR_W-64){1'b0}};
+ endgenerate
+
+ // Payload for stream command
+ wire [127:0] strc_init_payload = chdr128_strc_build(
+ {24'h0, cfg_fc_freq_bytes}, {16'h0, cfg_fc_freq_pkts},
+ /*op_data*/ 4'h0, CHDR_STRC_OPCODE_INIT, cfg_this_epid);
+ wire [127:0] strc_resync_payload = chdr128_strc_build(
+ strc_cnt_bytes, send_cnt_pkts,
+ /*op_data*/ 4'h0, CHDR_STRC_OPCODE_RESYNC, cfg_this_epid);
+ wire [127:0] strc_payload = fc_resync_req ? strc_resync_payload : strc_init_payload;
+
+ always @(*) begin
+ case (state)
+ ST_PASS_DATA: begin
+ chdr_out_tdata = mid_pkt ? s_axis_data_tdata : data_header;
+ chdr_out_tlast = s_axis_data_tlast;
+ chdr_out_tvalid = s_axis_data_tvalid && ok_to_send;
+ end
+ ST_STRC_HDR: begin
+ chdr_out_tdata = strc_header;
+ chdr_out_tlast = 1'b0;
+ chdr_out_tvalid = ok_to_send;
+ end
+ ST_STRC_W0: begin
+ chdr_out_tdata = strc_payload;
+ chdr_out_tlast = (CHDR_W < 128) ? 1'b0 : 1'b1;
+ chdr_out_tvalid = ok_to_send;
+ end
+ ST_STRC_W1: begin
+ // We will enter this state only if CHDR_W = 64
+ chdr_out_tdata = strc_payload[127:64];
+ chdr_out_tlast = 1'b1;
+ chdr_out_tvalid = ok_to_send;
+ end
+ default: begin
+ chdr_out_tdata = {CHDR_W{1'b0}};
+ chdr_out_tlast = 1'b0;
+ chdr_out_tvalid = 1'b0;
+ end
+ endcase
+ end
+ assign s_axis_data_tready = (state == ST_PASS_DATA) && chdr_out_tready && ok_to_send;
+
+ // Consume all messages when passing data forward. The flow control state is automatically
+ // updated outside the message FIFO. When a stream command is issued, we wait for the
+ // "wait" state to consume responses.
+ assign msg_o_tready = msg_o_tvalid && (state == ST_PASS_DATA || state == ST_STRC_WAIT);
+
+ // Acknowledge a flow control resync command
+ assign fc_resync_ack = fc_resync_req && (state == ST_STRC_W1) &&
+ chdr_out_tvalid && chdr_out_tready && chdr_out_tlast;
+
+ // ---------------------------------------------------
+ // Stream Status Reporting
+ // ---------------------------------------------------
+
+ wire runtime_err_stb = msg_o_tvalid && msg_o_tready && (state == ST_PASS_DATA);
+ assign seq_err_stb = runtime_err_stb && (msg_o_tdata == CHDR_STRS_STATUS_SEQERR);
+ assign data_err_stb = runtime_err_stb && (msg_o_tdata == CHDR_STRS_STATUS_DATAERR);
+ assign route_err_stb = runtime_err_stb && (msg_o_tdata == CHDR_STRS_STATUS_RTERR);
+
+ always @(posedge clk) begin
+ if (rst || !fc_enabled) begin
+ seq_err_cnt <= 32'd0;
+ data_err_cnt <= 32'd0;
+ route_err_cnt <= 32'd0;
+ end else begin
+ if (seq_err_stb)
+ seq_err_cnt <= seq_err_cnt + 32'd1;
+ if (data_err_stb)
+ data_err_cnt <= data_err_cnt + 32'd1;
+ if (route_err_stb)
+ route_err_cnt <= route_err_cnt + 32'd1;
+ end
+ end
+
+endmodule // chdr_stream_output
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_ctrl.v b/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_ctrl.v
new file mode 100644
index 000000000..1f9dba2eb
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_ctrl.v
@@ -0,0 +1,319 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_to_axis_ctrl
+// Description:
+// Converts from CHDR to AXIS-Control and vice versa.
+// This module has to handle remote control transactions
+// correctly. The CHDR frame has/needs the DstEPID, DstPort
+// SrcEPID and SrcPort and the AXIS-Ctrl frame has/needs
+// the DstPort, SrcPort, RemDstEPID and RemDstPort.
+//
+// Parameters:
+// - CHDR_W: Width of the CHDR bus in bits
+// - THIS_PORTID: The port number of the control xbar
+// that this module is connected to.
+//
+// Signals:
+// - s_rfnoc_chdr_* : Input CHDR stream (AXI-Stream)
+// - m_rfnoc_chdr_* : Output CHDR stream (AXI-Stream)
+// - s_rfnoc_ctrl_* : Input control stream (AXI-Stream)
+// - m_rfnoc_ctrl_* : Output control stream (AXI-Stream)
+
+module chdr_to_axis_ctrl #(
+ parameter CHDR_W = 256,
+ parameter [9:0] THIS_PORTID = 10'd0
+)(
+ // CHDR Bus (master and slave)
+ input wire rfnoc_chdr_clk,
+ input wire rfnoc_chdr_rst,
+ input wire [15:0] this_epid,
+ input wire [CHDR_W-1:0] s_rfnoc_chdr_tdata,
+ input wire s_rfnoc_chdr_tlast,
+ input wire s_rfnoc_chdr_tvalid,
+ output wire s_rfnoc_chdr_tready,
+ output wire [CHDR_W-1:0] m_rfnoc_chdr_tdata,
+ output wire m_rfnoc_chdr_tlast,
+ output wire m_rfnoc_chdr_tvalid,
+ input wire m_rfnoc_chdr_tready,
+ // AXIS-Control Bus (master and slave)
+ input wire rfnoc_ctrl_clk,
+ input wire rfnoc_ctrl_rst,
+ input wire [31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+ output wire [31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_axis_ctrl_utils.vh"
+
+ localparam [1:0] ST_CHDR_HDR = 2'd0; // Processing the CHDR header
+ localparam [1:0] ST_CHDR_MDATA = 2'd1; // Processing the CHDR metadata
+ localparam [1:0] ST_CTRL_HDR = 2'd2; // Processing the CHDR control header
+ localparam [1:0] ST_CTRL_BODY = 2'd3; // Processing the CHDR control body
+
+ // ---------------------------------------------------
+ // Input/output register slices
+ // ---------------------------------------------------
+ // - ch2ct: CHDR to Ctrl
+ // - ct2ch: Ctrl to CHDR
+
+ wire [CHDR_W-1:0] ch2ct_tdata, ct2ch_tdata;
+ wire ch2ct_tlast, ct2ch_tlast;
+ wire ch2ct_tvalid, ct2ch_tvalid;
+ wire ch2ct_tready, ct2ch_tready;
+
+ axi_fifo #(.WIDTH(CHDR_W+1), .SIZE(1)) ch2ct_reg_i (
+ .clk(rfnoc_chdr_clk), .reset(rfnoc_chdr_rst), .clear(1'b0),
+ .i_tdata({s_rfnoc_chdr_tlast, s_rfnoc_chdr_tdata}),
+ .i_tvalid(s_rfnoc_chdr_tvalid), .i_tready(s_rfnoc_chdr_tready),
+ .o_tdata({ch2ct_tlast, ch2ct_tdata}),
+ .o_tvalid(ch2ct_tvalid), .o_tready(ch2ct_tready),
+ .space(), .occupied()
+ );
+
+ axi_fifo #(.WIDTH(CHDR_W+1), .SIZE(1)) ct2ch_reg_i (
+ .clk(rfnoc_chdr_clk), .reset(rfnoc_chdr_rst), .clear(1'b0),
+ .i_tdata({ct2ch_tlast, ct2ch_tdata}),
+ .i_tvalid(ct2ch_tvalid), .i_tready(ct2ch_tready),
+ .o_tdata({m_rfnoc_chdr_tlast, m_rfnoc_chdr_tdata}),
+ .o_tvalid(m_rfnoc_chdr_tvalid), .o_tready(m_rfnoc_chdr_tready),
+ .space(), .occupied()
+ );
+
+ // ---------------------------------------------------
+ // CH2CT: CHDR => Ctrl path
+ // ---------------------------------------------------
+ // When converting CHDR => Ctrl we know we are dealing with
+ // a remote control transaction so we need to perform
+ // the following transformations to ensure that the packet
+ // has all the info to route downstream and has enough info
+ // to return to the master (of the transaction).
+ // - Use the CHDR DstPort as the Ctrl DstPort (forward the master's request)
+ // - Use THIS_PORTID as the Ctrl SrcPort (for the return path back here)
+ // - Use the CHDR SrcEPID as the Ctrl RemDstEPID (return path for CHDR packet)
+ // - Use the CHDR SrcPort as the Ctrl RemDstPort (return path in the downstream EP)
+ // - Ignore the CHDR DstEPID because packet is already here
+
+ reg [1:0] ch2ct_state = ST_CHDR_HDR;
+ reg [4:0] ch2ct_nmdata = CHDR_NO_MDATA;
+
+ always @(posedge rfnoc_chdr_clk) begin
+ if (rfnoc_chdr_rst) begin
+ ch2ct_state <= ST_CHDR_HDR;
+ end else if (ch2ct_tvalid && ch2ct_tready) begin
+ case (ch2ct_state)
+ ST_CHDR_HDR: begin
+ ch2ct_nmdata <= chdr_get_num_mdata(ch2ct_tdata[63:0]) - 5'd1;
+ if (!ch2ct_tlast)
+ ch2ct_state <= (chdr_get_num_mdata(ch2ct_tdata[63:0]) == 5'd0) ?
+ ST_CTRL_HDR : ST_CHDR_MDATA;
+ else
+ ch2ct_state <= ST_CHDR_HDR; // Premature termination
+ end
+ ST_CHDR_MDATA: begin
+ ch2ct_nmdata <= ch2ct_nmdata - 5'd1;
+ if (!ch2ct_tlast)
+ ch2ct_state <= (ch2ct_nmdata == CHDR_NO_MDATA) ? ST_CTRL_HDR : ST_CHDR_MDATA;
+ else
+ ch2ct_state <= ST_CHDR_HDR; // Premature termination
+ end
+ ST_CTRL_HDR: begin
+ ch2ct_state <= ch2ct_tlast ? ST_CHDR_HDR : ST_CTRL_BODY;
+ end
+ ST_CTRL_BODY: begin
+ if (ch2ct_tlast)
+ ch2ct_state <= ST_CHDR_HDR;
+ end
+ default: begin
+ // We should never get here
+ ch2ct_state <= ST_CHDR_HDR;
+ end
+ endcase
+ end
+ end
+
+ wire [(CHDR_W/32)-1:0] ch2ct_tkeep;
+ chdr_compute_tkeep #(.CHDR_W(CHDR_W), .ITEM_W(32)) chdr_tkeep_gen_i (
+ .clk(rfnoc_chdr_clk), .rst(rfnoc_chdr_rst),
+ .axis_tdata(ch2ct_tdata), .axis_tlast(ch2ct_tlast),
+ .axis_tvalid(ch2ct_tvalid), .axis_tready(ch2ct_tready),
+ .axis_tkeep(ch2ct_tkeep)
+ );
+
+ // Create the first two lines of the Ctrl word (wide)
+ // using data from CHDR packet
+ wire [CHDR_W-1:0] ch2ct_new_ctrl_hdr;
+ assign ch2ct_new_ctrl_hdr[63:0] = {
+ axis_ctrl_build_hdr_hi(
+ axis_ctrl_get_src_port(ch2ct_tdata[31:0]),
+ axis_ctrl_get_rem_dst_epid(ch2ct_tdata[63:32])
+ ),
+ axis_ctrl_build_hdr_lo(
+ axis_ctrl_get_is_ack (ch2ct_tdata[31:0]),
+ axis_ctrl_get_has_time(ch2ct_tdata[31:0]),
+ axis_ctrl_get_seq_num (ch2ct_tdata[31:0]),
+ axis_ctrl_get_num_data(ch2ct_tdata[31:0]),
+ THIS_PORTID,
+ axis_ctrl_get_dst_port(ch2ct_tdata[31:0])
+ )
+ };
+ generate if (CHDR_W > 64) begin
+ assign ch2ct_new_ctrl_hdr[CHDR_W-1:64] = ch2ct_tdata[CHDR_W-1:64];
+ end endgenerate
+
+ wire [CHDR_W-1:0] ch2ct_wctrl_tdata =
+ (ch2ct_state == ST_CTRL_HDR) ? ch2ct_new_ctrl_hdr : ch2ct_tdata;
+
+ axis_width_conv #(
+ .WORD_W(32), .IN_WORDS(CHDR_W/32), .OUT_WORDS(1),
+ .SYNC_CLKS(0), .PIPELINE("OUT")
+ ) ctrl_downsizer_i (
+ .s_axis_aclk(rfnoc_chdr_clk), .s_axis_rst(rfnoc_chdr_rst),
+ .s_axis_tdata(ch2ct_wctrl_tdata),
+ .s_axis_tkeep(ch2ct_tkeep),
+ .s_axis_tlast(ch2ct_tlast),
+ .s_axis_tvalid(ch2ct_tvalid && (ch2ct_state == ST_CTRL_HDR || ch2ct_state == ST_CTRL_BODY)),
+ .s_axis_tready(ch2ct_tready),
+ .m_axis_aclk(rfnoc_ctrl_clk), .m_axis_rst(rfnoc_ctrl_rst),
+ .m_axis_tdata(m_rfnoc_ctrl_tdata),
+ .m_axis_tkeep(/* Unused: OUT_WORDS=1 */),
+ .m_axis_tlast(m_rfnoc_ctrl_tlast),
+ .m_axis_tvalid(m_rfnoc_ctrl_tvalid),
+ .m_axis_tready(m_rfnoc_ctrl_tready)
+ );
+
+ // ---------------------------------------------------
+ // CT2CH: Ctrl => CHDR path
+ // ---------------------------------------------------
+ // When converting Ctrl => CHDR we know we are dealing with
+ // a remote control transaction so we need to perform
+ // the following transformations to ensure that the packet
+ // has all the info to route downstream and has enough info
+ // to return to the initiator of the transaction.
+ // - Use the Ctrl RemDstEPID as the CHDR DstEPID (forward the master's request)
+ // - Use the Ctrl RemDstPort as the CHDR DstPort (forward the master's request)
+ // - Use the this_epid as CHDR SrcEPID (return path for the CHDR packet)
+ // - Use the Ctrl SrcPort as the CHDR SrcPort (return path to the master)
+ // - Ignore the Ctrl DstPort because the packet has already been routed
+
+ wire [CHDR_W-1:0] ct2ch_wctrl_tdata;
+ wire ct2ch_wctrl_tlast, ct2ch_wctrl_tvalid, ct2ch_wctrl_tready;
+
+ axis_width_conv #(
+ .WORD_W(32), .IN_WORDS(1), .OUT_WORDS(CHDR_W/32),
+ .SYNC_CLKS(0), .PIPELINE("IN")
+ ) ctrl_upsizer_i (
+ .s_axis_aclk(rfnoc_ctrl_clk), .s_axis_rst(rfnoc_ctrl_rst),
+ .s_axis_tdata(s_rfnoc_ctrl_tdata),
+ .s_axis_tkeep(/* Unused: IN_WORDS=1 */),
+ .s_axis_tlast(s_rfnoc_ctrl_tlast),
+ .s_axis_tvalid(s_rfnoc_ctrl_tvalid),
+ .s_axis_tready(s_rfnoc_ctrl_tready),
+ .m_axis_aclk(rfnoc_chdr_clk), .m_axis_rst(rfnoc_chdr_rst),
+ .m_axis_tdata(ct2ch_wctrl_tdata),
+ .m_axis_tkeep(/* Unused: We are updating the CHDR length */),
+ .m_axis_tlast(ct2ch_wctrl_tlast),
+ .m_axis_tvalid(ct2ch_wctrl_tvalid),
+ .m_axis_tready(ct2ch_wctrl_tready)
+ );
+
+ reg [1:0] ct2ch_state = ST_CHDR_HDR;
+ reg [15:0] ct2ch_seqnum = 16'd0;
+
+ always @(posedge rfnoc_chdr_clk) begin
+ if (rfnoc_chdr_rst) begin
+ ct2ch_state <= ST_CHDR_HDR;
+ ct2ch_seqnum <= 16'd0;
+ end else if (ct2ch_tvalid && ct2ch_tready) begin
+ case (ct2ch_state)
+ ST_CHDR_HDR: begin
+ if (!ct2ch_tlast)
+ ct2ch_state <= ST_CTRL_HDR;
+ end
+ ST_CTRL_HDR: begin
+ if (ct2ch_tlast)
+ ct2ch_state <= ST_CHDR_HDR;
+ else
+ ct2ch_state <= ST_CTRL_BODY;
+ end
+ ST_CTRL_BODY: begin
+ if (ct2ch_tlast)
+ ct2ch_state <= ST_CHDR_HDR;
+ end
+ default: begin
+ // We should never get here
+ ct2ch_state <= ST_CHDR_HDR;
+ end
+ endcase
+ if (ct2ch_tlast)
+ ct2ch_seqnum <= ct2ch_seqnum + 16'd1;
+ end
+ end
+
+ // Hold the first line to generate info for the outgoing CHDR header
+ assign ct2ch_wctrl_tready = (ct2ch_state == ST_CTRL_HDR || ct2ch_state == ST_CTRL_BODY) ? ct2ch_tready : 1'b0;
+
+ wire [7:0] ct2ch_32bit_lines = 8'd3 + // Header + OpWord
+ (axis_ctrl_get_has_time(ct2ch_wctrl_tdata[31:0]) ? 8'd2 : 8'd0) + // Timestamp
+ ({4'h0, axis_ctrl_get_num_data(ct2ch_wctrl_tdata[31:0])}); // Data words
+
+ wire [15:0] ct2ch_chdr_lines = 16'd1 + // CHDR header
+ ct2ch_32bit_lines[7:$clog2(CHDR_W/32)] + // Convert 32-bit lines to CHDR_W
+ (|ct2ch_32bit_lines[$clog2(CHDR_W/32)-1:0]); // Residue
+
+ reg [63:0] ct2ch_chdr_tdata;
+ always @(*) begin
+ case (ct2ch_state)
+ ST_CHDR_HDR: begin
+ ct2ch_chdr_tdata = chdr_build_header(
+ 6'd0, /* VC */
+ 1'b0, 1'b0, /* eob, eov */
+ CHDR_PKT_TYPE_CTRL,
+ CHDR_NO_MDATA,
+ ct2ch_seqnum,
+ (ct2ch_chdr_lines << $clog2(CHDR_W/8)), /* length in bytes */
+ axis_ctrl_get_rem_dst_epid(ct2ch_wctrl_tdata[63:32])
+ );
+ end
+ ST_CTRL_HDR: begin
+ ct2ch_chdr_tdata = {
+ axis_ctrl_build_hdr_hi(
+ 10'd0, /* Unused in CHDR Control payload */
+ this_epid /* This is the SrcEPID */
+ ),
+ axis_ctrl_build_hdr_lo(
+ axis_ctrl_get_is_ack (ct2ch_wctrl_tdata[31:0]),
+ axis_ctrl_get_has_time(ct2ch_wctrl_tdata[31:0]),
+ axis_ctrl_get_seq_num (ct2ch_wctrl_tdata[31:0]),
+ axis_ctrl_get_num_data(ct2ch_wctrl_tdata[31:0]),
+ axis_ctrl_get_src_port(ct2ch_wctrl_tdata[31:0]),
+ axis_ctrl_get_rem_dst_port(ct2ch_wctrl_tdata[63:32])
+ )
+ };
+ end
+ default: begin
+ ct2ch_chdr_tdata = ct2ch_wctrl_tdata[63:0];
+ end
+ endcase
+ end
+
+ // Output signals
+ assign ct2ch_tdata[63:0] = ct2ch_chdr_tdata;
+ assign ct2ch_tlast = ct2ch_wctrl_tlast;
+ assign ct2ch_tvalid = ct2ch_wctrl_tvalid;
+ generate if (CHDR_W > 64) begin
+ assign ct2ch_tdata[CHDR_W-1:64] = ct2ch_wctrl_tdata[CHDR_W-1:64];
+ end endgenerate
+
+endmodule // chdr_to_axis_ctrl
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_data.v b/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_data.v
new file mode 100644
index 000000000..a00a9952c
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_data.v
@@ -0,0 +1,422 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_to_axis_data
+//
+// Description:
+//
+// A deframer module for CHDR data packets. It accepts an input CHDR stream
+// and produces an output data stream that includes the payload of the
+// packet, as well as timestamp and packet flags presented as sideband
+// information.
+//
+// This module also performs an optional clock crossing and data width
+// conversion from CHDR_W to a user requested width for the payload data bus.
+//
+// Parameters:
+// - CHDR_W : Width of the input CHDR bus in bits
+// - ITEM_W : Width of the output item bus in bits
+// - NIPC : The number of output items delivered per cycle
+// - SYNC_CLKS : Are the CHDR and data clocks synchronous to each other?
+// - INFO_FIFO_SIZE : Log2 of the FIFO size for the packet info data path
+// - PYLD_FIFO_SIZE : Log2 of the FIFO size for the payload data path
+//
+// Signals:
+// - s_axis_chdr_* : Input CHDR stream (AXI-Stream)
+// - m_axis_* : Output payload data stream (AXI-Stream)
+// - m_axis_mdata_* : Output mdata stream (AXI-Stream)
+// - flush_* : Signals for flush control and status
+//
+
+module chdr_to_axis_data #(
+ parameter CHDR_W = 256,
+ parameter ITEM_W = 32,
+ parameter NIPC = 2,
+ parameter SYNC_CLKS = 0,
+ parameter INFO_FIFO_SIZE = 5,
+ parameter PYLD_FIFO_SIZE = 5
+)(
+ // Clock, reset and settings
+ input wire axis_chdr_clk,
+ input wire axis_chdr_rst,
+ input wire axis_data_clk,
+ input wire axis_data_rst,
+ // CHDR in (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_chdr_tdata,
+ input wire s_axis_chdr_tlast,
+ input wire s_axis_chdr_tvalid,
+ output wire s_axis_chdr_tready,
+ // Payload data stream out (AXI-Stream)
+ output wire [(ITEM_W*NIPC)-1:0] m_axis_tdata,
+ output wire [NIPC-1:0] m_axis_tkeep,
+ output wire m_axis_tlast,
+ output wire m_axis_tvalid,
+ input wire m_axis_tready,
+ // Payload sideband information
+ output wire [63:0] m_axis_ttimestamp,
+ output wire m_axis_thas_time,
+ output wire [15:0] m_axis_tlength,
+ output wire m_axis_teob,
+ output wire m_axis_teov,
+ // Flush signals
+ input wire flush_en,
+ input wire [31:0] flush_timeout,
+ output wire flush_active,
+ output wire flush_done
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_axis_ctrl_utils.vh"
+
+ // ---------------------------------------------------
+ // Pipeline
+ // ---------------------------------------------------
+ localparam CHDR_KEEP_W = CHDR_W/ITEM_W;
+
+ wire [CHDR_W-1:0] in_chdr_tdata;
+ wire [CHDR_KEEP_W-1:0] in_chdr_tkeep;
+ wire in_chdr_tlast, in_chdr_tvalid;
+ reg in_chdr_tready;
+
+ axi_fifo_flop2 #(.WIDTH(CHDR_W+1)) in_pipe_i (
+ .clk(axis_chdr_clk), .reset(axis_chdr_rst), .clear(1'b0),
+ .i_tdata({s_axis_chdr_tlast, s_axis_chdr_tdata}),
+ .i_tvalid(s_axis_chdr_tvalid), .i_tready(s_axis_chdr_tready),
+ .o_tdata({in_chdr_tlast, in_chdr_tdata}),
+ .o_tvalid(in_chdr_tvalid), .o_tready(in_chdr_tready),
+ .space(), .occupied()
+ );
+
+ chdr_compute_tkeep #(.CHDR_W(CHDR_W), .ITEM_W(ITEM_W)) tkeep_gen_i (
+ .clk(axis_chdr_clk), .rst(axis_chdr_rst),
+ .axis_tdata(in_chdr_tdata), .axis_tlast(in_chdr_tlast),
+ .axis_tvalid(in_chdr_tvalid), .axis_tready(in_chdr_tready),
+ .axis_tkeep(in_chdr_tkeep)
+ );
+
+ // ---------------------------------------------------
+ // Input State Machine
+ // ---------------------------------------------------
+ localparam INFO_W = 64+1+16+1+1; // timestamp, has_time, length, eob, eov
+
+ wire [CHDR_W-1:0] in_pyld_tdata;
+ wire [CHDR_KEEP_W-1:0] in_pyld_tkeep;
+ wire in_pyld_tlast, in_pyld_tvalid, in_pyld_tready;
+
+ reg [INFO_W-1:0] in_info_tdata;
+ reg in_info_tvalid;
+ wire in_info_tready;
+
+ localparam [2:0] ST_HDR = 3'd0; // Processing the input CHDR header
+ localparam [2:0] ST_TS = 3'd1; // Processing the input CHDR timestamp
+ localparam [2:0] ST_MDATA = 3'd2; // Processing the input CHDR metadata word
+ localparam [2:0] ST_BODY = 3'd3; // Processing the input CHDR payload word
+ localparam [2:0] ST_DROP = 3'd4; // Something went wrong... Dropping packet
+
+ reg [2:0] state = ST_HDR;
+ reg [4:0] mdata_pending = CHDR_NO_MDATA;
+
+ reg [15:0] chdr_length_reg;
+ reg chdr_eob_reg, chdr_eov_reg;
+
+ // Shortcuts: CHDR header
+ wire [2:0] in_pkt_type = chdr_get_pkt_type(in_chdr_tdata[63:0]);
+ wire [4:0] in_num_mdata = chdr_get_num_mdata(in_chdr_tdata[63:0]);
+
+ always @(posedge axis_chdr_clk) begin
+ if (axis_chdr_rst) begin
+ state <= ST_HDR;
+ end else if (in_chdr_tvalid & in_chdr_tready) begin
+ case (state)
+
+ // ST_HDR: CHDR Header
+ // -------------------
+ ST_HDR: begin
+ // Always cache the number of metadata words
+ mdata_pending <= in_num_mdata;
+ // Figure out the next state
+ if (!in_chdr_tlast) begin
+ if (CHDR_W > 64) begin
+ // When CHDR_W > 64, the timestamp is a part of the header word.
+ // If this is a data packet (with/without a TS), we move on to the metadata/body
+ // state otherwise we drop it. Non-data packets should never reach here.
+ if (in_pkt_type == CHDR_PKT_TYPE_DATA || in_pkt_type == CHDR_PKT_TYPE_DATA_TS) begin
+ if (in_num_mdata != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_BODY;
+ end
+ end else begin
+ state <= ST_DROP;
+ end
+ end else begin
+ // When CHDR_W == 64, the timestamp comes after the header. Check if this is a data
+ // packet with a TS to figure out the next state. If no TS, then check for metadata
+ // to move to the next state. Drop any non-data packets.
+ chdr_length_reg <= chdr_calc_payload_length(CHDR_W, in_chdr_tdata);
+ chdr_eob_reg <= chdr_get_eob(in_chdr_tdata);
+ chdr_eov_reg <= chdr_get_eov(in_chdr_tdata);
+ if (in_pkt_type == CHDR_PKT_TYPE_DATA_TS) begin
+ state <= ST_TS;
+ end else if (in_pkt_type == CHDR_PKT_TYPE_DATA) begin
+ if (in_num_mdata != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_BODY;
+ end
+ end else begin
+ state <= ST_DROP;
+ end
+ end
+ end else begin // Premature termination
+ // Packets must have at least one payload line
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_TS: Timestamp (CHDR_W == 64 only)
+ // ------------------------------------
+ ST_TS: begin
+ if (!in_chdr_tlast) begin
+ if (mdata_pending != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_BODY;
+ end
+ end else begin // Premature termination
+ // Packets must have at least one payload line
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_MDATA: Metadata word
+ // -----------------------
+ ST_MDATA: begin
+ if (!in_chdr_tlast) begin
+ // Count down metadata and stop at 1
+ if (mdata_pending == 5'd1) begin
+ state <= ST_BODY;
+ end else begin
+ mdata_pending <= mdata_pending - 5'd1;
+ end
+ end else begin // Premature termination
+ // Packets must have at least one payload line
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_BODY: Payload word
+ // ---------------------
+ ST_BODY: begin
+ if (in_chdr_tlast) begin
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_DROP: Drop current packet
+ // ----------------------------
+ ST_DROP: begin
+ if (in_chdr_tlast) begin
+ state <= ST_HDR;
+ end
+ end
+
+ default: begin
+ // We should never get here
+ state <= ST_HDR;
+ end
+ endcase
+ end
+ end
+
+ // CHDR data goes to the payload stream only in the BODY state. Packets are
+ // expected to have at least one payload word so the CHDR tlast can be used
+ // as the payload tlast.
+ assign in_pyld_tdata = in_chdr_tdata;
+ assign in_pyld_tkeep = in_chdr_tkeep;
+ assign in_pyld_tlast = in_chdr_tlast;
+ assign in_pyld_tvalid = in_chdr_tvalid && (state == ST_BODY);
+
+ always @(*) begin
+ // Packet timestamp and flags go into the info FIFO, but only if it's a
+ // data packet since non-data packets will be discarded.
+ if (CHDR_W > 64) begin
+ // When CHDR_W > 64, all info will be in the first word of the CHDR packet
+ in_info_tdata = { in_chdr_tdata[127:64],
+ chdr_get_has_time(in_chdr_tdata),
+ chdr_calc_payload_length(CHDR_W, in_chdr_tdata),
+ chdr_get_eob(in_chdr_tdata),
+ chdr_get_eov(in_chdr_tdata) };
+ in_info_tvalid = in_chdr_tvalid && (state == ST_HDR &&
+ (in_pkt_type == CHDR_PKT_TYPE_DATA || in_pkt_type == CHDR_PKT_TYPE_DATA_TS));
+ end else begin
+ // When CHDR_W == 64, the flags will be in the first word of the packet,
+ // but the timestamp will be in the second word, if there is a timestamp.
+ if (state == ST_HDR && in_pkt_type == CHDR_PKT_TYPE_DATA) begin
+ // No timestamp in this case
+ in_info_tdata = { in_chdr_tdata[63:0], 1'b0,
+ chdr_calc_payload_length(CHDR_W, in_chdr_tdata),
+ chdr_get_eob(in_chdr_tdata), chdr_get_eov(in_chdr_tdata) };
+ in_info_tvalid = in_chdr_tvalid;
+ end else begin
+ // Assuming timestamp is present, so use flags from previous clock cycle
+ in_info_tdata = { in_chdr_tdata[63:0], 1'b1, chdr_length_reg,
+ chdr_eob_reg, chdr_eov_reg };
+ in_info_tvalid = in_chdr_tvalid && (state == ST_TS);
+ end
+ end
+
+ case (state)
+ ST_HDR : in_chdr_tready = in_info_tready;
+ ST_TS : in_chdr_tready = in_info_tready;
+ ST_MDATA : in_chdr_tready = 1'b1;
+ ST_BODY : in_chdr_tready = in_pyld_tready;
+ ST_DROP : in_chdr_tready = 1'b1;
+ default : in_chdr_tready = 1'b0;
+ endcase
+ end
+
+ // ---------------------------------------------------
+ // Payload and mdata FIFOs
+ // ---------------------------------------------------
+ wire [CHDR_W-1:0] out_pyld_tdata;
+ wire [CHDR_KEEP_W-1:0] out_pyld_tkeep;
+ wire out_pyld_tlast, out_pyld_tvalid, out_pyld_tready;
+
+ wire [INFO_W-1:0] out_info_tdata;
+ wire out_info_tvalid, out_info_tready;
+
+ wire [(ITEM_W*NIPC)-1:0] conv_pyld_tdata;
+ wire [NIPC-1:0] conv_pyld_tkeep;
+ wire conv_pyld_tlast, conv_pyld_tvalid, conv_pyld_tready;
+
+
+ generate if (SYNC_CLKS) begin : gen_sync_fifo
+ axi_fifo #(.WIDTH(INFO_W), .SIZE(INFO_FIFO_SIZE)) info_fifo_i (
+ .clk(axis_data_clk), .reset(axis_data_rst), .clear(1'b0),
+ .i_tdata(in_info_tdata),
+ .i_tvalid(in_info_tvalid), .i_tready(in_info_tready),
+ .o_tdata(out_info_tdata),
+ .o_tvalid(out_info_tvalid), .o_tready(out_info_tready),
+ .space(), .occupied()
+ );
+ axi_fifo #(.WIDTH(CHDR_W+CHDR_KEEP_W+1), .SIZE(PYLD_FIFO_SIZE)) pyld_fifo_i (
+ .clk(axis_data_clk), .reset(axis_data_rst), .clear(1'b0),
+ .i_tdata({in_pyld_tlast, in_pyld_tkeep, in_pyld_tdata}),
+ .i_tvalid(in_pyld_tvalid), .i_tready(in_pyld_tready),
+ .o_tdata({out_pyld_tlast, out_pyld_tkeep, out_pyld_tdata}),
+ .o_tvalid(out_pyld_tvalid), .o_tready(out_pyld_tready),
+ .space(), .occupied()
+ );
+ end else begin : gen_async_fifo
+ axi_fifo_2clk #(.WIDTH(INFO_W), .SIZE(INFO_FIFO_SIZE)) info_fifo_i (
+ .reset(axis_chdr_rst),
+ .i_aclk(axis_chdr_clk),
+ .i_tdata(in_info_tdata),
+ .i_tvalid(in_info_tvalid), .i_tready(in_info_tready),
+ .o_aclk(axis_data_clk),
+ .o_tdata(out_info_tdata),
+ .o_tvalid(out_info_tvalid), .o_tready(out_info_tready)
+ );
+ axi_fifo_2clk #(.WIDTH(CHDR_W+CHDR_KEEP_W+1), .SIZE(PYLD_FIFO_SIZE)) pyld_fifo_i (
+ .reset(axis_chdr_rst),
+ .i_aclk(axis_chdr_clk),
+ .i_tdata({in_pyld_tlast, in_pyld_tkeep, in_pyld_tdata}),
+ .i_tvalid(in_pyld_tvalid), .i_tready(in_pyld_tready),
+ .o_aclk(axis_data_clk),
+ .o_tdata({out_pyld_tlast, out_pyld_tkeep, out_pyld_tdata}),
+ .o_tvalid(out_pyld_tvalid), .o_tready(out_pyld_tready)
+ );
+ end endgenerate
+
+ // ---------------------------------------------------
+ // Data Width Converter: CHDR_W => ITEM_W*NIPC
+ // ---------------------------------------------------
+ generate
+ if (CHDR_W != ITEM_W*NIPC) begin : gen_axis_width_conv
+ axis_width_conv #(
+ .WORD_W(ITEM_W), .IN_WORDS(CHDR_W/ITEM_W), .OUT_WORDS(NIPC),
+ .SYNC_CLKS(1), .PIPELINE("NONE")
+ ) payload_width_conv_i (
+ .s_axis_aclk(axis_data_clk), .s_axis_rst(axis_data_rst),
+ .s_axis_tdata(out_pyld_tdata), .s_axis_tkeep(out_pyld_tkeep),
+ .s_axis_tlast(out_pyld_tlast), .s_axis_tvalid(out_pyld_tvalid),
+ .s_axis_tready(out_pyld_tready),
+ .m_axis_aclk(axis_data_clk), .m_axis_rst(axis_data_rst),
+ .m_axis_tdata(conv_pyld_tdata), .m_axis_tkeep(conv_pyld_tkeep),
+ .m_axis_tlast(conv_pyld_tlast), .m_axis_tvalid(conv_pyld_tvalid),
+ .m_axis_tready(conv_pyld_tready)
+ );
+ end else begin : no_gen_axis_width_conv
+ assign conv_pyld_tdata = out_pyld_tdata;
+ assign conv_pyld_tkeep = out_pyld_tkeep;
+ assign conv_pyld_tlast = out_pyld_tlast;
+ assign conv_pyld_tvalid = out_pyld_tvalid;
+ assign out_pyld_tready = conv_pyld_tready;
+ end
+ endgenerate
+
+ // ---------------------------------------------------
+ // Merge payload and info streams
+ // ---------------------------------------------------
+ // There should be one info word for each payload packet.
+ wire [INFO_W+(ITEM_W+1)*NIPC-1:0] flush_tdata;
+ wire flush_tlast;
+ wire flush_tvalid;
+ wire flush_tready;
+
+ assign flush_tdata = { out_info_tdata, conv_pyld_tkeep, conv_pyld_tdata };
+ assign flush_tlast = conv_pyld_tlast;
+ assign flush_tvalid = conv_pyld_tvalid && out_info_tvalid;
+ assign conv_pyld_tready = flush_tready && out_info_tvalid;
+ assign out_info_tready = conv_pyld_tready && conv_pyld_tlast && conv_pyld_tvalid;
+
+ // ---------------------------------------------------
+ // Flushing Logic
+ // ---------------------------------------------------
+ wire [31:0] flush_timeout_dclk;
+ wire flush_en_dclk;
+ wire flush_active_pyld_cclk;
+ wire flush_done_pyld_cclk;
+ wire flush_active_pyld;
+ wire flush_done_pyld;
+
+ synchronizer #(.WIDTH(2), .INITIAL_VAL(4'd0)) flush_2clk_rb_i (
+ .clk(axis_chdr_clk), .rst(1'b0),
+ .in({flush_active_pyld, flush_done_pyld}),
+ .out({flush_active_pyld_cclk, flush_done_pyld_cclk})
+ );
+ assign flush_active = flush_active_pyld_cclk;
+ assign flush_done = flush_done_pyld_cclk;
+
+ axi_fifo_2clk #(.WIDTH(33), .SIZE(1)) flush_2clk_ctrl_i (
+ .reset(axis_chdr_rst),
+ .i_aclk(axis_chdr_clk),
+ .i_tdata({flush_en, flush_timeout}), .i_tvalid(1'b1), .i_tready(),
+ .o_aclk(axis_data_clk),
+ .o_tdata({flush_en_dclk, flush_timeout_dclk}), .o_tvalid(), .o_tready(1'b1)
+ );
+
+ axis_packet_flush #(
+ .WIDTH(INFO_W+(ITEM_W+1)*NIPC), .FLUSH_PARTIAL_PKTS(0), .TIMEOUT_W(32), .PIPELINE("OUT")
+ ) pyld_flusher_i (
+ .clk(axis_data_clk), .reset(axis_data_rst),
+ .enable(flush_en_dclk), .timeout(flush_timeout_dclk),
+ .flushing(flush_active_pyld), .done(flush_done_pyld),
+ .s_axis_tdata(flush_tdata),
+ .s_axis_tlast(flush_tlast),
+ .s_axis_tvalid(flush_tvalid),
+ .s_axis_tready(flush_tready),
+ .m_axis_tdata({m_axis_ttimestamp, m_axis_thas_time, m_axis_tlength,
+ m_axis_teob, m_axis_teov, m_axis_tkeep, m_axis_tdata}),
+ .m_axis_tlast(m_axis_tlast),
+ .m_axis_tvalid(m_axis_tvalid),
+ .m_axis_tready(m_axis_tready)
+ );
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_data_mdata.v b/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_data_mdata.v
new file mode 100644
index 000000000..90eb5c767
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_data_mdata.v
@@ -0,0 +1,538 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_to_axis_data_mdata
+//
+// Description:
+//
+// A deframer module for CHDR data packets. It accepts an input CHDR stream,
+// and produces two output streams:
+//
+// 1) Payload data, which includes the payload of the packet, as well as
+// timestamp and packet flags presented as sideband information.
+// 2) Metadata (mdata), which contains only the metadata of the packet.
+//
+// This module also performs an optional clock crossing and data width
+// conversion from CHDR_W to a user requested width for the payload data bus.
+//
+// The metadata and data packets are interleaved, i.e., a mdata packet will
+// arrive before its corresponding data packet. However, if mdata prefetching
+// is enabled, the mdata for the next packet might arrive before the data for
+// the current packet has been consumed. In the case of a rate reduction,
+// this allows the module to sustain a gapless stream of payload items and a
+// bursty sideband mdata path. If there is no metadata in a packet, then an
+// empty packet is output on m_axis_mdata_* (i.e., m_axis_mdata_tkeep will be
+// set to 0).
+//
+// Parameters:
+//
+// - CHDR_W : Width of the input CHDR bus in bits
+// - ITEM_W : Width of the output item bus in bits
+// - NIPC : The number of output items delivered per cycle
+// - SYNC_CLKS : Are the CHDR and data clocks synchronous to each other?
+// - MDATA_FIFO_SIZE : FIFO size for the mdata path
+// - INFO_FIFO_SIZE : FIFO size for the packet info path
+// - PAYLOAD_FIFO_SIZE : FIFO size for the payload path
+// - MDATA_PREFETCH_EN : Is mdata prefetching enabled?
+//
+// Signals:
+//
+// - s_axis_chdr_* : Input CHDR stream (AXI-Stream)
+// - m_axis_* : Output payload data stream (AXI-Stream)
+// - m_axis_mdata_* : Output mdata stream (AXI-Stream)
+// - flush_* : Signals for flush control and status
+//
+
+module chdr_to_axis_data_mdata #(
+ parameter CHDR_W = 256,
+ parameter ITEM_W = 32,
+ parameter NIPC = 2,
+ parameter SYNC_CLKS = 0,
+ parameter MDATA_FIFO_SIZE = 1,
+ parameter INFO_FIFO_SIZE = 1,
+ parameter PAYLOAD_FIFO_SIZE = 1,
+ parameter MDATA_PREFETCH_EN = 1
+)(
+ // Clock, reset and settings
+ input wire axis_chdr_clk,
+ input wire axis_chdr_rst,
+ input wire axis_data_clk,
+ input wire axis_data_rst,
+ // CHDR in (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_chdr_tdata,
+ input wire s_axis_chdr_tlast,
+ input wire s_axis_chdr_tvalid,
+ output wire s_axis_chdr_tready,
+ // Payload data stream out (AXI-Stream)
+ output wire [(ITEM_W*NIPC)-1:0] m_axis_tdata,
+ output wire [NIPC-1:0] m_axis_tkeep,
+ output wire m_axis_tlast,
+ output wire m_axis_tvalid,
+ input wire m_axis_tready,
+ // Payload sideband information
+ output wire [63:0] m_axis_ttimestamp,
+ output wire m_axis_thas_time,
+ output wire [15:0] m_axis_tlength,
+ output wire m_axis_teob,
+ output wire m_axis_teov,
+ // Metadata stream out (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_mdata_tdata,
+ output wire m_axis_mdata_tlast,
+ output wire m_axis_mdata_tkeep,
+ output wire m_axis_mdata_tvalid,
+ input wire m_axis_mdata_tready,
+ // Flush signals
+ input wire flush_en,
+ input wire [31:0] flush_timeout,
+ output wire flush_active,
+ output wire flush_done
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_axis_ctrl_utils.vh"
+
+ // ---------------------------------------------------
+ // Pipeline
+ // ---------------------------------------------------
+ localparam CHDR_KEEP_W = CHDR_W/ITEM_W;
+
+ wire [CHDR_W-1:0] in_chdr_tdata;
+ wire [CHDR_KEEP_W-1:0] in_chdr_tkeep;
+ wire in_chdr_tlast, in_chdr_tvalid;
+ reg in_chdr_tready;
+
+ axi_fifo_flop2 #(.WIDTH(CHDR_W+1)) in_pipe_i (
+ .clk(axis_chdr_clk), .reset(axis_chdr_rst), .clear(1'b0),
+ .i_tdata({s_axis_chdr_tlast, s_axis_chdr_tdata}),
+ .i_tvalid(s_axis_chdr_tvalid), .i_tready(s_axis_chdr_tready),
+ .o_tdata({in_chdr_tlast, in_chdr_tdata}),
+ .o_tvalid(in_chdr_tvalid), .o_tready(in_chdr_tready),
+ .space(), .occupied()
+ );
+
+ chdr_compute_tkeep #(.CHDR_W(CHDR_W), .ITEM_W(ITEM_W)) tkeep_gen_i (
+ .clk(axis_chdr_clk), .rst(axis_chdr_rst),
+ .axis_tdata(in_chdr_tdata), .axis_tlast(in_chdr_tlast),
+ .axis_tvalid(in_chdr_tvalid), .axis_tready(in_chdr_tready),
+ .axis_tkeep(in_chdr_tkeep)
+ );
+
+ // ---------------------------------------------------
+ // Input State Machine
+ // ---------------------------------------------------
+ localparam INFO_W = 64+1+16+1+1; // timestamp, has_time, length, eob, eov
+
+ wire [CHDR_W-1:0] in_pyld_tdata;
+ wire [CHDR_KEEP_W-1:0] in_pyld_tkeep;
+ wire in_pyld_tlast, in_pyld_tvalid, in_pyld_tready;
+
+ reg [INFO_W-1:0] in_info_tdata;
+ reg in_info_tvalid;
+ wire in_info_tready;
+
+ wire [CHDR_W-1:0] in_mdata_tdata;
+ wire in_mdata_tkeep;
+ wire in_mdata_tlast, in_mdata_tvalid, in_mdata_tready;
+
+
+ localparam [2:0] ST_HDR = 3'd0; // Processing the input CHDR header
+ localparam [2:0] ST_TS = 3'd1; // Processing the input CHDR timestamp
+ localparam [2:0] ST_MDATA = 3'd2; // Processing the input CHDR metadata word
+ localparam [2:0] ST_BODY = 3'd3; // Processing the input CHDR payload word
+ localparam [2:0] ST_DROP = 3'd4; // Something went wrong... Dropping packet
+
+ reg [2:0] state = ST_HDR;
+ reg [4:0] mdata_pending = CHDR_NO_MDATA;
+ reg last_mdata_line;
+
+ reg [15:0] chdr_length_reg;
+ reg chdr_eob_reg, chdr_eov_reg;
+
+ // Shortcuts: CHDR header
+ wire [2:0] in_pkt_type = chdr_get_pkt_type(in_chdr_tdata[63:0]);
+ wire [4:0] in_num_mdata = chdr_get_num_mdata(in_chdr_tdata[63:0]);
+
+ always @(posedge axis_chdr_clk) begin
+ if (axis_chdr_rst) begin
+ state <= ST_HDR;
+ end else if (in_chdr_tvalid & in_chdr_tready) begin
+ case (state)
+
+ // ST_HDR: CHDR Header
+ // -------------------
+ ST_HDR: begin
+ // Always cache the number of metadata words
+ mdata_pending <= in_num_mdata;
+ // Figure out the next state
+ if (!in_chdr_tlast) begin
+ if (CHDR_W > 64) begin
+ // When CHDR_W > 64, the timestamp is a part of the header word.
+ // If this is a data packet (with/without a TS), we move on to the metadata/body
+ // state otherwise we drop it. Non-data packets should never reach here.
+ if (in_pkt_type == CHDR_PKT_TYPE_DATA || in_pkt_type == CHDR_PKT_TYPE_DATA_TS) begin
+ if (in_num_mdata != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_BODY;
+ end
+ end else begin
+ state <= ST_DROP;
+ end
+ end else begin
+ // When CHDR_W == 64, the timestamp comes after the header. Check if this is a data
+ // packet with a TS to figure out the next state. If no TS, then check for metadata
+ // to move to the next state. Drop any non-data packets.
+ chdr_length_reg <= chdr_calc_payload_length(CHDR_W, in_chdr_tdata);
+ chdr_eob_reg <= chdr_get_eob(in_chdr_tdata);
+ chdr_eov_reg <= chdr_get_eov(in_chdr_tdata);
+ if (in_pkt_type == CHDR_PKT_TYPE_DATA_TS) begin
+ state <= ST_TS;
+ end else if (in_pkt_type == CHDR_PKT_TYPE_DATA) begin
+ if (in_num_mdata != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_BODY;
+ end
+ end else begin
+ state <= ST_DROP;
+ end
+ end
+ end else begin // Premature termination
+ // Packets must have at least one payload line
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_TS: Timestamp (CHDR_W == 64 only)
+ // ------------------------------------
+ ST_TS: begin
+ if (!in_chdr_tlast) begin
+ if (mdata_pending != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_BODY;
+ end
+ end else begin // Premature termination
+ // Packets must have at least one payload line
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_MDATA: Metadata word
+ // -----------------------
+ ST_MDATA: begin
+ if (!in_chdr_tlast) begin
+ // Count down metadata and stop at 1
+ if (mdata_pending == 5'd1) begin
+ state <= ST_BODY;
+ end else begin
+ mdata_pending <= mdata_pending - 5'd1;
+ end
+ end else begin // Premature termination
+ // Packets must have at least one payload line
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_BODY: Payload word
+ // ---------------------
+ ST_BODY: begin
+ if (in_chdr_tlast) begin
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_DROP: Drop current packet
+ // ----------------------------
+ ST_DROP: begin
+ if (in_chdr_tlast) begin
+ state <= ST_HDR;
+ end
+ end
+
+ default: begin
+ // We should never get here
+ state <= ST_HDR;
+ end
+ endcase
+ end
+ end
+
+ // CHDR data goes to the payload stream only in the BODY state.
+ // Packets are expected to have at least one payload word so the
+ // CHDR tlast can be used as the payload tlast
+ assign in_pyld_tdata = in_chdr_tdata;
+ assign in_pyld_tkeep = in_chdr_tkeep;
+ assign in_pyld_tlast = in_chdr_tlast;
+ assign in_pyld_tvalid = in_chdr_tvalid && (state == ST_BODY);
+
+ // Only metadata goes into the mdata FIFO. However, if there is no metadata,
+ // then we want an empty packet to go into the mdata FIFO. We check the
+ // packet type because non-data packets will be discarded.
+ assign in_mdata_tdata = in_chdr_tdata;
+ assign in_mdata_tlast = in_chdr_tlast || last_mdata_line;
+ assign in_mdata_tkeep = (state == ST_MDATA);
+ assign in_mdata_tvalid = in_chdr_tvalid && (
+ (state == ST_MDATA) ||
+ (state == ST_HDR && in_num_mdata == CHDR_NO_MDATA &&
+ (in_pkt_type == CHDR_PKT_TYPE_DATA || in_pkt_type == CHDR_PKT_TYPE_DATA_TS)));
+
+ always @(*) begin
+ // Packet timestamp and flags go into the info FIFO, but only if it's a
+ // data packet since non-data packets will be discarded.
+ if (CHDR_W > 64) begin
+ // When CHDR_W > 64, all info will be in the first word of the CHDR packet
+ in_info_tdata = { in_chdr_tdata[127:64],
+ chdr_get_has_time(in_chdr_tdata),
+ chdr_calc_payload_length(CHDR_W, in_chdr_tdata),
+ chdr_get_eob(in_chdr_tdata),
+ chdr_get_eov(in_chdr_tdata) };
+ in_info_tvalid = in_chdr_tvalid && (state == ST_HDR &&
+ (in_pkt_type == CHDR_PKT_TYPE_DATA || in_pkt_type == CHDR_PKT_TYPE_DATA_TS));
+ end else begin
+ // When CHDR_W == 64, the flags will be in the first word of the packet,
+ // but the timestamp will be in the second word, if there is a timestamp.
+ if (state == ST_HDR && in_pkt_type == CHDR_PKT_TYPE_DATA) begin
+ // No timestamp in this case
+ in_info_tdata = { in_chdr_tdata[63:0], 1'b0,
+ chdr_calc_payload_length(CHDR_W, in_chdr_tdata),
+ chdr_get_eob(in_chdr_tdata), chdr_get_eov(in_chdr_tdata) };
+ in_info_tvalid = in_chdr_tvalid;
+ end else begin
+ // Assuming timestamp is present, so use flags from previous clock cycle
+ in_info_tdata = { in_chdr_tdata[63:0], 1'b1, chdr_length_reg,
+ chdr_eob_reg, chdr_eov_reg };
+ in_info_tvalid = in_chdr_tvalid && (state == ST_TS);
+ end
+ end
+
+ case (state)
+ ST_HDR: begin
+ in_chdr_tready = in_info_tready && in_mdata_tready;
+ last_mdata_line = (in_num_mdata == CHDR_NO_MDATA);
+ end
+ ST_TS: begin
+ in_chdr_tready = in_info_tready && in_mdata_tready;
+ last_mdata_line = 1'b0;
+ end
+ ST_MDATA: begin
+ in_chdr_tready = in_mdata_tready;
+ last_mdata_line = (mdata_pending == 5'd1);
+ end
+ ST_BODY: begin
+ in_chdr_tready = in_pyld_tready;
+ last_mdata_line = 1'b0;
+ end
+ ST_DROP: begin
+ in_chdr_tready = 1'b1;
+ last_mdata_line = 1'b0;
+ end
+ default: begin
+ in_chdr_tready = 1'b0;
+ last_mdata_line = 1'b0;
+ end
+ endcase
+ end
+
+ // ---------------------------------------------------
+ // Payload and mdata FIFOs
+ // ---------------------------------------------------
+ wire [CHDR_W-1:0] out_pyld_tdata;
+ wire [CHDR_KEEP_W-1:0] out_pyld_tkeep;
+ wire out_pyld_tlast, out_pyld_tvalid, out_pyld_tready;
+
+ wire tmp_mdata_tvalid, tmp_mdata_tready;
+ wire tmp_info_tready;
+
+ wire [(ITEM_W*NIPC)-1:0] flush_pyld_tdata;
+ wire [NIPC-1:0] flush_pyld_tkeep;
+ wire flush_pyld_tlast, flush_pyld_tvalid, flush_pyld_tready;
+ wire [INFO_W-1:0] flush_info_tdata;
+ wire [CHDR_W-1:0] flush_mdata_tdata;
+ wire flush_mdata_tkeep;
+ wire flush_mdata_tlast, flush_mdata_tvalid, flush_mdata_tready;
+
+ generate if (SYNC_CLKS) begin : gen_sync_fifo
+ axi_fifo #(.WIDTH(CHDR_W+2), .SIZE(MDATA_FIFO_SIZE)) mdata_fifo_i (
+ .clk(axis_data_clk), .reset(axis_data_rst), .clear(1'b0),
+ .i_tdata({in_mdata_tkeep, in_mdata_tlast, in_mdata_tdata}),
+ .i_tvalid(in_mdata_tvalid), .i_tready(in_mdata_tready),
+ .o_tdata({flush_mdata_tkeep, flush_mdata_tlast, flush_mdata_tdata}),
+ .o_tvalid(tmp_mdata_tvalid), .o_tready(tmp_mdata_tready),
+ .space(), .occupied()
+ );
+ axi_fifo #(.WIDTH(INFO_W), .SIZE(INFO_FIFO_SIZE)) info_fifo_i (
+ .clk(axis_data_clk), .reset(axis_data_rst), .clear(1'b0),
+ .i_tdata(in_info_tdata),
+ .i_tvalid(in_info_tvalid), .i_tready(in_info_tready),
+ .o_tdata(flush_info_tdata),
+ .o_tvalid(), .o_tready(tmp_info_tready),
+ .space(), .occupied()
+ );
+ axi_fifo #(.WIDTH(CHDR_W+CHDR_KEEP_W+1), .SIZE(PAYLOAD_FIFO_SIZE)) pyld_fifo_i (
+ .clk(axis_data_clk), .reset(axis_data_rst), .clear(1'b0),
+ .i_tdata({in_pyld_tlast, in_pyld_tkeep, in_pyld_tdata}),
+ .i_tvalid(in_pyld_tvalid), .i_tready(in_pyld_tready),
+ .o_tdata({out_pyld_tlast, out_pyld_tkeep, out_pyld_tdata}),
+ .o_tvalid(out_pyld_tvalid), .o_tready(out_pyld_tready),
+ .space(), .occupied()
+ );
+ end else begin : gen_async_fifo
+ axi_fifo_2clk #(.WIDTH(CHDR_W+2), .SIZE(MDATA_FIFO_SIZE)) mdata_fifo_i (
+ .reset(axis_chdr_rst),
+ .i_aclk(axis_chdr_clk),
+ .i_tdata({in_mdata_tkeep, in_mdata_tlast, in_mdata_tdata}),
+ .i_tvalid(in_mdata_tvalid), .i_tready(in_mdata_tready),
+ .o_aclk(axis_data_clk),
+ .o_tdata({flush_mdata_tkeep, flush_mdata_tlast, flush_mdata_tdata}),
+ .o_tvalid(tmp_mdata_tvalid), .o_tready(tmp_mdata_tready)
+ );
+ axi_fifo_2clk #(.WIDTH(INFO_W), .SIZE(INFO_FIFO_SIZE)) info_fifo_i (
+ .reset(axis_chdr_rst),
+ .i_aclk(axis_chdr_clk),
+ .i_tdata(in_info_tdata),
+ .i_tvalid(in_info_tvalid), .i_tready(in_info_tready),
+ .o_aclk(axis_data_clk),
+ .o_tdata(flush_info_tdata),
+ .o_tvalid(), .o_tready(tmp_info_tready)
+ );
+ axi_fifo_2clk #(.WIDTH(CHDR_W+CHDR_KEEP_W+1), .SIZE(PAYLOAD_FIFO_SIZE)) pyld_fifo_i (
+ .reset(axis_chdr_rst),
+ .i_aclk(axis_chdr_clk),
+ .i_tdata({in_pyld_tlast, in_pyld_tkeep, in_pyld_tdata}),
+ .i_tvalid(in_pyld_tvalid), .i_tready(in_pyld_tready),
+ .o_aclk(axis_data_clk),
+ .o_tdata({out_pyld_tlast, out_pyld_tkeep, out_pyld_tdata}),
+ .o_tvalid(out_pyld_tvalid), .o_tready(out_pyld_tready)
+ );
+ end endgenerate
+
+ // ---------------------------------------------------
+ // Data Width Converter: CHDR_W => ITEM_W*NIPC
+ // ---------------------------------------------------
+ wire tmp_pyld_tvalid, tmp_pyld_tready;
+
+ generate
+ if (CHDR_W != ITEM_W*NIPC) begin : gen_axis_width_conv
+ axis_width_conv #(
+ .WORD_W(ITEM_W), .IN_WORDS(CHDR_W/ITEM_W), .OUT_WORDS(NIPC),
+ .SYNC_CLKS(1), .PIPELINE("NONE")
+ ) payload_width_conv_i (
+ .s_axis_aclk(axis_data_clk), .s_axis_rst(axis_data_rst),
+ .s_axis_tdata(out_pyld_tdata), .s_axis_tkeep(out_pyld_tkeep),
+ .s_axis_tlast(out_pyld_tlast), .s_axis_tvalid(out_pyld_tvalid),
+ .s_axis_tready(out_pyld_tready),
+ .m_axis_aclk(axis_data_clk), .m_axis_rst(axis_data_rst),
+ .m_axis_tdata(flush_pyld_tdata), .m_axis_tkeep(flush_pyld_tkeep),
+ .m_axis_tlast(flush_pyld_tlast), .m_axis_tvalid(tmp_pyld_tvalid),
+ .m_axis_tready(tmp_pyld_tready)
+ );
+ end else begin : no_gen_axis_width_conv
+ assign flush_pyld_tdata = out_pyld_tdata;
+ assign flush_pyld_tkeep = out_pyld_tkeep;
+ assign flush_pyld_tlast = out_pyld_tlast;
+ assign tmp_pyld_tvalid = out_pyld_tvalid;
+ assign out_pyld_tready = tmp_pyld_tready;
+ end
+ endgenerate
+
+
+ // ---------------------------------------------------
+ // Output State Machine
+ // ---------------------------------------------------
+ reg [2:0] mdata_pkt_cnt = 3'd0, pyld_pkt_cnt = 3'd0;
+
+ // A payload packet can pass only if it is preceded by a mdata packet
+ wire pass_pyld = ((mdata_pkt_cnt - pyld_pkt_cnt) > 3'd0);
+ // A mdata packet has to be blocked if its corresponding payload packet hasn't passed except
+ // when prefetching is enabled. In that case one additional mdata packet is allowed to pass
+ wire pass_mdata = ((mdata_pkt_cnt - pyld_pkt_cnt) < (MDATA_PREFETCH_EN == 1 ? 3'd2 : 3'd1));
+
+ always @(posedge axis_data_clk) begin
+ if (axis_data_rst) begin
+ mdata_pkt_cnt <= 3'd0;
+ pyld_pkt_cnt <= 3'd0;
+ end else begin
+ if (flush_mdata_tvalid && flush_mdata_tready && flush_mdata_tlast)
+ mdata_pkt_cnt <= mdata_pkt_cnt + 3'd1;
+ if (flush_pyld_tvalid && flush_pyld_tready && flush_pyld_tlast)
+ pyld_pkt_cnt <= pyld_pkt_cnt + 3'd1;
+ end
+ end
+
+ assign flush_pyld_tvalid = tmp_pyld_tvalid && pass_pyld;
+ assign tmp_pyld_tready = flush_pyld_tready && pass_pyld;
+
+ // Only read the info FIFO once per packet
+ assign tmp_info_tready = tmp_pyld_tready && flush_pyld_tlast && tmp_pyld_tvalid;
+
+ assign flush_mdata_tvalid = tmp_mdata_tvalid && pass_mdata;
+ assign tmp_mdata_tready = flush_mdata_tready && pass_mdata;
+
+ // ---------------------------------------------------
+ // Flushing Logic
+ // ---------------------------------------------------
+ wire [31:0] flush_timeout_dclk;
+ wire flush_en_dclk;
+ wire flush_active_pyld_cclk, flush_active_mdata_cclk;
+ wire flush_done_pyld_cclk, flush_done_mdata_cclk;
+ wire flush_active_pyld, flush_active_mdata;
+ wire flush_done_pyld, flush_done_mdata;
+
+ synchronizer #(.WIDTH(4), .INITIAL_VAL(4'd0)) flush_2clk_rb_i (
+ .clk(axis_chdr_clk), .rst(1'b0),
+ .in({flush_active_pyld, flush_done_pyld,
+ flush_active_mdata, flush_done_mdata}),
+ .out({flush_active_pyld_cclk, flush_done_pyld_cclk,
+ flush_active_mdata_cclk, flush_done_mdata_cclk})
+ );
+ assign flush_active = flush_active_pyld_cclk | flush_active_mdata_cclk;
+ assign flush_done = flush_done_pyld_cclk & flush_done_mdata_cclk;
+
+ axi_fifo_2clk #(.WIDTH(33), .SIZE(1)) flush_2clk_ctrl_i (
+ .reset(axis_chdr_rst),
+ .i_aclk(axis_chdr_clk),
+ .i_tdata({flush_en, flush_timeout}), .i_tvalid(1'b1), .i_tready(),
+ .o_aclk(axis_data_clk),
+ .o_tdata({flush_en_dclk, flush_timeout_dclk}), .o_tvalid(), .o_tready(1'b1)
+ );
+
+ axis_packet_flush #(
+ .WIDTH(INFO_W+(ITEM_W+1)*NIPC), .FLUSH_PARTIAL_PKTS(0), .TIMEOUT_W(32), .PIPELINE("OUT")
+ ) pyld_flusher_i (
+ .clk(axis_data_clk), .reset(axis_data_rst),
+ .enable(flush_en_dclk), .timeout(flush_timeout_dclk),
+ .flushing(flush_active_pyld), .done(flush_done_pyld),
+ .s_axis_tdata({flush_info_tdata, flush_pyld_tkeep, flush_pyld_tdata}),
+ .s_axis_tlast(flush_pyld_tlast),
+ .s_axis_tvalid(flush_pyld_tvalid),
+ .s_axis_tready(flush_pyld_tready),
+ .m_axis_tdata({m_axis_ttimestamp, m_axis_thas_time, m_axis_tlength,
+ m_axis_teob, m_axis_teov, m_axis_tkeep, m_axis_tdata}),
+ .m_axis_tlast(m_axis_tlast),
+ .m_axis_tvalid(m_axis_tvalid),
+ .m_axis_tready(m_axis_tready)
+ );
+
+ axis_packet_flush #(
+ .WIDTH(CHDR_W+1), .FLUSH_PARTIAL_PKTS(0), .TIMEOUT_W(32), .PIPELINE("OUT")
+ ) mdata_flusher_i (
+ .clk(axis_data_clk), .reset(axis_data_rst),
+ .enable(flush_en_dclk), .timeout(flush_timeout_dclk),
+ .flushing(flush_active_mdata), .done(flush_done_mdata),
+ .s_axis_tdata({flush_mdata_tkeep, flush_mdata_tdata}),
+ .s_axis_tlast(flush_mdata_tlast),
+ .s_axis_tvalid(flush_mdata_tvalid),
+ .s_axis_tready(flush_mdata_tready),
+ .m_axis_tdata({m_axis_mdata_tkeep, m_axis_mdata_tdata}),
+ .m_axis_tlast(m_axis_mdata_tlast),
+ .m_axis_tvalid(m_axis_mdata_tvalid),
+ .m_axis_tready(m_axis_mdata_tready)
+ );
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_pyld_ctxt.v b/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_pyld_ctxt.v
new file mode 100644
index 000000000..f604584e8
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_to_axis_pyld_ctxt.v
@@ -0,0 +1,458 @@
+//
+// Copyright 2018-2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_to_axis_pyld_ctxt
+// Description:
+// A header deframer module for CHDR data packets.
+// Accepts an input CHDR stream, and produces two output streams:
+// 1) Payload, which contains the payload of the packet
+// 2) Context, which contains the header info in the packet i.e.
+// CHDR header, timestamp and metadata (marked with a tuser)
+// This module also performs an optional clock crossing and data
+// width convertion from CHDR_W to a user requested width for the
+// payload bus.
+// Context and data packets are interleaved i.e. a context packet
+// will arrive before its corresponding data packet. However, if
+// context prefetching is enabled, the context for the next packet
+// might arrive before the data for the current packet has been
+// consumed. In the case of a rate reduction, this allows the module
+// to sustain a gapless stream of payload items and a bursty
+// sideband context path.
+//
+// Parameters:
+// - CHDR_W: Width of the input CHDR bus in bits
+// - ITEM_W: Width of the output item bus in bits
+// - NIPC: The number of output items delievered per cycle
+// - SYNC_CLKS: Are the CHDR and data clocks synchronous to each other?
+// - CONTEXT_FIFO_SIZE: FIFO size for the context path
+// - PAYLOAD_FIFO_SIZE: FIFO size for the payload path
+// - CONTEXT_PREFETCH_EN: Is context prefetching enabled?
+//
+// Signals:
+// - s_axis_chdr_* : Input CHDR stream (AXI-Stream)
+// - m_axis_payload_* : Output payload stream (AXI-Stream)
+// - m_axis_context_* : Output context stream (AXI-Stream)
+// - flush_* : Signals for flush control and status
+//
+
+module chdr_to_axis_pyld_ctxt #(
+ parameter CHDR_W = 256,
+ parameter ITEM_W = 32,
+ parameter NIPC = 2,
+ parameter SYNC_CLKS = 0,
+ parameter CONTEXT_FIFO_SIZE = 1,
+ parameter PAYLOAD_FIFO_SIZE = 1,
+ parameter CONTEXT_PREFETCH_EN = 1
+)(
+ // Clock, reset and settings
+ input wire axis_chdr_clk,
+ input wire axis_chdr_rst,
+ input wire axis_data_clk,
+ input wire axis_data_rst,
+ // CHDR in (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_chdr_tdata,
+ input wire s_axis_chdr_tlast,
+ input wire s_axis_chdr_tvalid,
+ output wire s_axis_chdr_tready,
+ // Payload stream out (AXI-Stream)
+ output wire [(ITEM_W*NIPC)-1:0] m_axis_payload_tdata,
+ output wire [NIPC-1:0] m_axis_payload_tkeep,
+ output wire m_axis_payload_tlast,
+ output wire m_axis_payload_tvalid,
+ input wire m_axis_payload_tready,
+ // Context stream out (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_context_tdata,
+ output wire [3:0] m_axis_context_tuser,
+ output wire m_axis_context_tlast,
+ output wire m_axis_context_tvalid,
+ input wire m_axis_context_tready,
+ // Flush signals
+ input wire flush_en,
+ input wire [31:0] flush_timeout,
+ output wire flush_active,
+ output wire flush_done
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_axis_ctrl_utils.vh"
+
+ // ---------------------------------------------------
+ // Pipeline
+ // ---------------------------------------------------
+ localparam CHDR_KEEP_W = CHDR_W/ITEM_W;
+
+ wire [CHDR_W-1:0] in_chdr_tdata;
+ wire [CHDR_KEEP_W-1:0] in_chdr_tkeep;
+ wire in_chdr_tlast, in_chdr_tvalid;
+ reg in_chdr_tready;
+
+ axi_fifo_flop2 #(.WIDTH(CHDR_W+1)) in_pipe_i (
+ .clk(axis_chdr_clk), .reset(axis_chdr_rst), .clear(1'b0),
+ .i_tdata({s_axis_chdr_tlast, s_axis_chdr_tdata}),
+ .i_tvalid(s_axis_chdr_tvalid), .i_tready(s_axis_chdr_tready),
+ .o_tdata({in_chdr_tlast, in_chdr_tdata}),
+ .o_tvalid(in_chdr_tvalid), .o_tready(in_chdr_tready),
+ .space(), .occupied()
+ );
+
+ chdr_compute_tkeep #(.CHDR_W(CHDR_W), .ITEM_W(ITEM_W)) tkeep_gen_i (
+ .clk(axis_chdr_clk), .rst(axis_chdr_rst),
+ .axis_tdata(in_chdr_tdata), .axis_tlast(in_chdr_tlast),
+ .axis_tvalid(in_chdr_tvalid), .axis_tready(in_chdr_tready),
+ .axis_tkeep(in_chdr_tkeep)
+ );
+
+ // ---------------------------------------------------
+ // Input State Machine
+ // ---------------------------------------------------
+ wire [CHDR_W-1:0] in_pyld_tdata;
+ wire [CHDR_KEEP_W-1:0] in_pyld_tkeep;
+ wire in_pyld_tlast, in_pyld_tvalid, in_pyld_tready;
+
+ wire [CHDR_W-1:0] in_ctxt_tdata;
+ reg [3:0] in_ctxt_tuser;
+ wire in_ctxt_tlast, in_ctxt_tvalid, in_ctxt_tready;
+
+
+ localparam [2:0] ST_HDR = 3'd0; // Processing the input CHDR header
+ localparam [2:0] ST_TS = 3'd1; // Processing the input CHDR timestamp
+ localparam [2:0] ST_MDATA = 3'd2; // Processing the input CHDR metadata word
+ localparam [2:0] ST_BODY = 3'd3; // Processing the input CHDR payload word
+ localparam [2:0] ST_DROP = 3'd4; // Something went wrong... Dropping packet
+
+ reg [2:0] state = ST_HDR;
+ reg [4:0] mdata_pending = CHDR_NO_MDATA;
+ reg last_ctxt_line;
+
+ // Shortcuts: CHDR header
+ wire [2:0] in_pkt_type = chdr_get_pkt_type(in_chdr_tdata[63:0]);
+ wire [4:0] in_num_mdata = chdr_get_num_mdata(in_chdr_tdata[63:0]);
+
+ always @(posedge axis_chdr_clk) begin
+ if (axis_chdr_rst) begin
+ state <= ST_HDR;
+ end else if (in_chdr_tvalid & in_chdr_tready) begin
+ case (state)
+
+ // ST_HDR: CHDR Header
+ // -------------------
+ ST_HDR: begin
+ // Always cache the number of metadata words
+ mdata_pending <= in_num_mdata;
+ // Figure out the next state
+ if (!in_chdr_tlast) begin
+ if (CHDR_W > 64) begin
+ // When CHDR_W > 64, the timestamp is a part of the header word.
+ // If this is a data packet (with/without a TS), we move on to the metadata/body
+ // state otherwise we drop it. Non-data packets should never reach here.
+ if (in_pkt_type == CHDR_PKT_TYPE_DATA || in_pkt_type == CHDR_PKT_TYPE_DATA_TS) begin
+ if (in_num_mdata != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_BODY;
+ end
+ end else begin
+ state <= ST_DROP;
+ end
+ end else begin
+ // When CHDR_W == 64, the timestamp comes after the header. Check if this is a data
+ // packet with a TS to figure out the next state. If no TS, then check for metadata
+ // to move to the next state. Drop any non-data packets.
+ if (in_pkt_type == CHDR_PKT_TYPE_DATA_TS) begin
+ state <= ST_TS;
+ end else if (in_pkt_type == CHDR_PKT_TYPE_DATA) begin
+ if (in_num_mdata != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_BODY;
+ end
+ end else begin
+ state <= ST_DROP;
+ end
+ end
+ end else begin // Premature termination
+ // Packets must have at least one payload line
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_TS: Timestamp (CHDR_W == 64 only)
+ // ------------------------------------
+ ST_TS: begin
+ if (!in_chdr_tlast) begin
+ if (mdata_pending != CHDR_NO_MDATA) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_BODY;
+ end
+ end else begin // Premature termination
+ // Packets must have at least one payload line
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_MDATA: Metadata word
+ // -----------------------
+ ST_MDATA: begin
+ if (!in_chdr_tlast) begin
+ // Count down metadata and stop at 1
+ if (mdata_pending == 5'd1) begin
+ state <= ST_BODY;
+ end else begin
+ mdata_pending <= mdata_pending - 5'd1;
+ end
+ end else begin // Premature termination
+ // Packets must have at least one payload line
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_BODY: Payload word
+ // ---------------------
+ ST_BODY: begin
+ if (in_chdr_tlast) begin
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_DROP: Drop current packet
+ // ----------------------------
+ ST_DROP: begin
+ if (in_chdr_tlast) begin
+ state <= ST_HDR;
+ end
+ end
+
+ default: begin
+ // We should never get here
+ state <= ST_HDR;
+ end
+ endcase
+ end
+ end
+
+ // CHDR data goes to the payload stream only in the BODY state.
+ // Packets are expected to have at least one payload word so the
+ // CHDR tlast can be used as the payload tlast
+ assign in_pyld_tdata = in_chdr_tdata;
+ assign in_pyld_tkeep = in_chdr_tkeep;
+ assign in_pyld_tlast = in_chdr_tlast;
+ assign in_pyld_tvalid = in_chdr_tvalid && (state == ST_BODY);
+
+ // CHDR data goes to the context stream in the HDR,TS,MDATA state.
+ // tlast has to be recomputed for the context stream, however, we
+ // still need to correctly handle an errant packet without a payload
+ assign in_ctxt_tdata = in_chdr_tdata;
+ assign in_ctxt_tlast = in_chdr_tlast || last_ctxt_line;
+ assign in_ctxt_tvalid = in_chdr_tvalid && (state != ST_BODY && state != ST_DROP);
+
+ always @(*) begin
+ case (state)
+ ST_HDR: begin
+ // The header goes to the context stream
+ in_chdr_tready <= in_ctxt_tready;
+ in_ctxt_tuser <= (CHDR_W > 64) ? CONTEXT_FIELD_HDR_TS : CONTEXT_FIELD_HDR;
+ last_ctxt_line <= (in_num_mdata == 7'd0) && (
+ in_pkt_type == CHDR_PKT_TYPE_DATA ||
+ (in_pkt_type == CHDR_PKT_TYPE_DATA_TS && CHDR_W > 64));
+ end
+ ST_TS: begin
+ // The timestamp goes to the context stream
+ in_chdr_tready <= in_ctxt_tready;
+ in_ctxt_tuser <= CONTEXT_FIELD_TS;
+ last_ctxt_line <= (mdata_pending == CHDR_NO_MDATA);
+ end
+ ST_MDATA: begin
+ // The metadata goes to the context stream
+ in_chdr_tready <= in_ctxt_tready;
+ in_ctxt_tuser <= CONTEXT_FIELD_MDATA;
+ last_ctxt_line <= (mdata_pending == 5'd1);
+ end
+ ST_BODY: begin
+ // The body goes to the payload stream
+ in_chdr_tready <= in_pyld_tready;
+ in_ctxt_tuser <= 4'h0;
+ last_ctxt_line <= 1'b0;
+ end
+ ST_DROP: begin
+ // Errant packets get dropped
+ in_chdr_tready <= 1'b1;
+ in_ctxt_tuser <= 4'h0;
+ last_ctxt_line <= 1'b0;
+ end
+ default: begin
+ in_chdr_tready <= 1'b0;
+ in_ctxt_tuser <= 4'h0;
+ last_ctxt_line <= 1'b0;
+ end
+ endcase
+ end
+
+ // ---------------------------------------------------
+ // Payload and Context FIFOs
+ // ---------------------------------------------------
+ wire [CHDR_W-1:0] out_pyld_tdata;
+ wire [CHDR_KEEP_W-1:0] out_pyld_tkeep;
+ wire out_pyld_tlast, out_pyld_tvalid, out_pyld_tready;
+
+ wire tmp_ctxt_tvalid, tmp_ctxt_tready;
+
+ wire [(ITEM_W*NIPC)-1:0] flush_pyld_tdata;
+ wire [NIPC-1:0] flush_pyld_tkeep;
+ wire flush_pyld_tlast, flush_pyld_tvalid, flush_pyld_tready;
+ wire [CHDR_W-1:0] flush_ctxt_tdata;
+ wire [3:0] flush_ctxt_tuser;
+ wire flush_ctxt_tlast, flush_ctxt_tvalid, flush_ctxt_tready;
+
+ generate if (SYNC_CLKS) begin : gen_sync_fifo
+ axi_fifo #(.WIDTH(CHDR_W+4+1), .SIZE(CONTEXT_FIFO_SIZE)) ctxt_fifo_i (
+ .clk(axis_data_clk), .reset(axis_data_rst), .clear(1'b0),
+ .i_tdata({in_ctxt_tlast, in_ctxt_tuser, in_ctxt_tdata}),
+ .i_tvalid(in_ctxt_tvalid), .i_tready(in_ctxt_tready),
+ .o_tdata({flush_ctxt_tlast, flush_ctxt_tuser, flush_ctxt_tdata}),
+ .o_tvalid(tmp_ctxt_tvalid), .o_tready(tmp_ctxt_tready),
+ .space(), .occupied()
+ );
+ axi_fifo #(.WIDTH(CHDR_W+CHDR_KEEP_W+1), .SIZE(PAYLOAD_FIFO_SIZE)) pyld_fifo_i (
+ .clk(axis_data_clk), .reset(axis_data_rst), .clear(1'b0),
+ .i_tdata({in_pyld_tlast, in_pyld_tkeep, in_pyld_tdata}),
+ .i_tvalid(in_pyld_tvalid), .i_tready(in_pyld_tready),
+ .o_tdata({out_pyld_tlast, out_pyld_tkeep, out_pyld_tdata}),
+ .o_tvalid(out_pyld_tvalid), .o_tready(out_pyld_tready),
+ .space(), .occupied()
+ );
+ end else begin : gen_async_fifo
+ axi_fifo_2clk #(.WIDTH(CHDR_W+4+1), .SIZE(CONTEXT_FIFO_SIZE)) ctxt_fifo_i (
+ .reset(axis_chdr_rst),
+ .i_aclk(axis_chdr_clk),
+ .i_tdata({in_ctxt_tlast, in_ctxt_tuser, in_ctxt_tdata}),
+ .i_tvalid(in_ctxt_tvalid), .i_tready(in_ctxt_tready),
+ .o_aclk(axis_data_clk),
+ .o_tdata({flush_ctxt_tlast, flush_ctxt_tuser, flush_ctxt_tdata}),
+ .o_tvalid(tmp_ctxt_tvalid), .o_tready(tmp_ctxt_tready)
+ );
+ axi_fifo_2clk #(.WIDTH(CHDR_W+CHDR_KEEP_W+1), .SIZE(PAYLOAD_FIFO_SIZE)) pyld_fifo_i (
+ .reset(axis_chdr_rst),
+ .i_aclk(axis_chdr_clk),
+ .i_tdata({in_pyld_tlast, in_pyld_tkeep, in_pyld_tdata}),
+ .i_tvalid(in_pyld_tvalid), .i_tready(in_pyld_tready),
+ .o_aclk(axis_data_clk),
+ .o_tdata({out_pyld_tlast, out_pyld_tkeep, out_pyld_tdata}),
+ .o_tvalid(out_pyld_tvalid), .o_tready(out_pyld_tready)
+ );
+ end endgenerate
+
+ // ---------------------------------------------------
+ // Data Width Converter: CHDR_W => ITEM_W*NIPC
+ // ---------------------------------------------------
+ wire tmp_pyld_tvalid, tmp_pyld_tready;
+
+ axis_width_conv #(
+ .WORD_W(ITEM_W), .IN_WORDS(CHDR_W/ITEM_W), .OUT_WORDS(NIPC),
+ .SYNC_CLKS(1), .PIPELINE("NONE")
+ ) payload_width_conv_i (
+ .s_axis_aclk(axis_data_clk), .s_axis_rst(axis_data_rst),
+ .s_axis_tdata(out_pyld_tdata), .s_axis_tkeep(out_pyld_tkeep),
+ .s_axis_tlast(out_pyld_tlast), .s_axis_tvalid(out_pyld_tvalid),
+ .s_axis_tready(out_pyld_tready),
+ .m_axis_aclk(axis_data_clk), .m_axis_rst(axis_data_rst),
+ .m_axis_tdata(flush_pyld_tdata), .m_axis_tkeep(flush_pyld_tkeep),
+ .m_axis_tlast(flush_pyld_tlast), .m_axis_tvalid(tmp_pyld_tvalid),
+ .m_axis_tready(tmp_pyld_tready)
+ );
+
+ // ---------------------------------------------------
+ // Output State Machine
+ // ---------------------------------------------------
+ reg [2:0] ctxt_pkt_cnt = 3'd0, pyld_pkt_cnt = 3'd0;
+
+ // A payload packet can pass only if it is preceeded by a context packet
+ wire pass_pyld = ((ctxt_pkt_cnt - pyld_pkt_cnt) > 3'd0);
+ // A context packet has to be blocked if its corresponding payload packet hasn't passed except
+ // when prefetching is enabled. In that case one additional context packet is allowed to pass
+ wire pass_ctxt = ((ctxt_pkt_cnt - pyld_pkt_cnt) < (CONTEXT_PREFETCH_EN == 1 ? 3'd2 : 3'd1));
+
+ always @(posedge axis_data_clk) begin
+ if (axis_data_rst) begin
+ ctxt_pkt_cnt <= 3'd0;
+ pyld_pkt_cnt <= 3'd0;
+ end else begin
+ if (flush_ctxt_tvalid && flush_ctxt_tready && flush_ctxt_tlast)
+ ctxt_pkt_cnt <= ctxt_pkt_cnt + 3'd1;
+ if (flush_pyld_tvalid && flush_pyld_tready && flush_pyld_tlast)
+ pyld_pkt_cnt <= pyld_pkt_cnt + 3'd1;
+ end
+ end
+
+ assign flush_pyld_tvalid = tmp_pyld_tvalid && pass_pyld;
+ assign tmp_pyld_tready = flush_pyld_tready && pass_pyld;
+
+ assign flush_ctxt_tvalid = tmp_ctxt_tvalid && pass_ctxt;
+ assign tmp_ctxt_tready = flush_ctxt_tready && pass_ctxt;
+
+ // ---------------------------------------------------
+ // Flushing Logic
+ // ---------------------------------------------------
+ wire [31:0] flush_timeout_dclk;
+ wire flush_en_dclk;
+ wire flush_active_pyld_cclk, flush_active_ctxt_cclk;
+ wire flush_done_pyld_cclk, flush_done_ctxt_cclk;
+ wire flush_active_pyld, flush_active_ctxt;
+ wire flush_done_pyld, flush_done_ctxt;
+
+ synchronizer #(.WIDTH(4), .INITIAL_VAL(4'd0)) flush_2clk_rb_i (
+ .clk(axis_chdr_clk), .rst(1'b0),
+ .in({flush_active_pyld, flush_done_pyld,
+ flush_active_ctxt, flush_done_ctxt}),
+ .out({flush_active_pyld_cclk, flush_done_pyld_cclk,
+ flush_active_ctxt_cclk, flush_done_ctxt_cclk})
+ );
+ assign flush_active = flush_active_pyld_cclk | flush_active_ctxt_cclk;
+ assign flush_done = flush_done_pyld_cclk & flush_done_ctxt_cclk;
+
+ axi_fifo_2clk #(.WIDTH(33), .SIZE(1)) flush_2clk_ctrl_i (
+ .reset(axis_chdr_rst),
+ .i_aclk(axis_chdr_clk),
+ .i_tdata({flush_en, flush_timeout}), .i_tvalid(1'b1), .i_tready(),
+ .o_aclk(axis_data_clk),
+ .o_tdata({flush_en_dclk, flush_timeout_dclk}), .o_tvalid(), .o_tready(1'b1)
+ );
+
+ axis_packet_flush #(
+ .WIDTH((ITEM_W+1)*NIPC), .FLUSH_PARTIAL_PKTS(0), .TIMEOUT_W(32), .PIPELINE("OUT")
+ ) pyld_flusher_i (
+ .clk(axis_data_clk), .reset(axis_data_rst),
+ .enable(flush_en_dclk), .timeout(flush_timeout_dclk),
+ .flushing(flush_active_pyld), .done(flush_done_pyld),
+ .s_axis_tdata({flush_pyld_tkeep, flush_pyld_tdata}),
+ .s_axis_tlast(flush_pyld_tlast),
+ .s_axis_tvalid(flush_pyld_tvalid),
+ .s_axis_tready(flush_pyld_tready),
+ .m_axis_tdata({m_axis_payload_tkeep, m_axis_payload_tdata}),
+ .m_axis_tlast(m_axis_payload_tlast),
+ .m_axis_tvalid(m_axis_payload_tvalid),
+ .m_axis_tready(m_axis_payload_tready)
+ );
+
+ axis_packet_flush #(
+ .WIDTH(CHDR_W+4), .FLUSH_PARTIAL_PKTS(0), .TIMEOUT_W(32), .PIPELINE("OUT")
+ ) ctxt_flusher_i (
+ .clk(axis_data_clk), .reset(axis_data_rst),
+ .enable(flush_en_dclk), .timeout(flush_timeout_dclk),
+ .flushing(flush_active_ctxt), .done(flush_done_ctxt),
+ .s_axis_tdata({flush_ctxt_tuser, flush_ctxt_tdata}),
+ .s_axis_tlast(flush_ctxt_tlast),
+ .s_axis_tvalid(flush_ctxt_tvalid),
+ .s_axis_tready(flush_ctxt_tready),
+ .m_axis_tdata({m_axis_context_tuser, m_axis_context_tdata}),
+ .m_axis_tlast(m_axis_context_tlast),
+ .m_axis_tvalid(m_axis_context_tvalid),
+ .m_axis_tready(m_axis_context_tready)
+ );
+
+endmodule // chdr_to_axis_pyld_ctxt
diff --git a/fpga/usrp3/lib/rfnoc/core/chdr_to_chdr_data.v b/fpga/usrp3/lib/rfnoc/core/chdr_to_chdr_data.v
new file mode 100644
index 000000000..390d77bca
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/chdr_to_chdr_data.v
@@ -0,0 +1,55 @@
+//
+// Copyright 2018-2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axis_raw_data_to_chdr
+// Description:
+// A simple adapter for when CHDR data is requested as an
+// interface to user logic.
+//
+// Parameters:
+// - CHDR_W: Width of the input CHDR bus in bits
+//
+// Signals:
+// - s_axis_chdr_* : Input CHDR stream (AXI-Stream)
+// - m_axis_chdr_* : Output CHDR stream (AXI-Stream)
+// - flush_* : Signals for flush control and status
+//
+
+module chdr_to_chdr_data #(
+ parameter CHDR_W = 256
+)(
+ // Clock, reset and settings
+ input wire axis_chdr_clk,
+ input wire axis_chdr_rst,
+ // CHDR in (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_chdr_tdata,
+ input wire s_axis_chdr_tlast,
+ input wire s_axis_chdr_tvalid,
+ output wire s_axis_chdr_tready,
+ // CHDR in (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_chdr_tdata,
+ output wire m_axis_chdr_tlast,
+ output wire m_axis_chdr_tvalid,
+ input wire m_axis_chdr_tready,
+ // Flush signals
+ input wire flush_en,
+ input wire [31:0] flush_timeout,
+ output wire flush_active,
+ output wire flush_done
+);
+
+ axis_packet_flush #(
+ .WIDTH(CHDR_W), .FLUSH_PARTIAL_PKTS(0), .TIMEOUT_W(32), .PIPELINE("OUT")
+ ) chdr_flusher_i (
+ .clk(axis_chdr_clk), .reset(axis_chdr_rst),
+ .enable(flush_en), .timeout(flush_timeout),
+ .flushing(flush_active), .done(flush_done),
+ .s_axis_tdata(s_axis_chdr_tdata), .s_axis_tlast(s_axis_chdr_tlast),
+ .s_axis_tvalid(s_axis_chdr_tvalid), .s_axis_tready(s_axis_chdr_tready),
+ .m_axis_tdata(m_axis_chdr_tdata), .m_axis_tlast(m_axis_chdr_tlast),
+ .m_axis_tvalid(m_axis_chdr_tvalid), .m_axis_tready(m_axis_chdr_tready)
+ );
+
+endmodule // chdr_to_chdr_data
diff --git a/fpga/usrp3/lib/rfnoc/core/ctrlport.vh b/fpga/usrp3/lib/rfnoc/core/ctrlport.vh
new file mode 100644
index 000000000..7b5f9fcaa
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/ctrlport.vh
@@ -0,0 +1,26 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: ctrlport.vh
+// Description:
+// Defines constants for the control port interface.
+//
+// Requires rfnoc_axis_ctrl_utils.vh in same directory to be
+// included first.
+
+//---------------------------------------------------------------
+// Signal widths
+//---------------------------------------------------------------
+localparam CTRLPORT_ADDR_W = 20;
+localparam CTRLPORT_DATA_W = 32;
+localparam CTRLPORT_STS_W = 2;
+
+//---------------------------------------------------------------
+// Status values
+//---------------------------------------------------------------
+localparam [1:0] CTRL_STS_OKAY = 2'b00;
+localparam [1:0] CTRL_STS_CMDERR = 2'b01;
+localparam [1:0] CTRL_STS_TSERR = 2'b10;
+localparam [1:0] CTRL_STS_WARNING = 2'b11;
diff --git a/fpga/usrp3/lib/rfnoc/core/ctrlport_endpoint.v b/fpga/usrp3/lib/rfnoc/core/ctrlport_endpoint.v
new file mode 100644
index 000000000..4a7d7302a
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/ctrlport_endpoint.v
@@ -0,0 +1,284 @@
+//
+// Copyright 2018-2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: ctrlport_endpoint
+// Description:
+// A bidirectional AXIS-Control to Control-Port converter.
+// Use this module in noc_shell to interface between the user
+// logic (using ctrlport) and the rfnoc infrastructure (axis_ctrl)
+//
+// Parameters:
+// - THIS_PORTID: The 10-bit ID of the control XB port that is
+// connected to this converter.
+// - SYNC_CLKS: Is rfnoc_ctrl_clk and ctrlport_clk the same clock?
+// - AXIS_CTRL_MST_EN: Enable an AXIS-Ctrl master
+// - AXIS_CTRL_SLV_EN: Enable an AXIS-Ctrl slave
+// - SLAVE_FIFO_SIZE: FIFO depth for the slave port
+//
+// Signals:
+// - *_rfnoc_ctrl_* : Input/output AXIS-Control stream (AXI-Stream)
+// - *_ctrlport_* : Input/output control-port bus
+
+module ctrlport_endpoint #(
+ parameter [9:0] THIS_PORTID = 10'd0,
+ parameter SYNC_CLKS = 0,
+ parameter [0:0] AXIS_CTRL_MST_EN = 1,
+ parameter [0:0] AXIS_CTRL_SLV_EN = 1,
+ parameter SLAVE_FIFO_SIZE = 5
+)(
+ // Clocks, Resets, Misc
+ input wire rfnoc_ctrl_clk,
+ input wire rfnoc_ctrl_rst,
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+ // AXIS-Control Bus
+ input wire [31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+ output wire [31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready,
+ // Control Port Master (Request)
+ output wire m_ctrlport_req_wr,
+ output wire m_ctrlport_req_rd,
+ output wire [19:0] m_ctrlport_req_addr,
+ output wire [31:0] m_ctrlport_req_data,
+ output wire [3:0] m_ctrlport_req_byte_en,
+ output wire m_ctrlport_req_has_time,
+ output wire [63:0] m_ctrlport_req_time,
+ // Control Port Master (Response)
+ input wire m_ctrlport_resp_ack,
+ input wire [1:0] m_ctrlport_resp_status,
+ input wire [31:0] m_ctrlport_resp_data,
+ // Control Port Slave (Request)
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [9:0] s_ctrlport_req_portid,
+ input wire [15:0] s_ctrlport_req_rem_epid,
+ input wire [9:0] s_ctrlport_req_rem_portid,
+ input wire [31:0] s_ctrlport_req_data,
+ input wire [3:0] s_ctrlport_req_byte_en,
+ input wire s_ctrlport_req_has_time,
+ input wire [63:0] s_ctrlport_req_time,
+ // Control Port Slave (Response)
+ output wire s_ctrlport_resp_ack,
+ output wire [1:0] s_ctrlport_resp_status,
+ output wire [31:0] s_ctrlport_resp_data
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "rfnoc_chdr_utils.vh"
+ `include "rfnoc_axis_ctrl_utils.vh"
+
+ // ---------------------------------------------------
+ // Clock Crossing
+ // ---------------------------------------------------
+
+ wire [31:0] i_ctrl_tdata, o_ctrl_tdata;
+ wire i_ctrl_tlast, o_ctrl_tlast;
+ wire i_ctrl_tvalid, o_ctrl_tvalid;
+ wire i_ctrl_tready, o_ctrl_tready;
+
+ generate
+ if (SYNC_CLKS) begin : gen_sync_fifos
+ axi_fifo #(.WIDTH(32+1), .SIZE(1)) in_fifo_i (
+ .clk(ctrlport_clk), .reset(ctrlport_rst), .clear(1'b0),
+ .i_tdata({s_rfnoc_ctrl_tlast, s_rfnoc_ctrl_tdata}),
+ .i_tvalid(s_rfnoc_ctrl_tvalid), .i_tready(s_rfnoc_ctrl_tready),
+ .o_tdata({i_ctrl_tlast, i_ctrl_tdata}),
+ .o_tvalid(i_ctrl_tvalid), .o_tready(i_ctrl_tready),
+ .space(), .occupied()
+ );
+
+ axi_fifo #(.WIDTH(32+1), .SIZE(1)) out_fifo_i (
+ .clk(ctrlport_clk), .reset(ctrlport_rst), .clear(1'b0),
+ .i_tdata({o_ctrl_tlast, o_ctrl_tdata}),
+ .i_tvalid(o_ctrl_tvalid), .i_tready(o_ctrl_tready),
+ .o_tdata({m_rfnoc_ctrl_tlast, m_rfnoc_ctrl_tdata}),
+ .o_tvalid(m_rfnoc_ctrl_tvalid), .o_tready(m_rfnoc_ctrl_tready),
+ .space(), .occupied()
+ );
+ end else begin : gen_async_fifos
+ axi_fifo_2clk #(.WIDTH(32+1), .SIZE(1), .PIPELINE("IN")) in_fifo_i (
+ .reset(rfnoc_ctrl_rst),
+ .i_aclk(rfnoc_ctrl_clk),
+ .i_tdata({s_rfnoc_ctrl_tlast, s_rfnoc_ctrl_tdata}),
+ .i_tvalid(s_rfnoc_ctrl_tvalid), .i_tready(s_rfnoc_ctrl_tready),
+ .o_aclk(ctrlport_clk),
+ .o_tdata({i_ctrl_tlast, i_ctrl_tdata}),
+ .o_tvalid(i_ctrl_tvalid), .o_tready(i_ctrl_tready)
+ );
+
+ axi_fifo_2clk #(.WIDTH(32+1), .SIZE(1), .PIPELINE("OUT")) out_fifo_i (
+ .reset(ctrlport_rst),
+ .i_aclk(ctrlport_clk),
+ .i_tdata({o_ctrl_tlast, o_ctrl_tdata}),
+ .i_tvalid(o_ctrl_tvalid), .i_tready(o_ctrl_tready),
+ .o_aclk(rfnoc_ctrl_clk),
+ .o_tdata({m_rfnoc_ctrl_tlast, m_rfnoc_ctrl_tdata}),
+ .o_tvalid(m_rfnoc_ctrl_tvalid), .o_tready(m_rfnoc_ctrl_tready)
+ );
+
+ end
+ endgenerate
+
+ // ---------------------------------------------------
+ // MUXING
+ // ---------------------------------------------------
+ wire [31:0] mst_req_tdata, mst_resp_tdata ;
+ wire mst_req_tlast, mst_resp_tlast ;
+ wire mst_req_tvalid, mst_resp_tvalid;
+ wire mst_req_tready, mst_resp_tready;
+
+ wire [31:0] slv_req_tdata, slv_req_fifo_tdata, slv_resp_tdata ;
+ wire slv_req_tlast, slv_req_fifo_tlast, slv_resp_tlast ;
+ wire slv_req_tvalid, slv_req_fifo_tvalid, slv_resp_tvalid;
+ wire slv_req_tready, slv_req_fifo_tready, slv_resp_tready;
+
+ generate
+ if (AXIS_CTRL_MST_EN == 1'b1 && AXIS_CTRL_SLV_EN == 1'b1) begin : gen_mst_slv_muxing
+ wire [31:0] in_hdr;
+ axi_demux #(
+ .WIDTH(32), .SIZE(2), .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(0)
+ ) demux_i (
+ .clk(ctrlport_clk), .reset(ctrlport_rst), .clear(1'b0),
+ .header(in_hdr), .dest(axis_ctrl_get_is_ack(in_hdr)),
+ .i_tdata (i_ctrl_tdata ),
+ .i_tlast (i_ctrl_tlast ),
+ .i_tvalid(i_ctrl_tvalid),
+ .i_tready(i_ctrl_tready),
+ .o_tdata ({mst_resp_tdata, slv_req_tdata }),
+ .o_tlast ({mst_resp_tlast, slv_req_tlast }),
+ .o_tvalid({mst_resp_tvalid, slv_req_tvalid}),
+ .o_tready({mst_resp_tready, slv_req_tready})
+ );
+
+ axi_mux #(
+ .WIDTH(32), .SIZE(2), .PRIO(0), .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(0)
+ ) mux_i (
+ .clk(ctrlport_clk), .reset(ctrlport_rst), .clear(1'b0),
+ .i_tdata ({mst_req_tdata, slv_resp_tdata }),
+ .i_tlast ({mst_req_tlast, slv_resp_tlast }),
+ .i_tvalid({mst_req_tvalid, slv_resp_tvalid}),
+ .i_tready({mst_req_tready, slv_resp_tready}),
+ .o_tdata (o_ctrl_tdata ),
+ .o_tlast (o_ctrl_tlast ),
+ .o_tvalid(o_ctrl_tvalid),
+ .o_tready(o_ctrl_tready)
+ );
+
+ end else if (AXIS_CTRL_MST_EN == 1'b1) begin : gen_mst_muxing
+
+ assign mst_resp_tdata = i_ctrl_tdata;
+ assign mst_resp_tlast = i_ctrl_tlast;
+ assign mst_resp_tvalid = i_ctrl_tvalid;
+ assign i_ctrl_tready = mst_resp_tready;
+
+ assign o_ctrl_tdata = mst_req_tdata;
+ assign o_ctrl_tlast = mst_req_tlast;
+ assign o_ctrl_tvalid = mst_req_tvalid;
+ assign mst_req_tready = o_ctrl_tready;
+
+ end else begin : gen_no_mst_muxing
+
+ assign slv_req_tdata = i_ctrl_tdata;
+ assign slv_req_tlast = i_ctrl_tlast;
+ assign slv_req_tvalid = i_ctrl_tvalid;
+ assign i_ctrl_tready = slv_req_tready;
+
+ assign o_ctrl_tdata = slv_resp_tdata;
+ assign o_ctrl_tlast = slv_resp_tlast;
+ assign o_ctrl_tvalid = slv_resp_tvalid;
+ assign slv_resp_tready = o_ctrl_tready;
+
+ end
+ endgenerate
+
+ // ---------------------------------------------------
+ // AXIS Control Master and Slave
+ // ---------------------------------------------------
+
+ generate
+ if (AXIS_CTRL_MST_EN == 1'b1) begin : gen_ctrl_master
+ axis_ctrl_master #( .THIS_PORTID(THIS_PORTID) ) axis_ctrl_mst_i (
+ .clk (ctrlport_clk),
+ .rst (ctrlport_rst),
+ .s_axis_ctrl_tdata (mst_resp_tdata),
+ .s_axis_ctrl_tlast (mst_resp_tlast),
+ .s_axis_ctrl_tvalid (mst_resp_tvalid),
+ .s_axis_ctrl_tready (mst_resp_tready),
+ .m_axis_ctrl_tdata (mst_req_tdata),
+ .m_axis_ctrl_tlast (mst_req_tlast),
+ .m_axis_ctrl_tvalid (mst_req_tvalid),
+ .m_axis_ctrl_tready (mst_req_tready),
+ .ctrlport_req_wr (s_ctrlport_req_wr),
+ .ctrlport_req_rd (s_ctrlport_req_rd),
+ .ctrlport_req_addr (s_ctrlport_req_addr),
+ .ctrlport_req_portid (s_ctrlport_req_portid),
+ .ctrlport_req_rem_epid (s_ctrlport_req_rem_epid),
+ .ctrlport_req_rem_portid(s_ctrlport_req_rem_portid),
+ .ctrlport_req_data (s_ctrlport_req_data),
+ .ctrlport_req_byte_en (s_ctrlport_req_byte_en),
+ .ctrlport_req_has_time (s_ctrlport_req_has_time),
+ .ctrlport_req_time (s_ctrlport_req_time),
+ .ctrlport_resp_ack (s_ctrlport_resp_ack),
+ .ctrlport_resp_status (s_ctrlport_resp_status),
+ .ctrlport_resp_data (s_ctrlport_resp_data)
+ );
+ end else begin : gen_no_ctrl_master
+ assign mst_resp_tready = 1'b1;
+ assign mst_req_tlast = 1'b0;
+ assign mst_req_tvalid = 1'b0;
+ assign s_ctrlport_resp_ack = 1'b0;
+ end
+
+ if (AXIS_CTRL_SLV_EN == 1'b1) begin : gen_ctrl_slave
+ axi_fifo #(.WIDTH(32+1), .SIZE(SLAVE_FIFO_SIZE)) slv_fifo_i (
+ .clk(ctrlport_clk), .reset(ctrlport_rst), .clear(1'b0),
+ .i_tdata({slv_req_tlast, slv_req_tdata}),
+ .i_tvalid(slv_req_tvalid), .i_tready(slv_req_tready),
+ .o_tdata({slv_req_fifo_tlast, slv_req_fifo_tdata}),
+ .o_tvalid(slv_req_fifo_tvalid), .o_tready(slv_req_fifo_tready),
+ .space(), .occupied()
+ );
+
+ axis_ctrl_slave axis_ctrl_slv_i (
+ .clk (ctrlport_clk),
+ .rst (ctrlport_rst),
+ .s_axis_ctrl_tdata (slv_req_fifo_tdata),
+ .s_axis_ctrl_tlast (slv_req_fifo_tlast),
+ .s_axis_ctrl_tvalid (slv_req_fifo_tvalid),
+ .s_axis_ctrl_tready (slv_req_fifo_tready),
+ .m_axis_ctrl_tdata (slv_resp_tdata),
+ .m_axis_ctrl_tlast (slv_resp_tlast),
+ .m_axis_ctrl_tvalid (slv_resp_tvalid),
+ .m_axis_ctrl_tready (slv_resp_tready),
+ .ctrlport_req_wr (m_ctrlport_req_wr),
+ .ctrlport_req_rd (m_ctrlport_req_rd),
+ .ctrlport_req_addr (m_ctrlport_req_addr),
+ .ctrlport_req_data (m_ctrlport_req_data),
+ .ctrlport_req_byte_en (m_ctrlport_req_byte_en),
+ .ctrlport_req_has_time(m_ctrlport_req_has_time),
+ .ctrlport_req_time (m_ctrlport_req_time),
+ .ctrlport_resp_ack (m_ctrlport_resp_ack),
+ .ctrlport_resp_status (m_ctrlport_resp_status),
+ .ctrlport_resp_data (m_ctrlport_resp_data)
+ );
+ end else begin : gen_no_ctrl_slave
+ assign slv_req_fifo_tready = 1'b1;
+ assign slv_resp_tlast = 1'b0;
+ assign slv_resp_tvalid = 1'b0;
+ assign m_ctrlport_req_wr = 1'b0;
+ assign m_ctrlport_req_rd = 1'b0;
+ end
+ endgenerate
+
+endmodule // ctrlport_endpoint
+
diff --git a/fpga/usrp3/lib/rfnoc/core/rfnoc_axis_ctrl_utils.vh b/fpga/usrp3/lib/rfnoc/core/rfnoc_axis_ctrl_utils.vh
new file mode 100644
index 000000000..5c3dab8ac
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/rfnoc_axis_ctrl_utils.vh
@@ -0,0 +1,154 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+// =============================================================
+// AXIS-Ctrl Bitfields
+// =============================================================
+
+// -----------------------
+// Line 0: HDR_0
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 31 is_ack Is this an acknowledgment to a transaction?
+// 30 has_time Does the transaction have a timestamp?
+// 29:24 seq_num Sequence number
+// 23:20 num_data Number of data words
+// 19:10 src_port Ctrl XB port that the source block is on
+// 9:0 dst_port Ctrl XB port that the destination block is on
+
+// -----------------------
+// Line 1: HDR_1
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 31:26 <Reserved>
+// 25:16 rem_dst_port Ctrl XB port that the remote dest block is on
+// 15:0 rem_dst_epid Endpoint ID of the remote dest of this msg
+
+// -----------------------
+// Line 2: TS_LO (Optional)
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 31:0 timestamp Lower 32 bits of the timestamp
+
+// -----------------------
+// Line 3: TS_HI (Optional)
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 31:0 timestamp Upper 32 bits of the timestamp
+
+// -----------------------
+// Line 4: OP Word
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 31:30 status The status of the ack
+// 29:28 <Reserved>
+// 27:24 opcode Operation Code
+// 23:20 byte_en Byte enable strobe
+// 19:0 address Address for transaction
+
+// AXIS-Ctrl Status
+//
+localparam [1:0] AXIS_CTRL_STS_OKAY = 2'b00;
+localparam [1:0] AXIS_CTRL_STS_CMDERR = 2'b01;
+localparam [1:0] AXIS_CTRL_STS_TSERR = 2'b10;
+localparam [1:0] AXIS_CTRL_STS_WARNING = 2'b11;
+
+// AXIS-Ctrl Opcode Definitions
+//
+localparam [3:0] AXIS_CTRL_OPCODE_SLEEP = 4'd0;
+localparam [3:0] AXIS_CTRL_OPCODE_WRITE = 4'd1;
+localparam [3:0] AXIS_CTRL_OPCODE_READ = 4'd2;
+localparam [3:0] AXIS_CTRL_OPCODE_WRITE_READ = 4'd3;
+
+// AXIS-Ctrl Getter Functions
+//
+function [0:0] axis_ctrl_get_is_ack(input [31:0] header);
+ axis_ctrl_get_is_ack = header[31];
+endfunction
+
+function [0:0] axis_ctrl_get_has_time(input [31:0] header);
+ axis_ctrl_get_has_time = header[30];
+endfunction
+
+function [5:0] axis_ctrl_get_seq_num(input [31:0] header);
+ axis_ctrl_get_seq_num = header[29:24];
+endfunction
+
+function [3:0] axis_ctrl_get_num_data(input [31:0] header);
+ axis_ctrl_get_num_data = header[23:20];
+endfunction
+
+function [9:0] axis_ctrl_get_src_port(input [31:0] header);
+ axis_ctrl_get_src_port = header[19:10];
+endfunction
+
+function [9:0] axis_ctrl_get_dst_port(input [31:0] header);
+ axis_ctrl_get_dst_port = header[9:0];
+endfunction
+
+function [15:0] axis_ctrl_get_rem_dst_epid(input [31:0] header);
+ axis_ctrl_get_rem_dst_epid = header[15:0];
+endfunction
+
+function [9:0] axis_ctrl_get_rem_dst_port(input [31:0] header);
+ axis_ctrl_get_rem_dst_port = header[25:16];
+endfunction
+
+function [1:0] axis_ctrl_get_status(input [31:0] header);
+ axis_ctrl_get_status = header[31:30];
+endfunction
+
+function [3:0] axis_ctrl_get_opcode(input [31:0] header);
+ axis_ctrl_get_opcode = header[27:24];
+endfunction
+
+function [3:0] axis_ctrl_get_byte_en(input [31:0] header);
+ axis_ctrl_get_byte_en = header[23:20];
+endfunction
+
+function [19:0] axis_ctrl_get_address(input [31:0] header);
+ axis_ctrl_get_address = header[19:0];
+endfunction
+
+// AXIS-Ctrl Setter Functions
+//
+function [31:0] axis_ctrl_build_hdr_lo(
+ input [0:0] is_ack,
+ input [0:0] has_time,
+ input [5:0] seq_num,
+ input [3:0] num_data,
+ input [9:0] src_port,
+ input [9:0] dst_port
+);
+ axis_ctrl_build_hdr_lo = {is_ack, has_time, seq_num, num_data, src_port, dst_port};
+endfunction
+
+function [31:0] axis_ctrl_build_hdr_hi(
+ input [9:0] rem_dst_port,
+ input [15:0] rem_dst_epid
+);
+ axis_ctrl_build_hdr_hi = {6'h0, rem_dst_port, rem_dst_epid};
+endfunction
+
+function [31:0] chdr_ctrl_build_hdr_hi(
+ input [15:0] src_epid
+);
+ chdr_ctrl_build_hdr_hi = {16'h0, src_epid};
+endfunction
+
+function [31:0] axis_ctrl_build_op_word(
+ input [1:0] status,
+ input [3:0] opcode,
+ input [3:0] byte_en,
+ input [19:0] address
+);
+ axis_ctrl_build_op_word = {status, 2'b00, opcode, byte_en, address};
+endfunction
diff --git a/fpga/usrp3/lib/rfnoc/core/rfnoc_backend_iface.vh b/fpga/usrp3/lib/rfnoc/core/rfnoc_backend_iface.vh
new file mode 100644
index 000000000..ec5c152f6
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/rfnoc_backend_iface.vh
@@ -0,0 +1,52 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+// Each block has a backed interface that is 512 bits wide. This bus
+// is split into 16 32-bit registers to it is preferable to have fields
+// aligned at 32-bit boundaries
+
+// Backend Config
+localparam BEC_FLUSH_TIMEOUT_OFFSET = 0;
+localparam BEC_FLUSH_TIMEOUT_WIDTH = 32;
+localparam BEC_FLUSH_EN_OFFSET = BEC_FLUSH_TIMEOUT_OFFSET + BEC_FLUSH_TIMEOUT_WIDTH;
+localparam BEC_FLUSH_EN_WIDTH = 1;
+localparam BEC_SOFT_CTRL_RST_OFFSET = BEC_FLUSH_EN_OFFSET + BEC_FLUSH_EN_WIDTH;
+localparam BEC_SOFT_CTRL_RST_WIDTH = 1;
+localparam BEC_SOFT_CHDR_RST_OFFSET = BEC_SOFT_CTRL_RST_OFFSET + BEC_SOFT_CTRL_RST_WIDTH;
+localparam BEC_SOFT_CHDR_RST_WIDTH = 1;
+localparam BEC_TOTAL_WIDTH = BEC_SOFT_CHDR_RST_OFFSET + BEC_SOFT_CHDR_RST_WIDTH;
+
+localparam [511:0] BEC_DEFAULT_VAL = {
+ {(512-BEC_TOTAL_WIDTH){1'b0}},
+ 1'b1, // BEC_SOFT_CHDR_RST
+ 1'b1, // BEC_SOFT_CTRL_RST
+ 1'b0, // BEC_FLUSH_EN
+ 32'd0 // BEC_FLUSH_TIMEOUT
+};
+
+// Backend Status
+localparam BES_PROTO_VER_OFFSET = 0;
+localparam BES_PROTO_VER_WIDTH = 6;
+localparam BES_NUM_DATA_I_OFFSET = BES_PROTO_VER_OFFSET + BES_PROTO_VER_WIDTH;
+localparam BES_NUM_DATA_I_WIDTH = 6;
+localparam BES_NUM_DATA_O_OFFSET = BES_NUM_DATA_I_OFFSET + BES_NUM_DATA_I_WIDTH;
+localparam BES_NUM_DATA_O_WIDTH = 6;
+localparam BES_CTRL_FIFOSIZE_OFFSET = BES_NUM_DATA_O_OFFSET + BES_NUM_DATA_O_WIDTH;
+localparam BES_CTRL_FIFOSIZE_WIDTH = 6;
+localparam BES_CTRL_MAX_ASYNC_MSGS_OFFSET = BES_CTRL_FIFOSIZE_OFFSET + BES_CTRL_FIFOSIZE_WIDTH;
+localparam BES_CTRL_MAX_ASYNC_MSGS_WIDTH = 8;
+localparam BES_NOC_ID_OFFSET = BES_CTRL_MAX_ASYNC_MSGS_OFFSET + BES_CTRL_MAX_ASYNC_MSGS_WIDTH;
+localparam BES_NOC_ID_WIDTH = 32;
+localparam BES_FLUSH_ACTIVE_OFFSET = BES_NOC_ID_OFFSET + BES_NOC_ID_WIDTH;
+localparam BES_FLUSH_ACTIVE_WIDTH = 1;
+localparam BES_FLUSH_DONE_OFFSET = BES_FLUSH_ACTIVE_OFFSET + BES_FLUSH_ACTIVE_WIDTH;
+localparam BES_FLUSH_DONE_WIDTH = 1;
+localparam BES_DATA_MTU_OFFSET = BES_FLUSH_DONE_OFFSET + BES_FLUSH_DONE_WIDTH;
+localparam BES_DATA_MTU_WIDTH = 6;
+localparam BES_TOTAL_WIDTH = BES_DATA_MTU_OFFSET + BES_DATA_MTU_WIDTH;
+
+// Protocol version for this definition
+localparam [5:0] BACKEND_PROTO_VER = 6'd1; \ No newline at end of file
diff --git a/fpga/usrp3/lib/rfnoc/core/rfnoc_chdr_internal_utils.vh b/fpga/usrp3/lib/rfnoc/core/rfnoc_chdr_internal_utils.vh
new file mode 100644
index 000000000..1d70c0f1c
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/rfnoc_chdr_internal_utils.vh
@@ -0,0 +1,452 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+// =============================================================
+// Stream Status Bitfields
+// =============================================================
+
+// -----------------------
+// Line 0
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 63:24 capacity_bytes Downstream buffer capacity in bytes
+// 23:20 <reserved>
+// 19:16 status Stream status code (enumeration)
+// 15:0 src_epid Endpoint ID of the source of this msg
+
+// -----------------------
+// Line 1
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 63:24 xfercnt_pkts Transfer count in packets
+// 23:0 capacity_pkts Downstream buffer capacity in packets
+
+// -----------------------
+// Line 2
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 63:0 xfercnt_bytes Transfer count in bytes
+
+// -----------------------
+// Line 3
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 63:16 status_info Extended information about status (diagnostic only)
+// 15:0 buff_info Extended information about buffer state (diagnostic only)
+
+localparam [3:0] CHDR_STRS_STATUS_OKAY = 4'd0; // No error
+localparam [3:0] CHDR_STRS_STATUS_CMDERR = 4'd1; // Cmd execution failed
+localparam [3:0] CHDR_STRS_STATUS_SEQERR = 4'd2; // Sequence number discontinuity
+localparam [3:0] CHDR_STRS_STATUS_DATAERR = 4'd3; // Data integrity check failed
+localparam [3:0] CHDR_STRS_STATUS_RTERR = 4'd4; // Unexpected destination
+
+// 64-bit fields
+function [39:0] chdr64_strs_get_capacity_bytes(input [63:0] header);
+ chdr64_strs_get_capacity_bytes = header[63:24];
+endfunction
+
+function [3:0] chdr64_strs_get_status(input [63:0] header);
+ chdr64_strs_get_status = header[19:16];
+endfunction
+
+function [15:0] chdr64_strs_get_src_epid(input [63:0] header);
+ chdr64_strs_get_src_epid = header[15:0];
+endfunction
+
+function [39:0] chdr64_strs_get_xfercnt_pkts(input [63:0] header);
+ chdr64_strs_get_xfercnt_pkts = header[63:24];
+endfunction
+
+function [23:0] chdr64_strs_get_capacity_pkts(input [63:0] header);
+ chdr64_strs_get_capacity_pkts = header[23:0];
+endfunction
+
+function [63:0] chdr64_strs_get_xfercnt_bytes(input [63:0] header);
+ chdr64_strs_get_xfercnt_bytes = header[63:0];
+endfunction
+
+function [47:0] chdr64_strs_get_status_info(input [63:0] header);
+ chdr64_strs_get_status_info = header[63:16];
+endfunction
+
+function [15:0] chdr64_strs_get_buff_info(input [63:0] header);
+ chdr64_strs_get_buff_info = header[15:0];
+endfunction
+
+
+// 128-bit fields
+function [39:0] chdr128_strs_get_capacity_bytes(input [127:0] header);
+ chdr128_strs_get_capacity_bytes = chdr64_strs_get_capacity_bytes(header[63:0]);
+endfunction
+
+function [3:0] chdr128_strs_get_status(input [127:0] header);
+ chdr128_strs_get_status = chdr64_strs_get_status(header[63:0]);
+endfunction
+
+function [15:0] chdr128_strs_get_src_epid(input [127:0] header);
+ chdr128_strs_get_src_epid = chdr64_strs_get_src_epid(header[63:0]);
+endfunction
+
+function [23:0] chdr128_strs_get_capacity_pkts(input [127:0] header);
+ chdr128_strs_get_capacity_pkts = chdr64_strs_get_capacity_pkts(header[127:64]);
+endfunction
+
+function [39:0] chdr128_strs_get_xfercnt_pkts(input [127:0] header);
+ chdr128_strs_get_xfercnt_pkts = chdr64_strs_get_xfercnt_pkts(header[127:64]);
+endfunction
+
+function [63:0] chdr128_strs_get_xfercnt_bytes(input [127:0] header);
+ chdr128_strs_get_xfercnt_bytes = chdr64_strs_get_xfercnt_bytes(header[63:0]);
+endfunction
+
+function [47:0] chdr128_strs_get_status_info(input [127:0] header);
+ chdr128_strs_get_status_info = chdr64_strs_get_status_info(header[127:64]);
+endfunction
+
+function [15:0] chdr128_strs_get_buff_info(input [127:0] header);
+ chdr128_strs_get_buff_info = chdr64_strs_get_buff_info(header[127:64]);
+endfunction
+
+
+// 256-bit fields
+function [39:0] chdr256_strs_get_capacity_bytes(input [255:0] header);
+ chdr256_strs_get_capacity_bytes = chdr64_strs_get_capacity_bytes(header[63:0]);
+endfunction
+
+function [3:0] chdr256_strs_get_status(input [255:0] header);
+ chdr256_strs_get_status = chdr64_strs_get_status(header[63:0]);
+endfunction
+
+function [15:0] chdr256_strs_get_src_epid(input [255:0] header);
+ chdr256_strs_get_src_epid = chdr64_strs_get_src_epid(header[63:0]);
+endfunction
+
+function [23:0] chdr256_strs_get_capacity_pkts(input [255:0] header);
+ chdr256_strs_get_capacity_pkts = chdr64_strs_get_capacity_pkts(header[127:64]);
+endfunction
+
+function [39:0] chdr256_strs_get_xfercnt_pkts(input [255:0] header);
+ chdr256_strs_get_xfercnt_pkts = chdr64_strs_get_xfercnt_pkts(header[127:64]);
+endfunction
+
+function [63:0] chdr256_strs_get_xfercnt_bytes(input [255:0] header);
+ chdr256_strs_get_xfercnt_bytes = chdr64_strs_get_xfercnt_bytes(header[191:128]);
+endfunction
+
+function [47:0] chdr256_strs_get_status_info(input [255:0] header);
+ chdr256_strs_get_status_info = chdr64_strs_get_status_info(header[255:192]);
+endfunction
+
+function [15:0] chdr256_strs_get_buff_info(input [255:0] header);
+ chdr256_strs_get_buff_info = chdr64_strs_get_buff_info(header[255:192]);
+endfunction
+
+// Stream Status Setter Functions
+//
+
+// 64-bit fields
+function [63:0] chdr64_strs_build_w0(
+ input [39:0] capacity_bytes,
+ input [3:0] status,
+ input [15:0] src_epid
+);
+ chdr64_strs_build_w0 = {capacity_bytes, 4'h0, status, src_epid};
+endfunction
+
+function [63:0] chdr64_strs_build_w1(
+ input [39:0] xfercnt_pkts,
+ input [23:0] capacity_pkts
+);
+ chdr64_strs_build_w1 = {xfercnt_pkts, capacity_pkts};
+endfunction
+
+function [63:0] chdr64_strs_build_w2(
+ input [63:0] xfercnt_bytes
+);
+ chdr64_strs_build_w2 = xfercnt_bytes;
+endfunction
+
+function [63:0] chdr64_strs_build_w3(
+ input [47:0] status_info,
+ input [15:0] buff_info
+);
+ chdr64_strs_build_w3 = {status_info, buff_info};
+endfunction
+
+// 128-bit fields
+function [127:0] chdr128_strs_build_w0(
+ input [39:0] xfercnt_pkts,
+ input [23:0] capacity_pkts,
+ input [39:0] capacity_bytes,
+ input [3:0] status,
+ input [15:0] src_epid
+);
+ chdr128_strs_build_w0 = {
+ chdr64_strs_build_w1(xfercnt_pkts, capacity_pkts),
+ chdr64_strs_build_w0(capacity_bytes, status, src_epid)};
+endfunction
+
+function [127:0] chdr128_strs_build_w1(
+ input [47:0] status_info,
+ input [15:0] buff_info,
+ input [63:0] xfercnt_bytes
+);
+ chdr128_strs_build_w1 = {
+ chdr64_strs_build_w3(status_info, buff_info),
+ chdr64_strs_build_w2(xfercnt_bytes)};
+endfunction
+
+// 256-bit fields
+function [255:0] chdr256_strs_build(
+ input [47:0] status_info,
+ input [15:0] buff_info,
+ input [63:0] xfercnt_bytes,
+ input [39:0] xfercnt_pkts,
+ input [23:0] capacity_pkts,
+ input [39:0] capacity_bytes,
+ input [3:0] status,
+ input [15:0] src_epid
+);
+ chdr256_strs_build = {
+ chdr64_strs_build_w3(status_info, buff_info),
+ chdr64_strs_build_w2(xfercnt_bytes),
+ chdr64_strs_build_w1(xfercnt_pkts, capacity_pkts),
+ chdr64_strs_build_w0(capacity_bytes, status, src_epid)};
+endfunction
+
+// =============================================================
+// Stream Command Bitfields
+// =============================================================
+
+// -----------------------
+// Line 0
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 63:24 num_pkts Downstream buffer capacity in bytes
+// 23:20 op_data Payload for command
+// 19:16 op_code Command operation code (enumeration)
+// 15:0 src_epid Endpoint ID of the source of this msg
+
+// -----------------------
+// Line 1
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 63:0 num_bytes Transfer count in packets
+
+localparam [3:0] CHDR_STRC_OPCODE_INIT = 4'd0;
+localparam [3:0] CHDR_STRC_OPCODE_PING = 4'd1;
+localparam [3:0] CHDR_STRC_OPCODE_RESYNC = 4'd2;
+
+// 64-bit fields
+function [39:0] chdr64_strc_get_num_pkts(input [63:0] header);
+ chdr64_strc_get_num_pkts = header[63:24];
+endfunction
+
+function [3:0] chdr64_strc_get_op_data(input [63:0] header);
+ chdr64_strc_get_op_data = header[23:20];
+endfunction
+
+function [3:0] chdr64_strc_get_op_code(input [63:0] header);
+ chdr64_strc_get_op_code = header[19:16];
+endfunction
+
+function [15:0] chdr64_strc_get_src_epid(input [63:0] header);
+ chdr64_strc_get_src_epid = header[15:0];
+endfunction
+
+function [63:0] chdr64_strc_get_num_bytes(input [63:0] header);
+ chdr64_strc_get_num_bytes = header[63:0];
+endfunction
+
+// 128-bit fields
+function [39:0] chdr128_strc_get_num_pkts(input [127:0] header);
+ chdr128_strc_get_num_pkts = chdr64_strc_get_num_pkts(header[63:0]);
+endfunction
+
+function [3:0] chdr128_strc_get_op_data(input [127:0] header);
+ chdr128_strc_get_op_data = chdr64_strc_get_op_data(header[63:0]);
+endfunction
+
+function [3:0] chdr128_strc_get_op_code(input [127:0] header);
+ chdr128_strc_get_op_code = chdr64_strc_get_op_code(header[63:0]);
+endfunction
+
+function [15:0] chdr128_strc_get_src_epid(input [127:0] header);
+ chdr128_strc_get_src_epid = chdr64_strc_get_src_epid(header[63:0]);
+endfunction
+
+function [63:0] chdr128_strc_get_num_bytes(input [127:0] header);
+ chdr128_strc_get_num_bytes = chdr64_strc_get_num_bytes(header[127:64]);
+endfunction
+
+// Stream Command Setter Functions
+//
+
+// 64-bit fields
+
+function [63:0] chdr64_strc_build_w0(
+ input [39:0] num_pkts,
+ input [3:0] op_data,
+ input [3:0] op_code,
+ input [15:0] src_epid
+);
+ chdr64_strc_build_w0 = {num_pkts, op_data, op_code, src_epid};
+endfunction
+
+function [63:0] chdr64_strc_build_w1(
+ input [63:0] num_bytes
+);
+ chdr64_strc_build_w1 = num_bytes;
+endfunction
+
+// 128-bit fields
+function [127:0] chdr128_strc_build(
+ input [63:0] num_bytes,
+ input [39:0] num_pkts,
+ input [3:0] op_data,
+ input [3:0] op_code,
+ input [15:0] src_epid
+);
+ chdr128_strc_build = {
+ chdr64_strc_build_w1(num_bytes),
+ chdr64_strc_build_w0(num_pkts, op_data, op_code, src_epid)};
+endfunction
+
+// =============================================================
+// Management Packet Bitfields
+// =============================================================
+
+// -----------------------
+// HDR
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 63:48 proto_ver Protocol Version
+// 47:45 chdr_w Bitwidth of the CHDR interface
+// 44:26 <Reserved>
+// 25:16 num_hops Number of hops that this message will take (TTL)
+// 15:0 src_epid Endpoint ID of the source of this msg
+
+// -----------------------
+// OP
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 63:16 op_payload Operation Payload
+// 15:8 op_code Operation code
+// 7:0 ops_pending Number of operations pending in this hop
+
+localparam [2:0] CHDR_MGMT_WIDTH_64 = 3'd0;
+localparam [2:0] CHDR_MGMT_WIDTH_128 = 3'd1;
+localparam [2:0] CHDR_MGMT_WIDTH_256 = 3'd2;
+localparam [2:0] CHDR_MGMT_WIDTH_512 = 3'd3;
+
+function [2:0] chdr_w_to_enum(input integer bits);
+ if (bits == 512)
+ chdr_w_to_enum = CHDR_MGMT_WIDTH_512;
+ else if (bits == 256)
+ chdr_w_to_enum = CHDR_MGMT_WIDTH_256;
+ else if (bits == 128)
+ chdr_w_to_enum = CHDR_MGMT_WIDTH_128;
+ else
+ chdr_w_to_enum = CHDR_MGMT_WIDTH_64;
+endfunction
+
+localparam [7:0] CHDR_MGMT_OP_NOP = 8'd0;
+localparam [7:0] CHDR_MGMT_OP_ADVERTISE = 8'd1;
+localparam [7:0] CHDR_MGMT_OP_SEL_DEST = 8'd2;
+localparam [7:0] CHDR_MGMT_OP_RETURN = 8'd3;
+localparam [7:0] CHDR_MGMT_OP_INFO_REQ = 8'd4;
+localparam [7:0] CHDR_MGMT_OP_INFO_RESP = 8'd5;
+localparam [7:0] CHDR_MGMT_OP_CFG_WR_REQ = 8'd6;
+localparam [7:0] CHDR_MGMT_OP_CFG_RD_REQ = 8'd7;
+localparam [7:0] CHDR_MGMT_OP_CFG_RD_RESP = 8'd8;
+
+function [15:0] chdr_mgmt_get_proto_ver(input [63:0] header);
+ chdr_mgmt_get_proto_ver = header[63:48];
+endfunction
+
+function [2:0] chdr_mgmt_get_chdr_w(input [63:0] header);
+ chdr_mgmt_get_chdr_w = header[47:45];
+endfunction
+
+function [9:0] chdr_mgmt_get_num_hops(input [63:0] header);
+ chdr_mgmt_get_num_hops = header[25:16];
+endfunction
+
+function [15:0] chdr_mgmt_get_src_epid(input [63:0] header);
+ chdr_mgmt_get_src_epid = header[15:0];
+endfunction
+
+function [47:0] chdr_mgmt_get_op_payload(input [63:0] header);
+ chdr_mgmt_get_op_payload = header[63:16];
+endfunction
+
+function [7:0] chdr_mgmt_get_op_code(input [63:0] header);
+ chdr_mgmt_get_op_code = header[15:8];
+endfunction
+
+function [7:0] chdr_mgmt_get_ops_pending(input [63:0] header);
+ chdr_mgmt_get_ops_pending = header[7:0];
+endfunction
+
+function [63:0] chdr_mgmt_build_hdr(
+ input [15:0] proto_ver,
+ input [2:0] chdr_w,
+ input [9:0] num_hops,
+ input [15:0] src_epid
+);
+ chdr_mgmt_build_hdr = {proto_ver, chdr_w, 19'h0, num_hops, src_epid};
+endfunction
+
+function [63:0] chdr_mgmt_build_op(
+ input [47:0] op_payload,
+ input [7:0] op_code,
+ input [7:0] ops_pending
+);
+ chdr_mgmt_build_op = {op_payload, op_code, ops_pending};
+endfunction
+
+// Definition for the TID field for the output of chdr_mgmt_pkt_handler
+localparam [1:0] CHDR_MGMT_ROUTE_EPID = 2'd0; // Route based on EPID
+localparam [1:0] CHDR_MGMT_ROUTE_TDEST = 2'd1; // Route based on tdest field
+localparam [1:0] CHDR_MGMT_RETURN_TO_SRC = 2'd2; // Return packet to sender
+
+// -----------------------
+// OP specific fields
+// -----------------------
+
+localparam [3:0] NODE_TYPE_INVALID = 4'd0;
+localparam [3:0] NODE_TYPE_XBAR = 4'd1;
+localparam [3:0] NODE_TYPE_STREAM_EP = 4'd2;
+localparam [3:0] NODE_TYPE_TRANSPORT = 4'd3;
+
+function [47:0] chdr_mgmt_build_node_info(
+ input [17:0] ext_info,
+ input [9:0] node_inst,
+ input [3:0] node_type,
+ input [15:0] device_id
+);
+ chdr_mgmt_build_node_info = {ext_info, node_inst, node_type, device_id};
+endfunction
+
+function [9:0] chdr_mgmt_sel_dest_get_tdest(input [47:0] payload);
+ chdr_mgmt_sel_dest_get_tdest = payload[9:0];
+endfunction
+
+function [15:0] chdr_mgmt_cfg_reg_get_addr(input [47:0] payload);
+ chdr_mgmt_cfg_reg_get_addr = payload[15:0];
+endfunction
+
+function [31:0] chdr_mgmt_cfg_reg_get_data(input [47:0] payload);
+ chdr_mgmt_cfg_reg_get_data = payload[47:16];
+endfunction
diff --git a/fpga/usrp3/lib/rfnoc/core/rfnoc_chdr_utils.vh b/fpga/usrp3/lib/rfnoc/core/rfnoc_chdr_utils.vh
new file mode 100644
index 000000000..047d58bc0
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/rfnoc_chdr_utils.vh
@@ -0,0 +1,200 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+// =============================================================
+// CHDR Bitfields
+// =============================================================
+//
+// The Condensed Hierarchical Datagram for RFNoC (CHDR) is
+// a protocol that defines the fundamental unit of data transfer
+// in an RFNoC network.
+//
+// -----------------------
+// Header
+// -----------------------
+// Bits Name Meaning
+// ---- ---- -------
+// 63:58 vc Virtual Channel
+// 57 eob End of Burst Delimiter
+// 56 eov End of Vector Delimiter
+// 55:53 pkt_type Packet Type (enumeration)
+// 52:48 num_mdata Number of lines of metadata
+// 47:32 seq_num Sequence number for the packet
+// 31:16 length Length of the datagram in bytes
+// 15:0 dst_epid Destination Endpoint ID
+//
+// Field: Packet Type
+// -----------------------
+// 3'd0 Management
+// 3'd1 Stream Status
+// 3'd2 Stream Command
+// 3'd3 <Reserved>
+// 3'd4 Control Transaction
+// 3'd5 <Reserved>
+// 3'd6 Data (without timestamp)
+// 3'd7 Data (with timestamp)
+//
+
+// Special CHDR Values
+//
+
+// Packet Type
+localparam [2:0] CHDR_PKT_TYPE_MGMT = 3'd0;
+localparam [2:0] CHDR_PKT_TYPE_STRS = 3'd1;
+localparam [2:0] CHDR_PKT_TYPE_STRC = 3'd2;
+//localparam [2:0] RESERVED = 3'd3;
+localparam [2:0] CHDR_PKT_TYPE_CTRL = 3'd4;
+//localparam [2:0] RESERVED = 3'd5;
+localparam [2:0] CHDR_PKT_TYPE_DATA = 3'd6;
+localparam [2:0] CHDR_PKT_TYPE_DATA_TS = 3'd7;
+
+// Metadata
+localparam [4:0] CHDR_NO_MDATA = 5'd0;
+
+// EPID
+localparam [15:0] NULL_EPID = 16'd0;
+
+// CHDR Getter Functions
+//
+function [5:0] chdr_get_vc(input [63:0] header);
+ chdr_get_vc = header[63:58];
+endfunction
+
+function [0:0] chdr_get_eob(input [63:0] header);
+ chdr_get_eob = header[57];
+endfunction
+
+function [0:0] chdr_get_eov(input [63:0] header);
+ chdr_get_eov = header[56];
+endfunction
+
+function [2:0] chdr_get_pkt_type(input [63:0] header);
+ chdr_get_pkt_type = header[55:53];
+endfunction
+
+function [4:0] chdr_get_num_mdata(input [63:0] header);
+ chdr_get_num_mdata = header[52:48];
+endfunction
+
+function [15:0] chdr_get_seq_num(input [63:0] header);
+ chdr_get_seq_num = header[47:32];
+endfunction
+
+function [15:0] chdr_get_length(input [63:0] header);
+ chdr_get_length = header[31:16];
+endfunction
+
+function [15:0] chdr_get_dst_epid(input [63:0] header);
+ chdr_get_dst_epid = header[15:0];
+endfunction
+
+// CHDR Setter Functions
+//
+function [63:0] chdr_build_header(
+ input [5:0] vc,
+ input [0:0] eob,
+ input [0:0] eov,
+ input [2:0] pkt_type,
+ input [4:0] num_mdata,
+ input [15:0] seq_num,
+ input [15:0] length,
+ input [15:0] dst_epid
+);
+ chdr_build_header = {vc, eob, eov, pkt_type, num_mdata, seq_num, length, dst_epid};
+endfunction
+
+function [63:0] chdr_set_vc(
+ input [63:0] base_hdr,
+ input [5:0] vc
+);
+ chdr_set_vc = {vc, base_hdr[57:0]};
+endfunction
+
+function [63:0] chdr_set_eob(
+ input [63:0] base_hdr,
+ input [0:0] eob
+);
+ chdr_set_eob = {base_hdr[63:58], eob, base_hdr[56:0]};
+endfunction
+
+function [63:0] chdr_set_eov(
+ input [63:0] base_hdr,
+ input [0:0] eov
+);
+ chdr_set_eov = {base_hdr[63:57], eov, base_hdr[55:0]};
+endfunction
+
+function [63:0] chdr_set_delims(
+ input [63:0] base_hdr,
+ input [0:0] eob,
+ input [0:0] eov
+);
+ chdr_set_delims = {base_hdr[63:58], eob, eov, base_hdr[55:0]};
+endfunction
+
+function [63:0] chdr_set_pkt_type(
+ input [63:0] base_hdr,
+ input [2:0] pkt_type
+);
+ chdr_set_pkt_type = {base_hdr[63:56], pkt_type, base_hdr[52:0]};
+endfunction
+
+function [63:0] chdr_set_num_mdata(
+ input [63:0] base_hdr,
+ input [4:0] num_mdata
+);
+ chdr_set_num_mdata = {base_hdr[63:53], num_mdata, base_hdr[47:0]};
+endfunction
+
+function [63:0] chdr_set_seq_num(
+ input [63:0] base_hdr,
+ input [15:0] seq_num
+);
+ chdr_set_seq_num = {base_hdr[63:48], seq_num, base_hdr[31:0]};
+endfunction
+
+function [63:0] chdr_set_length(
+ input [63:0] base_hdr,
+ input [15:0] length
+);
+ chdr_set_length = {base_hdr[63:32], length, base_hdr[15:0]};
+endfunction
+
+function [63:0] chdr_set_dst_epid(
+ input [63:0] base_hdr,
+ input [15:0] dst_epid
+);
+ chdr_set_dst_epid = {base_hdr[63:16], dst_epid};
+endfunction
+
+// =============================================================
+// Data Packet Specific
+// =============================================================
+
+localparam [3:0] CONTEXT_FIELD_HDR = 4'd0;
+localparam [3:0] CONTEXT_FIELD_HDR_TS = 4'd1;
+localparam [3:0] CONTEXT_FIELD_TS = 4'd2;
+localparam [3:0] CONTEXT_FIELD_MDATA = 4'd3;
+
+function [0:0] chdr_get_has_time(input [63:0] header);
+ chdr_get_has_time = (chdr_get_pkt_type(header) == CHDR_PKT_TYPE_DATA_TS);
+endfunction
+
+// Calculate the payload length in bytes based on the CHDR_W and header
+function [15:0] chdr_calc_payload_length(input [31:0] chdr_w, input [63:0] header);
+ reg [15:0] payload_length, mdata_length, header_length;
+ begin
+ if (chdr_w == 64) begin
+ header_length = chdr_get_has_time(header) ? 2*(chdr_w/8) : (chdr_w/8);
+ end else begin
+ header_length = chdr_w/8;
+ end
+ mdata_length = chdr_get_num_mdata(header) * (chdr_w/8);
+ payload_length = chdr_get_length(header) - mdata_length - header_length;
+
+ chdr_calc_payload_length = payload_length;
+ end
+endfunction
diff --git a/fpga/usrp3/lib/rfnoc/core/rfnoc_core_kernel.v b/fpga/usrp3/lib/rfnoc/core/rfnoc_core_kernel.v
new file mode 100644
index 000000000..15a7940a4
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/core/rfnoc_core_kernel.v
@@ -0,0 +1,385 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_core_kernel
+// Description:
+// The main utility and software interface module for an
+// assembled rfnoc design
+//
+// Parameters:
+// - PROTOVER: RFNoC protocol version {8'd<major>, 8'd<minor>}
+// - DEVICE_TYPE: The device type to use in the Device Info register
+// - DEVICE_FAMILY: The device family (to pass to Xilinx primitives)
+// - SAFE_START_CLKS: Instantiate logic to ensure that all output
+// clocks are glitch-free and startup safely
+// - NUM_BLOCKS: Number of blocks instantiated in the design
+// - NUM_STREAM_ENDPOINTS: Number of stream EPs instantiated in the design
+// - NUM_ENDPOINTS_CTRL: Number of stream EPs connected to the ctrl crossbar
+// - NUM_TRANSPORTS: Number of transports instantiated in the design
+// - NUM_EDGES: Number of edges of static connection in the design
+// - CHDR_XBAR_PRESENT: 1 if the CHDR crossbar is present. If 0 then
+// transports are directly connected to SEPs
+// - EDGE_TBL_FILE: The memory init file for the static connection
+// adjacency list
+//
+// Signals:
+// - chdr_aclk : The input CHDR clock (may be unbuffered if SAFE_START_CLKS=1)
+// - chdr_aclk_locked : The PLL locked pin for the input CHDR clock (unused if SAFE_START_CLKS=0)
+// - ctrl_aclk : The input Control clock (may be unbuffered if SAFE_START_CLKS=1)
+// - ctrl_aclk_locked : The PLL locked pin for the input Control clock (unused if SAFE_START_CLKS=0)
+// - core_chdr_clk: Output stable CHDR clock for the rest of the design
+// - core_chdr_rst: Output sync CHDR reset for all infrastructure modules (not blocks)
+// - core_ctrl_clk: Output stable Control clock for the rest of the design
+// - core_ctrl_rst: Output sync Control reset for all infrastructure modules (not blocks)
+// - s_axis_ctrl_* : Slave AXIS-Ctrl for the primary (zero'th) control endpoint
+// - m_axis_ctrl_* : Master AXIS-Ctrl for the primary (zero'th) control endpoint
+// - device_id: The dynamic device_id to read through the Device Info register (domain: core_chdr_clk)
+// - rfnoc_core_config: The backend config port for all blocks in the design (domain: core_ctrl_clk)
+// - rfnoc_core_status: The backend status port for all blocks in the design (domain: core_ctrl_clk)
+
+module rfnoc_core_kernel #(
+ parameter [15:0] PROTOVER = {8'd1, 8'd0},
+ parameter [15:0] DEVICE_TYPE = 16'd0,
+ parameter DEVICE_FAMILY = "7SERIES",
+ parameter SAFE_START_CLKS = 0,
+ parameter [9:0] NUM_BLOCKS = 0,
+ parameter [9:0] NUM_STREAM_ENDPOINTS = 0,
+ parameter [9:0] NUM_ENDPOINTS_CTRL = 0,
+ parameter [9:0] NUM_TRANSPORTS = 0,
+ parameter [11:0] NUM_EDGES = 0,
+ parameter [0:0] CHDR_XBAR_PRESENT = 1,
+ parameter EDGE_TBL_FILE = ""
+)(
+ // Input clocks and resets
+ input wire chdr_aclk,
+ input wire chdr_aclk_locked,
+ input wire ctrl_aclk,
+ input wire ctrl_aclk_locked,
+ input wire core_arst,
+ // Output clocks and resets
+ output wire core_chdr_clk,
+ output wire core_chdr_rst,
+ output wire core_ctrl_clk,
+ output wire core_ctrl_rst,
+ // AXIS-Control Bus
+ input wire [31:0] s_axis_ctrl_tdata,
+ input wire s_axis_ctrl_tlast,
+ input wire s_axis_ctrl_tvalid,
+ output wire s_axis_ctrl_tready,
+ output wire [31:0] m_axis_ctrl_tdata,
+ output wire m_axis_ctrl_tlast,
+ output wire m_axis_ctrl_tvalid,
+ input wire m_axis_ctrl_tready,
+ // Global info (domain: core_chdr_clk)
+ input wire [15:0] device_id,
+ // Backend config/status for each block (domain: core_ctrl_clk)
+ output wire [(512*NUM_BLOCKS)-1:0] rfnoc_core_config,
+ input wire [(512*NUM_BLOCKS)-1:0] rfnoc_core_status
+);
+
+ `include "rfnoc_axis_ctrl_utils.vh"
+ `include "rfnoc_backend_iface.vh"
+
+ // -----------------------------------
+ // Clocking and Resets
+ // -----------------------------------
+
+ generate if (SAFE_START_CLKS == 1) begin
+ // Safe startup logic for the CHDR and Control clocks:
+ // chdr_aclk and ctrl_aclk can be unbuffered.
+ // Use a BUFGCE to disable the clock until the upstream
+ // PLLs have locked.
+
+ wire chdr_ce_clk, ctrl_ce_clk;
+ (* keep = "true" *) (* async_reg = "true" *) reg [7:0] chdr_clk_ce_shreg = 8'h0;
+ (* keep = "true" *) (* async_reg = "true" *) reg [7:0] ctrl_clk_ce_shreg = 8'h0;
+
+ // A glitch-free clock buffer with an enable
+ BUFGCE chdr_clk_buf_i (
+ .I (chdr_aclk),
+ .CE(chdr_clk_ce_shreg[7]),
+ .O (core_chdr_clk)
+ );
+ // A separate clock buffer for the CE signal
+ // We instantiate this manually to prevent the tools from instantiating
+ // the more scare BUFG here. There are a lot more BUFHs than BUFGs
+ BUFH chdr_ce_buf_i (
+ .I(chdr_aclk),
+ .O(chdr_ce_clk)
+ );
+ always @(posedge chdr_ce_clk) begin
+ chdr_clk_ce_shreg <= {chdr_clk_ce_shreg[6:0], chdr_aclk_locked};
+ end
+
+ // A glitch-free clock buffer with an enable
+ BUFGCE ctrl_clk_buf_i (
+ .I (ctrl_aclk),
+ .CE(ctrl_clk_ce_shreg[7]),
+ .O (core_ctrl_clk)
+ );
+ // A separate clock buffer for the CE signal
+ // We instantiate this manually to prevent the tools from instantiating
+ // the more scare BUFG here. There are a lot more BUFHs than BUFGs
+ BUFH ctrl_ce_buf_i (
+ .I(ctrl_aclk),
+ .O(ctrl_ce_clk)
+ );
+ always @(posedge ctrl_ce_clk) begin
+ ctrl_clk_ce_shreg <= {ctrl_clk_ce_shreg[6:0], ctrl_aclk_locked};
+ end
+ end else begin
+ // We assume that chdr_aclk and ctrl_aclk start safely and are glitch-free
+ assign core_chdr_clk = chdr_aclk;
+ assign core_ctrl_clk = ctrl_aclk;
+ end endgenerate
+
+ reset_sync rst_sync_chdr_i (
+ .clk(core_chdr_clk), .reset_in(core_arst), .reset_out(core_chdr_rst)
+ );
+ reset_sync rst_sync_ctrl_i (
+ .clk(core_ctrl_clk), .reset_in(core_arst), .reset_out(core_ctrl_rst)
+ );
+
+ // -----------------------------------
+ // AXIS-Ctrl Slave
+ // -----------------------------------
+
+ wire ctrlport_req_wr;
+ wire ctrlport_req_rd;
+ wire [19:0] ctrlport_req_addr;
+ wire [31:0] ctrlport_req_data;
+ reg ctrlport_resp_ack;
+ reg [31:0] ctrlport_resp_data;
+
+ // The port ID of this endpoint must be zero
+ localparam [9:0] RFNOC_CORE_PORT_ID = 10'd0;
+
+ ctrlport_endpoint #(
+ .THIS_PORTID(RFNOC_CORE_PORT_ID), .SYNC_CLKS(1),
+ .AXIS_CTRL_MST_EN(0), .AXIS_CTRL_SLV_EN(1),
+ .SLAVE_FIFO_SIZE(5)
+ ) ctrlport_ep_i (
+ .rfnoc_ctrl_clk (core_ctrl_clk ),
+ .rfnoc_ctrl_rst (core_ctrl_rst ),
+ .ctrlport_clk (core_ctrl_clk ),
+ .ctrlport_rst (core_ctrl_rst ),
+ .s_rfnoc_ctrl_tdata (s_axis_ctrl_tdata ),
+ .s_rfnoc_ctrl_tlast (s_axis_ctrl_tlast ),
+ .s_rfnoc_ctrl_tvalid (s_axis_ctrl_tvalid ),
+ .s_rfnoc_ctrl_tready (s_axis_ctrl_tready ),
+ .m_rfnoc_ctrl_tdata (m_axis_ctrl_tdata ),
+ .m_rfnoc_ctrl_tlast (m_axis_ctrl_tlast ),
+ .m_rfnoc_ctrl_tvalid (m_axis_ctrl_tvalid ),
+ .m_rfnoc_ctrl_tready (m_axis_ctrl_tready ),
+ .m_ctrlport_req_wr (ctrlport_req_wr ),
+ .m_ctrlport_req_rd (ctrlport_req_rd ),
+ .m_ctrlport_req_addr (ctrlport_req_addr ),
+ .m_ctrlport_req_data (ctrlport_req_data ),
+ .m_ctrlport_req_byte_en (/* not supported */),
+ .m_ctrlport_req_has_time (/* not supported */),
+ .m_ctrlport_req_time (/* not supported */),
+ .m_ctrlport_resp_ack (ctrlport_resp_ack ),
+ .m_ctrlport_resp_status (AXIS_CTRL_STS_OKAY ),
+ .m_ctrlport_resp_data (ctrlport_resp_data ),
+ .s_ctrlport_req_wr (1'b0 ),
+ .s_ctrlport_req_rd (1'b0 ),
+ .s_ctrlport_req_addr (20'd0 ),
+ .s_ctrlport_req_portid (10'd0 ),
+ .s_ctrlport_req_rem_epid (16'd0 ),
+ .s_ctrlport_req_rem_portid(10'd0 ),
+ .s_ctrlport_req_data (32'h0 ),
+ .s_ctrlport_req_byte_en (4'h0 ),
+ .s_ctrlport_req_has_time (1'b0 ),
+ .s_ctrlport_req_time (1'b0 ),
+ .s_ctrlport_resp_ack (/* unused */ ),
+ .s_ctrlport_resp_status (/* unused */ ),
+ .s_ctrlport_resp_data (/* unused */ )
+ );
+
+ // ------------------------------------------------
+ // Segment Address space into the three functions:
+ // - Block Specific (incl. global regs)
+ // - Connections
+ // ------------------------------------------------
+
+ reg [15:0] req_addr = 16'h0;
+ reg [31:0] req_data = 32'h0;
+ reg blk_req_wr = 1'b0;
+ reg blk_req_rd = 1'b0;
+ reg blk_resp_ack = 1'b0;
+ reg [31:0] blk_resp_data = 32'h0;
+ reg con_req_wr = 1'b0;
+ reg con_req_rd = 1'b0;
+ reg con_resp_ack = 1'b0;
+ reg [31:0] con_resp_data = 32'h0;
+
+ // Shortcuts
+ wire blk_addr_space = (ctrlport_req_addr[19:16] == 4'd0);
+ wire con_addr_space = (ctrlport_req_addr[19:16] == 4'd1);
+
+ // ControlPort MUX
+ always @(posedge core_ctrl_clk) begin
+ // Write strobe
+ blk_req_wr <= ctrlport_req_wr & blk_addr_space;
+ con_req_wr <= ctrlport_req_wr & con_addr_space;
+ // Read strobe
+ blk_req_rd <= ctrlport_req_rd & blk_addr_space;
+ con_req_rd <= ctrlport_req_rd & con_addr_space;
+ // Address and Data (shared)
+ req_addr <= ctrlport_req_addr[15:0];
+ req_data <= ctrlport_req_data;
+ // Response
+ ctrlport_resp_ack <= blk_resp_ack | con_resp_ack;
+ if (blk_resp_ack)
+ ctrlport_resp_data <= blk_resp_data;
+ else
+ ctrlport_resp_data <= con_resp_data;
+ end
+
+ // -----------------------------------
+ // Block Address Space
+ // -----------------------------------
+
+ // Arrange the backend block wires into a 2-d array where the
+ // outer index represents the slot number and the inner index represents
+ // a register index for that slot. We have 512 bits of read/write
+ // data which translates to 16 32-bit registers per slot. The first slot
+ // belongs to this endpoint, the next N slots map to the instantiated
+ // stream endpoints and the remaining slots map to block control and
+ // status endpoint. The slot number has a 1-to-1 mapping to the port
+ // number on the control crossbar.
+ localparam NUM_REGS_PER_SLOT = 512/32;
+ localparam NUM_SLOTS = 1 /*this*/ + NUM_STREAM_ENDPOINTS + NUM_BLOCKS;
+ localparam BLOCK_OFFSET = 1 /*this*/ + NUM_STREAM_ENDPOINTS;
+
+ reg [31:0] config_arr_2d [0:NUM_SLOTS-1][0:NUM_REGS_PER_SLOT-1];
+ wire [31:0] status_arr_2d [0:NUM_SLOTS-1][0:NUM_REGS_PER_SLOT-1];
+
+ genvar b, i;
+ generate
+ for (b = 0; b < NUM_BLOCKS; b=b+1) begin
+ for (i = 0; i < NUM_REGS_PER_SLOT; i=i+1) begin
+ assign rfnoc_core_config[(b*512)+(i*32) +: 32] = config_arr_2d[b+BLOCK_OFFSET][i];
+ assign status_arr_2d[b+BLOCK_OFFSET][i] = rfnoc_core_status[(b*512)+(i*32) +: 32];
+ end
+ end
+ endgenerate
+
+ integer m, n;
+ always @(posedge core_ctrl_clk) begin
+ if (core_ctrl_rst) begin
+ blk_resp_ack <= 1'b0;
+ for (m = 0; m < NUM_SLOTS; m = m + 1) begin
+ for (n = 0; n < NUM_REGS_PER_SLOT; n = n + 1) begin
+ config_arr_2d[m][n] <= BEC_DEFAULT_VAL[(n*32)+:32];
+ end
+ end
+ end else begin
+ // All transactions finish in 1 cycle
+ blk_resp_ack <= blk_req_wr | blk_req_rd;
+ // Handle register writes
+ if (blk_req_wr) begin
+ config_arr_2d[req_addr[$clog2(NUM_SLOTS)+5:6]][req_addr[5:2]] <= req_data;
+ end
+ // Handle register reads
+ if (blk_req_rd) begin
+ blk_resp_data <= status_arr_2d[req_addr[$clog2(NUM_SLOTS)+5:6]][req_addr[5:2]];
+ end
+ end
+ end
+
+ // Global Registers
+ localparam [3:0] REG_GLOBAL_PROTOVER = 4'd0; // Offset = 0x00
+ localparam [3:0] REG_GLOBAL_PORT_CNT = 4'd1; // Offset = 0x04
+ localparam [3:0] REG_GLOBAL_EDGE_CNT = 4'd2; // Offset = 0x08
+ localparam [3:0] REG_GLOBAL_DEVICE_INFO = 4'd3; // Offset = 0x0C
+ localparam [3:0] REG_GLOBAL_ENDPOINT_CTRL_CNT = 4'd4; // Offset = 0x10
+
+ // Clock-crossing for device_id.
+ // FIFO going from core_chdr_clk domain to core_ctrl_clk.
+ wire device_id_fifo_ovalid;
+ wire [15:0] device_id_fifo_odata;
+ axi_fifo_2clk # (
+ .WIDTH (16),
+ .SIZE (2)
+ ) device_id_fifo_i (
+ .reset (1'b0),
+ .i_aclk (core_chdr_clk),
+ .i_tdata (device_id),
+ .i_tvalid (1'b1),
+ .i_tready (),
+ .o_aclk (core_ctrl_clk),
+ .o_tdata (device_id_fifo_odata),
+ .o_tvalid (device_id_fifo_ovalid),
+ .o_tready (1'b1)
+ );
+ // Register the FIFO's output to always have valid data available.
+ reg [15:0] device_id_ctrl_clk = 16'h0;
+ always @(posedge core_ctrl_clk) begin
+ if (device_id_fifo_ovalid) begin
+ device_id_ctrl_clk <= device_id_fifo_odata;
+ end
+ end
+
+ // Signature and protocol version
+ assign status_arr_2d[RFNOC_CORE_PORT_ID][REG_GLOBAL_PROTOVER] = {16'h12C6, PROTOVER[15:0]};
+
+ // Global port count register
+ localparam [0:0] STATIC_ROUTER_PRESENT = (NUM_EDGES == 12'd0) ? 1'b0 : 1'b1;
+ assign status_arr_2d[RFNOC_CORE_PORT_ID][REG_GLOBAL_PORT_CNT] =
+ {STATIC_ROUTER_PRESENT, CHDR_XBAR_PRESENT,
+ NUM_TRANSPORTS[9:0], NUM_BLOCKS[9:0], NUM_STREAM_ENDPOINTS[9:0]};
+ // Global edge count register
+ assign status_arr_2d[RFNOC_CORE_PORT_ID][REG_GLOBAL_EDGE_CNT] = {20'd0, NUM_EDGES[11:0]};
+ // Device information
+ assign status_arr_2d[RFNOC_CORE_PORT_ID][REG_GLOBAL_DEVICE_INFO] = {DEVICE_TYPE, device_id_ctrl_clk};
+ // Number of stream endpoint connected to the ctrl crossbar
+ assign status_arr_2d[RFNOC_CORE_PORT_ID][REG_GLOBAL_ENDPOINT_CTRL_CNT] = {22'b0, NUM_ENDPOINTS_CTRL[9:0]};
+
+ // -----------------------------------
+ // Connections Address Space
+ // -----------------------------------
+
+ // All inter-block static connections must be stored in a memory
+ // file which will be used to initialize a ROM that can be read
+ // by software for topology discovery. The format of the memory
+ // must be as follows:
+ // * Word Width: 32 bits
+ // * Maximum Depth: 16384 entries
+ // * Layout:
+ // - 0x000 : HEADER
+ // - 0x001 : EDGE_0_DEF
+ // - 0x002 : EDGE_1_DEF
+ // ...
+ // - 0xFFF : EDGE_4094_DEF
+ //
+ // where:
+ // * HEADER = {18'd0, NumEntries[13:0]}
+ // * EDGE_<N>_DEF = {SrcBlkIndex[9:0], SrcBlkPort[5:0], DstBlkIndex[9:0], DstBlkPort[5:0]}
+ //
+ // The BlkIndex is the port number of the block on the control crossbar amd the BlkPort is
+ // the index of the input or output port of the block.
+
+ generate if (EDGE_TBL_FILE == "" || NUM_EDGES == 0) begin
+ // If no file is specified or if the number of edges is zero
+ // then just return zero for all transactions
+ always @(posedge core_ctrl_clk) begin
+ con_resp_ack <= (con_req_wr | con_req_rd);
+ con_resp_data <= 32'h0;
+ end
+ end else begin
+ // Initialize ROM from file and read it during a reg transaction
+ reg [31:0] edge_tbl_rom[0:NUM_EDGES];
+ initial begin
+ $readmemh(EDGE_TBL_FILE, edge_tbl_rom, 0, NUM_EDGES);
+ end
+ always @(posedge core_ctrl_clk) begin
+ con_resp_ack <= (con_req_wr | con_req_rd);
+ con_resp_data <= edge_tbl_rom[req_addr[$clog2(NUM_EDGES+1)+1:2]];
+ end
+ end endgenerate
+
+endmodule // rfnoc_core_kernel
+