+18









Martin Braun
Alex Williams
Andrej Rode
Ashish Chaudhari
Ben Hilburn
Ciro Nishiguchi
Daniel Jepson
Derek Kozel
EJ Kreinar
Humberto Jimenez
Ian Buckley
Jörg Hofrichter
Jon Kiser
Josh Blum
Jonathon Pendlum
Matt Ettus
Michael West
Moritz Fischer
Nick Foster
Nicolas Cuervo
Paul Butler
Paul David
Ryan Marlow
Sugandha Gupta
Sylvain Munaut
Trung Tran
Vidush Vishwanath
Wade Fife
6b67702ad7
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> Original-commit: bafa9d95453387814ef25e6b6256ba8db2df612f
570 lines
21 KiB
Verilog
570 lines
21 KiB
Verilog
//
|
|
// 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
|