目录
1 内容简介
本章的主要内容为GT 系列动态重配置的概念以及具体的工程应用中如何进行动态重配置(会涉及部分数字监管器的内容),从头了解动态重配置是什么,如何进行动态重配置,构建好GT IP核使用的一块拼图,让大家知用,会用,能用。
2 动态重配置端口
2.1 功能描述
动态重新配置端口 (DRP) 允许动态更改 GTHE3/4_CHANNEL 和 GTHE3/4_COMMON 原语的参数。DRP 接口是一种对处理器十分友好的同步接口,具有地址总线 (DRPADDR) 和单独的数据总线,用于读取 (DRPDO) 和写入 (DRPDI) 原语的配置数据。使能信号 (DRPEN)、读/写信号 (DRPWE) 和就绪/有效信号 (DRPRDY) 是实现读写操作、指示操作完成或指示数据可用性的控制信号。
2.2 端口和属性
2.2.1 GTHE3/4_CHANNEL
说明:
(1)写操作需要DRPWE 和 DRPEN 拉高一个 DRPCLK 周期的高电平,读操作DRPEN 拉高一个 DRPCLK 周期的高电平
(2)如果写入或读取 R/W 寄存器,DRPRDY 在启动 DRP 事务六个 DRPCLK 周期后会断言。对于只读寄存器,DRPRDY 断言的 DRPCLK 周期数取决于 DRPCLK 频率和 USRCLK 频率之间的关系。对于只读寄存器,如果在启动 DRP 事务后 500 个 DRPCLK 周期内未看到 DRPRDY,则使用端口 PCSRSVDIN[2] 重置 DRP 接口(如果使用 UltraScale FPGA)。对于UltraScale+ FPGA,使用 DRPRST 端口重置 DRP 接口。
(3)DRP 复位。在 XCLK 未切换时读取只读寄存器(例如,在复位或更改参考时钟期间)会导致 DRP 不返回 DRPRDY 信号并阻止进一步的 DRP 事务。在这种情况下,必须复位 DRP 接口,然后再启动进一步的 DRP 事务。
2.2.2 GTHE3/4_COMMON
2.3 使用模型
2.3.1 写操作
如图展示 DRP 写操作时序。当 DRPRDY 置位时,可以启动新的 DRP 操作。
2.3.2 读操作
如图展示 DRP 读取操作时序。当 DRPRDY 置位时,可以启动新的 DRP 操作。
2.4 实际操作
该部分的实操会通过GT IP核生成参考例程,增加DRP接口后仿真验证进行,并会把读写操作的程序粘贴在下方,对于读写操作的时序会深入分析,力求让大家让实际的业务中也可以熟练掌握如何使用操作。
2.4.1 IP例化界面参数
注:IP核参数如何配置根据自己的工程使用即可,不需要按照下面的进行配置,仅做参考
(1)Basic界面
(2)Physical Resources界面
(3)Optional Features界面
(4)Structural Options界面
(5)然后就可以点OK,生成IP核,并且打开参考例程(参考例程是Xilinx官方对于自己IP核的使用提供的参考程序模板,IP核生成完成后右上角提示Ready后即可打开)
2.4.2 DRP端口实操
(1)寻找端口位置,①DRP端口在参考程序中所在的位置DRP_example_wrapper.v下面的DRP模块(图1)②最开始发现没找到复位端口,发现是例程中就没有把复位端口引出来,如下图2,在IP核文件DRP.v下面的DRP_gtwizard_top模块中,有需要可以自己引出来(DRP为IP核例化名,若IP例化名称不一样,文件的前缀会有所差别)③如下图3,该部分端口的初始化位置在DRP_example_top.v下面
图1
图2
图3
(2)编写读写测试程序,一般对于以上类型的接口程序,程序编写可以拆分成接口驱动模块+上层调度模块,接口驱动:即将该接口的交互部分按照厂家或者芯片手册要求的时序封装成一个模块,提供更加简易的接口用于上层调用,向上与业务部分对接,向下驱动IP或者芯片;上层调度:则负责根据业务需求,比如说IP要修改工作模式,依次要修改那些寄存器,上层调度通过接口驱动模块提供的接口依次执行对应操作,完成业务功能的实现,具体操作的寄存器地址和含义可查阅UG476目录DRP Address Map of the GTH Transceiverin UltraScale+ FPGAs - GTHE4_COMMON Primitive DRP Address Map
GTHE3/4_COMMON原语参数,修改操作流程:
①首先读取该地址0008h的QPLL0_CFG0的寄存器值
②修改该寄存器值为55aah
③回读确认该值是否修改成功
图4 模块顶层例化
//-------------------------------------------------
// File Name: drp_common_ctrl.v
// Version: 1.0
// Date: 2024-11-24
// Author: YuBai
//-------------------------------------------------
module drp_common_ctrl(
input i_drp_common_clk ,
input i_drp_init_done ,
output [15: 0] o_drp_common_addr ,
output [15: 0] o_drp_common_di ,
output o_drp_common_en ,
output o_drp_common_we ,
input [15: 0] i_drp_common_do ,
input i_drp_common_rdy
);
//----------------Define---------------//
reg [ 1: 0] state_code = 2'd0 ;
reg state_op_vaild = 1'd1 ;
reg drp_common_rdy_d = 1'd0 ;
reg [15: 0] drp_reg_data = 16'd0 ;
reg [15: 0] drp_common_addr = 16'd0 ;
reg [15: 0] drp_common_di = 16'd0 ;
reg drp_common_en = 1'd0 ;
reg drp_common_we = 1'd0 ;
(*ASYNC_REG="true"*)
reg [ 2: 0] drp_init_done_d = 3'd0 ;
wire drp_rdy_vaild ;
wire drp_init_done_sync ;
//----------------Logic----------------//
assign drp_rdy_vaild = ({drp_common_rdy_d,i_drp_common_rdy} == 2'b01);
assign drp_init_done_sync = drp_init_done_d[2];
//CDC
always @(posedge i_drp_common_clk) begin
drp_init_done_d[2:0] <= {drp_init_done_d[1:0],i_drp_init_done};
end
always @(posedge i_drp_common_clk) begin
drp_common_rdy_d <= i_drp_common_rdy;
end
//state_code skip
always @(posedge i_drp_common_clk) begin
if(drp_init_done_sync == 1'd0) begin
state_code[1:0] <= 2'd0;
end
else if(drp_rdy_vaild) begin
state_code[1:0] <= state_code[1:0] + 1'd1;
end
else begin
state_code[1:0] <= state_code[1:0];
end
end
//Control of the number of state transitions
always @(posedge i_drp_common_clk) begin
if((drp_init_done_sync == 1'd0) || drp_rdy_vaild) begin
state_op_vaild <= 1'd1;
end
else if(state_op_vaild == 1'd1) begin
state_op_vaild <= 1'd0;
end
else begin
state_op_vaild <= state_op_vaild;
end
end
//Operation Control
always @(posedge i_drp_common_clk) begin
if(drp_init_done_sync == 1'd0) begin
drp_common_addr[15:0] <= 16'h0000;
drp_common_en <= 1'd0;
drp_common_we <= 1'd0;
drp_common_di[15:0] <= 16'h0000;
end
else if((state_code[1:0] == 2'd0) && state_op_vaild) begin // Read
drp_common_addr[15:0] <= 16'h0008;
drp_common_en <= 1'd1;
drp_common_we <= 1'd0;
drp_common_di[15:0] <= 16'h0000;
end
else if((state_code[1:0] == 2'd1) && state_op_vaild) begin // Write
drp_common_addr[15:0] <= 16'h0008;
drp_common_en <= 1'd1;
drp_common_we <= 1'd1;
drp_common_di[15:0] <= 16'h55aa;
end
else if((state_code[1:0] == 2'd2) && state_op_vaild) begin // Read
drp_common_addr[15:0] <= 16'h0008;
drp_common_en <= 1'd1;
drp_common_we <= 1'd0;
drp_common_di[15:0] <= 16'h0000;
end
else begin
drp_common_addr[15:0] <= 16'h0000;
drp_common_en <= 1'd0;
drp_common_we <= 1'd0;
drp_common_di[15:0] <= 16'h0000;
end
end
//Read Data
always @(posedge i_drp_common_clk) begin
if(drp_init_done_sync == 1'd0) begin
drp_reg_data[15:0] <= 16'd0;
end
else if(i_drp_common_rdy && (state_code[0] == 1'd0)) begin
drp_reg_data[15:0] <= i_drp_common_do[15:0];
end
else begin
drp_reg_data[15:0] <= drp_reg_data[15:0];
end
end
assign o_drp_common_addr[15:0] = drp_common_addr[15:0] ;
assign o_drp_common_en = drp_common_en ;
assign o_drp_common_we = drp_common_we ;
assign o_drp_common_di[15:0] = drp_common_di[15:0] ;
endmodule
state_code[1:0]:控制状态编码,0读,1写,2回读
state_op_vaild:控制每个状态只进行一次读/写操作
drp_reg_data[15:0]:在读操作的状态,读取的数据更新到此寄存器
drp_rdy_vaild:确认当前的操作有效
drp_init_done_d[2:0]:本模块的初始化信号与本地的Drpclk是异步的关系,所以通过该信号进行3拍同步,(*ASYNC_REG="true"*)语句可以指导编译工具在综合实现过程中识别该部分为跨时钟域的处理,进行专门的优化处理,更好地进行跨时钟域寄存采样。
3 数字监管器
两种接收器模式(LPM 和 DFE)使用自适应算法来优化链路。数字监视器提供了对这些自适应环路当前状态的可见性。数字监视器需要时钟;可以使用 DRPCLK 或 RXUSRCLK2。属性 RXDFE_CFG1 或 RXLPM_CFG 选择在 DMONITOROUT 端口上监视的自适应环路。输出端口 DMONITOROUT 包含所选环路的当前代码。环路有三个稳定状态:最小、最大或抖动。
注:该部分主要是用来调节链路参数,优化链路传输,GTH的IBERT中本身集成了该部分内容,7系列的没有集成,若涉及该部分内容,建议参考UG576 目录Shared Features -Channel PLL - Digital Monitor的描述,自行学习。
4 结束语
本章主要介绍了DRP端口的概念和使用,实际项目应用中需要考虑动态修改参数后是否需要复位GT链路的逻辑,以及GT链路什么时候重新趋于稳定。下一期预计会在下周天更新,更新内容为GT的TX(Transmitter),包括内部的结构和功能,可能会分为多期讲述。
Everyone如果发现有问题的地方欢迎批评指证,畅所欲言地交流。如果觉得本期内容对您有用,希望获得您的点赞,收藏和关注,感谢各位。
目前打算先更完GTH收发器的系列,后续可能会穿插加入LVDS串并转换解码或者MIPI接口的部分,具体更新那部分暂未确定,请敬请期待(*❦ω❦)。