FPGA——以太网设计(2)GMII与RGMII
- 基础知识
- (1)GMII
- (2)RGMII
- (3)IDDR
- GMII设计转RGMII接口
- 跨时钟传输模块
基础知识
(1)GMII
GMII:发送端时钟由MAC端提供
下降沿变化数据,上升沿采集数据
(2)RGMII
时钟是双沿采样
RGMII:ETH_RXCTL线同时表示有效和错误,有效和错误位相异或得到。
时钟偏移,方便采样
(3)IDDR
IDDR的三种模式
GMII设计转RGMII接口
千兆网:
输入和输出的时候,GMII的8位数据,先在时钟上升沿通过RGMII接口处理低四位,再在时钟的下降沿继续处理高四位。
百兆网:
只在时钟的上升沿通过RGMII接口处理低四位,下个时钟上升沿再处理高四位。所以在上升沿和下降沿都输入输出同一个数据就行。
module RGMII_Tri(/*--------rgmii port--------*/input i_rxc ,input [3 :0] i_rxd ,input i_rx_ctl ,output o_txc ,output [3 :0] o_txd ,output o_tx_ctl ,/*--------data port--------*/output o_rxc ,input [7 :0] i_send_data ,input i_send_valid ,output [7 :0] o_rec_data ,output o_rec_valid ,output o_rec_end ,output [1:0] o_speed ,output o_link
);reg [7 :0] ri_send_data =0 ;
reg ri_send_valid=0 ;
reg [7 :0] ro_rec_data = 0 ;
reg ro_rec_valid= 0 ;
reg ro_rec_end = 0 ;
reg r_cnt_10_100= 0 ;
reg r_tx_cnt_10_100 = 0 ;
reg [1 :0] ro_speed=0 ;
reg ro_link =0 ;
reg [1 :0] r_rec_valid=0 ;wire w_rxc_bufr ;
wire w_rxc_bufio ;
wire w_rxc_idelay ;
wire [3 :0] w_rxd_ibuf ;
wire w_rx_ctl_ibuf ;
(* mark_debug = "true" *)wire [7 :0] w_rec_data ;
(* mark_debug = "true" *)wire [1 :0] w_rec_valid ;
wire [3 :0] w_send_d1 ;
wire [3 :0] w_send_d2 ;
wire w_send_valid ;
wire i_speed1000 ;
wire w_txc ; assign w_txc = ~w_rxc_bufr;
assign o_rxc = w_rxc_bufr;
assign o_speed = ro_speed ;
assign o_link = ro_link ;
assign i_speed1000 = ro_speed == 2'b10 ? 1 : 0;
assign o_rec_data = ro_rec_data ;
assign o_rec_valid = ro_rec_valid;
assign o_rec_end = ro_rec_end ;OBUF #(.DRIVE (12 ), // Specify the output drive strength.IOSTANDARD ("DEFAULT" ), // Specify the output I/O standard.SLEW ("SLOW" ) // Specify the output slew rate
) OBUF_inst (.O (o_txc ), // Buffer output (connect directly to top-level port).I (w_txc ) // Buffer input
);// ODDR #(
// .DDR_CLK_EDGE ("OPPOSITE_EDGE" ), // "OPPOSITE_EDGE" or "SAME_EDGE"
// .INIT (1'b0 ), // Initial value of Q: 1'b0 or 1'b1
// .SRTYPE ("SYNC" ) // Set/Reset type: "SYNC" or "ASYNC"
// ) ODDR_inst (
// .Q (o_txc ), // 1-bit DDR output
// .C (w_rxc_bufr ), // 1-bit clock input
// .CE (1 ), // 1-bit clock enable input
// .D1 (0 ), // 1-bit data input (positive edge)
// .D2 (1 ), // 1-bit data input (negative edge)
// .R (0 ), // 1-bit reset
// .S (0 ) // 1-bit set
// );BUFIO BUFIO_inst (.O (w_rxc_bufio ),.I (i_rxc )
);BUFR #(.BUFR_DIVIDE ("BYPASS" ), .SIM_DEVICE ("7SERIES" )
)
BUFR_inst (.O (w_rxc_bufr ), .CE (1 ), .CLR (0 ), .I (i_rxc )
);// (* IODELAY_GROUP = "rgmii" *)
// IDELAYCTRL IDELAYCTRL_U0 (
// .RDY (RDY), // 1-bit output: Ready output
// .REFCLK (REFCLK), // 1-bit input: Reference clock input
// .RST (RST) // 1-bit input: Active high reset input
// );// (* IODELAY_GROUP = "rgmii" *)
// IDELAYE2 #(
// .CINVCTRL_SEL ("FALSE" ), // Enable dynamic clock inversion (FALSE, TRUE)
// .DELAY_SRC ("IDATAIN" ), // Delay input (IDATAIN, DATAIN)
// .HIGH_PERFORMANCE_MODE ("FALSE" ), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
// .IDELAY_TYPE ("FIXED" ), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
// .IDELAY_VALUE (0 ), // Input delay tap setting (0-31) 0.15625
// .PIPE_SEL ("FALSE" ), // Select pipelined mode, FALSE, TRUE
// .REFCLK_FREQUENCY (200.0 ), // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
// .SIGNAL_PATTERN ("DATA" ) // DATA, CLOCK input signal
// )
// IDELAYE2_inst (
// .CNTVALUEOUT (), // 5-bit output: Counter value output
// .DATAOUT (w_rxc_idelay ), // 1-bit output: Delayed data output
// .C (), // 1-bit input: Clock input
// .CE (), // 1-bit input: Active high enable increment/decrement input
// .CINVCTRL (), // 1-bit input: Dynamic clock inversion input
// .CNTVALUEIN (), // 5-bit input: Counter value input
// .DATAIN (), // 1-bit input: Internal delay data input
// .IDATAIN (i_rxc ), // 1-bit input: Data input from the I/O
// .INC (), // 1-bit input: Increment / Decrement tap delay input
// .LD (), // 1-bit input: Load IDELAY_VALUE input
// .LDPIPEEN (), // 1-bit input: Enable PIPELINE register to load data input
// .REGRST () // 1-bit input: Active-high reset tap-delay input
// );genvar rxd_i;
generate for(rxd_i = 0 ;rxd_i < 4 ;rxd_i = rxd_i + 1)
beginIBUF #(.IBUF_LOW_PWR ("TRUE" ), .IOSTANDARD ("DEFAULT" )) IBUF_U (.O (w_rxd_ibuf[rxd_i] ), // Buffer output.I (i_rxd[rxd_i] ) // Buffer input (connect directly to top-level port));IDDR #(.DDR_CLK_EDGE ("SAME_EDGE_PIPELINED" ),.INIT_Q1 (1'b0 ),.INIT_Q2 (1'b0 ),.SRTYPE ("SYNC" ) ) IDDR_u0 ( .Q1 (w_rec_data[rxd_i] ), // 1-bit output for positive edge of clock .Q2 (w_rec_data[rxd_i +4] ), // 1-bit output for negative edge of clock.C (w_rxc_bufio ), .CE (1 ),.D (w_rxd_ibuf[rxd_i] ), .R (0 ), .S (0 ) );
end
endgenerateIBUF #(.IBUF_LOW_PWR ("TRUE" ), .IOSTANDARD ("DEFAULT" )
)
IBUF_U
( .O (w_rx_ctl_ibuf ), // Buffer output.I (i_rx_ctl ) // Buffer input (connect directly to top-level port)
);IDDR #(.DDR_CLK_EDGE ("SAME_EDGE_PIPELINED" ),.INIT_Q1 (1'b0 ),.INIT_Q2 (1'b0 ),.SRTYPE ("SYNC" )
)
IDDR_u0
( .Q1 (w_rec_valid[0] ), // 1-bit output for positive edge of clock .Q2 (w_rec_valid[1] ), // 1-bit output for negative edge of clock.C (w_rxc_bufio ), .CE (1 ),.D (w_rx_ctl_ibuf ), .R (0 ), .S (0 )
);always@(posedge w_rxc_bufr)
beginif(!i_speed1000 && (&w_rec_valid))r_cnt_10_100 <= r_cnt_10_100 + 1;else r_cnt_10_100 <= 'd0;
end always@(posedge w_rxc_bufr)
beginif(&w_rec_valid && i_speed1000)ro_rec_valid <= 'd1;else ro_rec_valid <= r_cnt_10_100;
endalways@(posedge w_rxc_bufr)
beginif(i_speed1000)ro_rec_data <= w_rec_data;else ro_rec_data <= {w_rec_data[3:0],ro_rec_data[7:4]};
endalways@(posedge w_rxc_bufr)
beginr_rec_valid <= w_rec_valid;
endalways@(posedge w_rxc_bufr)
beginif(!w_rec_valid && r_rec_valid)ro_rec_end <= 'd1;else ro_rec_end <= 'd0;
endalways@(posedge w_rxc_bufr)
beginif(w_rec_valid == 'd0) beginro_speed <= w_rec_data[2:1];ro_link <= w_rec_data[0];end else beginro_speed <= ro_speed;ro_link <= ro_link ;end
end/*---------rgmii send--------*/
always@(posedge w_rxc_bufr)
beginri_send_data <= i_send_data;ri_send_valid <= i_send_valid;
endalways@(posedge w_rxc_bufr)
beginif(i_send_valid)r_tx_cnt_10_100 <= r_tx_cnt_10_100 + 1;else r_tx_cnt_10_100 <= 'd0;
endgenvar txd_i;
generate for(txd_i = 0 ;txd_i < 4 ; txd_i = txd_i + 1)
beginassign w_send_d1[txd_i] = i_speed1000 ? i_send_data[txd_i] : r_tx_cnt_10_100 == 0 ? i_send_data[txd_i] : ri_send_data[txd_i + 4];assign w_send_d2[txd_i] = i_speed1000 ? i_send_data[txd_i + 4] : r_tx_cnt_10_100 == 0 ? i_send_data[txd_i] : ri_send_data[txd_i + 4];ODDR #(.DDR_CLK_EDGE ("OPPOSITE_EDGE" ),.INIT (1'b0 ),.SRTYPE ("SYNC" ) ) ODDR_u (.Q (o_txd[txd_i] ), .C (w_txc ),.CE (1 ),.D1 (w_send_d1[txd_i] ), .D2 (w_send_d2[txd_i] ), .R (0 ),.S (0 ) );
end
endgenerateassign w_send_valid = i_speed1000 ? i_send_valid : i_send_valid | ri_send_valid;ODDR#(.DDR_CLK_EDGE ("OPPOSITE_EDGE" ),.INIT (1'b0 ),.SRTYPE ("SYNC" )
)
ODDR_uu0
(.Q (o_tx_ctl ), .C (w_txc ),.CE (1 ),.D1 (w_send_valid ), .D2 (w_send_valid ), .R (0 ),.S (0 )
);endmodule
跨时钟传输模块
module RGMII_RAM(input i_udp_stack_clk ,input [7 :0] i_GMII_data ,input i_GMII_valid ,output [7 :0] o_GMII_data ,output o_GMII_valid ,input i_rxc ,input i_speed1000 ,output [7 :0] o_send_data ,output o_send_valid ,input [7 :0] i_rec_data ,input i_rec_valid ,input i_rec_end
);/***************function**************//***************parameter*************//***************port******************/ /***************mechine***************//***************reg*******************/
reg [10:0] r_ram_addr_A=0 ;
reg [10:0] r_rec_len =0 ;
reg r_ram_en_B =0 ;
reg r_ram_en_B_1d=0 ;
reg r_ram_en_B_2d=0 ;
reg [10:0] r_ram_addr_B=0 ;
reg r_fifo_wr_en=0 ;
reg r_fifo_rd_en=0 ;
reg ri_rec_en =0 ;
reg r_read_run =0 ;
reg [10:0] r_read_cnt =0 ;
reg [7 :0] ro_GMII_data =0 ;
reg ro_GMII_valid=0 ;
reg [10:0] r_tx_ram_addr_A=10 ;
reg [10:0] r_tx_len=10 ;
reg r_tx_fifo_wren=0 ;
reg ri_GMII_valid=0 ;
reg r_tx_ram_en_B=0 ;
reg [10:0] r_tx_ram_addr_B=0 ;
reg r_tx_fifo_rden=0 ;
reg r_tx_read_run=0 ;
reg [10:0] r_tx_cnt =0 ;
reg [7 :0] ro_send_data =0 ;
reg ro_send_valid=0 ;
reg w_rxc=0 ;
reg ri_rec_end=0 ;
reg ro_send_valid_1d=0 ;/***************wire******************/
wire [7 :0] w_ram_dout_B ;
wire [10:0] w_fifo_dout ;
wire w_fifo_full ;
wire w_fifo_empty ;
wire [7 :0] w_tx_ram_dout ;
wire [10:0] w_tx_fifo_dout ;
wire w_tx_fifo_full ;
wire w_tx_fifo_empty ;/***************component*************/
RAM_8_1600 RAM_8_1600_U0 (.clka (i_rxc ), // input wire clka.ena (i_rec_valid ), // input wire ena.wea (i_rec_valid ), // input wire [0 : 0] wea.addra (r_ram_addr_A ), // input wire [10 : 0] addra.dina (i_rec_data ), // input wire [7 : 0] dina.douta ( ), // output wire [7 : 0] douta.clkb (i_udp_stack_clk), // input wire clkb.enb (r_ram_en_B ), // input wire enb.web (0 ), // input wire [0 : 0] web.addrb (r_ram_addr_B ), // input wire [10 : 0] addrb.dinb (0 ), // input wire [7 : 0] dinb.doutb (w_ram_dout_B ) // output wire [7 : 0] doutb
);FIFO_ASYNC_11_64 FIFO_ASYNC_11_64_u0 (.wr_clk (i_rxc ), // input wire wr_clk.rd_clk (i_udp_stack_clk), // input wire rd_clk.din (r_rec_len ), // input wire [10 : 0] din.wr_en (r_fifo_wr_en ), // input wire wr_en.rd_en (r_fifo_rd_en ), // input wire rd_en.dout (w_fifo_dout ), // output wire [10 : 0] dout.full (w_fifo_full ), // output wire full.empty (w_fifo_empty ) // output wire empty
);RAM_8_1600 RAM_8_1600_tx_U0 (.clka (i_udp_stack_clk ), // input wire clka.ena (i_GMII_valid ), // input wire ena.wea (i_GMII_valid ), // input wire [0 : 0] wea.addra (r_tx_ram_addr_A ), // input wire [10 : 0] addra.dina (i_GMII_data ), // input wire [7 : 0] dina.douta (), // output wire [7 : 0] douta.clkb (i_rxc ), // input wire clkb.enb (r_tx_ram_en_B ), // input wire enb.web (0 ), // input wire [0 : 0] web.addrb (r_tx_ram_addr_B ), // input wire [10 : 0] addrb.dinb (0 ), // input wire [7 : 0] dinb.doutb (w_tx_ram_dout ) // output wire [7 : 0] doutb
);FIFO_ASYNC_11_64 FIFO_ASYNC_11_64_tx_u0 (.wr_clk (i_udp_stack_clk ), // input wire wr_clk.rd_clk (i_rxc ), // input wire rd_clk.din (r_tx_len ), // input wire [10 : 0] din.wr_en (r_tx_fifo_wren ), // input wire wr_en.rd_en (r_tx_fifo_rden ), // input wire rd_en.dout (w_tx_fifo_dout ), // output wire [10 : 0] dout.full (w_tx_fifo_full ), // output wire full.empty (w_tx_fifo_empty ) // output wire empty
);/***************assign****************/
assign o_GMII_data = ro_GMII_data ;
assign o_GMII_valid = ro_GMII_valid ;
assign o_send_data = ro_send_data ;
assign o_send_valid = ro_send_valid_1d ;/***************always****************/
/*--------rgmii--------*/
always@(posedge i_rxc)
beginif(i_rec_valid)r_ram_addr_A <= r_ram_addr_A + 1;else if(i_rec_end)r_ram_addr_A <= 'd0;else r_ram_addr_A <= r_ram_addr_A;
endalways@(posedge i_rxc)
beginif(i_rec_valid)r_rec_len <= r_ram_addr_A + 1;else r_rec_len <= r_rec_len;
endalways@(posedge i_rxc)
beginri_rec_end <= i_rec_end;
endalways@(posedge i_rxc)
beginif(i_rec_end & !ri_rec_end)r_fifo_wr_en <= 'd1;else r_fifo_wr_en <= 'd0;
endalways@(posedge i_rxc)
beginif(r_tx_cnt == w_tx_fifo_dout)r_tx_read_run <= 'd0;else if(!w_tx_fifo_empty)r_tx_read_run <= 'd1;else r_tx_read_run <= r_tx_read_run;
endalways@(posedge i_rxc)
beginif(!r_tx_read_run && !w_tx_fifo_empty)r_tx_fifo_rden <= 'd1;else r_tx_fifo_rden <= 'd0;
endalways@(posedge i_rxc)
beginendalways@(posedge i_rxc)
beginif(i_speed1000)if(r_tx_cnt == w_tx_fifo_dout)r_tx_ram_en_B <= 'd0;else if(r_tx_fifo_rden)r_tx_ram_en_B <= 'd1;else r_tx_ram_en_B <= r_tx_ram_en_B;else if(r_tx_ram_en_B)r_tx_ram_en_B <= 'd0;else if(r_tx_fifo_rden || r_tx_read_run)r_tx_ram_en_B <= 'd1;else r_tx_ram_en_B <= 'd0;
endalways@(posedge i_rxc)
beginif(r_tx_ram_en_B)r_tx_ram_addr_B <= r_tx_ram_addr_B + 1;else r_tx_ram_addr_B <= 'd0;
endalways@(posedge i_rxc)
beginif(r_tx_ram_en_B)r_tx_cnt <= r_tx_cnt + 1;else r_tx_cnt <= 'd0;
endalways@(posedge i_rxc)
beginro_send_data <= w_tx_ram_dout;ro_send_valid <= r_tx_ram_en_B;
end
/*--------udp--------*/
always@(posedge i_udp_stack_clk)
beginif(r_read_cnt == w_fifo_dout)r_read_run <= 'd0;else if(!w_fifo_empty)r_read_run <= 'd1;else r_read_run <= r_read_run;
endalways@(posedge i_udp_stack_clk)
beginif(!r_read_run && !w_fifo_empty)r_fifo_rd_en <= 'd1;else r_fifo_rd_en <= 'd0;
endalways@(posedge i_udp_stack_clk)
beginif(r_read_cnt == w_fifo_dout) r_read_cnt <= 'd0;else if(r_ram_en_B) r_read_cnt <= r_read_cnt + 1;else r_read_cnt <= r_read_cnt;
endalways@(posedge i_udp_stack_clk)
beginif(r_read_cnt == w_fifo_dout)r_ram_en_B <= 'd0;else if(r_fifo_rd_en) r_ram_en_B <= 'd1;elser_ram_en_B <= r_ram_en_B;
endalways@(posedge i_udp_stack_clk)
beginif(r_ram_en_B)r_ram_addr_B <= r_ram_addr_B + 1;else r_ram_addr_B <= 'd0;
endalways@(posedge i_udp_stack_clk)
beginr_ram_en_B_1d <= r_ram_en_B;ro_GMII_data <= w_ram_dout_B;r_ram_en_B_2d <= r_ram_en_B_1d;
endalways@(posedge i_udp_stack_clk)
beginif(!r_ram_en_B & r_ram_en_B_1d)ro_GMII_valid <= 'd0;else if(r_ram_en_B_1d & !r_ram_en_B_2d)ro_GMII_valid <= 'd1;else ro_GMII_valid <= ro_GMII_valid;
endalways@(posedge i_udp_stack_clk)
beginif(i_GMII_valid)r_tx_ram_addr_A <= r_tx_ram_addr_A + 1;else r_tx_ram_addr_A <= 'd0;
endalways@(posedge i_udp_stack_clk)
beginif(i_GMII_valid)r_tx_len <= r_tx_ram_addr_A;else r_tx_len <= r_tx_len;
endalways@(posedge i_udp_stack_clk)
beginri_GMII_valid <= i_GMII_valid;ro_send_valid_1d <= ro_send_valid;
end always@(posedge i_udp_stack_clk)
beginif(!i_GMII_valid & ri_GMII_valid)r_tx_fifo_wren <= 'd1;else r_tx_fifo_wren <= 'd0;
endendmodule
有问题可以加企鹅群 658476482 交流