ZYNQ笔记(二十):Clocking Wizard 动态配置_vivado clocking wizard
版本:Vivado2020.2(Vitis)
任务:ZYNQ PS端 通过 AXI4Lite 接口配置 Clocking Wizard IP核输出时钟频率
目录
一、介绍
二、寄存器定义
三、配置
四、PS端代码
一、介绍
Xilinx 的 Clock Wizard IP核 用于在 FPGA 中生成和管理时钟信号。它支持 动态重配置(Dynamic Reconfiguration),允许在运行时通过 AXI4-Lite 接口 或 DRP 接口(这两个接口都是在配置IP核时的可选接口) 修改时钟参数(如频率、相位等),而无需重新编程 FPGA。
-
AXI4-Lite 接口:用于软件(如 MicroBlaze/Zynq PS)动态修改时钟参数
-
DRP(Dynamic Reconfiguration Port):硬件接口,直接访问 Clock Wizard 配置寄存器
二、寄存器定义
参考文章:
AMD Technical Information Portal
[小梅哥FPGA] 如何通过动态重配置实现FPGA时钟的实时频率/相位/占空比调整?
如何使用AXI4接口对PLL/MMCM输出时钟的频率和相位进行动态重配置 - Xilinx Vivado 开发板 - 芯路恒电子技术论坛 - Powered by Discuz!
每个寄存器数据为32位4字节,大部分寄存器地址间隔0x04。
Bit[25:16] = CLKFBOUT_FRAC Multiply(仅MMCM才有,VCO的1/8小数倍频系数)
Bit[15:8] = CLKFBOUT_MULT(VCO整数倍频系数)
Bit[7:0] = DIVCLK_DIVIDE(VCO分频系数)
Bit[31:0] = CLKFBOUT_PHASE
Bit[7:0] = CLKOUT0_DIVIDE
Bit[17:8] = CLKOUT0_FRAC Divide(仅MMCM才有,CLKOUT0的1/8小数倍频系数)
Bit[31:0] = CLKOUT0_PHASE
Bit[31:0] = CLKOUT0_DUTY
Bit[7:0] = CLKOUT1_DIVIDE
......
......
......
Bit[1] = SADDR, 写 0 将默认 GUI 中的参数加载到动态配置中;
写 1 将配置寄存器参数加载到动态配置中
三、配置
本次通过 ZYNQ PS 端进行配置, Clocking Wizard 配置如下:MMCM、勾选动态配置(频率配置)、AXI4Lite 接口(PS 通过该接口配置)、输入频率100MHz(根据实际频率设置)
四、PS端代码
参考的正点原子的代码,并他的基础上进行修改,因为源码实现只有一个输出时钟端口的 clk_wiz 的频率配置,修改为有两个时钟输出端口的 clk_wiz 频率配置,(但是我发现正点原子的小数部分设置没有除以8,我测试输出时钟输入给 DVI 转化模块驱动 HDMI 时,除以8后才对上时序频率,显示屏才出现图像,但是正点原子的驱动 LCD 的例程都没有除以8,可能存在精度问题,希望有搞清楚的可以在评论区补充一下):
clk_wiz.h:
#ifndef CLK_WIZ_H_#define CLK_WIZ_H_#include \"xil_types.h\"#define CLK_SR_OFFSET 0x04 //Status Register//clk_out0#define CLK_CFG0_OFFSET 0x200 //Clock Configuration Register 0#define CLK_CFG2_OFFSET 0x208 //Clock Configuration Register 2//clk_out1#define CLK_CFG5_OFFSET 0x214 //Clock Configuration Register 5#define CLK_CFG7_OFFSET 0x222 //Clock Configuration Register 7#define CLK_CFG23_OFFSET 0x25C //Clock Configuration Register 23void clk_wiz_cfg(u32 clk_device_id, double freq0, double freq1);#endif /* CLK_WIZ_H_ */
clk_wiz.c
#include \"xclk_wiz.h\"#include \"clk_wiz.h\"#include \"xparameters.h\"#define CLK_WIZ_IN_FREQ 100 //时钟IP核输入100MhzXClk_Wiz clk_wiz_inst; //时钟IP核驱动实例//时钟IP核动态重配置//参数1:时钟IP核的器件ID//参数2:时钟IP核输出的时钟0频率 单位:MHz//参数3:时钟IP核输出的时钟1频率 单位:MHzvoid clk_wiz_cfg(u32 clk_device_id, double freq0, double freq1){double div_factor = 0;u32 div_factor_int = 0;u32 dviv_factor_frac = 0;u32 clk_divide = 0;u32 status = 0;//初始化XCLK_WizXClk_Wiz_Config *clk_cfg_ptr;clk_cfg_ptr = XClk_Wiz_LookupConfig(clk_device_id);XClk_Wiz_CfgInitialize(&clk_wiz_inst,clk_cfg_ptr,clk_cfg_ptr->BaseAddr);//配置输入时钟倍频/分频系数(多个时钟输出就只用配置一次,后面都用这一个标准进行分配输出)XClk_Wiz_WriteReg(clk_cfg_ptr->BaseAddr,CLK_CFG0_OFFSET,0x00000a01); //10倍频,1分频(输出频率不能超过(CLK_WIZ_IN_FREQ*10/1)MHz)//配置输出时钟0频率if(freq0 <= 0){//计算分频系数div_factor = CLK_WIZ_IN_FREQ * 10 / freq0;div_factor_int = (u32)div_factor; //(取整)分频系数整数部分dviv_factor_frac = (u32)((div_factor - div_factor_int) * 1000 /8); //(取整)分频系数小数部分的8分之一(针对mmcm)clk_divide = div_factor_int | (dviv_factor_frac<BaseAddr,CLK_CFG2_OFFSET,clk_divide);}//配置输出时钟0频率if(freq1 <= 0){//计算分频系数div_factor = CLK_WIZ_IN_FREQ * 10 / freq1;div_factor_int = (u32)div_factor;dviv_factor_frac= (u32)((div_factor - div_factor_int) * 1000 /8);clk_divide = div_factor_int | (dviv_factor_frac<BaseAddr,CLK_CFG7_OFFSET,clk_divide);}// 调试(可选):打印当前寄存器值/*xil_printf(\"After config:\\n\");xil_printf(\"CLK_CFG0: 0x%08X\\n\", XClk_Wiz_ReadReg(clk_cfg_ptr->BaseAddr, CLK_CFG0_OFFSET));xil_printf(\"CLK_CFG2: 0x%08X\\n\", XClk_Wiz_ReadReg(clk_cfg_ptr->BaseAddr, CLK_CFG2_OFFSET));xil_printf(\"CLK_CFG5: 0x%08X\\n\", XClk_Wiz_ReadReg(clk_cfg_ptr->BaseAddr, CLK_CFG5_OFFSET));xil_printf(\"CLK_CFG7: 0x%08X\\n\", XClk_Wiz_ReadReg(clk_cfg_ptr->BaseAddr, CLK_CFG7_OFFSET));*///加载重配置的参数XClk_Wiz_WriteReg(clk_cfg_ptr->BaseAddr,CLK_CFG23_OFFSET,0x00000003);//获取时钟IP核的状态,判断是否重配置完成while(1){status = XClk_Wiz_ReadReg(clk_cfg_ptr->BaseAddr,CLK_SR_OFFSET);if(status&0x00000001) //Bit0 Locked信号return ;}}
函数调用
#include \"xclk_wiz.h\"#include \"clk_wiz.h\"#define CLK_WIZ_ID XPAR_CLK_WIZ_0_DEVICE_ID //时钟IP核器件IDint main(){double freq0 = 74.25; double freq1 = 371.25;//配置时钟IP输出频率(单位MHz)clk_wiz_cfg(CLK_WIZ_ID, freq0 , freq1);...return 0;}