概述
Clocking Wizard 可通过配置内部寄存器动态调整输出频率,配置接口可选 DRP
或 AXI4-Lite
,其中 AXI4-Lite
实际上是对 DRP
接口的封装
DRP
通过 DRP 接口配置相较 AXI4-Lite 接口要繁琐很多,需要的读者可前往 XAPP888 查看
AXI4-Lite
通过 AXI4-Lite 配置 Clocking Wizard 的流程如下
配置全局分频/倍频系数 DIVCLK_DIVDE 和 CLKFBOUT
地址偏移
寄存器名
默认值
读/写
描述
0x200
Clock Configuration Register 0
0x0101_0A00
RW
Bit[7:0] = DIVCLK_DIVDE Bit[15:8] = CLKFBOUT_MULT Bit[25:16] = CLKFBOUT_FRAC
DIVCLK_DIVDE 和 CLKFBOUT 的取值范围如下,MMCM 具有更大范围的分频系数且支持小数倍频
DIVCLK_DIVDE
CLKFBOUT
PLL
1-56
2-64
MMCM
1-106
2.000-64.000
CLKFBOUT 分为整数部分 (CLKFBOUT_MULT) 和小数部分 (CLKFBOUT_FRAC),其中小数部分 (CLKFBOUT_FRAC) 仅对 MMCM (E2/E4) 原语有效,即 MMCME3/PLL 原语 不支持小数倍频
CLKFBOUT 的小数部分 (CLKFBOUT_FRAC) 取值范围 0-875 (对应实际倍频值 0-0.875),步进为 125 (对应实际倍频值 0.125)。假设倍频系数为 8.125,则整数部分 Bit[15:8] 位应设置为 8 = 0x8,小数部分 Bit[25:16] 设置为 125 = 0x7D
配置各个通道的分频系数 CLKOUTx_DIVDE ,x 取值范围 0-6
地址偏移
寄存器名
读/写
描述
0x208 + x * 12
Clock Configuration Register (x * 3 + 2)
RW
Bit[7:0] = CLKOUTx_DIVDE Bit[17:8] = CLKOUT0_FRAC_Divde
例如 clkout3 的分频寄存器如下
地址偏移
寄存器名
读/写
描述
0x208 + 3 * 12 = 0x22C
Clock Configuration Register 11
RW
Bit[7:0] = DIVCLK_DIVDE
各个通道的分频系数取值范围如下,其中 clkout0 支持小数分频,clkout1 - clkout6 仅支持整数分频
clkout0
clkout1 - clkout6
PLL
1-128
1-128
MMCM
1.000-128.000
1-128
仅 MMCM (E2/E4) 原语支持小数分频,MMCME3/PLL 不支持
置位寄存器 23 中的 LOAD/SEN 和 SADDR 比特,将上述配置加载到内部寄存器
LOAD/SEN
:加载时钟配置寄存器数据至内部寄存器,当动态配置完成且时钟锁定时该比特被置为 0
SADDR
:若为 0 则加载 Clocking Wizard GUI 中的默认配置(上述配置无效),若为 1 则加载上述寄存器配置
地址偏移
寄存器名
默认值
读/写
描述
0x25C
Clock Configuration Register 23
0x0000_0000
RW
Bit[0] = LOAD / SEN Bit[1] = SADDR
若想恢复默认配置,可向寄存器 0x25C 写入 0x0000_0001
等待时钟锁定,对于非 VERSAL 系列可通过 SR 寄存器监测时钟状态
地址偏移
寄存器名
默认值
读/写
描述
0x4
Status Register (SR)
0x0000_0000
R
Bit[0] = Locked
Xilinx 提供了一些库函数用来完成上述配置,其本质上是对上述寄存器读写的封装,下面介绍几个常用的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 XClk_Wiz_Config *XClk_Wiz_LookupConfig (u32 DeviceId) ; u32 XClk_Wiz_CfgInitialize (XClk_Wiz *InstancePtr, XClk_Wiz_Config *Config, UINTPTR EffectiveAddr) ; u32 XClk_Wiz_SetRate (XClk_Wiz *InstancePtr, u64 SetRate) ; u32 XClk_Wiz_WaitForLock (XClk_Wiz *InstancePtr) ;
当 仅有单个时钟输出 且 不需要小数倍频 时,使用上述库函数操作比较方便;如果需要 多个时钟输出 或 输出频率较为复杂 ,建议首先在 Clocking Wizard 配置页面设置所需频率并记录相关参数,然后配置相关寄存器
示例
下面演示两种常见的时钟动态配置示例
单个时钟输出且不需要小数分频
多个时钟输出
单时钟输出
输入为 300 MHz 差分时钟,输出单端时钟由 100 MHz 动态调整为 10 MHz
首先配置 Clocking Wizard 的原语为 MMCM,勾选 Dynamic Reconfig,设置 Dynamic Reconfig Interface 为 AXI4Lite
为了直观感受时钟变化,将输出时钟分频 10_000_000 倍后连接至 LED,系统整体框图如下
分频器代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 module div_clk #( parameter DIVDE = 32 , parameter RST_TIME = 32 , localparam CLK_CNT_TH = (DIVDE >> 1 ), localparam CLK_RST_CNT = DIVDE * RST_TIME ) ( input clk, input rst_n, output reg div_clk, output reg div_resetn ); reg [31 :0 ] cnt; reg [31 :0 ] clk_rst_cnt; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin cnt <= 'b0 ; end else if (cnt == CLK_CNT_TH - 1'b1 ) begin cnt <= 'b0 ; end else begin cnt <= cnt + 1'b1 ; end end always @(posedge clk or negedge rst_n) begin if (!rst_n) begin div_clk <= 1'b0 ; end else if (cnt == CLK_CNT_TH - 1'b1 ) begin div_clk <= ~div_clk; end end always @(posedge clk or negedge rst_n) begin if (!rst_n) begin clk_rst_cnt <= 'b0 ; end else if (clk_rst_cnt < CLK_RST_CNT) begin clk_rst_cnt <= clk_rst_cnt + 1'b1 ; end else begin clk_rst_cnt <= clk_rst_cnt; end end always @(posedge clk or negedge rst_n) begin if (!rst_n) begin div_resetn <= 1'b0 ; end else if (clk_rst_cnt == CLK_RST_CNT - 1'b1 ) begin div_resetn <= 1'b1 ; end else begin div_resetn <= div_resetn; end end endmodule
PS 端代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include "stdio.h" #include "xclk_wiz.h" #include "xil_printf.h" #define CLOCK_WIZARD_DEVICE_ID XPAR_CLK_WIZ_0_DEVICE_ID int main () { u32 Status; u32 XClk_Rate; XClk_Wiz XClk_Wiz; XClk_Wiz_Config *XClk_Wiz_Config; XClk_Wiz_Config = XClk_Wiz_LookupConfig(CLOCK_WIZARD_DEVICE_ID); if (!XClk_Wiz_Config) { xil_printf("[ERROR] Clocking wizard init failed, cannot find config\n" ); return XST_FAILURE; } Status = XClk_Wiz_CfgInitialize(&XClk_Wiz, XClk_Wiz_Config, XClk_Wiz_Config->BaseAddr); if (Status != XST_SUCCESS) { xil_printf("[ERROR] Clocking wizard config failed\n" ); return Status; } Status = XClk_Wiz_SetRate(&XClk_Wiz, 10 ); if (Status != XST_SUCCESS) { xil_printf("[ERROR] Clocking wizard set rate failed\n" ); return Status; } XClk_Wiz_WriteReg(XClk_Wiz_Config->BaseAddr, 0x0000025C , XCLK_WIZ_RECONFIG_LOAD | XCLK_WIZ_RECONFIG_SADDR); Status = XClk_Wiz_WaitForLock(&XClk_Wiz); if (Status != XST_SUCCESS) { xil_printf("[ERROR] Clock wizard lock failed\n" ); return Status; } while (1 ) { } return 0 ; }
通过观察 LED 闪烁频率即可验证配置是否生效,用示波器测量输出时钟频率是否准确
多时钟输出
输入为 300 MHz 差分时钟,通道 0 输出频率由 100 MHz 动态调整为 78 MHz,通道 1 输出频率由 100 MHz 动态调整为 169 MHz
首先配置 Clocking Wizard 的原语为 MMCM,勾选 Dynamic Reconfig,设置 Dynamic Reconfig Interface 为 AXI4Lite
设置 clk_out1 为 78 MHz,clk_out2 为 169 MHz,在 MMCM Settings 页面查看相关配置
VCO 输出频率为 300 × 84.5 / 25 = 1014 MHz
通道 0 输出频率为 VCO / 13 = 78 MHz,通道 1 输出频率为 VCO / 6 = 169 MHz
为了直观感受时钟变化,将输出时钟分频 100_000_000 倍后连接至 LED,系统整体框图如下
PS 端整体代码如下,频率调整部分在 XClk_Wizard_Config
函数中完成,其实就是按照 1.2 中的流程配置寄存器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #include "stdio.h" #include "xclk_wiz.h" #include "xil_printf.h" #define XCLK_WIZARD_BASEADDR XPAR_CLK_WIZ_0_BASEADDR u32 XClk_Wizard_Reset (u32 DeviceBaseAddr) ; u32 XClk_Wizard_Config (u32 DeviceBaseAddr) ; int main () { u32 Status; while (1 ) { Status = XClk_Wizard_Reset(XCLK_WIZARD_BASEADDR); if (Status != XST_SUCCESS) { return Status; } xil_printf("[INFO] 100MHz\n" ); usleep(3 * 1000 * 1000 ); Status = XClk_Wizard_Config(XCLK_WIZARD_BASEADDR); if (Status != XST_SUCCESS) { return Status; } xil_printf("[INFO] 78MHz\n" ); usleep(3 * 1000 * 1000 ); } } u32 XClk_Wizard_Reset (u32 DeviceBaseAddr) { if (!DeviceBaseAddr) { return XST_FAILURE; } Xil_Out32(DeviceBaseAddr + 0x25C , XCLK_WIZ_RECONFIG_LOAD); return XST_SUCCESS; } u32 XClk_Wizard_Config (u32 DeviceBaseAddr) { u32 Count; if (!DeviceBaseAddr) { return XST_FAILURE; } Xil_Out32(DeviceBaseAddr + 0x200 , (500 << 16 ) | (84 << 8 ) | 25 ); Xil_Out32(DeviceBaseAddr + 0x208 + 0 * 12 , 13 ); Xil_Out32(DeviceBaseAddr + 0x208 + 1 * 12 , 6 ); Xil_Out32(DeviceBaseAddr + 0x25C , XCLK_WIZ_RECONFIG_LOAD | XCLK_WIZ_RECONFIG_SADDR); Count = 0 ; while (Count < 1000 ) { if (Xil_In32(DeviceBaseAddr + 0x4 ) & XCLK_WIZ_LOCK) { return XST_SUCCESS; } Count++; } return XST_FAILURE; }
参考文档
[1] PG065. Clocking Wizard v6.0 LogiCORE IP Product Guide
[2] XAPP888. MMCM and PLL Dynamic Reconfiguration Application Note