diff options
Diffstat (limited to 'fpga/usrp3/lib/xge_interface/xge64_to_axi64.v')
-rw-r--r-- | fpga/usrp3/lib/xge_interface/xge64_to_axi64.v | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/xge_interface/xge64_to_axi64.v b/fpga/usrp3/lib/xge_interface/xge64_to_axi64.v new file mode 100644 index 000000000..f4af538c5 --- /dev/null +++ b/fpga/usrp3/lib/xge_interface/xge64_to_axi64.v @@ -0,0 +1,311 @@ +// +// Copyright 2013 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +// Adds 6 bytes at the beginning of every packet +// This gives us good32/64bit alignment of IP/UDP headers. +// +// The 6 bytes added include an octet passed as a parameter allowing a label to +// be added as metatdata in the header padding. This is typically the ingress +// port to be tagged in the packet as metadata. +// +// bit[65] EOF +// bit[64] SOF +// bit[68:66] occ +// +// This design will break if downstream can not be guarenteed to be ready to accept data. +// XGE MAC expects to be able to stream whole packet with no handshaking. +// We force downstream packet gate to discard packet by signalling error with tlast and +// resynchronizing with upstream. +// + +module xge64_to_axi64 + #(parameter LABEL=0) + ( + input clk, + input reset, + input clear, + input [63:0] datain, + input [2:0] occ, + input sof, + input eof, + input err, + input valid, + output reg [63:0] axis_tdata, + output reg [3:0] axis_tuser, + output reg axis_tlast, + output reg axis_tvalid, // Signal data avilable to downstream + input axis_tready + ); + + localparam EMPTY = 0; + localparam IN_USE = 1; + localparam FLUSHING3 = 2; + localparam FLUSHING4 = 3; + localparam FLUSHING5 = 4; + localparam FLUSHING6 = 5; + localparam FLUSHING7 = 6; + localparam FLUSHING8 = 7; + localparam ERROR1 = 8; + + + localparam EOF1 = 3'b001; + localparam EOF2 = 3'b010; + localparam EOF3 = 3'b011; + localparam EOF4 = 3'b100; + localparam EOF5 = 3'b101; + localparam EOF6 = 3'b110; + localparam EOF7 = 3'b111; + localparam EOF8 = 3'b000; + + reg [3:0] state; + reg err_reg; + reg [47:0] holding_reg; + + always @(posedge clk) + if(reset | clear) begin + state <= EMPTY; + axis_tdata <= 0; + holding_reg <= 0; + axis_tvalid <= 0; + end else begin + // Defaults + axis_tvalid <= 0; + axis_tuser <= 0; + axis_tlast <= 0; + err_reg <= 0; + + case(state) + EMPTY: begin + if (valid & axis_tready & sof) begin + // Start of packet should always be received in this state. + // It should NEVER be possible to get a packet from the MAC with EOF also set in + // the first 64 bits so not designed for. + // Add pad. Store last 6 octets into holding, change state to show data in holding. + state <= IN_USE; + axis_tvalid <= 1; + end + else if (valid & ~axis_tready) + // Assert on this condition, add H/W to deal with overflow later. + $display("ERROR: xge64_to_axi64, valid & ~axis_tready"); + + holding_reg <= datain[16 +: 48]; + axis_tdata[48 +: 16] <= datain[15:0]; + axis_tdata[47:0] <= {40'h0, LABEL}; // Tag packet with label + end + + IN_USE: begin + if (valid & axis_tready & (eof | err)) begin + // End of packet should always be received in this state. + // If Error is asserted from MAC, immediate EOF is forced, + // and the error flag set in tuser. State machine will return to WAIT + // state and search for new SOF thereby discarding anything left of error packet. + // + // In the case of 3 through 8 valid octets in the final 64bits input, + // we must run another cycle afterwards since we have 6 more bytes still in holding. + err_reg <= err; + holding_reg <= datain[16 +: 48]; + axis_tdata[63:48] <= datain[15:0]; + axis_tdata[47:0] <= holding_reg; + axis_tvalid <= 1; + + case(occ[2:0]) + // 8 valid Octets in last word of packet, finish next cycle + 0: begin + state <= FLUSHING8; + end + // 7 valid Octets in last word of packet, finish next cycle + 7: begin + state <= FLUSHING7; + end + // 6 valid octets in last word of packet, finish next cycle + 6: begin + state <= FLUSHING6; + end + // 5 valid octets in last word of packet, finish next cycle + 5: begin + state <= FLUSHING5; + end + // 4 valid octets in last word of packet, finish next cycle + 4: begin + state <= FLUSHING4; + end + // 3 valid octets in last word of packet, finish next cycle + 3: begin + state <= FLUSHING3; + end + // 2 valid octets in last word of packet, finish this cycle + 2: begin + axis_tuser <= {err,EOF8}; + state <= EMPTY; + axis_tlast <= 1; + end + // 1 valid octets in last word of packet, finish this cycle + 1: begin + axis_tuser <= {err,EOF7}; + state <= EMPTY; + axis_tlast <= 1; + end + endcase // case (occ[2:0]) + end // if (valid & axis_tready & eof) + else if (valid & axis_tready) begin + // No EOF indication so in packet payload somewhere still. + state <= IN_USE; + holding_reg <= datain[16 +: 48]; + axis_tdata[63:48] <= datain[15:0]; + axis_tdata[47:0] <= holding_reg; + axis_tvalid <= 1; + end + else if (valid & ~axis_tready) begin + // Assert on this condition + $display("ERROR: xge64_to_axi64, valid & ~axis_tready"); + // Keep error state asserted ready for downstream to accept + state <= ERROR1; + axis_tlast <= 1; + axis_tvalid <= 1; + axis_tuser <= {1'b1, EOF8}; // Force error in this packet. + end else if (~valid) begin + // Assert on this condition, don't expect the MAC to ever throtle dataflow intra-packet. + $display("ERROR: xge64_to_axi64, ~valid "); + state <= ERROR1; + axis_tlast <= 1; + axis_tvalid <= 1; + axis_tuser <= {1'b1, EOF8}; // Force error in this packet. + end + end // case: IN_USE + + FLUSHING3: begin + if (axis_tready) begin + // EOF has been received last cycle. + // Ethernet interframe gap means we don't have to search for back-to-back EOF-SOF here. + // 1 valid Octets to finish + state <= EMPTY; + axis_tlast <= 1; + axis_tuser <= {err_reg, EOF1}; + axis_tdata[47:0] <= holding_reg; + axis_tvalid <= 1; + end else begin + state <= ERROR1; + axis_tlast <= 1; + axis_tvalid <= 1; + axis_tuser <= {1'b1, EOF8}; // Force error in this packet. + end // else: !if(axis_tready) + end + + FLUSHING4: begin + if (axis_tready) begin + // EOF has been received last cycle. + // Ethernet interframe gap means we don't have to search for back-to-back EOF-SOF here. + // 2 valid Octets to finish + state <= EMPTY; + axis_tlast <= 1; + axis_tuser <= {err_reg, EOF2}; + axis_tdata[47:0] <= holding_reg; + axis_tvalid <= 1; + end else begin + state <= ERROR1; + axis_tlast <= 1; + axis_tvalid <= 1; + axis_tuser <= {1'b1, EOF8}; // Force error in this packet. + end // else: !if(axis_tready) + end + + FLUSHING5: begin + if (axis_tready) begin + // EOF has been received last cycle. + // Ethernet interframe gap means we don't have to search for back-to-back EOF-SOF here. + // 3 valid Octets to finish + state <= EMPTY; + axis_tlast <= 1; + axis_tuser <= {err_reg, EOF3}; + axis_tdata[47:0] <= holding_reg; + axis_tvalid <= 1; + end else begin + state <= ERROR1; + axis_tlast <= 1; + axis_tvalid <= 1; + axis_tuser <= {1'b1, EOF8}; // Force error in this packet. + end // else: !if(axis_tready) + end + + FLUSHING6: begin + if (axis_tready) begin + // EOF has been received last cycle. + // Ethernet interframe gap means we don't have to search for back-to-back EOF-SOF here. + // 4 valid Octets to finish + state <= EMPTY; + axis_tlast <= 1; + axis_tuser <= {err_reg, EOF4}; + axis_tdata[47:0] <= holding_reg; + axis_tvalid <= 1; + end else begin + state <= ERROR1; + axis_tlast <= 1; + axis_tvalid <= 1; + axis_tuser <= {1'b1, EOF8}; // Force error in this packet. + end // else: !if(axis_tready) + end + + FLUSHING7: begin + if (axis_tready) begin + // EOF has been received last cycle. + // Ethernet interframe gap means we don't have to search for back-to-back EOF-SOF here. + // 5 valid Octets to finish + state <= EMPTY; + axis_tlast <= 1; + axis_tuser <= {err_reg, EOF5}; + axis_tdata[47:0] <= holding_reg; + axis_tvalid <= 1; + end else begin + state <= ERROR1; + axis_tlast <= 1; + axis_tvalid <= 1; + axis_tuser <= {1'b1, EOF8}; // Force error in this packet. + end // else: !if(axis_tready) + end + + FLUSHING8: begin + if (axis_tready) begin + // EOF has been received last cycle. + // Ethernet interframe gap means we don't have to search for back-to-back EOF-SOF here. + // 6 valid Octets to finish + state <= EMPTY; + axis_tlast <= 1; + axis_tuser <= {err_reg, EOF6}; + axis_tdata[47:0] <= holding_reg; + axis_tvalid <= 1; + end else begin + state <= ERROR1; + axis_tlast <= 1; + axis_tvalid <= 1; + axis_tuser <= {1'b1, EOF8}; // Force error in this packet. + end // else: !if(axis_tready) + end + + ERROR1: begin + // We were already actively receiving a packet from the upstream MAC and the downstream + // signaled not ready by de-asserting tready. Since we can't back pressure the MAC we have to + // abandon the current packet, discarding any data already sent down stream by sending an asserted error + // with a tlast when ever tready becomes asserted again. Meanwhile we start dropping arriving MAC + // data on the floor since there is nothing useful we can do with it currently. + if (axis_tready) + begin + // OK tready is asserted again so tlast is geting accepted this cycle along with an asserted error. + state <= EMPTY; + end else begin + // Keep error state asserted ready for downstream to accept + axis_tlast <= 1; + axis_tvalid <= 1; + axis_tuser <= {1'b1, EOF8}; // Force error in this packet. + end + end // case: ERROR1 + + + endcase // case(state) + end // else: !if(reset | clear) + +endmodule |