天天看点

RGMII自适应网络数据的处理方式

首先先夸一下黑金的FPGA开发板和相关教程,真心不错,下面的内容也是学习黑金的开发板获得的。

一、 RGMII简单说明

RGMII实际上是是简化版的GMII,时钟频率依旧是125MHz,为了保持1000Mbps的传输速率不变,在时钟的上升沿和下降沿都采样数据,在参考时钟的上升沿处理GMII接口中的TXD[3:0]/RXD[3:0],在下降沿处理TXD[7:4]/RXD[7:4],在TX_EN信号线上传送TX_EN和TX_ER两种信息,在TX_CLK的上升沿发送TX_EN,下降沿发送TX_ER;同样,RX_DV信号线上传输RX_DV和RX_ER两种信息,在RX_CLK的上升沿发送RX_DV,下降沿发送RX_ER。采用RGMII的主要作用是为了降低电路成本。

RGMII自适应网络数据的处理方式

RGMII还有个优势就是同时也兼容100Mbps和10Mbps两种速率,所以对于自适应的网络特别适用,此时的参考时钟分别为25MHz和2.5MHz。对于数据的处理在上升沿进行处理,与100Mbps的处理方式有所不同,所以为了达到自适应网络,FPGA程序部分需要做点处理,可能处理的方式有很多种,下面推荐一种处理方式。

二、 具体实现

下面方法以xilinx为模板做的。一般3种通信速率的PHY芯片都支持自检测通信速率,可以通过这个检测作为参考进行相应的处理。在数据的接口部分3种速率的处理方式都是一样的,在时钟的上升沿和下降沿都对数据进行处理,会用到原语IDDR和ODDR(这两个原语一个针对接收,一个针对发送,主要是在时钟的上升沿和下降沿都对数据进行处理)。为10Mbps/100Mbps时,接口传送的数据接口为4位,而数据处理时为8位处理,所以这可以当做是跨时钟域处理了,这就用到FIFO了,这里不管发送还是接收,都会用到2个FIFO,这2个FIFO的作用一个用来缓存数据,一个用来缓存数据长度,有人可能会觉得缓存数据长度的FIFO是多余的,非也,听我细细道来。在实际应用中,很多情况下发送的帧数据的长度并不是一个固定值,而是一个随机值,其次还有,这一帧数据还没处理结束,下一帧数据就过来了,所以也要对数据长度做一个缓存。其次,这个方式也不仅仅针对自适应以太网,在很多通信跨时钟域处理中都可以应用此方法。

下面还是看看程序的实现。

首先接口部分,可以看看上面的图,接收时钟由PHY部分提供,发送时钟由MAC部分提供,在写代码时,可以直接进行赋值就可以了,如果这个时钟不仅仅做一个转换的话,还是建议调用一下原语BUFG,这是提供全局时钟的。

ODDR #(
    .DDR_CLK_EDGE("SAME_EDGE")
  ) rgmii_txc_out (
    .Q (rgmii_txc),
    .C (gmii_tx_clk_s),
    .CE(1),
    .D1(1),
    .D2(0),
    .R(tx_reset_sync),
    .S(0));


  generate
  genvar i;
  for (i = 0; i < 4; i = i + 1) begin : gen_tx_data
    ODDR #(
      .DDR_CLK_EDGE("SAME_EDGE")
    ) rgmii_td_out (
      .Q (rgmii_td[i]),
      .C (gmii_tx_clk_s),
      .CE(1),
      .D1(gmii_txd_r_d1[i]),
      .D2(gmii_txd_low[i]),
      .R(tx_reset_sync),
      .S(0));
  end
  endgenerate

  ODDR #(
    .DDR_CLK_EDGE("SAME_EDGE")
  ) rgmii_tx_ctl_out (
    .Q (rgmii_tx_ctl),
    .C (gmii_tx_clk_s),
    .CE(1),
    .D1(gmii_tx_en_r_d1),
    .D2(rgmii_tx_ctl_r),
    .R(tx_reset_sync),
    .S(0));

  
     
      generate
      for (i = 0; i < 4; i = i + 1) begin
        IDDR #(
          .DDR_CLK_EDGE("SAME_EDGE_PIPELINED")
        ) rgmii_rx_iddr (
          .Q1(gmii_rxd_s[i]),
          .Q2(gmii_rxd_s[i+4]),
          .C(gmii_rx_clk),
          .CE(1),
          .D(rgmii_rd[i]),
          .R(0),
          .S(0));
      end
      endgenerate
    
      IDDR #(
        .DDR_CLK_EDGE("SAME_EDGE_PIPELINED")
      ) rgmii_rx_ctl_iddr (
        .Q1(gmii_rx_dv_s),
        .Q2(rgmii_rx_ctl_s),
        .C(gmii_rx_clk),
        .CE(1),
        .D(rgmii_rx_ctl),
        .R(0),
        .S(0));

           

