1//
2// Copyright 2018-2019 Ettus Research, A National Instruments Company
3//
4// SPDX-License-Identifier: LGPL-3.0-or-later
5//
6// Module: chdr_stream_input
7// Description:
8//   Implements the CHDR input port for a stream endpoint.
9//   The module accepts stream command and data packets and
10//   emits stream status packets. Flow control and error state
11//   is communicated using stream status packets. There are no
12//   external config interfaces because all configuration is done
13//   using stream command packets.
14//
15// Parameters:
16//   - CHDR_W: Width of the CHDR bus in bits
17//   - BUFF_SIZE: Buffer size in log2 of the number of words in the
18//                ingress buffer for the stream
19//   - FLUSH_TIMEOUT_W: log2 of the number of cycles to wait in order
20//                      to flush the input stream
21//   - SIGNAL_ERRS: If set to 1 then all stream errors will be notified
22//                  upstream, otherwise ALL errors are ignored
23//
24// Signals:
25//   - s_axis_chdr_* : Input CHDR stream (AXI-Stream)
26//   - m_axis_chdr_* : Output flow-controlled CHDR stream (AXI-Stream)
27//   - m_axis_strs_* : Output stream status (AXI-Stream)
28//   - data_err_stb  : If asserted, a data error notification is sent upstream
29//
30
31module chdr_stream_input #(
32  parameter CHDR_W          = 256,
33  parameter BUFF_SIZE       = 14,
34  parameter FLUSH_TIMEOUT_W = 14,
35  parameter MONITOR_EN      = 1,
36  parameter SIGNAL_ERRS     = 1
37)(
38  // Clock, reset and settings
39  input  wire              clk,
40  input  wire              rst,
41  // CHDR in (AXI-Stream)
42  input  wire [CHDR_W-1:0] s_axis_chdr_tdata,
43  input  wire              s_axis_chdr_tlast,
44  input  wire              s_axis_chdr_tvalid,
45  output wire              s_axis_chdr_tready,
46  // Flow controlled data out (AXI-Stream)
47  output wire [CHDR_W-1:0] m_axis_data_tdata,
48  output wire              m_axis_data_tlast,
49  output wire              m_axis_data_tvalid,
50  input  wire              m_axis_data_tready,
51  // Stream status out (AXI-Stream)
52  output reg  [CHDR_W-1:0] m_axis_strs_tdata,
53  output wire              m_axis_strs_tlast,
54  output wire              m_axis_strs_tvalid,
55  input  wire              m_axis_strs_tready,
56  // External stream error signal
57  input  wire              data_err_stb
58);
59
60  // The buffer size depends on the BUFF_SIZE parameter
61  localparam [40:0] BUFF_SIZE_BYTES = ((41'h1 << BUFF_SIZE) * (CHDR_W / 8)) - 41'h1;
62  // This is a flit-buffer. No packet limits
63  localparam [23:0] BUFF_SIZE_PKTS  = 24'hFFFFFF;
64
65  // ---------------------------------------------------
66  //  RFNoC Includes
67  // ---------------------------------------------------
68  `include "rfnoc_chdr_utils.vh"
69  `include "rfnoc_chdr_internal_utils.vh"
70
71  // ---------------------------------------------------
72  //  Ingress Buffer and Flow Control Logic
73  // ---------------------------------------------------
74  wire [CHDR_W-1:0] buff_tdata;
75  wire              buff_tlast, buff_tvalid;
76  reg               buff_tready;
77  wire [15:0]       buff_info;
78
79  chdr_ingress_fifo #(
80    .WIDTH(CHDR_W), .SIZE(BUFF_SIZE)
81  ) ingress_fifo_i (
82    .clk(clk), .reset(rst), .clear(1'b0),
83    .i_tdata(s_axis_chdr_tdata), .i_tlast(s_axis_chdr_tlast),
84    .i_tvalid(s_axis_chdr_tvalid), .i_tready(s_axis_chdr_tready),
85    .o_tdata(buff_tdata), .o_tlast(buff_tlast),
86    .o_tvalid(buff_tvalid), .o_tready(buff_tready)
87  );
88
89  generate if (MONITOR_EN) begin
90    wire [BUFF_SIZE:0] occ_lines;
91    axis_fifo_monitor #( .COUNT_W(BUFF_SIZE+1) ) fifo_mon_i (
92      .clk(clk), .reset(rst),
93      .i_tlast(s_axis_chdr_tlast), .i_tvalid(s_axis_chdr_tvalid), .i_tready(s_axis_chdr_tready),
94      .o_tlast(buff_tlast), .o_tvalid(buff_tvalid), .o_tready(buff_tready),
95      .i_sop(), .i_eop(), .o_sop(), .o_eop(),
96      .occupied(occ_lines), .occupied_pkts()
97    );
98    // buff_info represents a fraction of the fullness of the buffer
99    // fullness percentage = (buff_info / 32768) * 100
100    if (BUFF_SIZE + 1 >= 16)
101      assign buff_info = occ_lines[BUFF_SIZE:(BUFF_SIZE-15)];
102    else
103      assign buff_info = {occ_lines, {(15-BUFF_SIZE){1'b0}}};
104  end else begin
105    assign buff_info = 16'd0;
106  end endgenerate
107
108  // Flow Control State
109  // xfer_cnt: Total transfer count since fc_enabled = 1
110  // accum: Transfer count since last FC response
111  // fc_freq: The threshold for sending an FC response
112  reg [63:0] xfer_cnt_bytes = 64'd0;
113  reg [39:0] xfer_cnt_pkts  = 40'd0;
114  reg [63:0] accum_bytes    = 64'd0;
115  reg [39:0] accum_pkts     = 40'd0;
116  reg [63:0] fc_freq_bytes  = 64'd0;
117  reg [39:0] fc_freq_pkts   = 40'd0;
118
119  // State machine transition signals info
120  reg        fc_enabled = 1'b0;   // Is flow control enabled?
121  wire       fc_ping;             // A flow control response was requested
122  wire       fc_first_resp;       // Send the first flow control response
123  wire       fc_refresh;          // Refresh accumulated values
124  wire       fc_override;         // Override total xfer counts
125  reg        fc_override_del = 1'b0;
126  reg  [3:0] fc_due_shreg = 4'hF; // Is a response due? (shift register)
127
128  // Endpoint IDs of this endpoint and the stream source
129  reg [15:0] this_epid = 16'd0, return_epid = 16'd0;
130
131  // Cached values from a stream command
132  reg [63:0] strc_num_bytes;
133  reg [39:0] strc_num_pkts;
134  reg [3:0]  strc_op_data;  // Unused for now
135  reg [3:0]  strc_op_code;
136
137  // Total transfer count updater
138  always @(posedge clk) begin
139    if (rst || !fc_enabled) begin
140      // Reset
141      xfer_cnt_bytes <= 64'd0;
142      xfer_cnt_pkts  <= 40'd0;
143    end else if (fc_override) begin
144      // Override
145      xfer_cnt_bytes <= strc_num_bytes;
146      xfer_cnt_pkts  <= strc_num_pkts;
147    end else if (buff_tvalid && buff_tready) begin
148      // Count
149      xfer_cnt_bytes <= xfer_cnt_bytes + (CHDR_W/8);
150      if (buff_tlast)
151        xfer_cnt_pkts <= xfer_cnt_pkts + 40'd1;
152    end
153  end
154
155  // Accumulated transfer count updater
156  always @(posedge clk) begin
157    if (rst || !fc_enabled || fc_refresh) begin
158      // Reset
159      accum_bytes <= 64'd0;
160      accum_pkts  <= 40'd0;
161    end else if (buff_tvalid && buff_tready) begin
162      // Count
163      accum_bytes <= accum_bytes + (CHDR_W/8);
164      if (buff_tlast)
165        accum_pkts <= accum_pkts + 40'd1;
166    end
167  end
168
169  // Flow control trigger
170  // Why a shift-register here?
171  // 1. For edge detection
172  // 2. To allow the tools to re-time the wide comparators.
173  //    We don't care about the latency here because stream
174  //    status messages are asynchronous wrt the input.
175  always @(posedge clk) begin
176    if (rst || !fc_enabled) begin
177      // Reset to all ones so we don't send an extra stream status packet
178      // immediately after flow control is re-enabled. This also ensures we
179      // don't send an extra status packet when we get the first init command
180      // which has zero for num_bytes and num_pkts.
181      fc_due_shreg <= 4'hF;
182    end else begin
183      fc_due_shreg <= {
184        fc_due_shreg[2:0],
185        (accum_bytes >= fc_freq_bytes) || (accum_pkts >= fc_freq_pkts)
186      };
187    end
188  end
189  wire fc_resp_due = fc_due_shreg[2] && !fc_due_shreg[3];
190
191   // ---------------------------------------------------
192  //  Stream Command Handler
193  // ---------------------------------------------------
194  localparam [2:0] ST_IN_HDR    = 3'd0;   // The CHDR header of an input pkt
195  localparam [2:0] ST_IN_DATA   = 3'd1;   // The CHDR body (incl. mdata) of an input pkt
196  localparam [2:0] ST_STRC_W0   = 3'd2;   // The first word of a stream command
197  localparam [2:0] ST_STRC_W1   = 3'd3;   // The second word of a stream command
198  localparam [2:0] ST_STRC_EXEC = 3'd4;   // A stream command is executing
199  localparam [2:0] ST_FLUSH     = 3'd5;   // Input is flushing
200  localparam [2:0] ST_DROP      = 3'd6;   // Current packet is being dropped
201
202  reg [2:0]   state = ST_IN_HDR;          // State of the input state machine
203  reg         pkt_too_long = 1'b0;        // Error case. Packet is too long
204  reg         is_first_data_pkt = 1'b1;   // Is this the first data pkt after fc_enabled = 1?
205  reg         is_first_strc_pkt = 1'b1;   // Is this the strm cmd data pkt after fc_enabled = 1?
206  reg [15:0]  exp_data_seq_num = 16'd0;   // Expected sequence number for the next data pkt
207  reg [15:0]  exp_strc_seq_num = 16'd0;   // Expected sequence number for the next stream cmd pkt
208  reg [15:0]  strc_dst_epid = 16'd0;      // EPID in CHDR header of STRC packet
209
210  reg [FLUSH_TIMEOUT_W-1:0] flush_counter = {FLUSH_TIMEOUT_W{1'b0}};
211
212  // Shortcuts
213  wire is_data_pkt =
214    chdr_get_pkt_type(buff_tdata[63:0]) == CHDR_PKT_TYPE_DATA ||
215    chdr_get_pkt_type(buff_tdata[63:0]) == CHDR_PKT_TYPE_DATA_TS;
216  wire is_strc_pkt =
217    chdr_get_pkt_type(buff_tdata[63:0]) == CHDR_PKT_TYPE_STRC;
218
219  // Error Logic
220  wire data_seq_err_stb = (state == ST_IN_HDR) && is_data_pkt && !is_first_data_pkt &&
221    (chdr_get_seq_num(buff_tdata[63:0]) != exp_data_seq_num);
222  wire strc_seq_err_stb = (state == ST_IN_HDR) && is_strc_pkt && !is_first_strc_pkt &&
223    (chdr_get_seq_num(buff_tdata[63:0]) != exp_strc_seq_num);
224  wire seq_err_stb = (data_seq_err_stb || strc_seq_err_stb) && buff_tvalid && buff_tready;
225
226  wire route_err_stb = buff_tvalid && buff_tready && (state == ST_IN_HDR) &&
227    (chdr_get_dst_epid(buff_tdata[63:0]) != this_epid);
228
229  // Break critical paths to response FIFO
230  reg [47:0] stream_err_info = 48'h0;
231  reg        stream_err_stb = 1'b0;
232  reg [3:0]  stream_err_status = CHDR_STRS_STATUS_OKAY;
233
234  always @(posedge clk) begin
235    if (rst || (SIGNAL_ERRS == 0)) begin
236      stream_err_stb <= 1'b0;
237    end else begin
238      stream_err_stb <= seq_err_stb | route_err_stb | data_err_stb;
239      if (seq_err_stb) begin
240        stream_err_status <= CHDR_STRS_STATUS_SEQERR;
241        // The extended info has the packet type (to detect which stream
242        // had an error), the expected and actual sequence number.
243        stream_err_info <= {13'h0, chdr_get_pkt_type(buff_tdata[63:0]),
244            data_seq_err_stb ? exp_data_seq_num : exp_strc_seq_num,
245            chdr_get_seq_num(buff_tdata[63:0])};
246      end else if (route_err_stb) begin
247        stream_err_status <= CHDR_STRS_STATUS_RTERR;
248        // The extended info has the expected and actual destination EPID.
249        stream_err_info <= {16'd0, this_epid, chdr_get_dst_epid(buff_tdata[63:0])};
250      end else begin
251        stream_err_status <= CHDR_STRS_STATUS_DATAERR;
252        // The extended info has the expected and actual destination EPID.
253        stream_err_info <= {16'd0, this_epid, chdr_get_dst_epid(buff_tdata[63:0])};
254      end
255    end
256  end
257
258  // Input State Machine
259  // - Pass data packets forward
260  // - Consume stream cmd packets
261  always @(posedge clk) begin
262    if (rst) begin
263      state <= ST_IN_HDR;
264      pkt_too_long <= 1'b0;
265      fc_enabled <= 1'b0;
266    end else begin
267      case (state)
268        ST_IN_HDR: begin
269          if (buff_tvalid && buff_tready) begin
270            if (!buff_tlast) begin
271              // Classify packet and...
272              if (is_strc_pkt) begin
273                // ...consume if it is a stream command or...
274                state <= ST_STRC_W0;
275              end else if (is_data_pkt) begin
276                // ...pass to output if it is a data packet...
277                state <= ST_IN_DATA;
278              end else begin
279                // ... otherwise drop.
280                state <= ST_DROP;
281              end
282            end
283            // Update other state vars
284            pkt_too_long <= 1'b0;
285            if (is_strc_pkt) begin
286              is_first_strc_pkt <= 1'b0;
287              strc_dst_epid <= chdr_get_dst_epid(buff_tdata[63:0]);
288              exp_strc_seq_num <= chdr_get_seq_num(buff_tdata[63:0]) + 16'd1;
289            end else if (is_data_pkt) begin
290              is_first_data_pkt <= 1'b0;
291              exp_data_seq_num <= chdr_get_seq_num(buff_tdata[63:0]) + 16'd1;
292            end
293          end
294        end
295        ST_IN_DATA: begin
296          // Pass the data packet forward
297          if (buff_tvalid && buff_tready && buff_tlast)
298            state <= ST_IN_HDR;
299        end
300        ST_STRC_W0: begin
301          if (buff_tvalid && buff_tready) begin
302            // Consume the first word of a stream command packet
303            if (CHDR_W > 64) begin
304              strc_num_bytes <= chdr128_strc_get_num_bytes(buff_tdata[127:0]);
305              strc_num_pkts  <= chdr128_strc_get_num_pkts (buff_tdata[127:0]);
306              strc_op_data   <= chdr128_strc_get_op_data  (buff_tdata[127:0]);
307              strc_op_code   <= chdr128_strc_get_op_code  (buff_tdata[127:0]);
308              return_epid    <= chdr128_strs_get_src_epid (buff_tdata[127:0]);
309              state <= ST_STRC_EXEC;
310              pkt_too_long <= ~buff_tlast;
311            end else begin
312              strc_num_pkts <= chdr64_strc_get_num_pkts(buff_tdata[63:0]);
313              strc_op_data  <= chdr64_strc_get_op_data (buff_tdata[63:0]);
314              strc_op_code  <= chdr64_strc_get_op_code (buff_tdata[63:0]);
315              return_epid   <= chdr64_strs_get_src_epid(buff_tdata[63:0]);
316              state <= ST_STRC_W1;
317            end
318          end
319        end
320        ST_STRC_W1: begin
321          if (buff_tvalid && buff_tready) begin
322            // Consume the second word of a stream command packet
323            strc_num_bytes <= chdr64_strc_get_num_bytes(buff_tdata[63:0]);
324            state <= ST_STRC_EXEC;
325            pkt_too_long <= ~buff_tlast;
326          end
327        end
328        ST_STRC_EXEC: begin
329          case (strc_op_code)
330            CHDR_STRC_OPCODE_INIT: begin
331              // Configure FC but disable it temporarily
332              fc_freq_bytes <= strc_num_bytes;
333              fc_freq_pkts <= strc_num_pkts;
334              this_epid <= strc_dst_epid;
335              fc_enabled <= 1'b0;
336              // Flush the input
337              state <= ST_FLUSH;
338              flush_counter <= {FLUSH_TIMEOUT_W{1'b1}};
339            end
340            CHDR_STRC_OPCODE_PING: begin
341              // Ping can complete in 1 cycle
342              state <= pkt_too_long ? ST_DROP : ST_IN_HDR;
343            end
344            CHDR_STRC_OPCODE_RESYNC: begin
345              // Resync can complete in 1 cycle
346              state <= pkt_too_long ? ST_DROP : ST_IN_HDR;
347            end
348            default: begin
349              state <= pkt_too_long ? ST_DROP : ST_IN_HDR;
350            end
351          endcase
352        end
353        ST_FLUSH: begin
354          // Drop until the next packet arrives
355          if (buff_tvalid && buff_tready) begin
356            flush_counter <= {FLUSH_TIMEOUT_W{1'b1}};
357          end else begin
358            flush_counter <= flush_counter - 'd1;
359            if (flush_counter == {FLUSH_TIMEOUT_W{1'b0}}) begin
360              // Done flushing. Re-arm flow control and reset packet
361              // sequence check info.
362              fc_enabled <= 1'b1;
363              is_first_data_pkt <= 1'b1;
364              is_first_strc_pkt <= 1'b1;
365              state <= ST_IN_HDR;
366            end
367          end
368        end
369        ST_DROP: begin
370          // Drop until the next packet arrives
371          if (buff_tvalid && buff_tready && buff_tlast)
372            state <= ST_IN_HDR;
373        end
374        default: begin
375          // We should never get here
376          state <= ST_IN_HDR;
377        end
378      endcase
379    end
380  end
381
382  always @(*) begin
383    case (state)
384      ST_IN_HDR:
385        buff_tready = m_axis_data_tready || !is_data_pkt;
386      ST_IN_DATA:
387        buff_tready = m_axis_data_tready;
388      ST_STRC_W0:
389        buff_tready = 1'b1;
390      ST_STRC_W1:
391        buff_tready = 1'b1;
392      ST_FLUSH:
393        buff_tready = 1'b1;
394      ST_DROP:
395        buff_tready = 1'b1;
396      default:
397        buff_tready = 1'b0;
398    endcase
399  end
400
401  // Logic to drive output port
402  assign m_axis_data_tdata  = buff_tdata;
403  assign m_axis_data_tlast  = buff_tlast;
404  assign m_axis_data_tvalid = buff_tvalid &&
405    ((state == ST_IN_HDR && is_data_pkt) || state == ST_IN_DATA);
406
407  // Logic to drive triggers
408  assign fc_ping = (state == ST_STRC_EXEC) && (strc_op_code == CHDR_STRC_OPCODE_PING);
409  assign fc_first_resp = (state == ST_FLUSH) && (flush_counter == {FLUSH_TIMEOUT_W{1'b0}});
410  assign fc_override = (state == ST_STRC_EXEC) && (strc_op_code == CHDR_STRC_OPCODE_RESYNC);
411  always @(posedge clk) fc_override_del <= fc_override;
412
413  wire [51:0] resp_o_tdata;
414  wire        resp_o_tvalid;
415  wire        resp_o_tready;
416  reg  [51:0] resp_i_tdata;
417  reg         resp_i_tvalid = 1'b0;
418
419  // Send a stream status packet for the following cases:
420  // - Immediately after initialization
421  // - If a response is explicitly requested (ping)
422  // - If a response is due i.e. we have exceeded the frequency
423  // - If FC is resynchronized via a stream cmd
424  // - If an error is detected in the stream
425  always @(posedge clk) begin
426    if (rst) begin
427      resp_i_tvalid <= 1'b0;
428      resp_i_tdata  <= 52'h0;
429    end else begin
430      resp_i_tvalid <= fc_first_resp || fc_ping || fc_resp_due || fc_override_del || stream_err_stb;
431      resp_i_tdata  <= stream_err_stb ? {stream_err_info, stream_err_status} : {48'h0, CHDR_STRS_STATUS_OKAY};
432    end
433  end
434
435  // ---------------------------------------------------
436  //  Stream Status Responder
437  // ---------------------------------------------------
438  localparam [2:0] ST_STRS_IDLE = 3'd0;   // Waiting for response to post
439  localparam [2:0] ST_STRS_HDR  = 3'd1;   // Sending response CHDR header
440  localparam [2:0] ST_STRS_W0   = 3'd2;   // Sending first response word
441  localparam [2:0] ST_STRS_W1   = 3'd3;   // Sending second response word
442  localparam [2:0] ST_STRS_W2   = 3'd4;   // Sending third response word
443  localparam [2:0] ST_STRS_W3   = 3'd5;   // Sending fourth response word
444  localparam [2:0] ST_STRS_DONE = 3'd6;   // Consuming response
445
446  reg [2:0]  resp_state = ST_STRS_IDLE;   // State of the responder
447  reg [15:0] resp_seq_num = 16'd0;        // Current sequence number of response
448
449  assign fc_refresh = (resp_state == ST_STRS_DONE);
450
451  assign resp_o_tready = (resp_state == ST_STRS_DONE || !fc_enabled);
452
453  // A FIFO that holds up to 32 posted responses and status information
454  // NOTE: This is a lossy FIFO. If the downstream response port is clogged
455  // then we will drop responses. That should never happen in a normal operating
456  // scenario.
457  axi_fifo #(.WIDTH(48 + 4), .SIZE(5)) resp_fifo_i (
458    .clk(clk), .reset(rst), .clear(1'b0),
459    .i_tdata(resp_i_tdata), .i_tvalid(resp_i_tvalid), .i_tready(/* Lossy FIFO */),
460    .o_tdata(resp_o_tdata), .o_tvalid(resp_o_tvalid), .o_tready(resp_o_tready),
461    .space(), .occupied()
462  );
463
464  // Responder State Machine
465  // - Wait for response to appear in FIFO
466  // - Output a full packet (different # of xfers depending on CHDR_W)
467  always @(posedge clk) begin
468    if (rst || !fc_enabled) begin
469      resp_state <= ST_STRS_IDLE;
470      resp_seq_num <= 16'd0;
471    end else begin
472      case (resp_state)
473        ST_STRS_IDLE: begin
474          if (resp_o_tvalid)
475            resp_state <= ST_STRS_HDR;
476        end
477        ST_STRS_HDR: begin
478          if (m_axis_strs_tready)
479            resp_state <= ST_STRS_W0;
480        end
481        ST_STRS_W0: begin
482          if (m_axis_strs_tready)
483            if (CHDR_W < 256)
484              resp_state <= ST_STRS_W1;
485            else
486              resp_state <= ST_STRS_DONE;
487        end
488        ST_STRS_W1: begin
489          if (m_axis_strs_tready)
490            if (CHDR_W < 128)
491              resp_state <= ST_STRS_W2;
492            else
493              resp_state <= ST_STRS_DONE;
494        end
495        ST_STRS_W2: begin
496          if (m_axis_strs_tready)
497            resp_state <= ST_STRS_W3;
498        end
499        ST_STRS_W3: begin
500          if (m_axis_strs_tready)
501            resp_state <= ST_STRS_DONE;
502        end
503        ST_STRS_DONE: begin
504          resp_state <= ST_STRS_IDLE;
505          resp_seq_num <= resp_seq_num + 16'd1;
506        end
507        default: begin
508          // We should never get here
509          resp_state <= ST_STRS_IDLE;
510        end
511      endcase
512    end
513  end
514
515  // Output data. Header and Payload
516  wire [63:0] strs_header = chdr_build_header(
517    /*VC*/ 6'd0, /*eob*/ 1'b0, /*eov*/ 1'b0, CHDR_PKT_TYPE_STRS, CHDR_NO_MDATA,
518    resp_seq_num, 16'd32+(CHDR_W/8), return_epid);
519  wire [255:0] strs_payload = chdr256_strs_build(
520    /*statusinfo*/ resp_o_tdata[51:4], buff_info,
521    xfer_cnt_bytes, xfer_cnt_pkts,
522    BUFF_SIZE_PKTS[23:0], BUFF_SIZE_BYTES[39:0],
523    resp_o_tdata[3:0], this_epid);
524
525  // m_axis_strs_* signal values depend on CHDR_W
526  generate
527    if (CHDR_W == 64) begin
528      // Response spans 5 transfers (header + 4 words)
529      assign m_axis_strs_tlast = (resp_state == ST_STRS_W3);
530      always @(*) begin
531        case (resp_state)
532          ST_STRS_W0:
533            m_axis_strs_tdata = strs_payload[63:0];
534          ST_STRS_W1:
535            m_axis_strs_tdata = strs_payload[127:64];
536          ST_STRS_W2:
537            m_axis_strs_tdata = strs_payload[191:128];
538          ST_STRS_W3:
539            m_axis_strs_tdata = strs_payload[255:192];
540          default:
541            m_axis_strs_tdata = strs_header;
542        endcase
543      end
544    end else if (CHDR_W == 128) begin
545      // Response spans 3 transfers (header + 2 words)
546      assign m_axis_strs_tlast = (resp_state == ST_STRS_W1);
547      always @(*) begin
548        case (resp_state)
549          ST_STRS_W0:
550            m_axis_strs_tdata = strs_payload[127:0];
551          ST_STRS_W1:
552            m_axis_strs_tdata = strs_payload[255:128];
553          default:
554            m_axis_strs_tdata = {64'h0, strs_header};
555        endcase
556      end
557    end else begin
558      // Response spans 2 transfers (header + word)
559      assign m_axis_strs_tlast = (resp_state == ST_STRS_W0);
560      always @(*) begin
561        case (resp_state)
562          ST_STRS_W0:
563            m_axis_strs_tdata[255:0] = strs_payload;
564          default:
565            m_axis_strs_tdata[255:0] = {192'h0, strs_header};
566        endcase
567        if (CHDR_W > 256) begin
568          m_axis_strs_tdata[CHDR_W-1:256] = 'h0;
569        end
570      end
571    end
572  endgenerate
573
574  assign m_axis_strs_tvalid = (resp_state != ST_STRS_IDLE) && (resp_state != ST_STRS_DONE);
575
576endmodule // chdr_stream_input
577