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)
begin
IBUF #(
.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
endgenerate
IBUF #(
.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)
begin
if(!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)
begin
if(&w_rec_valid && i_speed1000)
ro_rec_valid <= 'd1;
else
ro_rec_valid <= r_cnt_10_100;
end
always@(posedge w_rxc_bufr)
begin
if(i_speed1000)
ro_rec_data <= w_rec_data;
else
ro_rec_data <= {w_rec_data[3:0],ro_rec_data[7:4]};
end
always@(posedge w_rxc_bufr)
begin
r_rec_valid <= w_rec_valid;
end
always@(posedge w_rxc_bufr)
begin
if(!w_rec_valid && r_rec_valid)
ro_rec_end <= 'd1;
else
ro_rec_end <= 'd0;
end
always@(posedge w_rxc_bufr)
begin
if(w_rec_valid == 'd0) begin
ro_speed <= w_rec_data[2:1];
ro_link <= w_rec_data[0];
end else begin
ro_speed <= ro_speed;
ro_link <= ro_link ;
end
end
/*---------rgmii send--------*/
always@(posedge w_rxc_bufr)
begin
ri_send_data <= i_send_data;
ri_send_valid <= i_send_valid;
end
always@(posedge w_rxc_bufr)
begin
if(i_send_valid)
r_tx_cnt_10_100 <= r_tx_cnt_10_100 + 1;
else
r_tx_cnt_10_100 <= 'd0;
end
genvar txd_i;
generate for(txd_i = 0 ;txd_i < 4 ; txd_i = txd_i + 1)
begin
assign 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
endgenerate
assign 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)
begin
if(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;
end
always@(posedge i_rxc)
begin
if(i_rec_valid)
r_rec_len <= r_ram_addr_A + 1;
else
r_rec_len <= r_rec_len;
end
always@(posedge i_rxc)
begin
ri_rec_end <= i_rec_end;
end
always@(posedge i_rxc)
begin
if(i_rec_end & !ri_rec_end)
r_fifo_wr_en <= 'd1;
else
r_fifo_wr_en <= 'd0;
end
always@(posedge i_rxc)
begin
if(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;
end
always@(posedge i_rxc)
begin
if(!r_tx_read_run && !w_tx_fifo_empty)
r_tx_fifo_rden <= 'd1;
else
r_tx_fifo_rden <= 'd0;
end
always@(posedge i_rxc)
begin
end
always@(posedge i_rxc)
begin
if(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;
end
always@(posedge i_rxc)
begin
if(r_tx_ram_en_B)
r_tx_ram_addr_B <= r_tx_ram_addr_B + 1;
else
r_tx_ram_addr_B <= 'd0;
end
always@(posedge i_rxc)
begin
if(r_tx_ram_en_B)
r_tx_cnt <= r_tx_cnt + 1;
else
r_tx_cnt <= 'd0;
end
always@(posedge i_rxc)
begin
ro_send_data <= w_tx_ram_dout;
ro_send_valid <= r_tx_ram_en_B;
end
/*--------udp--------*/
always@(posedge i_udp_stack_clk)
begin
if(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;
end
always@(posedge i_udp_stack_clk)
begin
if(!r_read_run && !w_fifo_empty)
r_fifo_rd_en <= 'd1;
else
r_fifo_rd_en <= 'd0;
end
always@(posedge i_udp_stack_clk)
begin
if(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;
end
always@(posedge i_udp_stack_clk)
begin
if(r_read_cnt == w_fifo_dout)
r_ram_en_B <= 'd0;
else if(r_fifo_rd_en)
r_ram_en_B <= 'd1;
else
r_ram_en_B <= r_ram_en_B;
end
always@(posedge i_udp_stack_clk)
begin
if(r_ram_en_B)
r_ram_addr_B <= r_ram_addr_B + 1;
else
r_ram_addr_B <= 'd0;
end
always@(posedge i_udp_stack_clk)
begin
r_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;
end
always@(posedge i_udp_stack_clk)
begin
if(!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;
end
always@(posedge i_udp_stack_clk)
begin
if(i_GMII_valid)
r_tx_ram_addr_A <= r_tx_ram_addr_A + 1;
else
r_tx_ram_addr_A <= 'd0;
end
always@(posedge i_udp_stack_clk)
begin
if(i_GMII_valid)
r_tx_len <= r_tx_ram_addr_A;
else
r_tx_len <= r_tx_len;
end
always@(posedge i_udp_stack_clk)
begin
ri_GMII_valid <= i_GMII_valid;
ro_send_valid_1d <= ro_send_valid;
end
always@(posedge i_udp_stack_clk)
begin
if(!i_GMII_valid & ri_GMII_valid)
r_tx_fifo_wren <= 'd1;
else
r_tx_fifo_wren <= 'd0;
end
endmodule
有问题可以加企鹅群 658476482 交流