上面是接口调用原语部分。

在接收数据时,要注意一下,在接口接收数据时接收了8位数据,其实在10Mbps/100Mbps时,高4位的数据就是低四位的数据;计数器计数的值为帧头的一半;还有就是缓存数据长度的FIFO写使能在数据有效信号的下降沿进行使能操作。具体实现主要分为2个状态,一个是检查缓存数据长度的FIFO是否有数据状态,另一个就是读FIFO数据状态了。具体相关代码如下:

`timescale 1ns / 1ps  
//
// Module Name:    ethernet_test 
//
module gmii_rx_buffer
(
 input              clk,
 input              rst_n,
 input              eth_100m_en,   //ethernet 100M enable
 input              eth_10m_en,    //ethernet 100M enable
 input              link,          //ethernet link signal
 input              gmii_rx_dv,    //gmii rx dv
 input  [7:0]       gmii_rxd,      //gmii rxd
 
 (* MARK_DEBUG="true" *)output reg         e10_100_rx_dv, //ethernet 10/100 rx_dv
 (* MARK_DEBUG="true" *)output    [7:0]    e10_100_rxd    //ethernet 10/100 rxd
 
);


reg  [15:0]           rx_cnt        ;   //write fifo counter
reg                   rx_wren       ;   //write fifo wren
reg  [7:0]            rx_wdata      ;   //write fifo data
reg  [15:0]           rx_data_cnt   ;   //read fifo counter
reg                   rx_rden       ;   //read fifo rden
wire [7:0]            rx_rdata      ;   //read fifo data
reg  [3:0]            rxd_high      ;   //rxd high 4 bit
reg  [3:0]            rxd_low       ;   //rxd low 4 bit
                      
(* MARK_DEBUG="true" *)reg                   gmii_rx_dv_d0 ;
(* MARK_DEBUG="true" *)reg                   gmii_rx_dv_d1 ;
reg                   gmii_rx_dv_d2 ;
                      
reg  [15:0]           pack_len      ;   //package length                  
reg  [1:0]            len_cnt       ;   //length latch counter
wire [4:0]            pack_num      ;   //length fifo usedw
reg                   rx_len_wren   ;   //length wren
reg  [15:0]           rx_len_wdata  ;   //length write data
reg                   rx_len_rden   ;   //length rden
wire [15:0]           rx_len        ;   //legnth read data


localparam IDLE             = 4'd0 ;
localparam CHECK_FIFO       = 4'd1 ;
localparam LEN_LATCH        = 4'd2 ;
localparam REC_WAIT         = 4'd3 ;
localparam READ_FIFO        = 4'd4 ;
localparam REC_END          = 4'd5 ;

reg [3:0]    state  ;
reg [3:0]    next_state ;

always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
      state  <=  IDLE  ;
    else
      state  <= next_state ;  
  end
  
always @(*)
  begin
    case(state)
	IDLE  : 
	  begin
		  next_state <= CHECK_FIFO ;
	  end
    CHECK_FIFO :
      begin
	    if (pack_num > 5'd0)                // if length fifo usedw > 0, means there is package in data fifo
		  next_state <= LEN_LATCH ;
		else 
		  next_state <= CHECK_FIFO ;		 
		end	
	LEN_LATCH:
	  begin
	    if (len_cnt == 2'd3)               // delay some clock
		  next_state <= REC_WAIT ;
		else 
		  next_state <= LEN_LATCH ;
	  end
	REC_WAIT :
	    next_state <= READ_FIFO ;	
	READ_FIFO :
      begin
	    if (rx_data_cnt == pack_len - 1)   // when reach package length read end
		  next_state <= REC_END ;
		else 
		  next_state <= READ_FIFO ;
		end	
	REC_END :
	      next_state <= IDLE ;
    default :
	   next_state <= IDLE ;
	endcase
  end  



  
/*************************************************
write length to fifo
*************************************************/
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
    begin
	  gmii_rx_dv_d0  <= 1'b0 ;
	  gmii_rx_dv_d1  <= 1'b0 ;
	  gmii_rx_dv_d2  <= 1'b0 ;
	end
	else
	begin
	  gmii_rx_dv_d0  <= gmii_rx_dv  ;
	  gmii_rx_dv_d1  <= gmii_rx_dv_d0 ;
	  gmii_rx_dv_d2  <= gmii_rx_dv_d1 ;
	end
  end
//write rx length wren to fifo when gmii_rx_dv negedge
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  rx_len_wren <=  1'b0 ;
	else if (gmii_rx_dv == 1'b0 && gmii_rx_dv_d0 == 1'b1)
	  rx_len_wren <=  eth_100m_en | eth_10m_en  ;
	else 
	  rx_len_wren <=  1'b0 ;
end

always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  rx_cnt   <= 16'd0  ;
	//else if (eth_10m_en &  (gmii_rx_dv_d0 | gmii_rx_dv_d1))     //when 10M mode, there is one unnecessary 4 bits data need to be take out
	else if (eth_10m_en &  (gmii_rx_dv | gmii_rx_dv_d0))     //when 10M mode, there is one unnecessary 4 bits data need to be take out
	  rx_cnt   <= rx_cnt + 1'b1 ;
	else if (eth_100m_en & (gmii_rx_dv | gmii_rx_dv_d1))
	  rx_cnt   <= rx_cnt + 1'b1 ;
	else if (state == REC_WAIT)
	  rx_cnt   <= 16'd0  ;
  end
  
//write length to fifo 
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  rx_len_wdata <= 16'd0 ;
	else
	  rx_len_wdata <=  rx_cnt ;
end



/*************************************************
write data to fifo
*************************************************/
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
    begin
	  rxd_high   <= 4'd0 ;
	  rxd_low    <= 4'd0 ;
	end
	else if (gmii_rx_dv | gmii_rx_dv_d1)
	begin
	  if (rx_cnt[0])
	  begin
        rxd_high <= gmii_rxd[3:0] ;	
	  end
      else
	  begin
        rxd_low  <= gmii_rxd[3:0] ;
	  end
    end
    else
    begin
	  rxd_high <= 4'd0 ;
	  rxd_low  <= 4'd0 ;
    end
  end	

always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
    begin
	  rx_wren  <= 1'b0 ;	
	  rx_wdata <= 8'd0 ;
	end
	else if (gmii_rx_dv_d1)
	begin
	  if (rx_cnt[0])
	  begin
        rx_wren  <= 1'b0 ;		
	  end
      else
	  begin
		rx_wdata <= {rxd_high,rxd_low} ;
        rx_wren  <= eth_100m_en | eth_10m_en ;		
	  end
    end
    else
    begin
	  rx_wren  <= 1'b0 ;	
	  rx_wdata <= 8'd0 ;
    end
  end  

/*************************************************
read length from fifo
*************************************************/
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  rx_len_rden <=  1'b0 ;
	else if (state == LEN_LATCH && len_cnt == 2'd0)
	  rx_len_rden <=  eth_100m_en | eth_10m_en  ;
	else 
	  rx_len_rden <=  1'b0 ;
end

always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  len_cnt <=  2'd0 ;
	else if (state == LEN_LATCH)
	  len_cnt <=  len_cnt + 1'b1 ;
	else 
	  len_cnt <=  2'd0 ;
end
//package total length  
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  pack_len <= 16'd0 ;
	else if (state == REC_WAIT)
	  pack_len <= rx_len/2 ;
end


/*************************************************
read data from fifo
*************************************************/	


//read data counter  
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  rx_data_cnt   <= 16'd0  ;
	else if (state == READ_FIFO)
	  rx_data_cnt   <= rx_data_cnt + 1'b1 ;
	else
	  rx_data_cnt   <= 16'd0  ;
  end


//read enable 
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  rx_rden   <= 1'b0  ;
	else if (state == READ_FIFO)
	  rx_rden   <= eth_100m_en | eth_10m_en  ;
	else 
	  rx_rden   <= 1'b0  ;
  end 

  
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  e10_100_rx_dv   <= 1'b0  ;
	else 
	  e10_100_rx_dv   <= rx_rden  ;
  end  

assign e10_100_rxd = rx_rdata ;


 eth_data_fifo rx_fifo 
 (
    .clk        (clk            ),                // input wire clk
    .rst        (~link          ),                // input wire rst
    .din        (rx_wdata       ),                // input wire [7 : 0] din
    .wr_en      (rx_wren        ),            // input wire wr_en
    .rd_en      (rx_rden        ),            // input wire rd_en
    .dout       (rx_rdata       ),              // output wire [7 : 0] dout
    .full       (               ),              // output wire full
    .empty      (               ),            // output wire empty
    .data_count (               )  // output wire [11 : 0] data_count
    );  
 
len_fifo rx_len_fifo
    (	
    .clk        (clk            ),                // input wire clk
    .rst        (~link          ),                // input wire rst
    .din        (rx_len_wdata   ),                // input wire [7 : 0] din
    .wr_en      (rx_len_wren    ),            // input wire wr_en
    .rd_en      (rx_len_rden    ),            // input wire rd_en
    .dout       (rx_len         ),              // output wire [7 : 0] dout
    .full       (               ),              // output wire full
    .empty      (               ),            // output wire empty
    .data_count (pack_num       )  // output wire [11 : 0] data_count
    );  
endmodule

           

在发送时,也和接收数据时差不多,只不过把接收数据的处理方式反过来了。具体相关代码如下:

`timescale 1ns / 1ps  
//
// Module Name:    ethernet_test 
//
module gmii_tx_buffer
(
 input              clk,
 input              rst_n, 
 input              eth_10_100m_en,      //ethernet 10M/100M enable
 input              link,                //ethernet link signal
 input              gmii_tx_en,          //gmii tx enable
 input  [7:0]       gmii_txd,            //gmii txd
 output reg         e10_100_tx_en,       //ethernet 10/100M tx enable
 output reg [7:0]   e10_100_txd          //ethernet 10/100M txd

 
);

