Files
b210-k7-fpga/lib/axi4s_sv/axi4s_remove_bytes.sv
T
Wade Fife 287659ddeb fpga: Fix first arg in calls to $fatal()
This fixes warnings regarding the first argument to $fatal(), which is
supposed to be a number indicating what diagnostics to display. 1
corresponds to "Prints simulation time and location".


Original-commit: 58763bdfdaa276155ec6b79a3e5420de17172c7a
2022-06-28 10:34:18 -05:00

802 lines
30 KiB
Systemverilog

//
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module : axi4s_remove_bytes
//
// Description:
// Remove bytes from a packet. 1 removal can happen per
// packet. The removal is made by delaying the output
// by a clock, and then combining the new and old word
// and providing a combination of shifted words.
// This implementation requires that the user field
// holds the number of valid bytes in the word, and the MSB of the user field
// indicates if the MAC had an error.
//
// The block will hold off the input if it goes to the BONUS State.
//
// This block is intended to remove data from the beginning or middle
// of a packet. You can truncate a packet by setting REM_END to -1.
//
// LIMITATIONS
// The block will set the error bit if you put in a packet between
// REM_END and REM_START length, and it is unable to cleanly signal
// and end to the packet. (there is no way to send a zero byte valid
// packet using tuser protocol.
// Packets must be terminated with tlast.
//
// Parameters:
// REM_START - First byte to remove (0 means start)
// REM_END - Last byte to remove (-1 means truncate from REM START)
//
module axi4s_remove_bytes #(
REM_START=0,
REM_END=8
)(
interface.slave i, // AxiStreamIf or AxiStreamPacketIf
interface.master o // AxiStreamIf or AxiStreamPacketIf
);
localparam BYTES_PER_WORD = i.DATA_WIDTH/8;
// tUSER - always {error,numbytes}
localparam UWIDTH = $clog2(BYTES_PER_WORD+1);
localparam ERROR = UWIDTH-1; // MSB is the error bit.
localparam TRUNCATE = REM_END < 0;
// END is inclusive so +1
localparam BYTES_REMOVED = TRUNCATE ? 1 :
REM_END-REM_START+1;
// how many bytes into the word for start and end point
localparam START_BYTE = REM_START% BYTES_PER_WORD;
localparam START_WORD = REM_START/ BYTES_PER_WORD;
localparam END_BYTE = TRUNCATE ? BYTES_PER_WORD-1 :
REM_END % BYTES_PER_WORD;
localparam END_WORD = TRUNCATE ? 65535 : // max word counter value
REM_END / BYTES_PER_WORD;
localparam FIRST_BYTE_AFTER = (END_BYTE+1) % BYTES_PER_WORD;
localparam BYTE_SHIFT = BYTES_REMOVED % BYTES_PER_WORD;
localparam BYTE_CARRY = BYTES_PER_WORD - BYTE_SHIFT;
// CASE differentiators
localparam SINGLE = BYTES_REMOVED <= BYTES_PER_WORD;
localparam START_AT_LSB = START_BYTE == 0;
localparam END_AT_MSB = END_BYTE == BYTES_PER_WORD-1;
localparam EXACT = START_AT_LSB && END_AT_MSB;
localparam MIDDLE = END_BYTE >= START_BYTE;
`include "axi4s.vh"
// Parameter Checks
initial begin
assert (i.DATA_WIDTH == o.DATA_WIDTH) else
$fatal(1, "DATA_WIDTH mismatch");
end
AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
.TKEEP(0),.MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
s0(i.clk,i.rst);
AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
.TKEEP(0),.MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
s1(i.clk,i.rst);
// implement specialized cases
if (REM_START == 0 && !EXACT) begin : start_not_exact
// START at zero but still shifted
axi4s_remove_bytes_start #(
.REM_END(REM_END)
) axi4s_remove_bytes_start_i (
.i(i), .o(o)
);
end else begin : general
// move from AxiStreamIfc to AxiStreamPacketIf
always_comb begin
`AXI4S_ASSIGN(s0,i)
end
typedef enum {MS_EXACT, MS_START_AT_LSB, MS_END_AT_MSB,
SINGLE_MIDDLE,MULTI_MIDDLE, MS_WRAP} case_t;
case_t MCASE;
logic reached_start;
logic reached_end;
logic reached_end_plus;
// memory for holding old values
logic [s0.DATA_WIDTH-1:0] last_tdata;
logic [s0.DATA_WIDTH-1:0] first_tdata;
logic [UWIDTH-1:0] first_tuser;
// various flavors of data shifting
logic [s0.DATA_WIDTH-1:0] trunc_data;
logic [s0.DATA_WIDTH-1:0] remaining_shift_data;
logic [s0.DATA_WIDTH-1:0] prefirst_shifted_data;
logic [s0.DATA_WIDTH-1:0] first_shifted_data;
logic [s0.DATA_WIDTH-1:0] one_word_data;
logic [s0.DATA_WIDTH-1:0] bonus_data;
logic [15:0] word_count; // Oversized to 65536 words
logic error_bit, error_bit_old;
logic [UWIDTH-1:0] in_byte_count;
//---------------------------------------
// remove state machine
//---------------------------------------
typedef enum {ST_PRE_REMOVE, ST_TRUNCATE, ST_REMOVING,
ST_POST_REMOVE, ST_BONUS} remove_state_t;
remove_state_t remove_state = ST_PRE_REMOVE;
remove_state_t next_remove_state = ST_PRE_REMOVE;
always_comb in_byte_count = get_bytes(s0.tuser);
// Cache a couple of words from the bus
always_ff @(posedge s0.clk) begin
if (s0.rst) begin
last_tdata = 0;
first_tdata = 0;
first_tuser = 0;
end else
if (s0.tvalid && s0.tready &&
(MCASE == MULTI_MIDDLE || MCASE==MS_START_AT_LSB))
last_tdata = s0.tdata;
if (s0.tvalid && s0.tready &&
(reached_start || next_remove_state==ST_POST_REMOVE ||
(remove_state!=ST_REMOVING && remove_state!= ST_TRUNCATE))) begin
first_tdata = s0.tdata;
first_tuser = s0.tuser;
end
end
//***************** DATA SHIFTING CASES ***********************/
//-----------------------------------------------------------------------
// user write function
// this module ASSUMES user includes error in the MSB and the rest is the
// number of bytes in the word
//-----------------------------------------------------------------------
function automatic logic [START_BYTE*8-1:0] start_part([s0.DATA_WIDTH-1:0] data);
begin
// workaround :: modelsim optimizer can fail if there is a possibility of a 0+:0
localparam MY_START_BYTE = START_BYTE ? START_BYTE : 1;
return data[0+:MY_START_BYTE*8];
end
endfunction
function automatic logic [s0.DATA_WIDTH-1-FIRST_BYTE_AFTER*8:0] end_part([s0.DATA_WIDTH-1:0] data);
begin
return data[s0.DATA_WIDTH-1:FIRST_BYTE_AFTER*8];
end
endfunction
function automatic logic [s0.DATA_WIDTH-1-BYTE_SHIFT*8:0] bs_part([s0.DATA_WIDTH-1:0] data);
begin
return data[s0.DATA_WIDTH-1:BYTE_SHIFT*8];
end
endfunction
// Examples
//
// ENDING CASE 1
// Incoming packet outgoing packet
// ///////////////////////////////////////////////
// D0 C0 B0 A0 <- word 0
// D1 XX XX XX <- R(6:4)) D0 C0 B0 A0
// D2 C2 B2 A2 C2 B2 A2 D1
// D0 C0 B0 A0 <- next packet D2
// D0 C0 B0 A0
//
// ENDING CASE2
// Incoming packet outgoing packet
// ///////////////////////////////////////////////
// D0 C0 B0 A0 <- word 0
// D1 XX XX XX <- R(6:4)) D0 C0 B0 A0
// C2 B2 A2 C2 B2 A2 D1
// D0 C0 B0 A0 <- next packet
// D0 C0 B0 A0
// Middle of Word case
// Incoming packet outgoing packet
// ///////////////////////////////////////////////
// D0 C0 B0 A0 <- word 0
// D1 XX XX A1 <- R(7:6) D0 C0 B0 A0
// D2 C2 B2 A2 B2 A2 D1 A1
// D3 C3 B3 A3 B3 A3 D2 C2
// D0 C0 B0 A0 <- next packet D3 C3
//
// Easy Truncation (can handle dynamically)
// Incoming packet outgoing packet
// ///////////////////////////////////////////////
// D0 C0 B0 A0 <- word 0
// D1 C1 B1 A1 D0 C0 B0 A0
// XX XX XX <- R(11:8)) D1 C1 B1 A1 <- TLAST HERE
// D0 C0 B0 A0 <- next packet
// D0 C0 B0 A0
// Truncation case requiring REM_END=-1
// because last word is to far away to see tlast.
// Incoming packet outgoing packet
// ///////////////////////////////////////////////
// D0 C0 B0 A0 <- word 0
// XX XX XX XX <- R(-1:4)) D0 C0 B0 A0 <- TLAST HERE
// XX XX XX XX
// XX XX XX XX
// D0 C0 B0 A0 <- next packet
// D0 C0 B0 A0
// Remove from Front
// Incoming packet outgoing packet
// ///////////////////////////////////////////////
// XX XX XX XX <- R(0:7)
// XX XX XX XX <-
// C2 B2 A2
// D0 C0 B0 A0 <- next packet C2 B2 A2
// D0 C0 B0 A0
//
// Remove 1 byte on back to back 1 word packets
// Incoming packet outgoing packet
// ///////////////////////////////////////////////
// D0 C0 XX A0 <- R(1:1)
// D0 C0 XX A0 <- R(1:1) D0 C0 A0
// D0 C0 XX A0 <- R(1:1) D0 C0 A0
// D0 C0 A0
//
//
// Note these should all be static shifts. We don't want to infer a barrel shifter.
if (EXACT) begin // Remove whole words
always_comb begin
MCASE = MS_EXACT;
first_shifted_data = s0.tdata;
remaining_shift_data = s0.tdata;
one_word_data = s0.tdata;
trunc_data = first_tdata;
bonus_data = 'bX;
end
end else if (START_AT_LSB) begin // Remove start of word shift case
// EXAMPLE XX XX XX
// for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0 with START BYTE = 0(A0) END_BYTE = 2(C0) BYTE_SHIFT=3
// 1st word would be C1 B1 A1/H0 G0 F0 E0 D0
// [23:0] C1 B1 A1 / [63:24] H0 G0 F0 E0 D0
// same as remaining_shift_data above
// EXAMPLE XX XX XX XX XX XX XX XX
// for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0
// XX XX XX
// H1 G1 F1 E1 D1 C1 B1 A1 with START BYTE = 0(A0) END_BYTE = 2(10)(C1) BYTE_SHIFT=3
// 1st word would be C2 B2 A2/H1 G1 F1 E1 D1
// [23:0] C2 B2 A2 / [63:24] H1 G1 F1 E1 D1
// same as remaining_shift_data above
// NOTE: Entire words are thrown away at start, so no caching required
always_comb begin
MCASE = MS_START_AT_LSB;
first_shifted_data = {s0.tdata,bs_part(last_tdata)};
if (BYTE_SHIFT==0)
remaining_shift_data = s0.tdata;
else
remaining_shift_data = {s0.tdata,bs_part(last_tdata)};
bonus_data = 'bX;
one_word_data = end_part(s0.tdata);
trunc_data = first_tdata;
bonus_data = 'bX;
bonus_data = bs_part(s0.tdata);
end
end else if (END_AT_MSB) begin // Remove end of word shift case
// EXAMPLE XX XX
// for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0 with START BYTE = 6(G0) END_BYTE = 7(H0) BYTE_SHIFT=2
// 1st word would be B1 A1/F0 E0 D0 C0 B0 A0
// [15:0] B1 A1 / [47:0] F0 E0 D0 C0 B0 A0
// EXAMPLE XX XX
// for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0
// XX XX XX XX XX XX XX XX
// H1 G1 F1 E1 D1 C1 B1 A1 with START BYTE = 6(G0) END_BYTE = 7(15)(H0) BYTE_SHIFT=2
// 1st word would be B2 A2/F0 E0 D0 C0 B0 A0
// NOTE: Uses 1st Data (from when we reach the first word
// [15:0] B2 A2 / [47:0] F0 E0 D0 C0 B0 A0
always_comb begin
MCASE = MS_END_AT_MSB;
first_shifted_data = {s0.tdata,start_part(first_tdata)};
if (BYTE_SHIFT==0)
remaining_shift_data = s0.tdata;
else
remaining_shift_data = {s0.tdata,bs_part(first_tdata)};
one_word_data = s0.tdata;
trunc_data = first_tdata;
bonus_data = 'bX;
bonus_data = bs_part(s0.tdata);
end
end else if(MIDDLE) begin // Remove middle of word shift case
// EXAMPLE XX XX XX XX XX XX
// for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0 with START BYTE = 1(B0) END_BYTE = 6(G0) BYTE_SHIFT=6
// 1st word would be F1 E1 D1 C1 B1 A1/H0/A0
// [47:0] F1 E1 D1 C1 B1 A1 [63:56] H0 [7:0] A0
// EXAMPLE XX XX XX XX XX XX XX
// for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0
// XX XX XX XX XX XX XX
// H1 G1 F1 E1 D1 C1 B1 A1 with START BYTE = 1(B0) END_BYTE = 6(14)(G0) BYTE_SHIFT=6
// 1st word would be F2 E2 D2 C2 B2 A2/H1/A0
// NOTE: Uses first Data from when we reach the first word.
// Also, must advance one clock beyond end for this case.
// [47:0] F2 E2 D2 C2 B2 A2 [63:56] H1 [7:0] A0
// remaining words F2 E2 D2 C2 B2 A2/H1 G1
// [47:0] F2 E2 D2 C2 B2 A2 [63:48] H1 G1
// same as remaining_shift_data above
always_comb begin
if (SINGLE) begin
MCASE = SINGLE_MIDDLE;
first_shifted_data = {s0.tdata,end_part(first_tdata),start_part(first_tdata)};
end else begin
MCASE = MULTI_MIDDLE;
prefirst_shifted_data = {end_part(s0.tdata),start_part(first_tdata)};
first_shifted_data = {s0.tdata,end_part(last_tdata),start_part(first_tdata)};
end
if (BYTE_SHIFT==0)
remaining_shift_data = s0.tdata;
else
remaining_shift_data = {s0.tdata,bs_part(first_tdata)};
one_word_data = {end_part(s0.tdata),start_part(s0.tdata)};
trunc_data = first_tdata;
bonus_data = 'bX;
bonus_data = bs_part(s0.tdata);
end
end else begin //wrapped case
// EXAMPLE XX XX
// for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0 with START BYTE = 6(G0) END_BYTE = 2(10)(C1) BYTE_SHIFT=5
// XX XX XX
// H1 G1 F1 E1 D1 C1 B1 A1
//
// 1st word would be E1 D1/F0 E0 D0 C0 B0 A0
// [39:24] E1 D1 / [47:0] F0 E0 D0 C0 B0 A0
// remaining words E2 D2 C2 B2 A2/H1 G1 F1
// [39:0] E2 D2 C2 B2 A2 / H1 G1 F1 [63:40]
// same as remaining_shift_data_above // EXAMPLE XX XX
// for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0
// XX XX XX XX XX XX XX XX
// H1 G1 F1 E1 D1 C1 B1 A1
// XX XX XX
// H2 G2 F2 E2 D2 C2 B2 A2 with START BYTE = 6(G0) END_BYTE = 2(10)(C1) BYTE_SHIFT=5
//
// 1st word would be E2 D2/F0 E0 D0 C0 B0 A0
// NOTE: Uses 1st Data (from when we reach the first word ;
// [39:24] E2 D2 / [47:0] F0 E0 D0 C0 B0 A0
always_comb begin
MCASE = MS_WRAP;
first_shifted_data = {end_part(s0.tdata),start_part(first_tdata)};
if (BYTE_SHIFT==0)
remaining_shift_data = s0.tdata;
else
remaining_shift_data = {s0.tdata,bs_part(first_tdata)};
one_word_data = s0.tdata;
trunc_data = first_tdata;
bonus_data = 'bX;
bonus_data = bs_part(s0.tdata);
end
end
typedef enum {PASS_THRU,BONUS,REM_SHIFT_DATA,FIRST_SHIFT_DATA,
PREFIRST_SHIFT_DATA,TRUNCATE_DATA,ONE_WORD} data_mux_sel_t;
data_mux_sel_t data_mux_sel = PASS_THRU;
always_comb begin : data_mux
s1.tdata = s0.tdata;
case (data_mux_sel)
PASS_THRU : s1.tdata = s0.tdata;
ONE_WORD : s1.tdata = one_word_data;
FIRST_SHIFT_DATA : s1.tdata = first_shifted_data;
PREFIRST_SHIFT_DATA : if (MCASE==MULTI_MIDDLE)
s1.tdata = prefirst_shifted_data;
else
s1.tdata = first_shifted_data;
REM_SHIFT_DATA : if (!TRUNCATE)
s1.tdata = remaining_shift_data;
TRUNCATE_DATA : if (TRUNCATE)
s1.tdata = trunc_data;
BONUS : if (!TRUNCATE && !EXACT)
s1.tdata = bonus_data;
default : s1.tdata = s0.tdata;
endcase
end
//-----------------------------------------------------------------------
// user write function
// this module ASSUMES user includes error in the MSB and the rest is the
// number of bytes in the word
//-----------------------------------------------------------------------
function automatic [UWIDTH-1:0] uwrite(error=0,[UWIDTH-2:0] bytes=0);
begin
return {error,bytes};
end
endfunction
//-----------------------------------------------------------------------
// get_error -extract error from tuser
//-----------------------------------------------------------------------
function automatic get_error([UWIDTH-1:0] tuser);
begin
return tuser[ERROR];
end
endfunction
//-----------------------------------------------------------------------
// get_bytes -extract num_bytes from tuser
//-----------------------------------------------------------------------
function automatic [UWIDTH-1:0] get_bytes([UWIDTH-1:0] tuser);
logic [UWIDTH-1:0] bytes;
begin
if (tuser[UWIDTH-2:0] == 0) bytes = BYTES_PER_WORD;
else bytes = tuser[UWIDTH-2:0];
return bytes;
end
endfunction
// Debug state used to determine which sub-case is taken in simulation
typedef enum {D_IDLE, D_REACHED_START, D_TRUNCATE, D_LAST, D_NOT_LAST,
D_LAST_WO_END, D_LAST_W_END, D_LAST_W_END_BONUS, D_LAST_W_END_PLUS,
D_REACHED_END_PLUS} debug_t;
debug_t debug = D_IDLE;
always_ff @(posedge s0.clk) begin
if (s0.rst) begin
error_bit_old <= 0;
end else begin
// must hold until bonus completes
if (s1.tlast && s1.tvalid && s1.tready && remove_state==ST_BONUS) begin
error_bit_old <= 0;
// or clear if not going to bonus
end else if (s0.tlast && s0.tvalid && s0.tready && next_remove_state!=ST_BONUS) begin
error_bit_old <= 0;
// but they set based on the input
end else if (s0.tvalid && s0.tready) begin
error_bit_old <= error_bit;
end
end
end
assign error_bit = get_error(s0.tuser) || error_bit_old;
// When truncating we want to hold the last valid word until
// the end so we can accumulate any errors that might of occured
if (TRUNCATE && START_BYTE==0) begin
always_comb reached_start = s0.reached_packet_byte(REM_START-1);
end else begin
always_comb reached_start = s0.reached_packet_byte(REM_START);
end
// the WRAP case leans forward one word since it bridges to
// the next word so it needs to reach end_plus early
always_comb begin : reached_end_comb
if (MCASE==MS_WRAP) begin
reached_end = s0.reached_packet_byte(REM_END);
reached_end_plus = s0.reached_packet_byte(REM_END);
end else begin
reached_end = s0.reached_packet_byte(REM_END);
reached_end_plus = s0.reached_packet_byte(REM_END+BYTES_PER_WORD);
end
end
// because s0.tready feeds back and generates a
// change event for the entire interface,
// it can trigger an infinite loop of assignment
// even when nothing is changing. This breaks
// the feedback loop.
logic s0_tready;
always_comb s0.tready = s0_tready;
// Remove State Machine
always_comb begin : remove_next_state
// default assignment of next_state
next_remove_state = remove_state;
debug = D_IDLE;
data_mux_sel = PASS_THRU;
s1.tuser = s0.tuser;
s1.tlast = s0.tlast;
s1.tvalid = s0.tvalid;
s0_tready = s1.tready;
case (remove_state)
// *****************************************************
// PRE_REMOVE - wait till we reach REM_START
// *****************************************************
ST_PRE_REMOVE: begin
//defaults
data_mux_sel = PASS_THRU;
s1.tuser = s0.tuser;
s1.tlast = s0.tlast;
s1.tvalid = s0.tvalid;
s0_tready = s1.tready;
if (reached_start) begin // based only on word count
debug = D_REACHED_START;
// Truncating word end
if (TRUNCATE && s0.tlast) begin
s1.tlast = 1;
data_mux_sel = ONE_WORD;
debug = D_TRUNCATE;
// get number of bytes based on if we had enough to surpass END_BYTE
if (START_BYTE == 0) // Exact case
s1.tuser = uwrite(error_bit,in_byte_count);
else if (in_byte_count < START_BYTE)
s1.tuser = uwrite(error_bit,in_byte_count);
else
s1.tuser = uwrite(error_bit,START_BYTE);
end else if (TRUNCATE && !s0.tlast) begin
s1.tlast = 1;
data_mux_sel = ONE_WORD;
debug = D_TRUNCATE;
if (s1.tready && s0.tvalid) begin
s1.tlast = 0;
s1.tvalid = 0;
next_remove_state = ST_TRUNCATE;
end
// packet ends
end else if (s0.tlast) begin
s1.tlast = 1;
data_mux_sel = ONE_WORD;
debug = D_LAST;
// get number of bytes based on if we had enough to surpass END_BYTE
if (in_byte_count < START_BYTE)
s1.tuser = uwrite(error_bit,in_byte_count);
else if (START_WORD != END_WORD)
s1.tuser = uwrite(error_bit,START_BYTE);
else if (in_byte_count < END_BYTE+1)
s1.tuser = uwrite(error_bit,START_BYTE);
else
s1.tuser = uwrite(error_bit,in_byte_count - BYTES_REMOVED);
// if we are on the first word of the removal and have no way to terminate the packet
// set error.
if ((START_WORD != END_WORD && START_BYTE == 0) || EXACT)
s1.tuser[ERROR] = 1;
// if removal starts at the start of the packet, squelch the packet.
if ( (START_WORD != END_WORD && REM_START == 0) ||
// also if we don't have enough data to publish 1 byte
((in_byte_count <= END_BYTE+1) && REM_START == 0)) begin
s1.tlast = 0;
s1.tvalid = 0;
end
end else begin // not the last word
debug = D_NOT_LAST;
s1.tvalid = 0;
if (s0.tvalid) begin
// we will always need to wait for some more data before
// forming the next word if this was not the start of the packet
next_remove_state = ST_REMOVING;
end
end
end
end //ST_PRE_REMOVE
// *****************************************************
// TRUNCATE - wait for end of packet to put out the
// last word (so we can see if error bit asserts)
// *****************************************************
ST_TRUNCATE: begin
if (TRUNCATE) begin // Simplify synthesis
// get number of bytes based on if we had enough to surpass END_BYTE
if (get_bytes(first_tuser) < START_BYTE)
s1.tuser = uwrite(error_bit,get_bytes(first_tuser));
else
s1.tuser = uwrite(error_bit,START_BYTE);
data_mux_sel = TRUNCATE_DATA;
s1.tlast = s0.tlast;
s1.tvalid = s0.tlast && s0.tvalid;
s0_tready = 1;
if (s1.tready && s0.tvalid && s0.tlast) begin
next_remove_state = ST_PRE_REMOVE;
end
end
end
// *****************************************************
// REMOVING - burn words until we have data to
// start sending again
// *****************************************************
ST_REMOVING: begin
//defaults
data_mux_sel = FIRST_SHIFT_DATA;
s1.tuser = 0;
s1.tlast = 0;
s1.tvalid = 0;
s0_tready = 1;
// if we don't reach the end of the removal
// it is an error case because we don't
// have any valid data to send with the tlast.
if (s0.tlast && !reached_end && !reached_end_plus) begin
debug = D_LAST_WO_END;
s1.tuser = uwrite(1,0);
s1.tlast = 1;
s1.tvalid = s0.tvalid;
s0_tready = s1.tready;
// started from zero we have pushed
// zero data so we can just squelch the packet
if (REM_START==0)
s1.tvalid = 0;
if (s1.tready && s0.tvalid) begin
next_remove_state = ST_PRE_REMOVE;
end
// end of packet and we have some data to send
// but we didn't buffer the extra word of end data yet
end else if (s0.tlast && reached_end && !reached_end_plus) begin
debug = D_LAST_W_END;
if (MCASE==MULTI_MIDDLE)
data_mux_sel = PREFIRST_SHIFT_DATA;
else if (MCASE==MS_START_AT_LSB)
data_mux_sel = ONE_WORD;
s1.tlast = 1;
// if we are exact and started from zero we have pushed
// zero data so we can just squelch the packet
if (EXACT && REM_START==0)
s1.tvalid = 0;
else
s1.tvalid = s0.tvalid;
s0_tready = s1.tready;
if (MCASE==MULTI_MIDDLE)
if (in_byte_count <= FIRST_BYTE_AFTER)
s1.tuser = uwrite(1,0); // not enough data to avoid error
else
s1.tuser = uwrite(error_bit,in_byte_count + BYTE_CARRY);
else if (MCASE==MS_END_AT_MSB)
s1.tuser = uwrite(1,0); // not enough data to avoid error
else if (in_byte_count <= FIRST_BYTE_AFTER)
if (REM_START == 0)
s1.tvalid = 0;
else
s1.tuser = uwrite(1,0); // not enough data to avoid error
else
s1.tuser = uwrite(error_bit,in_byte_count - FIRST_BYTE_AFTER);
// if we are exact and have already published some data
// that data is unterminated and we have no way to
// set a packet end.
if (EXACT && REM_START!=0)
s1.tuser[ERROR] = 1;
if (s1.tready && s0.tvalid) begin
next_remove_state = ST_PRE_REMOVE;
end
// end of packet and we have some some data to send
// and we have more data then we can fit in the
// the current word
end else if (s0.tlast && reached_end_plus && in_byte_count > BYTE_SHIFT
&& BYTE_SHIFT != 0) begin
debug = D_LAST_W_END_BONUS;
s1.tlast = 0;
s1.tvalid = s0.tvalid;
s0_tready = 0; // don't let a advance
if (s0.tvalid && s1.tready) begin
next_remove_state = ST_BONUS;
end
// end of packet and we have some some data to send
// and we were ready to send data anyways
end else if(s0.tlast && reached_end_plus) begin
debug = D_LAST_W_END_PLUS;
s1.tlast = 1;
s1.tvalid = s0.tvalid;
s0_tready = s1.tready;
if (EXACT)
s1.tuser = uwrite(error_bit,in_byte_count);
else begin
s1.tuser = uwrite(error_bit,in_byte_count + BYTE_CARRY);
end
if (MCASE==MS_WRAP && in_byte_count <= FIRST_BYTE_AFTER)
s1.tuser = uwrite(1,0); // not enough data to avoid error
if (s1.tready && s0.tvalid) begin
next_remove_state = ST_PRE_REMOVE;
end
// we are ready to send the first byte after the shift
end else if(!s0.tlast && reached_end_plus) begin
debug = D_REACHED_END_PLUS;
s1.tlast = 0;
s1.tvalid = s0.tvalid;
s0_tready = s1.tready;
s1.tuser = uwrite(error_bit,BYTES_PER_WORD);
if (s1.tready && s0.tvalid) begin
next_remove_state = ST_POST_REMOVE;
end
end
end
// *****************************************************
// POST_REMOVAL waiting for end
// *****************************************************
ST_POST_REMOVE: begin
//defaults
data_mux_sel = REM_SHIFT_DATA;
s1.tuser = uwrite(error_bit,BYTES_PER_WORD);
s1.tlast = 0;
s1.tvalid = s0.tvalid;
s0_tready = s1.tready;
// reached the end, but we have extra bytes to send
if (s0.tlast && in_byte_count > BYTE_SHIFT
&& BYTE_SHIFT != 0) begin
s1.tlast = 0;
s0_tready = 0; // don't let a advance
if (s0.tvalid && s1.tready) begin
next_remove_state = ST_BONUS;
end
// reached the end, and don't need the bonus state
end else if (s0.tlast) begin
s1.tlast = 1;
s1.tuser = uwrite(error_bit,in_byte_count + BYTES_PER_WORD-BYTE_SHIFT);
if (s1.tready && s0.tvalid) begin
next_remove_state = ST_PRE_REMOVE;
end
end
end
// *****************************************************
// BONUS write out any overflow words
// *****************************************************
ST_BONUS: begin
//defaults
data_mux_sel = BONUS;
s1.tuser = uwrite(error_bit,in_byte_count-BYTE_SHIFT);
s1.tlast = 1;
s1.tvalid = s0.tvalid;
s0_tready = s1.tready;
if (s1.tready && s0.tvalid) begin
next_remove_state = ST_PRE_REMOVE;
end
end
// We should never get here
default: begin
next_remove_state = ST_PRE_REMOVE;
end
endcase
end
always_ff @(posedge s0.clk) begin
if (s0.rst) begin
remove_state <= ST_PRE_REMOVE;
end else begin
remove_state <= next_remove_state;
end
end
// move from AxiStreamIfc to AxiStreamPacketIf
always_comb begin
`AXI4S_ASSIGN(o,s1)
end
end
endmodule : axi4s_remove_bytes