diff options
Diffstat (limited to 'fpga/usrp3/lib/xge/rtl/verilog/tx_dequeue.v')
-rw-r--r-- | fpga/usrp3/lib/xge/rtl/verilog/tx_dequeue.v | 938 |
1 files changed, 938 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/xge/rtl/verilog/tx_dequeue.v b/fpga/usrp3/lib/xge/rtl/verilog/tx_dequeue.v new file mode 100644 index 000000000..8444514ee --- /dev/null +++ b/fpga/usrp3/lib/xge/rtl/verilog/tx_dequeue.v @@ -0,0 +1,938 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// File name "tx_dequeue.v" //// +//// //// +//// This file is part of the "10GE MAC" project //// +//// http://www.opencores.org/cores/xge_mac/ //// +//// //// +//// Author(s): //// +//// - A. Tanguay (antanguay@opencores.org) //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2008 AUTHORS. All rights reserved. //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// + + +`include "defines.v" + +module tx_dequeue(/*AUTOARG*/ + // Outputs + txdfifo_ren, txhfifo_ren, txhfifo_wdata, txhfifo_wstatus, + txhfifo_wen, xgmii_txd, xgmii_txc, status_txdfifo_udflow_tog, + // Inputs + clk_xgmii_tx, reset_xgmii_tx_n, ctrl_tx_enable_ctx, + status_local_fault_ctx, status_remote_fault_ctx, txdfifo_rdata, + txdfifo_rstatus, txdfifo_rempty, txdfifo_ralmost_empty, + txhfifo_rdata, txhfifo_rstatus, txhfifo_rempty, + txhfifo_ralmost_empty, txhfifo_wfull, txhfifo_walmost_full + ); +`include "CRC32_D64.v" +`include "CRC32_D8.v" +`include "utils.v" + +input clk_xgmii_tx; +input reset_xgmii_tx_n; + +input ctrl_tx_enable_ctx; + +input status_local_fault_ctx; +input status_remote_fault_ctx; + +input [63:0] txdfifo_rdata; +input [7:0] txdfifo_rstatus; +input txdfifo_rempty; +input txdfifo_ralmost_empty; + +input [63:0] txhfifo_rdata; +input [7:0] txhfifo_rstatus; +input txhfifo_rempty; +input txhfifo_ralmost_empty; + +input txhfifo_wfull; +input txhfifo_walmost_full; + +output txdfifo_ren; + +output txhfifo_ren; + +output [63:0] txhfifo_wdata; +output [7:0] txhfifo_wstatus; +output txhfifo_wen; + +output [63:0] xgmii_txd; +output [7:0] xgmii_txc; + +output status_txdfifo_udflow_tog; + + + + +/*AUTOREG*/ +// Beginning of automatic regs (for this module's undeclared outputs) +reg status_txdfifo_udflow_tog; +reg txdfifo_ren; +reg txhfifo_ren; +reg [63:0] txhfifo_wdata; +reg txhfifo_wen; +reg [7:0] txhfifo_wstatus; +reg [7:0] xgmii_txc; +reg [63:0] xgmii_txd; +// End of automatics + +/*AUTOWIRE*/ + + +reg [63:0] xgxs_txd; +reg [7:0] xgxs_txc; + +reg [63:0] next_xgxs_txd; +reg [7:0] next_xgxs_txc; + +reg [2:0] curr_state_enc; +reg [2:0] next_state_enc; + +reg [0:0] curr_state_pad; +reg [0:0] next_state_pad; + +reg start_on_lane0; +reg next_start_on_lane0; + +reg [2:0] ifg_deficit; +reg [2:0] next_ifg_deficit; + +reg ifg_4b_add; +reg next_ifg_4b_add; + +reg ifg_8b_add; +reg next_ifg_8b_add; + +reg ifg_8b2_add; +reg next_ifg_8b2_add; + +reg [7:0] eop; +reg [7:0] next_eop; + +reg [63:32] xgxs_txd_barrel; +reg [7:4] xgxs_txc_barrel; + +reg [63:0] txhfifo_rdata_d1; + +reg [13:0] byte_cnt; + +reg [31:0] crc32_d64; +reg [31:0] crc32_d8; +reg [31:0] crc32_tx; + +reg [63:0] shift_crc_data; +reg [3:0] shift_crc_eop; +reg [3:0] shift_crc_cnt; + +reg [31:0] crc_data; + +reg frame_available; +reg next_frame_available; + +reg [63:0] next_txhfifo_wdata; +reg [7:0] next_txhfifo_wstatus; +reg next_txhfifo_wen; + +reg txdfifo_ren_d1; + +parameter [2:0] + SM_IDLE = 3'd0, + SM_PREAMBLE = 3'd1, + SM_TX = 3'd2, + SM_EOP = 3'd3, + SM_TERM = 3'd4, + SM_TERM_FAIL = 3'd5, + SM_IFG = 3'd6; + +parameter [0:0] + SM_PAD_EQ = 1'd0, + SM_PAD_PAD = 1'd1; + + +//--- +// RC layer + +always @(posedge clk_xgmii_tx or negedge reset_xgmii_tx_n) begin + + if (reset_xgmii_tx_n == 1'b0) begin + + xgmii_txd <= {8{`IDLE}}; + xgmii_txc <= 8'hff; + + end + else begin + + + //--- + // RC Layer, insert local or remote fault messages based on status + // of fault state-machine + + if (status_local_fault_ctx) begin + + // If local fault detected, send remote fault message to + // link partner + + xgmii_txd <= {`REMOTE_FAULT, 8'h0, 8'h0, `SEQUENCE, + `REMOTE_FAULT, 8'h0, 8'h0, `SEQUENCE}; + xgmii_txc <= {4'b0001, 4'b0001}; + end + else if (status_remote_fault_ctx) begin + + // If remote fault detected, inhibit transmission and send + // idle codes + + xgmii_txd <= {8{`IDLE}}; + xgmii_txc <= 8'hff; + end + else begin + xgmii_txd <= xgxs_txd; + xgmii_txc <= xgxs_txc; + end + end + +end + + +always @(posedge clk_xgmii_tx or negedge reset_xgmii_tx_n) begin + + if (reset_xgmii_tx_n == 1'b0) begin + + curr_state_enc <= SM_IDLE; + + start_on_lane0 <= 1'b1; + ifg_deficit <= 3'b0; + ifg_4b_add <= 1'b0; + ifg_8b_add <= 1'b0; + ifg_8b2_add <= 1'b0; + + eop <= 8'b0; + + txhfifo_rdata_d1 <= 64'b0; + + xgxs_txd_barrel <= {4{`IDLE}}; + xgxs_txc_barrel <= 4'hf; + + frame_available <= 1'b0; + + xgxs_txd <= {8{`IDLE}}; + xgxs_txc <= 8'hff; + + status_txdfifo_udflow_tog <= 1'b0; + + end + else begin + + curr_state_enc <= next_state_enc; + + start_on_lane0 <= next_start_on_lane0; + ifg_deficit <= next_ifg_deficit; + ifg_4b_add <= next_ifg_4b_add; + ifg_8b_add <= next_ifg_8b_add; + ifg_8b2_add <= next_ifg_8b2_add; + + eop <= next_eop; + + txhfifo_rdata_d1 <= txhfifo_rdata; + + xgxs_txd_barrel <= next_xgxs_txd[63:32]; + xgxs_txc_barrel <= next_xgxs_txc[7:4]; + + frame_available <= next_frame_available; + + //--- + // Barrel shifter. Previous stage always align packet with LANE0. + // This stage allow us to shift packet to align with LANE4 if needed + // for correct inter frame gap (IFG). + + if (next_start_on_lane0) begin + + xgxs_txd <= next_xgxs_txd; + xgxs_txc <= next_xgxs_txc; + + end + else begin + + xgxs_txd <= {next_xgxs_txd[31:0], xgxs_txd_barrel}; + xgxs_txc <= {next_xgxs_txc[3:0], xgxs_txc_barrel}; + + end + + //--- + // FIFO errors, used to generate interrupts. + + if (txdfifo_ren && txdfifo_rempty) begin + status_txdfifo_udflow_tog <= ~status_txdfifo_udflow_tog; + end + + end + +end + +always @(/*AS*/crc32_tx or ctrl_tx_enable_ctx or curr_state_enc or eop + or frame_available or ifg_4b_add or ifg_8b2_add or ifg_8b_add + or ifg_deficit or start_on_lane0 or status_local_fault_ctx + or txhfifo_ralmost_empty or txhfifo_rdata_d1 + or txhfifo_rempty or txhfifo_rstatus) begin + + next_state_enc = curr_state_enc; + + next_start_on_lane0 = start_on_lane0; + next_ifg_deficit = ifg_deficit; + next_ifg_4b_add = ifg_4b_add; + next_ifg_8b_add = ifg_8b_add; + next_ifg_8b2_add = ifg_8b2_add; + + next_eop = eop; + + next_xgxs_txd = {8{`IDLE}}; + next_xgxs_txc = 8'hff; + + txhfifo_ren = 1'b0; + + next_frame_available = frame_available; + + case (curr_state_enc) + + SM_IDLE: + begin + + // Wait for frame to be available. There should be a least N bytes in the + // data fifo or a crc in the control fifo. The N bytes in the data fifo + // give time to the enqueue engine to calculate crc and write it to the + // control fifo. If crc is already in control fifo we can start transmitting + // with no concern. Transmission is inhibited if local or remote faults + // are detected. + + if (ctrl_tx_enable_ctx && frame_available && + !status_local_fault_ctx && !status_local_fault_ctx) begin + + txhfifo_ren = 1'b1; + next_state_enc = SM_PREAMBLE; + + end + else begin + + next_frame_available = !txhfifo_ralmost_empty; + next_ifg_4b_add = 1'b0; + + end + + end + + SM_PREAMBLE: + begin + + // On reading SOP from fifo, send SFD and preamble characters + + if (txhfifo_rstatus[`TXSTATUS_SOP]) begin + + next_xgxs_txd = {`SFD, {6{`PREAMBLE}}, `START}; + next_xgxs_txc = 8'h01; + + txhfifo_ren = 1'b1; + + next_state_enc = SM_TX; + + end + else begin + + next_frame_available = 1'b0; + next_state_enc = SM_IDLE; + + end + + + // Depending on deficit idle count calculations, add 4 bytes + // or IFG or not. This will determine on which lane start the + // next frame. + + if (ifg_4b_add) begin + next_start_on_lane0 = 1'b0; + end + else begin + next_start_on_lane0 = 1'b1; + end + + end + + SM_TX: + begin + + next_xgxs_txd = txhfifo_rdata_d1; + next_xgxs_txc = 8'h00; + + txhfifo_ren = 1'b1; + + + // Wait for EOP indication to be read from the fifo, then + // transition to next state. + + if (txhfifo_rstatus[`TXSTATUS_EOP]) begin + + txhfifo_ren = 1'b0; + next_frame_available = !txhfifo_ralmost_empty; + next_state_enc = SM_EOP; + + end + else if (txhfifo_rempty || txhfifo_rstatus[`TXSTATUS_SOP]) begin + + // Failure condition, we did not see EOP and there + // is no more data in fifo or SOP, force end of packet transmit. + + next_state_enc = SM_TERM_FAIL; + + end + + next_eop[0] = txhfifo_rstatus[2:0] == 3'd1; + next_eop[1] = txhfifo_rstatus[2:0] == 3'd2; + next_eop[2] = txhfifo_rstatus[2:0] == 3'd3; + next_eop[3] = txhfifo_rstatus[2:0] == 3'd4; + next_eop[4] = txhfifo_rstatus[2:0] == 3'd5; + next_eop[5] = txhfifo_rstatus[2:0] == 3'd6; + next_eop[6] = txhfifo_rstatus[2:0] == 3'd7; + next_eop[7] = txhfifo_rstatus[2:0] == 3'd0; + + end + + SM_EOP: + begin + + // Insert TERMINATE character in correct lane depending on position + // of EOP read from fifo. Also insert CRC read from control fifo. + + if (eop[0]) begin + next_xgxs_txd = {{2{`IDLE}}, `TERMINATE, + crc32_tx[31:0], txhfifo_rdata_d1[7:0]}; + next_xgxs_txc = 8'b11100000; + end + + if (eop[1]) begin + next_xgxs_txd = {`IDLE, `TERMINATE, + crc32_tx[31:0], txhfifo_rdata_d1[15:0]}; + next_xgxs_txc = 8'b11000000; + end + + if (eop[2]) begin + next_xgxs_txd = {`TERMINATE, crc32_tx[31:0], txhfifo_rdata_d1[23:0]}; + next_xgxs_txc = 8'b10000000; + end + + if (eop[3]) begin + next_xgxs_txd = {crc32_tx[31:0], txhfifo_rdata_d1[31:0]}; + next_xgxs_txc = 8'b00000000; + end + + if (eop[4]) begin + next_xgxs_txd = {crc32_tx[23:0], txhfifo_rdata_d1[39:0]}; + next_xgxs_txc = 8'b00000000; + end + + if (eop[5]) begin + next_xgxs_txd = {crc32_tx[15:0], txhfifo_rdata_d1[47:0]}; + next_xgxs_txc = 8'b00000000; + end + + if (eop[6]) begin + next_xgxs_txd = {crc32_tx[7:0], txhfifo_rdata_d1[55:0]}; + next_xgxs_txc = 8'b00000000; + end + + if (eop[7]) begin + next_xgxs_txd = {txhfifo_rdata_d1[63:0]}; + next_xgxs_txc = 8'b00000000; + end + + if (!frame_available) begin + + // If there is not another frame ready to be transmitted, interface + // will go idle and idle deficit idle count calculation is irrelevant. + // Set deficit to 0. + + next_ifg_deficit = 3'b0; + + end + else begin + + // Idle deficit count calculated based on number of "wasted" bytes + // between TERMINATE and alignment of next frame in LANE0. + + next_ifg_deficit = ifg_deficit + + {2'b0, eop[0] | eop[4]} + + {1'b0, eop[1] | eop[5], 1'b0} + + {1'b0, eop[2] | eop[6], + eop[2] | eop[6]}; + end + + // IFG corrections based on deficit count and previous starting lane + // Calculated based on following table: + // + // DIC=0 DIC=1 DIC=2 DIC=3 + // ------------- ------------- ------------- ------------- + // PktLen IFG Next IFG Next IFG Next IFG Next + // Modulus Length DIC Length DIC Length DIC Length DIC + // ----------------------------------------------------------------------- + // 0 12 0 12 1 12 2 12 3 + // 1 11 1 11 2 11 3 15 0 + // 2 10 2 10 3 14 0 14 1 + // 3 9 3 13 0 13 1 13 2 + // + // + // In logic it translates into adding 4, 8, or 12 bytes of IFG relative + // to LANE0. + // IFG and Add columns assume no deficit applied + // IFG+DIC and Add+DIC assume deficit must be applied + // + // Start lane 0 Start lane 4 + // EOP Pads IFG IFG+DIC Add Add+DIC Add Add IFG + // 0 3 11 15 8 12 12 16 + // 1 2 10 14 8 12 12 16 + // 2 1 9 13 8 12 12 16 + // 3 8 12 12 4 4 8 8 + // 4 7 11 15 4 8 8 12 + // 5 6 10 14 4 8 8 12 + // 6 5 9 13 4 8 8 12 + // 7 4 12 12 8 8 12 12 + + if (!frame_available) begin + + // If there is not another frame ready to be transmitted, interface + // will go idle and idle deficit idle count calculation is irrelevant. + + next_ifg_4b_add = 1'b0; + next_ifg_8b_add = 1'b0; + next_ifg_8b2_add = 1'b0; + + end + else if (next_ifg_deficit[2] == ifg_deficit[2]) begin + + // Add 4 bytes IFG + + next_ifg_4b_add = (eop[0] & !start_on_lane0) | + (eop[1] & !start_on_lane0) | + (eop[2] & !start_on_lane0) | + (eop[3] & start_on_lane0) | + (eop[4] & start_on_lane0) | + (eop[5] & start_on_lane0) | + (eop[6] & start_on_lane0) | + (eop[7] & !start_on_lane0); + + // Add 8 bytes IFG + + next_ifg_8b_add = (eop[0]) | + (eop[1]) | + (eop[2]) | + (eop[3] & !start_on_lane0) | + (eop[4] & !start_on_lane0) | + (eop[5] & !start_on_lane0) | + (eop[6] & !start_on_lane0) | + (eop[7]); + + // Add another 8 bytes IFG + + next_ifg_8b2_add = 1'b0; + + end + else begin + + // Add 4 bytes IFG + + next_ifg_4b_add = (eop[0] & start_on_lane0) | + (eop[1] & start_on_lane0) | + (eop[2] & start_on_lane0) | + (eop[3] & start_on_lane0) | + (eop[4] & !start_on_lane0) | + (eop[5] & !start_on_lane0) | + (eop[6] & !start_on_lane0) | + (eop[7] & !start_on_lane0); + + // Add 8 bytes IFG + + next_ifg_8b_add = (eop[0]) | + (eop[1]) | + (eop[2]) | + (eop[3] & !start_on_lane0) | + (eop[4]) | + (eop[5]) | + (eop[6]) | + (eop[7]); + + // Add another 8 bytes IFG + + next_ifg_8b2_add = (eop[0] & !start_on_lane0) | + (eop[1] & !start_on_lane0) | + (eop[2] & !start_on_lane0); + + end + + if (|eop[2:0]) begin + + if (frame_available) begin + + // Next state depends on number of IFG bytes to be inserted. + // Skip idle state if needed. + + if (next_ifg_8b2_add) begin + next_state_enc = SM_IFG; + end + else if (next_ifg_8b_add) begin + next_state_enc = SM_IDLE; + end + else begin + txhfifo_ren = 1'b1; + next_state_enc = SM_PREAMBLE; + end + + end + else begin + next_state_enc = SM_IFG; + end + end + + if (|eop[7:3]) begin + next_state_enc = SM_TERM; + end + + end + + SM_TERM: + begin + + // Insert TERMINATE character in correct lane depending on position + // of EOP read from fifo. Also insert CRC read from control fifo. + + if (eop[3]) begin + next_xgxs_txd = {{7{`IDLE}}, `TERMINATE}; + next_xgxs_txc = 8'b11111111; + end + + if (eop[4]) begin + next_xgxs_txd = {{6{`IDLE}}, `TERMINATE, crc32_tx[31:24]}; + next_xgxs_txc = 8'b11111110; + end + + if (eop[5]) begin + next_xgxs_txd = {{5{`IDLE}}, `TERMINATE, crc32_tx[31:16]}; + next_xgxs_txc = 8'b11111100; + end + + if (eop[6]) begin + next_xgxs_txd = {{4{`IDLE}}, `TERMINATE, crc32_tx[31:8]}; + next_xgxs_txc = 8'b11111000; + end + + if (eop[7]) begin + next_xgxs_txd = {{3{`IDLE}}, `TERMINATE, crc32_tx[31:0]}; + next_xgxs_txc = 8'b11110000; + end + + // Next state depends on number of IFG bytes to be inserted. + // Skip idle state if needed. + + if (frame_available && !ifg_8b_add) begin + txhfifo_ren = 1'b1; + next_state_enc = SM_PREAMBLE; + end + else if (frame_available) begin + next_state_enc = SM_IDLE; + end + else begin + next_state_enc = SM_IFG; + end + + end + + SM_TERM_FAIL: + begin + + next_xgxs_txd = {{7{`IDLE}}, `TERMINATE}; + next_xgxs_txc = 8'b11111111; + next_state_enc = SM_IFG; + + end + + SM_IFG: + begin + + next_state_enc = SM_IDLE; + + end + + default: + begin + next_state_enc = SM_IDLE; + end + + endcase + +end + + +always @(/*AS*/crc32_d64 or txhfifo_wen or txhfifo_wstatus) begin + + if (txhfifo_wen && txhfifo_wstatus[`TXSTATUS_SOP]) begin + crc_data = 32'hffffffff; + end + else begin + crc_data = crc32_d64; + end + +end + +always @(/*AS*/byte_cnt or curr_state_pad or txdfifo_rdata + or txdfifo_rempty or txdfifo_ren_d1 or txdfifo_rstatus + or txhfifo_walmost_full) begin + + next_state_pad = curr_state_pad; + + next_txhfifo_wdata = txdfifo_rdata; + next_txhfifo_wstatus = txdfifo_rstatus; + + txdfifo_ren = 1'b0; + next_txhfifo_wen = 1'b0; + + case (curr_state_pad) + + SM_PAD_EQ: begin + + + //--- + // If room availabe in hoding fifo and data available in + // data fifo, transfer data words. If transmit state machine + // is reading from fifo we can assume room will be available. + + if (!txhfifo_walmost_full) begin + + txdfifo_ren = !txdfifo_rempty; + + end + + + //--- + // This logic dependent on read during previous cycle. + + if (txdfifo_ren_d1) begin + + next_txhfifo_wen = 1'b1; + + // On EOP, decide if padding is required for this packet. + + if (txdfifo_rstatus[`TXSTATUS_EOP]) begin + + if (byte_cnt < 14'd56) begin + + next_txhfifo_wstatus = `TXSTATUS_NONE; + txdfifo_ren = 1'b0; + next_state_pad = SM_PAD_PAD; + + end + else if (byte_cnt == 14'd56 && + (txdfifo_rstatus[2:0] == 3'd1 || + txdfifo_rstatus[2:0] == 3'd2 || + txdfifo_rstatus[2:0] == 3'd3)) begin + + // Pad up to LANE3, keep the other 4 bytes for crc that will + // be inserted by dequeue engine. + + next_txhfifo_wstatus[2:0] = 3'd4; + + // Pad end bytes with zeros. + + if (txdfifo_rstatus[2:0] == 3'd1) + next_txhfifo_wdata[31:8] = 24'b0; + if (txdfifo_rstatus[2:0] == 3'd2) + next_txhfifo_wdata[31:16] = 16'b0; + if (txdfifo_rstatus[2:0] == 3'd3) + next_txhfifo_wdata[31:24] = 8'b0; + + txdfifo_ren = 1'b0; + + end + else begin + + txdfifo_ren = 1'b0; + + end + + end + + end + + end + + SM_PAD_PAD: begin + + //--- + // Pad packet to 64 bytes by writting zeros to holding fifo. + + if (!txhfifo_walmost_full) begin + + next_txhfifo_wdata = 64'b0; + next_txhfifo_wstatus = `TXSTATUS_NONE; + next_txhfifo_wen = 1'b1; + + if (byte_cnt == 14'd56) begin + + + // Pad up to LANE3, keep the other 4 bytes for crc that will + // be inserted by dequeue engine. + + next_txhfifo_wstatus[`TXSTATUS_EOP] = 1'b1; + next_txhfifo_wstatus[2:0] = 3'd4; + + next_state_pad = SM_PAD_EQ; + + end + + end + + end + + default: + begin + next_state_pad = SM_PAD_EQ; + end + + endcase + +end + + +always @(posedge clk_xgmii_tx or negedge reset_xgmii_tx_n) begin + + if (reset_xgmii_tx_n == 1'b0) begin + + curr_state_pad <= SM_PAD_EQ; + + txdfifo_ren_d1 <= 1'b0; + + txhfifo_wdata <= 64'b0; + txhfifo_wstatus <= 8'b0; + txhfifo_wen <= 1'b0; + + byte_cnt <= 14'b0; + + shift_crc_data <= 64'b0; + shift_crc_eop <= 4'b0; + shift_crc_cnt <= 4'b0; + + end + else begin + + curr_state_pad <= next_state_pad; + + txdfifo_ren_d1 <= txdfifo_ren; + + txhfifo_wdata <= next_txhfifo_wdata; + txhfifo_wstatus <= next_txhfifo_wstatus; + txhfifo_wen <= next_txhfifo_wen; + + + //--- + // Reset byte count on SOP + + if (next_txhfifo_wen) begin + + if (next_txhfifo_wstatus[`TXSTATUS_SOP]) begin + + byte_cnt <= 14'd8; + + end + else begin + + byte_cnt <= byte_cnt + 14'd8; + + end + + end + + + //--- + // Calculate CRC as data is written to holding fifo. The holding fifo creates + // a delay that allow the CRC calculation to complete before the end of the frame + // is ready to be transmited. + + if (txhfifo_wen) begin + + crc32_d64 <= nextCRC32_D64(reverse_64b(txhfifo_wdata), crc_data); + + end + + if (txhfifo_wen && txhfifo_wstatus[`TXSTATUS_EOP]) begin + + // Last bytes calculated 8-bit at a time instead of 64-bit. Start + // this process at the end of the frame. + + crc32_d8 <= crc32_d64; + + shift_crc_data <= txhfifo_wdata; + shift_crc_cnt <= 4'd9; + + if (txhfifo_wstatus[2:0] == 3'b0) begin + shift_crc_eop <= 4'd8; + end + else begin + shift_crc_eop <= {1'b0, txhfifo_wstatus[2:0]}; + end + + end + else if (shift_crc_eop != 4'b0) begin + + // Complete crc calculation 8-bit at a time until finished. This can + // be 1 to 8 bytes long. + + crc32_d8 <= nextCRC32_D8(reverse_8b(shift_crc_data[7:0]), crc32_d8); + + shift_crc_data <= {8'b0, shift_crc_data[63:8]}; + shift_crc_eop <= shift_crc_eop - 4'd1; + + end + + + //--- + // Update CRC register at the end of calculation. Always update after 8 + // cycles for deterministic results, even if a single byte was present in + // last data word. + + if (shift_crc_cnt == 4'b1) begin + + crc32_tx <= ~reverse_32b(crc32_d8); + + end + else begin + + shift_crc_cnt <= shift_crc_cnt - 4'd1; + + end + + end + +end + +endmodule + |