(* MARK_DEBUG="true" *)reg  [7:0]            tx_wdata    ;      //tx data fifo write data
(* MARK_DEBUG="true" *)reg                   tx_wren     ;      //tx data fifo write enable
(* MARK_DEBUG="true" *)reg                   tx_rden     ;      //tx data fifo read enable
(* MARK_DEBUG="true" *)reg  [15:0]           tx_data_cnt ;      //tx data counter
(* MARK_DEBUG="true" *)wire [7:0]            tx_rdata    ;      //tx fifo read data
                      
                                  
reg  [16:0]           pack_len    ;      //package length
reg                   tx_en       ;      //tx enable
reg  [3:0]            txd_high    ;      //high 4 bits
reg  [3:0]            txd_low     ;      //low 4 bits
                                  
reg                   tx_en_d0    ; 
reg                   tx_en_d1    ;
                      
reg   [15:0]          tx_len_cnt    ;    //tx length counter
reg                   gmii_tx_en_d0 ;                          
reg   [1:0]           len_cnt       ;    //length latch counter
wire  [4:0]           pack_num      ;    //length fifo usedw
reg                   tx_len_wren   ;    //length fifo wren
reg                   tx_len_rden   ;    //length fifo rden
wire  [15:0]          tx_len_wdata  ;    //length fifo write data
wire  [15:0]          tx_len        ;    //length fifo read data



