--- /dev/null
+\r
+module crc\r
+ (input clk, \r
+ input reset,\r
+ input clear, \r
+ input [7:0] data,\r
+ input calc,\r
+ output [31:0] crc_out);\r
+ \r
+ function[31:0] NextCRC;\r
+ input[7:0] D;\r
+ input[31:0] C;\r
+ reg[31:0] NewCRC;\r
+ begin\r
+ NewCRC[0] = C[24]^C[30]^D[1]^D[7];\r
+ NewCRC[1] = C[25]^C[31]^D[0]^D[6]^C[24]^C[30]^D[1]^D[7];\r
+ NewCRC[2] = C[26]^D[5]^C[25]^C[31]^D[0]^D[6]^C[24]^C[30]^D[1]^D[7];\r
+ NewCRC[3] = C[27]^D[4]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6];\r
+ NewCRC[4] = C[28]^D[3]^C[27]^D[4]^C[26]^D[5]^C[24]^C[30]^D[1]^D[7];\r
+ 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];\r
+ 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];\r
+ NewCRC[7] = C[31]^D[0]^C[29]^D[2]^C[27]^D[4]^C[26]^D[5]^C[24]^D[7];\r
+ NewCRC[8] = C[0]^C[28]^D[3]^C[27]^D[4]^C[25]^D[6]^C[24]^D[7];\r
+ NewCRC[9] = C[1]^C[29]^D[2]^C[28]^D[3]^C[26]^D[5]^C[25]^D[6];\r
+ NewCRC[10] = C[2]^C[29]^D[2]^C[27]^D[4]^C[26]^D[5]^C[24]^D[7];\r
+ NewCRC[11] = C[3]^C[28]^D[3]^C[27]^D[4]^C[25]^D[6]^C[24]^D[7];\r
+ 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];\r
+ 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];\r
+ NewCRC[14] = C[6]^C[31]^D[0]^C[30]^D[1]^C[28]^D[3]^C[27]^D[4]^C[26]^D[5];\r
+ NewCRC[15] = C[7]^C[31]^D[0]^C[29]^D[2]^C[28]^D[3]^C[27]^D[4];\r
+ NewCRC[16] = C[8]^C[29]^D[2]^C[28]^D[3]^C[24]^D[7];\r
+ NewCRC[17] = C[9]^C[30]^D[1]^C[29]^D[2]^C[25]^D[6];\r
+ NewCRC[18] = C[10]^C[31]^D[0]^C[30]^D[1]^C[26]^D[5];\r
+ NewCRC[19] = C[11]^C[31]^D[0]^C[27]^D[4];\r
+ NewCRC[20] = C[12]^C[28]^D[3];\r
+ NewCRC[21] = C[13]^C[29]^D[2];\r
+ NewCRC[22] = C[14]^C[24]^D[7];\r
+ NewCRC[23] = C[15]^C[25]^D[6]^C[24]^C[30]^D[1]^D[7];\r
+ NewCRC[24] = C[16]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6];\r
+ NewCRC[25] = C[17]^C[27]^D[4]^C[26]^D[5];\r
+ NewCRC[26] = C[18]^C[28]^D[3]^C[27]^D[4]^C[24]^C[30]^D[1]^D[7];\r
+ NewCRC[27] = C[19]^C[29]^D[2]^C[28]^D[3]^C[25]^C[31]^D[0]^D[6];\r
+ NewCRC[28] = C[20]^C[30]^D[1]^C[29]^D[2]^C[26]^D[5];\r
+ NewCRC[29] = C[21]^C[31]^D[0]^C[30]^D[1]^C[27]^D[4];\r
+ NewCRC[30] = C[22]^C[31]^D[0]^C[28]^D[3];\r
+ NewCRC[31] = C[23]^C[29]^D[2];\r
+ NextCRC = NewCRC;\r
+ end\r
+ endfunction\r
+\r
+ reg [31:0] crc_reg;\r
+ always @ (posedge clk)\r
+ if (reset | clear)\r
+ crc_reg <= 32'hffffffff;\r
+ else if (calc)\r
+ crc_reg <= NextCRC(data,crc_reg);\r
+ \r
+ 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],\r
+ crc_reg[16],crc_reg[17],crc_reg[18],crc_reg[19],crc_reg[20],crc_reg[21],crc_reg[22],crc_reg[23],\r
+ crc_reg[8],crc_reg[9],crc_reg[10],crc_reg[11],crc_reg[12],crc_reg[13],crc_reg[14],crc_reg[15],\r
+ crc_reg[0],crc_reg[1],crc_reg[2],crc_reg[3],crc_reg[4],crc_reg[5],crc_reg[6],crc_reg[7] };\r
+ \r
+endmodule // crc\r
--- /dev/null
+\r
+// RX side of flow control -- when we are running out of RX space, send a PAUSE\r
+\r
+module flow_ctrl_rx\r
+ (input rst,\r
+ //host processor\r
+ input pause_frame_send_en,\r
+ input [15:0] pause_quanta_set,\r
+ input [15:0] fc_hwmark,\r
+ input [15:0] fc_lwmark,\r
+ // From MAC_rx_ctrl\r
+ input rx_clk,\r
+ input [15:0] rx_fifo_space,\r
+ // MAC_tx_ctrl\r
+ input tx_clk,\r
+ output reg xoff_gen,\r
+ output reg xon_gen,\r
+ input xoff_gen_complete,\r
+ input xon_gen_complete\r
+ );\r
+ \r
+ // ****************************************************************************** \r
+ // Force our TX to send a PAUSE frame because our RX is nearly full\r
+ // ******************************************************************************\r
+\r
+ reg xon_int, xoff_int;\r
+ reg [21:0] countdown;\r
+ \r
+ always @(posedge rx_clk or posedge rst)\r
+ if(rst)\r
+ begin\r
+ xon_int <= 0;\r
+ xoff_int <= 0;\r
+ end\r
+ else \r
+ begin\r
+ xon_int <= 0;\r
+ xoff_int <= 0;\r
+ if(pause_frame_send_en)\r
+ if(countdown == 0)\r
+ if(rx_fifo_space < fc_lwmark)\r
+ xoff_int <= 1;\r
+ else\r
+ ;\r
+ else\r
+ if(rx_fifo_space > fc_hwmark)\r
+ xon_int <= 1;\r
+ end // else: !if(rst)\r
+ \r
+ reg xoff_int_d1, xon_int_d1;\r
+\r
+ always @(posedge rx_clk)\r
+ xon_int_d1 <= xon_int;\r
+ always @(posedge rx_clk)\r
+ xoff_int_d1 <= xoff_int;\r
+ \r
+ always @ (posedge tx_clk or posedge rst)\r
+ if (rst)\r
+ xoff_gen <=0;\r
+ else if (xoff_gen_complete)\r
+ xoff_gen <=0;\r
+ else if (xoff_int | xoff_int_d1)\r
+ xoff_gen <=1;\r
+ \r
+ always @ (posedge tx_clk or posedge rst)\r
+ if (rst)\r
+ xon_gen <=0;\r
+ else if (xon_gen_complete)\r
+ xon_gen <=0;\r
+ else if (xon_int | xon_int_d1)\r
+ xon_gen <=1; \r
+\r
+ wire [15:0] pq_reduced = pause_quanta_set - 2;\r
+ \r
+ always @(posedge tx_clk or posedge rst)\r
+ if(rst)\r
+ countdown <= 0;\r
+ else if(xoff_gen)\r
+ countdown <= {pq_reduced,6'd0};\r
+ else if(xon_gen)\r
+ countdown <= 0;\r
+ else if(countdown != 0)\r
+ countdown <= countdown - 1;\r
+ \r
+endmodule // flow_ctrl\r
--- /dev/null
+\r
+// TX side of flow control -- when other side sends PAUSE, we wait\r
+\r
+module flow_ctrl_tx\r
+ (input rst,\r
+ input tx_clk,\r
+ //host processor\r
+ input tx_pause_en,\r
+ // From MAC_rx_ctrl\r
+ input [15:0] pause_quanta,\r
+ input pause_quanta_val,\r
+ // MAC_tx_ctrl\r
+ output pause_apply,\r
+ input pause_quanta_sub);\r
+ \r
+ // ****************************************************************************** \r
+ // Inhibit our TX from transmitting because they sent us a PAUSE frame\r
+ // ******************************************************************************\r
+\r
+ reg [15:0] pause_quanta_counter;\r
+ reg pqval_d1, pqval_d2; \r
+\r
+ always @(posedge tx_clk) pqval_d1 <= pause_quanta_val;\r
+ always @(posedge tx_clk) pqval_d2 <= pqval_d1;\r
+\r
+ always @ (posedge tx_clk or posedge rst)\r
+ if (rst)\r
+ pause_quanta_counter <= 0;\r
+ else if (pqval_d1 & ~pqval_d2)\r
+ pause_quanta_counter <= pause_quanta; \r
+ else if((pause_quanta_counter!=0) & pause_quanta_sub)\r
+ pause_quanta_counter <= pause_quanta_counter - 1;\r
+\r
+ assign pause_apply = tx_pause_en & (pause_quanta_counter != 0);\r
+ \r
+endmodule // flow_ctrl\r
--- /dev/null
+
+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
--- /dev/null
+
+
+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
--- /dev/null
+
+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