aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/core
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2020-01-23 16:10:22 -0800
committerMartin Braun <martin.braun@ettus.com>2020-01-28 09:35:36 -0800
commitbafa9d95453387814ef25e6b6256ba8db2df612f (patch)
tree39ba24b5b67072d354775272e687796bb511848d /fpga/usrp3/lib/rfnoc/core
parent3075b981503002df3115d5f1d0b97d2619ba30f2 (diff)
downloaduhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.gz
uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.bz2
uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.zip
Merge FPGA repository back into UHD repository
The FPGA codebase was removed from the UHD repository in 2014 to reduce the size of the repository. However, over the last half-decade, the split between the repositories has proven more burdensome than it has been helpful. By merging the FPGA code back, it will be possible to create atomic commits that touch both FPGA and UHD codebases. Continuous integration testing is also simplified by merging the repositories, because it was previously difficult to automatically derive the correct UHD branch when testing a feature branch on the FPGA repository. This commit also updates the license files and paths therein. We are therefore merging the repositories again. Future development for FPGA code will happen in the same repository as the UHD host code and MPM code. == Original Codebase and Rebasing == The original FPGA repository will be hosted for the foreseeable future at its original local location: https://github.com/EttusResearch/fpga/ It can be used for bisecting, reference, and a more detailed history. The final commit from said repository to be merged here is 05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as v4.0.0.0-pre-uhd-merge. If you have changes in the FPGA repository that you want to rebase onto the UHD repository, simply run the following commands: - Create a directory to store patches (this should be an empty directory): mkdir ~/patches - Now make sure that your FPGA codebase is based on the same state as the code that was merged: cd src/fpga # Or wherever your FPGA code is stored git rebase v4.0.0.0-pre-uhd-merge Note: The rebase command may look slightly different depending on what exactly you're trying to rebase. - Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge: git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches Note: Make sure that only patches are stored in your output directory. It should otherwise be empty. Make sure that you picked the correct range of commits, and only commits you wanted to rebase were exported as patch files. - Go to the UHD repository and apply the patches: cd src/uhd # Or wherever your UHD repository is stored git am --directory fpga ~/patches/* rm -rf ~/patches # This is for cleanup == Contributors == The following people have contributed mainly to these files (this list is not complete): Co-authored-by: Alex Williams <alex.williams@ni.com> Co-authored-by: Andrej Rode <andrej.rode@ettus.com> Co-authored-by: Ashish Chaudhari <ashish@ettus.com> Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com> Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com> Co-authored-by: Daniel Jepson <daniel.jepson@ni.com> Co-authored-by: Derek Kozel <derek.kozel@ettus.com> Co-authored-by: EJ Kreinar <ej@he360.com> Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com> Co-authored-by: Ian Buckley <ian.buckley@gmail.com> Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com> Co-authored-by: Jon Kiser <jon.kiser@ni.com> Co-authored-by: Josh Blum <josh@joshknows.com> Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com> Co-authored-by: Martin Braun <martin.braun@ettus.com> Co-authored-by: Matt Ettus <matt@ettus.com> Co-authored-by: Michael West <michael.west@ettus.com> Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com> Co-authored-by: Nick Foster <nick@ettus.com> Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Paul David <paul.david@ettus.com> Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com> Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com> Co-authored-by: Sylvain Munaut <tnt@246tNt.com> Co-authored-by: Trung Tran <trung.tran@ettus.com> Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com> Co-authored-by: Wade Fife <wade.fife@ettus.com>
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
+