Implement Eth flow control using pause frames
authorMatt Ettus <matt@ettus.com>
Sat, 5 Sep 2009 05:23:27 +0000 (22:23 -0700)
committerMatt Ettus <matt@ettus.com>
Sat, 5 Sep 2009 05:23:27 +0000 (22:23 -0700)
Not fully tested, but it seems to work without frame errors, sequence
number errors or ethernet overruns.  Still of course will get tx underruns
on a slow machine, and the transmitted signal has some issues though.

usrp2/firmware/lib/eth_mac.c
usrp2/firmware/lib/eth_mac_regs.h
usrp2/fpga/simple_gemac/flow_ctrl_rx.v
usrp2/fpga/simple_gemac/simple_gemac.v
usrp2/fpga/simple_gemac/simple_gemac_wb.v
usrp2/fpga/simple_gemac/simple_gemac_wrapper.v
usrp2/fpga/top/u2_rev3/Makefile

index becd93644b1f60a8f885493a2609fac69244c9d4..2ef1f73f4c8e9879e68821eb6e37869a49f5c1bc 100644 (file)
@@ -46,7 +46,10 @@ eth_mac_init(const u2_mac_addr_t *src)
   eth_mac->miimoder = 25;      // divider from CPU clock (50MHz/25 = 2MHz)
 
   eth_mac_set_addr(src);
-  eth_mac->settings = MAC_SET_PAUSE_EN | MAC_SET_PASS_BCAST | MAC_SET_PASS_UCAST;  // 0x39; 
+  eth_mac->settings = MAC_SET_PAUSE_EN | MAC_SET_PASS_BCAST | MAC_SET_PASS_UCAST | MAC_SET_PAUSE_SEND_EN; 
+
+  eth_mac->pause_time = 38;
+  eth_mac->pause_thresh = 1200;
 
   // set rx flow control high and low water marks
   // unsigned int lwmark = (2*2048 + 64)/4; // 2 * 2048-byte frames + 1 * 64-byte pause frame
index a14c0064135531f7ddc5181ba4f125047ce12a42..d680f8de0bb2a7b715f0171799ab611e91869269 100644 (file)
@@ -35,6 +35,8 @@ typedef struct {
   volatile int miicommand;
   volatile int miistatus;
   volatile int miirx_data;
+  volatile int pause_time;
+  volatile int pause_thresh;
 } eth_mac_regs_t;
 
 // settings register
