Files
b210-k7-fpga/lib/simple_gemac/simple_gemac_tx.v
T
Ben Hilburn fbbc991a7d Pushing the bulk of UHD-3.7.0 code.
Original-commit: ff1546f8137f7f92bb250f685561b0c34cc0e053
2014-02-14 12:05:07 -08:00

260 lines
6.6 KiB
Verilog

//
// Copyright 2011 Ettus Research LLC
//
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;
*/