From 7df18a16ff4d9cf5ab95871a5b8d1a96f184f725 Mon Sep 17 00:00:00 2001 From: matt Date: Mon, 30 Mar 2009 22:52:00 +0000 Subject: work in progress on a simpler gigabit-only mac git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@10715 221aa14e-8319-0410-a670-987f0aec2ac5 --- simple_gemac/crc.v | 63 +++++++++++++ simple_gemac/flow_ctrl_rx.v | 85 ++++++++++++++++++ simple_gemac/flow_ctrl_tx.v | 36 ++++++++ simple_gemac/simple_gemac.v | 55 ++++++++++++ simple_gemac/simple_gemac_tb.v | 88 ++++++++++++++++++ simple_gemac/simple_gemac_tx.v | 200 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 527 insertions(+) create mode 100644 simple_gemac/crc.v create mode 100644 simple_gemac/flow_ctrl_rx.v create mode 100644 simple_gemac/flow_ctrl_tx.v create mode 100644 simple_gemac/simple_gemac.v create mode 100644 simple_gemac/simple_gemac_tb.v create mode 100644 simple_gemac/simple_gemac_tx.v (limited to 'simple_gemac') diff --git a/simple_gemac/crc.v b/simple_gemac/crc.v new file mode 100644 index 000000000..c54623859 --- /dev/null +++ b/simple_gemac/crc.v @@ -0,0 +1,63 @@ + +module crc + (input clk, + input reset, + input clear, + input [7:0] data, + input calc, + output [31:0] crc_out); + + function[31:0] NextCRC; + input[7:0] D; + input[31:0] C; + reg[31:0] NewCRC; + begin + NewCRC[0] = C[24]^C[30]^D[1]^D[7]; + NewCRC[1] = C[25]^C[31]^D[0]^D[6]^C[24]^C[30]^D[1]^D[7]; + NewCRC[2] = C[26]^D[5]^C[25]^C[31]^D[0]^D[6]^C[24]^C[30]^D[1]^D[7]; + NewCRC[3] = C[27]^D[4]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6]; + NewCRC[4] = C[28]^D[3]^C[27]^D[4]^C[26]^D[5]^C[24]^C[30]^D[1]^D[7]; + NewCRC[5] = C[29]^D[2]^C[28]^D[3]^C[27]^D[4]^C[25]^C[31]^D[0]^D[6]^C[24]^C[30]^D[1]^D[7]; + NewCRC[6] = C[30]^D[1]^C[29]^D[2]^C[28]^D[3]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6]; + NewCRC[7] = C[31]^D[0]^C[29]^D[2]^C[27]^D[4]^C[26]^D[5]^C[24]^D[7]; + NewCRC[8] = C[0]^C[28]^D[3]^C[27]^D[4]^C[25]^D[6]^C[24]^D[7]; + NewCRC[9] = C[1]^C[29]^D[2]^C[28]^D[3]^C[26]^D[5]^C[25]^D[6]; + NewCRC[10] = C[2]^C[29]^D[2]^C[27]^D[4]^C[26]^D[5]^C[24]^D[7]; + NewCRC[11] = C[3]^C[28]^D[3]^C[27]^D[4]^C[25]^D[6]^C[24]^D[7]; + NewCRC[12] = C[4]^C[29]^D[2]^C[28]^D[3]^C[26]^D[5]^C[25]^D[6]^C[24]^C[30]^D[1]^D[7]; + NewCRC[13] = C[5]^C[30]^D[1]^C[29]^D[2]^C[27]^D[4]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6]; + NewCRC[14] = C[6]^C[31]^D[0]^C[30]^D[1]^C[28]^D[3]^C[27]^D[4]^C[26]^D[5]; + NewCRC[15] = C[7]^C[31]^D[0]^C[29]^D[2]^C[28]^D[3]^C[27]^D[4]; + NewCRC[16] = C[8]^C[29]^D[2]^C[28]^D[3]^C[24]^D[7]; + NewCRC[17] = C[9]^C[30]^D[1]^C[29]^D[2]^C[25]^D[6]; + NewCRC[18] = C[10]^C[31]^D[0]^C[30]^D[1]^C[26]^D[5]; + NewCRC[19] = C[11]^C[31]^D[0]^C[27]^D[4]; + NewCRC[20] = C[12]^C[28]^D[3]; + NewCRC[21] = C[13]^C[29]^D[2]; + NewCRC[22] = C[14]^C[24]^D[7]; + NewCRC[23] = C[15]^C[25]^D[6]^C[24]^C[30]^D[1]^D[7]; + NewCRC[24] = C[16]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6]; + NewCRC[25] = C[17]^C[27]^D[4]^C[26]^D[5]; + NewCRC[26] = C[18]^C[28]^D[3]^C[27]^D[4]^C[24]^C[30]^D[1]^D[7]; + NewCRC[27] = C[19]^C[29]^D[2]^C[28]^D[3]^C[25]^C[31]^D[0]^D[6]; + NewCRC[28] = C[20]^C[30]^D[1]^C[29]^D[2]^C[26]^D[5]; + NewCRC[29] = C[21]^C[31]^D[0]^C[30]^D[1]^C[27]^D[4]; + NewCRC[30] = C[22]^C[31]^D[0]^C[28]^D[3]; + NewCRC[31] = C[23]^C[29]^D[2]; + NextCRC = NewCRC; + end + endfunction + + reg [31:0] crc_reg; + always @ (posedge clk) + if (reset | clear) + crc_reg <= 32'hffffffff; + else if (calc) + crc_reg <= NextCRC(data,crc_reg); + + assign crc_out = ~{crc_reg[24],crc_reg[25],crc_reg[26],crc_reg[27],crc_reg[28],crc_reg[29],crc_reg[30],crc_reg[31], + crc_reg[16],crc_reg[17],crc_reg[18],crc_reg[19],crc_reg[20],crc_reg[21],crc_reg[22],crc_reg[23], + crc_reg[8],crc_reg[9],crc_reg[10],crc_reg[11],crc_reg[12],crc_reg[13],crc_reg[14],crc_reg[15], + crc_reg[0],crc_reg[1],crc_reg[2],crc_reg[3],crc_reg[4],crc_reg[5],crc_reg[6],crc_reg[7] }; + +endmodule // crc diff --git a/simple_gemac/flow_ctrl_rx.v b/simple_gemac/flow_ctrl_rx.v new file mode 100644 index 000000000..7ded9e08b --- /dev/null +++ b/simple_gemac/flow_ctrl_rx.v @@ -0,0 +1,85 @@ + +// RX side of flow control -- when we are running out of RX space, send a PAUSE + +module flow_ctrl_rx + (input rst, + //host processor + input pause_frame_send_en, + input [15:0] pause_quanta_set, + input [15:0] fc_hwmark, + input [15:0] fc_lwmark, + // From MAC_rx_ctrl + input rx_clk, + input [15:0] rx_fifo_space, + // MAC_tx_ctrl + input tx_clk, + output reg xoff_gen, + output reg xon_gen, + input xoff_gen_complete, + input xon_gen_complete + ); + + // ****************************************************************************** + // Force our TX to send a PAUSE frame because our RX is nearly full + // ****************************************************************************** + + reg xon_int, xoff_int; + reg [21:0] countdown; + + always @(posedge rx_clk or posedge rst) + if(rst) + begin + xon_int <= 0; + xoff_int <= 0; + end + else + begin + xon_int <= 0; + xoff_int <= 0; + if(pause_frame_send_en) + if(countdown == 0) + if(rx_fifo_space < fc_lwmark) + xoff_int <= 1; + else + ; + else + if(rx_fifo_space > fc_hwmark) + xon_int <= 1; + end // else: !if(rst) + + reg xoff_int_d1, xon_int_d1; + + always @(posedge rx_clk) + xon_int_d1 <= xon_int; + always @(posedge rx_clk) + xoff_int_d1 <= xoff_int; + + always @ (posedge tx_clk or posedge rst) + if (rst) + xoff_gen <=0; + else if (xoff_gen_complete) + xoff_gen <=0; + else if (xoff_int | xoff_int_d1) + xoff_gen <=1; + + always @ (posedge tx_clk or posedge rst) + if (rst) + xon_gen <=0; + else if (xon_gen_complete) + xon_gen <=0; + else if (xon_int | xon_int_d1) + xon_gen <=1; + + wire [15:0] pq_reduced = pause_quanta_set - 2; + + always @(posedge tx_clk or posedge rst) + if(rst) + countdown <= 0; + else if(xoff_gen) + countdown <= {pq_reduced,6'd0}; + else if(xon_gen) + countdown <= 0; + else if(countdown != 0) + countdown <= countdown - 1; + +endmodule // flow_ctrl diff --git a/simple_gemac/flow_ctrl_tx.v b/simple_gemac/flow_ctrl_tx.v new file mode 100644 index 000000000..9f7556de4 --- /dev/null +++ b/simple_gemac/flow_ctrl_tx.v @@ -0,0 +1,36 @@ + +// TX side of flow control -- when other side sends PAUSE, we wait + +module flow_ctrl_tx + (input rst, + input tx_clk, + //host processor + input tx_pause_en, + // From MAC_rx_ctrl + input [15:0] pause_quanta, + input pause_quanta_val, + // MAC_tx_ctrl + output pause_apply, + input pause_quanta_sub); + + // ****************************************************************************** + // Inhibit our TX from transmitting because they sent us a PAUSE frame + // ****************************************************************************** + + reg [15:0] pause_quanta_counter; + reg pqval_d1, pqval_d2; + + always @(posedge tx_clk) pqval_d1 <= pause_quanta_val; + always @(posedge tx_clk) pqval_d2 <= pqval_d1; + + always @ (posedge tx_clk or posedge rst) + if (rst) + pause_quanta_counter <= 0; + else if (pqval_d1 & ~pqval_d2) + pause_quanta_counter <= pause_quanta; + else if((pause_quanta_counter!=0) & pause_quanta_sub) + pause_quanta_counter <= pause_quanta_counter - 1; + + assign pause_apply = tx_pause_en & (pause_quanta_counter != 0); + +endmodule // flow_ctrl diff --git a/simple_gemac/simple_gemac.v b/simple_gemac/simple_gemac.v new file mode 100644 index 000000000..0769fe615 --- /dev/null +++ b/simple_gemac/simple_gemac.v @@ -0,0 +1,55 @@ + +module simple_gemac + (input clk125, input reset, + // GMII + output GMII_GTX_CLK, output GMII_TX_EN, output GMII_TX_ER, output [7:0] GMII_TXD, + input GMII_RX_CLK, input GMII_RX_DV, input GMII_RX_ER, input [7:0] GMII_RXD, + + // Flow Control Interface + input pause_req, input [15:0] pause_time, + + // RX Client Interface + output rx_clk, output [7:0] rx_data, output rx_valid, output rx_error, output rx_ack, + + // TX Client Interface + output tx_clk, input [7:0] tx_data, input tx_valid, input tx_error, output tx_ack + ); + + localparam SGE_IFG = 8'd12; // 12 should be the absolute minimum + localparam SGE_RESPECT_FLOW_CTRL = 1'b1; // stop sending if other side requests + + wire rst_rxclk, rst_txclk; + oneshot_2clk tx_rst_1shot (.clk_in(tx_clk),.in(reset),.clk_out(tx_clk),.out(rst_txclk)); // FIXME clocks + oneshot_2clk rx_rst_1shot (.clk_in(sys_clk),.in(reset),.clk_out(rx_clk),.out(rst_rxclk)); + + wire [15:0] pause_quanta_rcvd; + + simple_gemac_tx simple_gemac_tx + (.clk125(clk125),.reset(rst_txclk), + .GMII_GTX_CLK(GMII_GTX_CLK), .GMII_TX_EN(GMII_TX_EN), + .GMII_TX_ER(GMII_TX_ER), .GMII_TXD(GMII_TXD), + .tx_clk(tx_clk), .tx_data(tx_data), .tx_valid(tx_valid), .tx_error(tx_error), .tx_ack(tx_ack), + .ifg(SGE_IFG), .mac_addr(48'hF1_F2_F3_F4_F5_F6), + .pause_req(pause_req), .pause_time(pause_time), // We request flow control + .pause_apply(pause_apply), .pause_applied(pause_applied) // We respect flow control + ); +/* + simple_gemac_rx simple_gemac_rx + (.reset(rst_rxclk), + .GMII_RX_CLK(GMII_RX_CLK), .GMII_RX_DV(GMII_RX_DV), + .GMII_RX_ER(GMII_RX_ER), .GMII_RXD(GMII_RXD), + .rx_clk(rx_clk), .rx_data(rx_data), .rx_valid(rx_valid), .rx_error(rx_error), .rx_ack(rx_ack), + .pause_quanta_rcvd(pause_qanta_rcvd), .pause_rcvd(pause_rcvd) + ); + */ + flow_ctrl_tx flow_ctrl_tx + (.rst(reset_txclk), .tx_clk(tx_clk), + .tx_pause_en(SGE_RESPECT_FLOW_CTRL), + .pause_quanta(pause_quanta_rcvd), // 16 bit value + .pause_quanta_val(pause_rcvd), + .pause_apply(pause_apply), + .pause_quanta_sub(pause_applied) + ); + + +endmodule // simple_gemac diff --git a/simple_gemac/simple_gemac_tb.v b/simple_gemac/simple_gemac_tb.v new file mode 100644 index 000000000..61dbbe4c9 --- /dev/null +++ b/simple_gemac/simple_gemac_tb.v @@ -0,0 +1,88 @@ + + +module simple_gemac_tb; + + + reg clk = 0; + reg reset = 1; + + initial #1000 reset = 0; + always #50 clk = ~clk; + + wire GMII_RX_DV, GMII_RX_ER, GMII_TX_EN, GMII_TX_ER; + wire [7:0] GMII_RXD, GMII_TXD; + + wire rx_valid, rx_error, rx_ack; + wire tx_ack; + reg tx_valid = 0, tx_error = 0; + + wire [7:0] rx_data; + reg [7:0] tx_data; + + wire [15:0] pause_time = 16'hBEEF; + reg pause_req = 0; + + simple_gemac simple_gemac + (.clk125(clk), .reset(reset), + .GMII_GTX_CLK(GMII_GTX_CLK), .GMII_TX_EN(GMII_TX_EN), + .GMII_TX_ER(GMII_TX_ER), .GMII_TXD(GMII_TXD), + .GMII_RX_CLK(GMII_RX_CLK), .GMII_RX_DV(GMII_RX_DV), + .GMII_RX_ER(GMII_RX_ER), .GMII_RXD(GMII_RXD), + .pause_req(pause_req), .pause_time(pause_time), + .rx_clk(rx_clk), .rx_data(rx_data), + .rx_valid(rx_valid), .rx_error(rx_error), .rx_ack(rx_ack), + .tx_clk(tx_clk), .tx_data(tx_data), + .tx_valid(tx_valid), .tx_error(tx_error), .tx_ack(tx_ack) + ); + + task SendFlowCtrl; + begin + $display("Sending Flow Control"); + @(posedge clk); + pause_req <= 1; + @(posedge clk); + pause_req <= 0; + end + endtask // SendFlowCtrl + + reg [31:0] count; + task SendPacket; + input [7:0] data_start; + input [31:0] data_len; + begin + $display("Sending Packet"); + count <= 0; + tx_data <= data_start; + tx_error <= 0; + tx_valid <= 1; + while(~tx_ack) + @(posedge tx_clk); + while(count < data_len) + begin + tx_data <= tx_data + 1; + count <= count + 1; + @(posedge clk); + end + tx_valid <= 0; + @(posedge tx_clk); + end + endtask // SendPacket + + initial $dumpfile("simple_gemac_tb.vcd"); + initial $dumpvars(0,simple_gemac_tb); + + initial + begin + @(negedge reset); + repeat (20) + @(posedge clk); + SendFlowCtrl; + repeat (100) + @(posedge clk); + SendPacket(8'hAA,10); + repeat (1000) + @(posedge clk); + $finish; + end + +endmodule // simple_gemac_tb diff --git a/simple_gemac/simple_gemac_tx.v b/simple_gemac/simple_gemac_tx.v new file mode 100644 index 000000000..cddb18a3a --- /dev/null +++ b/simple_gemac/simple_gemac_tx.v @@ -0,0 +1,200 @@ + +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 pause_applied + ); + + 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 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; + + localparam TX_IDLE = 0; + localparam TX_PREAMBLE = 1; + localparam TX_SOF_DEL = TX_PREAMBLE + 7; + localparam TX_IN_FRAME = TX_SOF_DEL + 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 = 56; + localparam TX_PAUSE_PRE = TX_PAUSE + 1; + localparam TX_PAUSE_SOF = 64; + localparam TX_PAUSE_END = TX_PAUSE_SOF + 18; + + 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) + tx_state <= TX_PREAMBLE; + 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) + 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 : + tx_state <= TX_PAUSE_PRE; + TX_PAUSE_END : + tx_state <= TX_PAD; + default : + tx_state <= tx_state + 1; + endcase // case (tx_state) + + always @(posedge tx_clk) + if(reset) + begin + GMII_TX_EN <= 0; + GMII_TX_ER <= 0; + GMII_TXD <= 0; + end + else + casex(tx_state) + TX_IDLE : + begin + GMII_TX_EN <= 0; + GMII_TX_ER <= 0; + GMII_TXD <= 0; + end + TX_PREAMBLE, TX_PAUSE_PRE : + begin + GMII_TXD <= 8'h55; + GMII_TX_EN <= 1; + end + TX_SOF_DEL, TX_PAUSE_SOF : + GMII_TXD <= 8'hD5; + TX_IN_FRAME, TX_IN_FRAME_2 : + GMII_TXD <= tx_data; + TX_ERROR : + begin + GMII_TX_ER <= 1; + GMII_TXD <= 0; + end + 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]; + TX_PAD : + GMII_TXD <= 0; + 8'b01xx_xxxx : // In Pause Frame + GMII_TXD <= 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 = 1; + + crc crc(.clk(tx_clk), .reset(reset), .clear(clear_crc), + .data(GMII_TXD), .calc(calc_crc), .crc_out(crc_out)); + + assign tx_ack = (tx_state == TX_IN_FRAME); +endmodule // simple_gemac_tx -- cgit v1.2.3