aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp2/simple_gemac/simple_gemac_tx.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp2/simple_gemac/simple_gemac_tx.v')
-rw-r--r--fpga/usrp2/simple_gemac/simple_gemac_tx.v271
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;
+*/