RGMII回环:IDDR+ODDR+差分接口
一、实验内容
1、通过IDDR和ODDR的方式完成RGMII协议;
2、外部接口使用OBUFDS、IBUFDS转换成差分接口;
3、数据转换及传输:顶层文件自己产生100次数,每个数都是8bit,传给oddr模块,oddr模块经过转换再传出4bit,这4bit数据再输出到外部(仿真的时候,可以接到输入,实际板测可以用跳线帽短接,也可以传给另一块板子,进行回环实验),4bit数据重新从顶层输入到iddr模块,iddr模块经过转换,输出8bit数据,输出到外部。
二、原理解释
本实验参考了部分文章,一些原理请直接看以下文章:
1、VIVADO IDDR与ODDR原语的使用
2、Xilinx 差分信号 LVDS传输实战
三、程序
1、顶层文件:
这里面的diff定义=lvds,之所以定义成diff,是老大说这样对于IO的适用范围更大。
顶层模块例化了一个iddr_top模块,一个PLL模块(输入50mhz系统时钟,输出125mhz高速时钟采集数据),一个oddr模块。
`timescale 1ns / 1ps
module I_ODDR_ETH_DIFF_TOP(
//System Interfaces
input sclk ,
input rst_n ,
input rx_clk_p ,//IDDR IN CLK
input rx_clk_n ,//IDDR IN CLK
input [ 3:0] rx_data_p ,//IDDR IN DATA
input [ 3:0] rx_data_n ,//IDDR IN DATA
output tx_clk_p ,//ODDR CLK
output tx_clk_n ,//ODDR CLK
output [ 3:0] tx_data_p ,//ODDR DATA
output [ 3:0] tx_data_n ,//ODDR DATA
output gb_out_clk_p ,//IDDR OUT CLK
output gb_out_clk_n ,//IDDR OUT CLK
output [ 7:0] gb_out_data_p ,//IDDR OUT DATA
output [ 7:0] gb_out_data_n //IDDR OUT DATA
);
wire [3:0] rx_data;
wire [3:0] tx_data;
reg [ 7:0] gb_tx_data ;
wire phy_rst_n ;
wire tx_data_ctrl ;
reg gb_tx_data_en ;
reg [ 7:0] gb_tx_cnt ;
wire gb_tx_clk ;
wire gb_out_clk ;
wire [7:0] gb_out_data ;
clk_wiz_1 clk_wiz_1_inst
(
// Clock out ports
.clk_out1(gb_tx_clk),
// Clock in ports
.clk_in1(sclk) //iddr clk_90 default:sclk
);
// diff out, 把iddr转化的数据,通过diff发送出去
// 输出50M相位90度的差分时钟
OBUFDS #(
.IOSTANDARD ( "DEFAULT" ), // Specify the output I/O standard
.SLEW ( "SLOW" )
)
dclko_OBUFDS
(
.O ( tx_clk_p ),
.OB ( tx_clk_n ),
.I ( tx_clk )
);
genvar ao;
generate
for (ao = 0; ao < 4; ao = ao+1) begin
// 输出oddr的数据
OBUFDS #(
.IOSTANDARD ( "DEFAULT" ), // Specify the output I/O standard
.SLEW ( "SLOW" )
)
dout_OBUFDS
(
.O ( tx_data_p[ao] ),
.OB ( tx_data_n[ao] ),
.I ( tx_data[ao] )
);
end
endgenerate
//diff in
// 还原单端时钟
IBUFDS
#(
.DIFF_TERM ( "TRUE" ), // Differential Termination
.IBUF_LOW_PWR ( "TRUE" ), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD ( "DEFAULT" ) // Specify the input I/O standard
)
rxlki_IBUFDS
(
.O ( rx_clk ), // 1-bit output: Buffer output
.I ( rx_clk_p ), // 1-bit input: Diff_p buffer input (connect directly to top-level port)
.IB ( rx_clk_n ) // 1-bit input: Diff_n buffer input (connect directly to top-level port)
);
// 还原单端数据
genvar i;
generate
for (i = 0; i < 4; i = i+1) begin
IBUFDS
#(
.DIFF_TERM ( "TRUE" ), // Differential Termination
.IBUF_LOW_PWR ( "TRUE" ), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD ( "DEFAULT" ) // Specify the input I/O standard
)
rxdatai_IBUFDS
(
.O ( rx_data[i] ), // 1-bit output: Buffer output
.I ( rx_data_p[i] ), // 1-bit input: Diff_p buffer input (connect directly to top-level port)
.IB ( rx_data_n[i] ) // 1-bit input: Diff_n buffer input (connect directly to top-level port)
);
end
endgenerate
//IDDR 8bit
genvar io;
generate
for (io = 0; io < 8; io = io+1) begin
// 输出oddr的数据
OBUFDS #(
.IOSTANDARD ( "DEFAULT" ), // Specify the output I/O standard
.SLEW ( "SLOW" )
)
iddrout_OBUFDS
(
.O ( gb_out_data_p[io] ),
.OB ( gb_out_data_n[io] ),
.I ( gb_out_data[io] )
);
end
endgenerate
OBUFDS #(
.IOSTANDARD ( "DEFAULT" ), // Specify the output I/O standard
.SLEW ( "SLOW" )
)
iddrclko_OBUFDS
(
.O ( gb_out_clk_p ),
.OB ( gb_out_clk_n ),
.I ( gb_out_clk )
);
always@(posedge gb_tx_clk or negedge phy_rst_n)
if(phy_rst_n == 1'b0)
gb_tx_cnt <=8'b0;
else if(gb_tx_cnt == 8'd99)
gb_tx_cnt <=8'd0;
else
gb_tx_cnt <=gb_tx_cnt+1'b1;
always @(posedge gb_tx_clk or negedge rst_n)
if(rst_n == 1'b0)begin
gb_tx_data <=8'd0;
gb_tx_data_en <=1'b0;
end
else if(phy_rst_n == 1'b1)begin
gb_tx_data_en <=1'b1;
if (gb_tx_cnt < 8'd7)
gb_tx_data <= 8'h55;
else if(gb_tx_cnt == 8'd7)
gb_tx_data <= 8'hd5;
else
gb_tx_data <= gb_tx_cnt;
end
else begin
gb_tx_data <=8'd0;
gb_tx_data_en <=1'b0;
end
oddr_ctrl oddr_ctrl_inst(
//System Interfaces
.rst_n (rst_n ),
//Gigbit Interfaces
.tx_data (tx_data ),
.tx_data_ctrl (tx_data_ctrl ),
.tx_clk (tx_clk ),
//Communication Interfaces
.gb_tx_data (gb_tx_data ),
.gb_tx_data_en (gb_tx_data_en ),
.gb_tx_data_err (1'b0 ),
.gb_tx_clk (gb_tx_clk )
);
IDDR_TOP gbit_top_inat(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//Gigbit Interfaces
.phy_rst_n (phy_rst_n ),
.rx_data (rx_data ),
.rx_ctrl (tx_data_ctrl ),
.gb_rx_data (gb_out_data ),
.rx_clk_90 (gb_out_clk ),
.rx_clk (rx_clk )
);
endmodule
2、子模块
2.1 oddr模块
负责将8bit转换成4bit。
`timescale 1ns / 1ps
module oddr_ctrl(
//System Interfaces
input rst_n ,
//Gigbit Interfaces
output wire [ 3:0] tx_data ,
output wire tx_data_ctrl ,
output wire tx_clk ,
//Communication Interfaces
input [ 7:0] gb_tx_data ,
input gb_tx_data_en ,
input gb_tx_data_err ,
input gb_tx_clk
);
ODDR #(
.DDR_CLK_EDGE ("SAME_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_ctrl (
.Q (tx_data_ctrl ), // 1-bit DDR output
.C (gb_tx_clk ), // 1-bit clock input
.CE (1'b1 ), // 1-bit clock enable input
.D1 (gb_tx_data_en ), // 1-bit data input (positive edge)
.D2 (gb_tx_data_err ), // 1-bit data input (negative edge)
.R (~rst_n ), // 1-bit reset
.S (1'b0 ) // 1-bit set
);
ODDR #(
.DDR_CLK_EDGE ("SAME_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_clk (
.Q (tx_clk ), // 1-bit DDR output
.C (gb_tx_clk ), // 1-bit clock input
.CE (1'b1 ), // 1-bit clock enable input
.D1 (1'b1 ), // 1-bit data input (positive edge)
.D2 (1'b0 ), // 1-bit data input (negative edge)
.R (~rst_n ), // 1-bit reset
.S (1'b0 ) // 1-bit set
);
genvar i;
generate
for (i = 0; i < 4; i = i+1) begin
ODDR #(
.DDR_CLK_EDGE ("SAME_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_data (
.Q (tx_data[i] ), // 1-bit DDR output
.C (gb_tx_clk ), // 1-bit clock input
.CE (1'b1 ), // 1-bit clock enable input
.D1 (gb_tx_data[i] ), // 1-bit data input (positive edge)
.D2 (gb_tx_data[4+i] ), // 1-bit data input (negative edge)
.R (~rst_n ), // 1-bit reset
.S (1'b0 ) // 1-bit set
);
end
endgenerate
endmodule
2.2、iddr顶层模块
例化了一个PLL(输入125Mhz时钟,输出125mhz,90度相位时钟,用于时钟中心位数据采集),和一个iddr子模块;
`timescale 1ns / 1ps
module IDDR_TOP(
//System Interfaces
input sclk ,
input rst_n ,
//Gigbit Interfaces
output reg phy_rst_n ,
input [ 3:0] rx_data ,
input rx_ctrl ,
output [ 7:0] gb_rx_data ,
output rx_clk_90 ,
input rx_clk
);
reg [20:0] phy_rst_cnt ;
//wire rx_clk_90 ;
//iddr_ctrl_inst
wire gb_rx_data_en ;
wire gb_rx_data_err ;
clk_wiz_0 clk_wiz_0_inst(
// Clock out ports
.clk_out1 (rx_clk_90 ), // output clk_out1
// Clock in ports
.clk_in1 (rx_clk )
);
iddr_ctrl iddr_ctrl_inst(
//System Interfaces
.rst_n (rst_n ),
//Gigabit Interfaces
.rx_data (rx_data ),
.rx_ctrl (rx_ctrl ),
.rx_clk (rx_clk_90 ),
//Communication Interfaces
.gb_rx_data (gb_rx_data ),
.gb_rx_data_en (gb_rx_data_en ),
.gb_rx_data_err (gb_rx_data_err )
);
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
phy_rst_cnt <= 21'd0;
else if(phy_rst_cnt[20] == 1'b0)
phy_rst_cnt <= phy_rst_cnt + 1'b1;
else
phy_rst_cnt <= phy_rst_cnt;
//phy芯片复位
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
phy_rst_n <= 1'b0;
else if(phy_rst_cnt[20] == 1'b1)
phy_rst_n <= 1'b1;
else
phy_rst_n <= phy_rst_n;
endmodule
2.3、iddr子模块
将oddr输出的4bit数据转换成8bit数据。
`timescale 1ns / 1ps
module iddr_ctrl(
//System Interfaces
input rst_n ,
//Gigabit Interfaces
input [ 3:0] rx_data ,
input rx_ctrl ,
input rx_clk ,
//Communication Interfaces
output reg [ 7:0] gb_rx_data ,
output reg gb_rx_data_en ,
output reg gb_rx_data_err
);
wire [ 7:0] data ;
wire data_en ;
wire data_err ;
IDDR #(
.DDR_CLK_EDGE ("OPPOSITE_EDGE" ), // "OPPOSITE_EDGE", "SAME_EDGE"
// or "SAME_EDGE_PIPELINED"
.INIT_Q1 (1'b0 ), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2 (1'b0 ), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE ("SYNC" ) // Set/Reset type: "SYNC" or "ASYNC"
) IDDR_ctrl (
.Q1 (data_en ), // 1-bit output for positive edge of clock
.Q2 (data_err ), // 1-bit output for negative edge of clock
.C (rx_clk ), // 1-bit clock input
.CE (1'b1 ), // 1-bit clock enable input
.D (rx_ctrl ), // 1-bit DDR data input
.R (~rst_n ), // 1-bit reset
.S (1'b0 ) // 1-bit set
);
genvar i;
generate
for (i = 0; i < 4; i = i+1) begin
IDDR #(
.DDR_CLK_EDGE ("OPPOSITE_EDGE" ), // "OPPOSITE_EDGE", "SAME_EDGE"
// or "SAME_EDGE_PIPELINED"
.INIT_Q1 (1'b0 ), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2 (1'b0 ), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE ("SYNC" ) // Set/Reset type: "SYNC" or "ASYNC"
) IDDR_ctrl (
.Q1 (data[i] ), // 1-bit output for positive edge of clock
.Q2 (data[4+i] ), // 1-bit output for negative edge of clock
.C (rx_clk ), // 1-bit clock input
.CE (1'b1 ), // 1-bit clock enable input
.D (rx_data[i] ), // 1-bit DDR data input
.R (~rst_n ), // 1-bit reset
.S (1'b0 ) // 1-bit set
);
end
endgenerate
always @(posedge rx_clk or negedge rst_n)
if(rst_n == 1'b0)
gb_rx_data <= 8'd0;
else
gb_rx_data <= data;
always @(posedge rx_clk or negedge rst_n)
if(rst_n == 1'b0)
gb_rx_data_err <= 1'b0;
else
gb_rx_data_err <= data_err;
always @(posedge rx_clk or negedge rst_n)
if(rst_n == 1'b0)
gb_rx_data_en <= 1'b0;
else
gb_rx_data_en <= data_en;
endmodule
3、仿真
仿真时钟50mhz。需要仿真21ms才能看到计数器开始计数,数据开始循环(因为这里为了兼容以太网口的phy芯片,需要一小段时间进行初始化);
`timescale 1ns / 1ps
`define CLOCK 8
//测试功能:双沿4bit数据变单沿8bit数据——FPGA接收,单沿8bit数据变双沿4bit数据——FPGA发送;增加DIFF接口;
module tb_eth_i_oddr_diff;
reg sclk ;
//reg sclk_p ;
//reg sclk_n ;
reg rst_n ;
//reg gb_tx_clk ;
wire tx_clk ;
initial begin
rst_n <= 1'b0;
// gb_tx_clk = 1'b0;
sclk = 1'b0;
// sclk_p = 1'b0;
// sclk_n = 1'b1;
#(3000*`CLOCK)
rst_n <= 1'b1;
end
//always #(`CLOCK/2) gb_tx_clk = ~gb_tx_clk;
always #(10) sclk = ~sclk;
//always #(10) sclk_p = ~sclk_p;
//always #(10) sclk_n = ~sclk_n;
wire dclk_p;
wire dclk_n;
wire [3:0] gb_data_p;
wire [3:0] gb_data_n;
I_ODDR_ETH_DIFF_TOP u_I_ODDR_ETH_DIFF_TOP(
//System Interfaces
.sclk (sclk) ,
.rst_n (rst_n) ,
.rx_clk_p (dclk_p) ,
.rx_clk_n (dclk_n) ,
.rx_data_p (gb_data_p) ,
.rx_data_n (gb_data_n) ,
.tx_clk_p (dclk_p) ,
.tx_clk_n (dclk_n) ,
.tx_data_p (gb_data_p),
.tx_data_n (gb_data_n),
.gb_out_clk_p () ,
.gb_out_clk_n () ,
.gb_out_data_p () ,
.gb_out_data_n ()
);
endmodule
最好使用Modelsim进行仿真,数据比较详细
设计文件路径:I_ODDR_DIFF_test\I_ODDR_DIFF\I_ODDR_LVDS.srcs\sources_1\new
编译路径:I_ODDR_DIFF_test\I_ODDR_DIFF\I_ODDR_LVDS.sim\sim_1\behav\modelsim\compile.bat
启动路径:I_ODDR_DIFF_test\I_ODDR_DIFF\I_ODDR_LVDS.sim\sim_1\behav\modelsim\simulate.bat
波形归类脚本:
onerror {resume}
quietly WaveActivateNextPane {} 0
add wave -noupdate /tb_eth_i_oddr_diff/sclk
add wave -noupdate /tb_eth_i_oddr_diff/rst_n
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/gb_tx_cnt
add wave -noupdate /glbl/GSR
add wave -noupdate -divider ODDR
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/tx_clk
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/tx_clk_p
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/tx_clk_n
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/gb_tx_data
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/tx_data
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/tx_data_p
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/tx_data_n
add wave -noupdate -divider IDDR
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/gbit_top_inat/phy_rst_n
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/rx_clk
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/gbit_top_inat/rx_clk_90
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/gb_out_clk_p
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/gb_out_clk_n
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/gb_out_data
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/gb_out_data_p
add wave -noupdate /tb_eth_i_oddr_diff/u_I_ODDR_ETH_DIFF_TOP/gb_out_data_n
TreeUpdate [SetDefaultTree]
WaveRestoreCursors {{Cursor 1} {20995528826 ps} 0}
quietly wave cursor active 1
configure wave -namecolwidth 382
configure wave -valuecolwidth 100
configure wave -justifyvalue left
configure wave -signalnamewidth 0
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2
configure wave -gridoffset 0
configure wave -gridperiod 1
configure wave -griddelta 40
configure wave -timeline 0
configure wave -timelineunits ns
update
WaveRestoreZoom {20996884656 ps} {20997587062 ps}
4、注意
本实验主要是测IDDR+ODDR+差分接口,仿真满足RGMII协议。如果用于实际板测以太网实验,有个bug,但是因为这个工程我做的比较久了,当时那个bug没有做笔记,现在一时想不起来😂😂😂😂😂
后面我如果想起来,再补充到这里。
----------------2023/11/22 更新,每天只要做PLL就会想起这个残留问题,总算想起来了-----------------------
类似RGMII这种,他是不能通过PLL来做相位采集的,一般得用器件自带的delay,不然信号对不齐。pll 会有一个问题,就是多时钟同步估计会比较困难,也没那么准。😂😂😂😂😂
5、下载工程及仿真
下载链接:RGMII回环:IDDR+ODDR+差分接口
https://download.csdn.net/download/weixin_46423500/88527095