@@ -44,6 +46,7 @@ typedef struct {
 #define MAC_SET_PASS_BCAST (1 << 3)  // Sends broadcast frames through (normally on)
 #define MAC_SET_PASS_MCAST (1 << 4)  // Sends multicast frames that match mcast addr (normally off)
 #define MAC_SET_PASS_UCAST (1 << 5)  // Sends unicast (normal) frames through if they hit in address filter (normally on)
+#define MAC_SET_PAUSE_SEND_EN (1 << 6) // Enables sending pause frames
 
 // miicommand register
 #define MIIC_SCANSSTAT (1 << 0)        // Scan status
index 7ded9e08b2c52ac82e9a6ffb9093ab103d8b7d7b..b13334d0ec389583a5214d5c2f23ccd5fce01dac 100644 (file)
@@ -2,84 +2,59 @@
 // 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
+  (input pause_request_en, input [15:0] pause_time, input [15:0] pause_thresh,\r
+   input rx_clk, input rx_reset, input [15:0] rx_fifo_space,\r
+   input tx_clk, input tx_reset, output reg pause_req, output reg [15:0] pause_time_req\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
+   // RX Clock Domain\r
+   reg xon, xoff;\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
+   wire [15:0] pause_low_thresh = pause_thresh;\r
+   wire [15:0] pause_hi_thresh = 16'hFFFF;\r
+   wire [21:0] pq_reduced = {pause_time,6'd0} - 1700;\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
+   always @(posedge rx_clk)\r
+     if(rx_reset)\r
+       xoff <= 0;\r
+     else\r
+       xoff <= (pause_request_en & (countdown==0) & (rx_fifo_space < pause_low_thresh));\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
+   always @(posedge rx_clk)\r
+     if(rx_reset)\r
+       xon  <= 0;\r
+     else\r
+       xon  <= ((countdown!=0) & (rx_fifo_space > pause_hi_thresh));\r
    \r
-   always @(posedge tx_clk or posedge rst)\r
-     if(rst)\r
+   always @(posedge rx_clk)\r
+     if(rx_reset)\r
        countdown <= 0;\r
-     else if(xoff_gen)\r
-       countdown <= {pq_reduced,6'd0};\r
-     else if(xon_gen)\r
+     else if(xoff)\r
+       countdown <= pq_reduced;\r
+     else if(xon)\r
        countdown <= 0;\r
      else if(countdown != 0)\r
        countdown <= countdown - 1;\r
+\r
+   // Cross clock domains\r
+   oneshot_2clk send_xon (.clk_in(rx_clk), .in(xon), .clk_out(tx_clk), .out(xon_tx));\r
+   oneshot_2clk send_xoff (.clk_in(rx_clk), .in(xoff), .clk_out(tx_clk), .out(xoff_tx));\r
+   \r
+   always @(posedge tx_clk)\r
+     if(xoff_tx)\r
+       pause_time_req <= pause_time;\r
+     else if(xon_tx)\r
+       pause_time_req <= 0;\r
+\r
+   always @(posedge tx_clk)\r
+     if(tx_reset)\r
+       pause_req      <= 0;\r
+     else \r
+       pause_req      <= xon_tx | xoff_tx;\r
    \r
-endmodule // flow_ctrl\r
+endmodule // flow_ctrl_rx\r
index 5ec2fa2bac90db0692a019ca346a25547b87384a..868a668199a43daa411c7f637e5c170f5b1c4019 100644 (file)
@@ -6,7 +6,7 @@ module simple_gemac
    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, input pause_en,
+   input pause_req, input [15:0] pause_time_req, input pause_respect_en,
 
    // Settings
    input [47:0] ucast_addr, input [47:0] mcast_addr,
@@ -33,7 +33,7 @@ module simple_gemac
       .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(ucast_addr),
-      .pause_req(pause_req), .pause_time(pause_time),  // We request flow control
+      .pause_req(pause_req), .pause_time(pause_time_req),  // We request flow control
       .pause_apply(pause_apply), .paused(paused)  // We respect flow control
       );
 
@@ -50,7 +50,7 @@ module simple_gemac
 
    flow_ctrl_tx flow_ctrl_tx
      (.rst(rst_txclk), .tx_clk(tx_clk),
-      .tx_pause_en(pause_en),
+      .tx_pause_en(pause_respect_en),
       .pause_quanta(pause_quanta_rcvd), // 16 bit value
       .pause_quanta_val(pause_rcvd),
       .pause_apply(pause_apply),
index cc2cdf7ecb62628b467b92d94a29c27ba44c8f7c..6df277e3ee627ae574b51f6521051e9dc43a2d2f 100644 (file)
@@ -24,7 +24,9 @@ module simple_gemac_wb
    inout mdio, output mdc,
    output [47:0] ucast_addr, output [47:0] mcast_addr,
    output pass_ucast, output pass_mcast, output pass_bcast,
-   output pass_pause, output pass_all, output pause_en  );
+   output pass_pause, output pass_all, 
+   output pause_respect_en, output pause_request_en, 
+   output [15:0] pause_time, output [15:0] pause_thresh  );
 
    wire   acc    = wb_cyc & wb_stb;
    wire   wr_acc  = wb_cyc & wb_stb & wb_we;
@@ -36,10 +38,10 @@ module simple_gemac_wb
      else
        wb_ack   <= acc & ~wb_ack;
        
-   wire [5:0] misc_settings;
-   assign {pass_ucast, pass_mcast, pass_bcast, pass_pause, pass_all, pause_en}         = misc_settings;
+   wire [6:0] misc_settings;
+   assign {pause_request_en, pass_ucast, pass_mcast, pass_bcast, pass_pause, pass_all, pause_respect_en} = misc_settings;
 
-   wb_reg #(.ADDR(0),.DEFAULT(6'b111001))
+   wb_reg #(.ADDR(0),.DEFAULT(7'b0111001))
    wb_reg_settings (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
                    .dat_i(wb_dat_i), .dat_o(misc_settings) );
    wb_reg #(.ADDR(1),.DEFAULT(0))
@@ -131,6 +133,14 @@ module simple_gemac_wb
       .WCtrlDataStart(WCtrlDataStart), .RStatStart(RStatStart), 
       .UpdateMIIRX_DATAReg(UpdateMIIRX_DATAReg) );
 
+   wb_reg #(.ADDR(11),.DEFAULT(0))
+   wb_reg_pausetime (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
+                    .dat_i(wb_dat_i), .dat_o(pause_time) );
+   
+   wb_reg #(.ADDR(12),.DEFAULT(0))
+   wb_reg_pausethresh (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
+                      .dat_i(wb_dat_i), .dat_o(pause_thresh) );
+   
    always @(posedge wb_clk)
      case(wb_adr[7:2])
        0 : wb_dat_o <= misc_settings;
@@ -144,6 +154,8 @@ module simple_gemac_wb
        8 : wb_dat_o <= MIICOMMAND;
        9 : wb_dat_o <= MIISTATUS;
        10: wb_dat_o <= MIIRX_DATA;
+       11: wb_dat_o <= pause_time;
+       12: wb_dat_o <= pause_thresh;
      endcase // case (wb_adr[7:2])
    
 endmodule // simple_gemac_wb
index de445476cb9c496e3415e62ddcb2b25e94f0b965..7511f3fb9650a67a93d417ac0695a88948a6c576 100644 (file)
@@ -140,7 +140,12 @@ module simple_gemac_wrapper
       .ll_src_rdy(tx_ll_src_rdy), .ll_dst_rdy(tx_ll_dst_rdy),
       .tx_data(tx_data), .tx_valid(tx_valid), .tx_error(tx_error), .tx_ack(tx_ack));
 
-   wire [31:0] debug_tx, debug_rx;
+   flow_ctrl_rx flow_ctrl_rx
+     (.pause_request_en(pause_request_en), .pause_time(pause_time), .pause_thresh(pause_thresh),
+      .rx_clk(rx_clk), .rx_reset(rx_reset), .rx_fifo_space(rx_fifo_space),
+      .tx_clk(tx_clk), .tx_reset(tx_reset), .pause_req(pause_req), .pause_time_req(pause_time_req));
+   
+   wire [31:0]           debug_tx, debug_rx;
    
    assign debug_tx  = { { tx_ll_data },
                        { tx_ll_sof, tx_ll_eof, tx_ll_src_rdy, tx_ll_dst_rdy, 
index 7847b8c72d7a5dc8512af1e31c9b4be6e12e9e6d..94681f6cd028f08ded39b2e974d29b035d1b8d67 100644 (file)
@@ -90,6 +90,7 @@ simple_gemac/simple_gemac_rx.v \
 simple_gemac/crc.v \
 simple_gemac/delay_line.v \
 simple_gemac/flow_ctrl_tx.v \
+simple_gemac/flow_ctrl_rx.v \
 simple_gemac/address_filter.v \
 simple_gemac/ll8_to_txmac.v \
 simple_gemac/rxmac_to_ll8.v \