diff options
Diffstat (limited to 'fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_start.sv')
-rw-r--r-- | fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_start.sv | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_start.sv b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_start.sv new file mode 100644 index 000000000..7c6950ae7 --- /dev/null +++ b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_start.sv @@ -0,0 +1,333 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module : axi4s_remove_bytes_start +// Description: +// Specialized case of the Remove Bytes problem. This +// sub design is called when REM_START=0 and not EXACT +// +// 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 NOT hold off the input if it goes to the BONUS State. +// +// Parameters: +// REM_END - Last byte to remove (Truncate case is invalid) +// + +module axi4s_remove_bytes_start #( + REM_END=3 +)( + interface i, // AxiStreamIf or AxiStreamPacketIf + interface o // AxiStreamIf or AxiStreamPacketIf +); + + // tUSER - always {error,numbytes} + // +1 ,0 to numbytes-1 (0 is a full word) + localparam UWIDTH = $clog2(i.BYTES_PER_WORD)+1; + localparam ERROR = UWIDTH-1; // MSB is the error bit. + + // END is inclusive so +1 + localparam BYTES_REMOVED = REM_END+1; + + // how many bytes into the word for start and end point + localparam END_BYTE = REM_END % i.BYTES_PER_WORD; + localparam END_WORD = REM_END / i.BYTES_PER_WORD; + + // SHIFT_POINT + // BYTE_CARY | BYTE_SHIFT // + // EXAMPLES (32 bit) + // REM_END = 0 3 | 1 i[0] a[3:1] + // REM_END = 1 2 | 2 i[1:0] a[3:2] + // REM_END = 2 1 | 3 i[2:0] a[3] + // REM_END = 3 4 | 0 // EXACT - not valid here + // we use BYTE_CARY bytes from a + // we use BYTE_SHIFT bytes from i + localparam BYTE_SHIFT = BYTES_REMOVED % i.BYTES_PER_WORD; + localparam BYTE_CARRY = i.BYTES_PER_WORD - BYTE_SHIFT; + + initial begin + assert(END_BYTE != i.BYTES_PER_WORD-1 ) else + $fatal(1,"Required to be not EXACT"); + end + + `include "axi4s.vh" + + 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); + AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH), + .TKEEP(0),.MAX_PACKET_BYTES(i.MAX_PACKET_BYTES)) + s2(i.clk,i.rst); + + // move from AxiStreamIfc to AxiStreamPacketIf + always_comb begin + `AXI4S_ASSIGN(s0,i) + end + + //--------------------------------------- + // remove state machine + //--------------------------------------- + typedef enum {ST_REMOVING, ST_POST_REMOVE} remove_state_t; + remove_state_t remove_state = ST_REMOVING; + remove_state_t next_remove_state = ST_REMOVING; + + + logic s0_wr,s1_rd; + always_comb s0_wr = s0.tvalid && s0.tready; + always_comb s1_rd = s1.tvalid && s1.tready; + // FIFO to buffer up 1 previous value. MUST be 1! + always_ff @(posedge s1.clk) begin + if (s1.rst) begin + // upstream + s1.tdata <= 0; + s1.tuser <= 0; + s1.tlast <= 0; + s1.tvalid <= 0; // fullness + end else begin + + // store data on write + if (s0_wr) begin + // check that we never attempt + // a write that will overflow + // Only 2 valid write times + // (1) Write while empty(tvalid=0) + // (2) Write while reading + assert (!s1.tvalid || s1_rd) else + $fatal(1,"Overflow write"); + + s1.tdata <= s0.tdata; + s1.tuser <= s0.tuser; + s1.tlast <= s0.tlast; + s1.tvalid <= 1; //full + + // if we read without a write + // declare it empty! + end else if (s1_rd) begin + s1.tdata <= 'X; + s1.tuser <= 'X; + s1.tlast <= 1'bX; + s1.tvalid <= 0; //empty + end + + end + end + + // ready to write when not full, or being read + // passes s1_ready back cominatorially + always_comb s0.tready = !s1.tvalid || s1_rd; + + //***************** DATA SHIFTING ***********************/ + 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 + + // Note these should all be static shifts. We don't want to infer a barrel shifter. + + // 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 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 shift_data above + // NOTE: Entire words are thrown away at start, so no caching required + logic [s0.DATA_WIDTH-1:0] shift_data; + always_comb shift_data = {s0.tdata,bs_part(s1.tdata)}; + + //----------------------------------------------------------------------- + // 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 = s0.BYTES_PER_WORD; + else bytes = tuser[UWIDTH-2:0]; + return bytes; + end + endfunction + + logic [UWIDTH-1:0] s0_byte_count, s1_byte_count; + always_comb s0_byte_count = get_bytes(s0.tuser); + always_comb s1_byte_count = get_bytes(s1.tuser); + + logic s1_error_bit, s1_error_bit_old; + logic s0_error_bit, s0_error_bit_old; + + always_ff @(posedge s0.clk) begin + if (s0.rst) begin + s0_error_bit_old <= 0; + s1_error_bit_old <= 0; + end else begin + // clear at the end of the packet + if (s1.tlast && s1.tvalid && s1.tready) begin + s1_error_bit_old <= 0; + // but they set based on the input + end else if (s1.tvalid && s1.tready) begin + s1_error_bit_old <= s1_error_bit; + end + // clear at the end of the packet + if (s0.tlast && s0.tvalid && s0.tready) begin + s0_error_bit_old <= 0; + // but they set based on the input + end else if (s1.tvalid && s1.tready) begin + s0_error_bit_old <= s0_error_bit; + end + + end + end + + assign s1_error_bit = get_error(s1.tuser) && s1.tvalid || s1_error_bit_old; + assign s0_error_bit = get_error(s0.tuser) && s0.tvalid || s0_error_bit_old; + + logic s1_reached_end; + always_comb s1_reached_end = s1.reached_packet_byte(REM_END); + + // Remove Statemachine + always_comb begin : remove_next_state + // default assignment of next_state + next_remove_state = remove_state; + // In REM_START=0 case, always shift the same ammount. + s2.tdata = shift_data; + s2.tuser = s1.tuser; + s2.tlast = s1.tlast; + s2.tvalid = s1.tvalid; + s1.tready = s2.tready; + case (remove_state) + // ***************************************************** + // REMOVING - burn words until we have data to + // start sending again + // ***************************************************** + ST_REMOVING: begin + //defaults + s2.tuser = 0; + s2.tlast = 0; + s2.tvalid = 0; + s1.tready = 1; + + //reached removal point + if (s1.tvalid && s1_reached_end) begin + + // if we didn't get enough bytes to fill the word + // squelch the packet + if (s1.tlast && s1_byte_count <= BYTE_SHIFT) begin + s2.tlast = 1; + s2.tvalid = 0; + s1.tready = 1; + // ending in top half of word + end else if (s0.tlast && s0_byte_count <= BYTE_SHIFT) begin + s2.tlast = 1; + s2.tuser = uwrite(s1_error_bit || s0_error_bit,s0_byte_count + BYTE_CARRY); + s2.tvalid = s0.tvalid && s1.tvalid; + s2.tlast = 1; + s1.tready = s2.tready && s0.tvalid; + // ending in bottom half of word + end else if (s1.tlast) begin + s2.tlast = 1; + s2.tuser = uwrite(s1_error_bit,s1_byte_count - BYTE_SHIFT); + s2.tvalid = s1.tvalid; + s1.tready = s2.tready; + // end of removal start sending data! + end else begin + s2.tuser = uwrite(s1_error_bit || s0_error_bit,s0.BYTES_PER_WORD); + s2.tvalid = s0.tvalid && s1.tvalid; + s2.tlast = 0; + s1.tready = s2.tready && s0.tvalid; + if (s2.tready && s0.tvalid && s1.tvalid) begin + next_remove_state = ST_POST_REMOVE; + end + end + end + + end + + // ***************************************************** + // POST_REMOVAL waiting for end + // ***************************************************** + ST_POST_REMOVE: begin + + //defaults + s2.tuser = uwrite(s1_error_bit || s0_error_bit,s0.BYTES_PER_WORD); + s2.tvalid = s0.tvalid && s1.tvalid; + s2.tlast = 0; + s1.tready = s2.tready && s0.tvalid; + + // word is {i[BS-1:0],a[MSB:BS]} + // 32 bit example + // BS1 i[0] : a[3:1] -> + // BS2 i[1:0]: a[3:2] + // BS3 i[2:0]: a[3] + // ending in top half of word + if (s0.tlast && s0_byte_count <= BYTE_SHIFT) begin + s2.tuser = uwrite(s1_error_bit || s0_error_bit,s0_byte_count + BYTE_CARRY); + s2.tvalid = s0.tvalid && s1.tvalid; + s2.tlast = 1; + if (s2.tready && s0.tvalid && s1.tvalid) begin + next_remove_state = ST_REMOVING; + end + // ending in bottom half of word + end else if (s1.tlast) begin + s2.tuser = uwrite(s1_error_bit,s1_byte_count - BYTE_SHIFT); + s2.tvalid = s1.tvalid; + s2.tlast = 1; + if (s2.tready && s1.tvalid) begin + next_remove_state = ST_REMOVING; + end + // ending in second half of shift word + end + end + + // We should never get here + default: begin + next_remove_state = ST_REMOVING; + end + endcase + end + + always_ff @(posedge s1.clk) begin + if (s1.rst) begin + remove_state <= ST_REMOVING; + end else begin + remove_state <= next_remove_state; + end + end + + // move from AxiStreamPacketIf to AxiStreamIfc + always_comb begin + `AXI4S_ASSIGN(o,s2) + end + +endmodule : axi4s_remove_bytes_start |