diff options
Diffstat (limited to 'fpga/usrp2/simple_gemac/simple_gemac_tx.v')
-rw-r--r-- | fpga/usrp2/simple_gemac/simple_gemac_tx.v | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/fpga/usrp2/simple_gemac/simple_gemac_tx.v b/fpga/usrp2/simple_gemac/simple_gemac_tx.v new file mode 100644 index 000000000..ecabc3dad --- /dev/null +++ b/fpga/usrp2/simple_gemac/simple_gemac_tx.v @@ -0,0 +1,271 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +module simple_gemac_tx + (input clk125, input reset, + output GMII_GTX_CLK, output reg GMII_TX_EN, output reg GMII_TX_ER, output reg [7:0] GMII_TXD, + output tx_clk, input [7:0] tx_data, input tx_valid, input tx_error, output tx_ack, + input [7:0] ifg, input [47:0] mac_addr, + input pause_req, input [15:0] pause_time, + input pause_apply, output reg paused + ); + + reg tx_en_pre, tx_er_pre; + reg [7:0] txd_pre; + + assign GMII_GTX_CLK = clk125; + assign tx_clk = clk125; + + reg [7:0] tx_state; + reg [7:0] ifg_ctr; + reg [15:0] frame_len_ctr; + reg [7:0] pause_ctr, pause_dat; + + wire in_ifg = (ifg_ctr != 0); + + wire [31:0] crc_out; + + localparam TX_IDLE = 0; + localparam TX_PREAMBLE = 1; + localparam TX_SOF_DEL = TX_PREAMBLE + 7; + localparam TX_FIRSTBYTE = TX_SOF_DEL + 1; + localparam TX_IN_FRAME = TX_FIRSTBYTE + 1; + localparam TX_IN_FRAME_2 = TX_IN_FRAME + 1; + localparam TX_PAD = TX_IN_FRAME_2 + 1; + localparam TX_CRC_0 = 16; + localparam TX_CRC_1 = TX_CRC_0 + 1; + localparam TX_CRC_2 = TX_CRC_0 + 2; + localparam TX_CRC_3 = TX_CRC_0 + 3; + localparam TX_ERROR = 32; + localparam TX_PAUSE = 55; + localparam TX_PAUSE_SOF = TX_PAUSE + 7; + localparam TX_PAUSE_FIRST = TX_PAUSE_SOF + 1; + localparam TX_PAUSE_END = TX_PAUSE_SOF + 18; + + localparam MIN_FRAME_LEN = 64 + 8 - 4; // Min frame length includes preamble but not CRC + localparam MAX_FRAME_LEN = 8192; // How big are the jumbo frames we want to handle? + always @(posedge tx_clk) + if(reset |(tx_state == TX_IDLE)) + frame_len_ctr <= 0; + else + frame_len_ctr <= frame_len_ctr + 1; + + reg send_pause; + reg [15:0] pause_time_held; + + always @(posedge tx_clk) + if(reset) + send_pause <= 0; + else if(pause_req) + send_pause <= 1; + else if(tx_state == TX_PAUSE) + send_pause <= 0; + + always @(posedge tx_clk) + if(pause_req) + pause_time_held <= pause_time; + + always @(posedge tx_clk) + if(reset) + tx_state <= TX_IDLE; + else + case(tx_state) + TX_IDLE : + if(~in_ifg) + if(send_pause) + tx_state <= TX_PAUSE; + else if(tx_valid & ~pause_apply) + tx_state <= TX_PREAMBLE; + TX_FIRSTBYTE : + if(tx_error) + tx_state <= TX_ERROR; // underrun + else if(~tx_valid) + tx_state <= TX_PAD; + else + tx_state <= TX_IN_FRAME; + TX_IN_FRAME : + if(tx_error) + tx_state <= TX_ERROR; // underrun + else if(~tx_valid) + tx_state <= TX_PAD; + else if(frame_len_ctr == MIN_FRAME_LEN - 1) + tx_state <= TX_IN_FRAME_2; + TX_IN_FRAME_2 : + if(tx_error) + tx_state <= TX_ERROR; // underrun + else if(~tx_valid) + tx_state <= TX_CRC_0; + TX_PAD : + if(frame_len_ctr == MIN_FRAME_LEN) + tx_state <= TX_CRC_0; + TX_CRC_3 : + tx_state <= TX_IDLE; + TX_ERROR : + tx_state <= TX_IDLE; + TX_PAUSE_END : + tx_state <= TX_PAD; + default : + tx_state <= tx_state + 1; + endcase // case (tx_state) + + always @(posedge tx_clk) + if(reset) + begin + tx_en_pre <= 0; + tx_er_pre <= 0; + txd_pre <= 0; + end + else + casex(tx_state) + TX_IDLE : + begin + tx_en_pre <= 0; + tx_er_pre <= 0; + txd_pre <= 0; + end + TX_PREAMBLE, TX_PAUSE : + begin + txd_pre <= 8'h55; + tx_en_pre <= 1; + end + TX_SOF_DEL, TX_PAUSE_SOF : + txd_pre <= 8'hD5; + TX_FIRSTBYTE, TX_IN_FRAME, TX_IN_FRAME_2 : + txd_pre <= tx_valid ? tx_data : 0; + TX_ERROR : + begin + tx_er_pre <= 1; + txd_pre <= 0; + end + TX_CRC_3 : + tx_en_pre <= 0; + TX_PAD : + txd_pre <= 0; + TX_PAUSE_FIRST, 8'b01xx_xxxx : // In Pause Frame + txd_pre <= pause_dat; + endcase // case (tx_state) + + localparam SGE_FLOW_CTRL_ADDR = 48'h01_80_C2_00_00_01; + always @(posedge tx_clk) + case(tx_state) + TX_PAUSE_SOF : + pause_dat <= SGE_FLOW_CTRL_ADDR[47:40]; // Note everything must be 1 cycle early + TX_PAUSE_SOF + 1: + pause_dat <= SGE_FLOW_CTRL_ADDR[39:32]; + TX_PAUSE_SOF + 2: + pause_dat <= SGE_FLOW_CTRL_ADDR[31:24]; + TX_PAUSE_SOF + 3: + pause_dat <= SGE_FLOW_CTRL_ADDR[23:16]; + TX_PAUSE_SOF + 4: + pause_dat <= SGE_FLOW_CTRL_ADDR[15:8]; + TX_PAUSE_SOF + 5: + pause_dat <= SGE_FLOW_CTRL_ADDR[7:0]; + TX_PAUSE_SOF + 6: + pause_dat <= mac_addr[47:40]; + TX_PAUSE_SOF + 7: + pause_dat <= mac_addr[39:32]; + TX_PAUSE_SOF + 8: + pause_dat <= mac_addr[31:24]; + TX_PAUSE_SOF + 9: + pause_dat <= mac_addr[23:16]; + TX_PAUSE_SOF + 10: + pause_dat <= mac_addr[15:8]; + TX_PAUSE_SOF + 11: + pause_dat <= mac_addr[7:0]; + TX_PAUSE_SOF + 12: + pause_dat <= 8'h88; // Type = 8808 = MAC ctrl frame + TX_PAUSE_SOF + 13: + pause_dat <= 8'h08; + TX_PAUSE_SOF + 14: + pause_dat <= 8'h00; // Opcode = 0001 = PAUSE + TX_PAUSE_SOF + 15: + pause_dat <= 8'h01; + TX_PAUSE_SOF + 16: + pause_dat <= pause_time_held[15:8]; + TX_PAUSE_SOF + 17: + pause_dat <= pause_time_held[7:0]; + endcase // case (tx_state) + + wire start_ifg = (tx_state == TX_CRC_3); + always @(posedge tx_clk) + if(reset) + ifg_ctr <= 100; + else if(start_ifg) + ifg_ctr <= ifg; + else if(ifg_ctr != 0) + ifg_ctr <= ifg_ctr - 1; + + wire clear_crc = (tx_state == TX_IDLE); + + wire calc_crc = + (tx_state==TX_IN_FRAME) | + (tx_state==TX_IN_FRAME_2) | + (tx_state==TX_PAD) | + (tx_state[6]); + + crc crc(.clk(tx_clk), .reset(reset), .clear(clear_crc), + .data(txd_pre), .calc(calc_crc), .crc_out(crc_out)); + + assign tx_ack = (tx_state == TX_FIRSTBYTE); + + always @(posedge tx_clk) + begin + GMII_TX_EN <= tx_en_pre; + GMII_TX_ER <= tx_er_pre; + case(tx_state) + TX_CRC_0 : + GMII_TXD <= crc_out[31:24]; + TX_CRC_1 : + GMII_TXD <= crc_out[23:16]; + TX_CRC_2 : + GMII_TXD <= crc_out[15:8]; + TX_CRC_3 : + GMII_TXD <= crc_out[7:0]; + default : + GMII_TXD <= txd_pre; + endcase // case (tx_state) + end + + // report that we are paused only when we get back to IDLE + always @(posedge tx_clk) + if(reset) + paused <= 0; + else if(~pause_apply) + paused <= 0; + else if(tx_state == TX_IDLE) + paused <= 1; + +endmodule // simple_gemac_tx + +// Testing code +/* + reg [7:0] crc_ctr; + reg calc_crc_d1; + always @(posedge tx_clk) + calc_crc_d1 <= calc_crc; + + always @(posedge tx_clk) + if(reset) + crc_ctr <= 0; + else if(calc_crc) + crc_ctr <= crc_ctr+1; + else if(calc_crc_d1) + $display("CRC COUNT = %d",crc_ctr); + else + crc_ctr <= 0; +*/ |