// // Copyright 2020 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: LGPL-3.0-or-later // // Module: axi4s_add_bytes // // Description: // // Add zero filled bytes to a packet. // tUser = {error,trailing bytes}; // // LIMITATIONS // The block only adds bytes to the beginning of a word. // // Parameters: // ADD_START - Add bytes before this point (0 means start) // 0 is the only supported value right now // ADD_BYTES - Number of bytes to add // SYNC - When 1 we wait for the start word to be // valid before we start shifting. // When 0 we aggressively pad 0 early, but // it means the extra space may be added before // we setup the values we want to overwrite onto // that space. module axi4s_add_bytes #( int ADD_START = 0, int ADD_BYTES = 6, bit SYNC = 1 ) ( 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); //packet position in bytes of the last removed byte. localparam ADD_END = ADD_START + ADD_BYTES-1; //packet position in bytes of the 1st byte after removal. localparam ADD_RESTART = ADD_END+1; ////////////// Byte offsets in a word ///////////////// localparam START_BYTE = ADD_START % BYTES_PER_WORD; localparam END_BYTE = ADD_END % BYTES_PER_WORD; localparam RESTART_BYTE = ADD_RESTART % BYTES_PER_WORD; // An Important shift offset localparam BYTE_SHIFT = (BYTES_PER_WORD - RESTART_BYTE)%BYTES_PER_WORD; // Subcase Recognition // EXACT case - the removal expression is removing an entire word localparam EXACT = BYTE_SHIFT == 0; `include "axi4s.vh" // Parameter Checks initial begin assert (i.DATA_WIDTH == o.DATA_WIDTH) else $fatal("DATA_WIDTH mismatch"); assert (i.USER_WIDTH == o.USER_WIDTH) else $fatal("USER_WIDTH mismatch"); assert (i.USER_WIDTH >= UWIDTH) else $fatal("i.USER_WIDTH is to small"); assert (o.USER_WIDTH >= UWIDTH) else $fatal("o.USER_WIDTH is to small"); assert (ADD_START == 0) else $fatal("Only tested for ADD_START = 0"); 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); // move from AxiStreamIfc to AxiStreamPacketIf always_comb begin `AXI4S_ASSIGN(s0,i) end logic reached_start; logic reached_end; logic byte_overflow; logic [s0.DATA_WIDTH-1:0] zero_data; logic [s0.DATA_WIDTH-1:0] last_tdata; logic [s0.DATA_WIDTH-1:0] remaining_shift_data; logic [s0.DATA_WIDTH-1:0] last_shift_data; logic [s0.DATA_WIDTH-1:0] first_shifted_data; logic error_bit, error_bit_old; // Cache a couple of words from the bus always_ff @(posedge s0.clk) begin if (s0.rst) begin last_tdata <= 0; end else if (s0.tvalid && s0.tready) begin last_tdata <= s0.tdata; end end if (EXACT) begin always_comb begin // If END_BYTE=3 zero_data = 'b0; first_shifted_data = s0.tdata; remaining_shift_data = s0.tdata; last_shift_data = s0.tdata; end end else begin always_comb begin zero_data = 'b0; // If END_BYTE=2 [7:0] [23:0] // If END_BYTE=1 [15:0] [15:0] // If END_BYTE=0 [23:0] [7:0] first_shifted_data = {s0.tdata[BYTE_SHIFT*8-1:0],zero_data[END_BYTE*8+7:0]}; // If END_BYTE=0 [23:0] [31:24] remaining_shift_data = {s0.tdata[BYTE_SHIFT*8-1:0],last_tdata[s0.DATA_WIDTH-1:BYTE_SHIFT*8]}; // If END_BYTE=0 [23:0] [31:24] last_shift_data = {zero_data[BYTE_SHIFT*8-1:0],s0.tdata[s0.DATA_WIDTH-1:BYTE_SHIFT*8]}; end 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[UWIDTH-1]; 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 //--------------------------------------- // remove state machine //--------------------------------------- typedef enum {ST_PRE_ADD, ST_ADDING, ST_POST_ADD,ST_BONUS} add_state_t; add_state_t add_state = ST_PRE_ADD; add_state_t next_add_state = ST_PRE_ADD; always_ff @(posedge s0.clk) begin if (s0.rst) begin error_bit_old <= 0; end else begin // must hold until output completes if (s1.tlast && s1.tvalid && s1.tready) 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 // Find the landmark bytes always_comb error_bit = get_error(s0.tuser) || error_bit_old; always_comb begin reached_start = s1.reached_packet_byte(ADD_START); reached_end = s1.reached_packet_byte(ADD_START+ADD_BYTES); end if (EXACT) begin always_comb byte_overflow = 0; end else begin always_comb byte_overflow = get_bytes(s0.tuser) > BYTE_SHIFT; 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; // ADD state machine always_comb begin // default assignment of next_state next_add_state = add_state; s1.tuser = s0.tuser; s1.tlast = s0.tlast; s1.tvalid = s0.tvalid; s1.tdata = first_shifted_data; s0_tready = s1.tready; case (add_state) // ***************************************************** // PRE_ADD - wait till we reach ADD_START // ***************************************************** ST_PRE_ADD: begin if (!SYNC || s0.tvalid) begin // reached start and end in same clock and end of word if (reached_start && reached_end && s0.tlast) begin // if final word has more bytes than we can fit. if (byte_overflow) begin s1.tlast = 0; s1.tvalid = s0.tvalid; s0_tready = 0; // don't advance s1.tdata = first_shifted_data; s1.tuser = uwrite(error_bit,BYTES_PER_WORD); if (s0.tvalid && s1.tready) begin next_add_state = ST_BONUS; end // we can finish this clock because final word // didn't overflow into an additional word. end else begin s1.tlast = 1; s1.tvalid = s0.tvalid; s0_tready = s1.tready; s1.tdata = first_shifted_data; s1.tuser = uwrite(error_bit,get_bytes(s0.tuser) + RESTART_BYTE); // NO state advance end // reached start and end, and not the end of the packet end else if (reached_start && reached_end && !s0.tlast) begin s1.tlast = 0; s1.tvalid = s0.tvalid; s0_tready = s1.tready; s1.tdata = first_shifted_data; s1.tuser = uwrite(error_bit,BYTES_PER_WORD); if (s0.tvalid && s1.tready) begin next_add_state = ST_POST_ADD; end // reached start but not the end of byte insertion end else if (reached_start && !reached_end) begin s1.tlast = 0; s1.tvalid = 1; s0_tready = 0; // don't advance s1.tdata = zero_data; s1.tuser = uwrite(0,BYTES_PER_WORD); if (s1.tready) begin next_add_state = ST_ADDING; end end end end //ST_PRE_REMOVE // ***************************************************** // REMOVING - burn words until we have data to // start sending again // ***************************************************** ST_ADDING: begin //defaults s1.tlast = 0; s1.tvalid = 1; s0_tready = 0; // don't advance s1.tdata = zero_data; s1.tuser = uwrite(0,BYTES_PER_WORD); // reached the end of incoming packet and data insertion if (reached_end && s0.tlast) begin // if final word has more bytes than we can fit. if (byte_overflow) begin s1.tlast = 0; s1.tvalid = s0.tvalid; s0_tready = 0; // don't advance s1.tdata = first_shifted_data; s1.tuser = uwrite(error_bit,BYTES_PER_WORD); if (s0.tvalid && s1.tready) begin next_add_state = ST_BONUS; end end else begin // we can finish this clock because final word // didn't overflow into an additional word. s1.tlast = 1; s1.tvalid = s0.tvalid; s0_tready = s1.tready; s1.tdata = first_shifted_data; s1.tuser = uwrite(error_bit,get_bytes(s0.tuser) + RESTART_BYTE); if (s0.tvalid && s1.tready) begin next_add_state = ST_PRE_ADD; end end // reached the end of data insertion - not end of packet end else if (reached_end && !s0.tlast) begin s1.tlast = 0; s1.tvalid = s0.tvalid; s0_tready = s1.tready; s1.tdata = first_shifted_data; s1.tuser = uwrite(error_bit,BYTES_PER_WORD); if (s0.tvalid && s1.tready) begin next_add_state = ST_POST_ADD; end end end // ***************************************************** // POST_ADD waiting for end // ***************************************************** ST_POST_ADD: begin //defaults s1.tlast = 0; s1.tvalid = s0.tvalid; s0_tready = s1.tready; s1.tdata = remaining_shift_data; s1.tuser = uwrite(error_bit,BYTES_PER_WORD); // reached the end, but we have extra bytes to send if (s0.tlast && byte_overflow) begin s1.tlast = 0; s0_tready = 0; // don't let a advance if (s0.tvalid && s1.tready) begin next_add_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,get_bytes(s0.tuser) + RESTART_BYTE); if (s1.tready && s0.tvalid) begin next_add_state = ST_PRE_ADD; end end end // ***************************************************** // BONUS write out any overflow words // ***************************************************** ST_BONUS: begin //defaults s1.tdata = last_shift_data; s1.tuser = uwrite(error_bit,get_bytes(s0.tuser)+ RESTART_BYTE); s1.tlast = 1; s1.tvalid = s0.tvalid; s0_tready = s1.tready; if (s1.tready && s0.tvalid) begin next_add_state = ST_PRE_ADD; end end // We should never get here default: begin next_add_state = ST_PRE_ADD; end endcase end always_ff @(posedge s0.clk) begin if (s0.rst) begin add_state <= ST_PRE_ADD; end else begin add_state <= next_add_state; end end always_comb begin `AXI4S_ASSIGN(o,s1) end endmodule : axi4s_add_bytes