localparam IDLE            = 4'd0 ;
localparam CHECK_FIFO      = 4'd1 ;
localparam LEN_LATCH       = 4'd2 ;
localparam SEND_WAIT       = 4'd3 ;
localparam SEND            = 4'd4 ;
localparam SEND_WAIT_1     = 4'd5 ;
localparam SEND_END        = 4'd6 ;


reg [3:0]    state  ;
reg [3:0]    next_state ;

always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
      state  <=  IDLE  ;
    else
      state  <= next_state ;  
  end


  
always @(*)
  begin
    case(state)
	IDLE  :        
	  begin
		  next_state <= CHECK_FIFO ;
	  end
	CHECK_FIFO :   
      begin
	    if (pack_num > 5'd0)                  //check length fifo, if usedw > 0 ,there is a package in data fifo
		  next_state <= LEN_LATCH ;
		else 
		  next_state <= CHECK_FIFO ;		 
		end	
	LEN_LATCH:     
	  begin
	    if (len_cnt == 2'd3)                  //wait for read length fifo data
		  next_state <= SEND_WAIT ;
		else 
		  next_state <= LEN_LATCH ;
	  end
	SEND_WAIT :    
	  next_state <= SEND ;
	SEND :         
      begin
	    if (tx_data_cnt == pack_len - 1)      //read data fifo and send out
		  next_state <= SEND_WAIT_1 ;
		else 
		  next_state <= SEND ;
		end	
	SEND_WAIT_1 :   
      begin
	    if (tx_data_cnt == pack_len + 1)      //wait some clock for data latch
		  next_state <= SEND_END ;
		else 
		  next_state <= SEND_WAIT_1 ;
		end	
	SEND_END :    
	      next_state <= IDLE ;
    default :
	   next_state <= IDLE ;
	endcase
  end  
/*************************************************
write length to tx_len_fifo
*************************************************/
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  gmii_tx_en_d0 <= 1'b0 ;
	else
	  gmii_tx_en_d0 <=  gmii_tx_en  ;
end
//write tx length to fifo when gmii_tx_en negedge
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  tx_len_wren <=  1'b0 ;
	else if (gmii_tx_en == 1'b0 && gmii_tx_en_d0 == 1'b1)
	  tx_len_wren <=  eth_10_100m_en  ;
	else 
	  tx_len_wren <=  1'b0 ;
end
//calculate tx length  
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  tx_len_cnt <= 16'd0 ;
	else if (gmii_tx_en)
	  tx_len_cnt <=  tx_len_cnt + 1'b1  ;
	else if (tx_len_wren)
	  tx_len_cnt <= 16'd0 ;
end

assign tx_len_wdata = tx_len_cnt ;     //write length data 

/*************************************************
read length from tx_len_fifo
*************************************************/
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  tx_len_rden <=  1'b0 ;
	else if (state == LEN_LATCH && len_cnt == 2'd0)
	  tx_len_rden <=  eth_10_100m_en  ;
	else 
	  tx_len_rden <=  1'b0 ;
end

always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  len_cnt <=  2'd0 ;
	else if (state == LEN_LATCH)
	  len_cnt <=  len_cnt + 1'b1 ;
	else 
	  len_cnt <=  2'd0 ;
end
//package total length  
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  pack_len <= 17'd0 ;
	else if (state == SEND_WAIT)
	  pack_len <= 2*(tx_len) ;
end
//write data to tx_fifo
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	begin
	  tx_wren   <= 1'b0 ;
	  tx_wdata  <= 8'd0 ;
	end
	else
	begin
	  tx_wren   <= gmii_tx_en & eth_10_100m_en ;
	  tx_wdata  <= gmii_txd ;
	end
  end  

/*************************************************
read tx_fifo
*************************************************/	
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  tx_data_cnt   <= 16'd0  ;
	else if (state == SEND || state == SEND_WAIT_1)
	  tx_data_cnt   <= tx_data_cnt + 1'b1 ;
	else 
	  tx_data_cnt   <= 16'd0  ;
  end
//read data enable 
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  tx_rden   <= 1'b0  ;
	else if (state == SEND)
	  tx_rden   <= ~tx_data_cnt[0] & eth_10_100m_en ;
	else 
	  tx_rden   <= 1'b0  ;
  end
//gmii tx enable
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  tx_en   <= 1'b0  ;
	else if (state == SEND)
	  tx_en   <= 1'b1 ;
	else 
	  tx_en   <= 1'b0  ;
  end

always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	begin
	  tx_en_d0   <= 1'b0  ;
	  tx_en_d1   <= 1'b0  ;
	end
	else
	begin
	  tx_en_d0   <= tx_en ;
	  tx_en_d1   <= tx_en_d0 ;
	end
  end


always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	begin
	  txd_high <= 4'd0 ;
	  txd_low  <= 4'd0 ;
	end
	else
	begin
	  if (tx_data_cnt[0])
	    txd_high   <= tx_rdata[7:4]  ;
	  else 
	    txd_low   <= tx_rdata[3:0]  ;
	end
  end  

//ethernet 10/100M tx enable  
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  e10_100_tx_en   <= 1'b0  ;
	else
	  e10_100_tx_en   <= tx_en_d1 ;
  end
  
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
	  e10_100_txd   <= 8'd0  ;
	else if (tx_data_cnt[0])
	  e10_100_txd   <= {txd_low[3:0],txd_low[3:0]} ;
	else
	  e10_100_txd   <= {txd_high[3:0],txd_high[3:0]} ;
  end 


 eth_data_fifo tx_fifo 
 (
    .clk        (clk            ),                // input wire clk
    .rst        (~link          ),                // input wire rst
    .din        (tx_wdata       ),                // input wire [7 : 0] din
    .wr_en      (tx_wren        ),            // input wire wr_en
    .rd_en      (tx_rden        ),            // input wire rd_en
    .dout       (tx_rdata       ),              // output wire [7 : 0] dout
    .full       (               ),              // output wire full
    .empty      (               ),            // output wire empty
    .data_count (               )  // output wire [11 : 0] data_count
    );  
 
len_fifo tx_len_fifo
    (	
    .clk        (clk            ),                // input wire clk
    .rst        (~link          ),                // input wire rst
    .din        (tx_len_wdata   ),                // input wire [7 : 0] din
    .wr_en      (tx_len_wren    ),            // input wire wr_en
    .rd_en      (tx_len_rden    ),            // input wire rd_en
    .dout       (tx_len         ),              // output wire [7 : 0] dout
    .full       (               ),              // output wire full
    .empty      (               ),            // output wire empty
    .data_count (pack_num       )  // output wire [11 : 0] data_count
    );  
    
endmodule