aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_start.sv
diff options
context:
space:
mode:
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.sv333
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