概述

Clocking Wizard 可通过配置内部寄存器动态调整输出频率,配置接口可选 DRPAXI4-Lite,其中 AXI4-Lite 实际上是对 DRP 接口的封装

image-20240910111652989

DRP

通过 DRP 接口配置相较 AXI4-Lite 接口要繁琐很多,需要的读者可前往 XAPP888 查看

image-20240912163942182

AXI4-Lite

通过 AXI4-Lite 配置 Clocking Wizard 的流程如下

  1. 配置全局分频/倍频系数 DIVCLK_DIVDECLKFBOUT

    地址偏移 寄存器名 默认值 读/写 描述
    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 原语 不支持小数倍频

    image-20240911121221116

    CLKFBOUT 的小数部分 (CLKFBOUT_FRAC) 取值范围 0-875(对应实际倍频值 0-0.875),步进为 125(对应实际倍频值 0.125)。假设倍频系数为 8.125,则整数部分 Bit[15:8] 位应设置为 8 = 0x8,小数部分 Bit[25:16] 设置为 125 = 0x7D

  2. 配置各个通道的分频系数 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 不支持

  3. 置位寄存器 23 中的 LOAD/SENSADDR 比特,将上述配置加载到内部寄存器

    • 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

  4. 等待时钟锁定,对于非 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
/**
* @brief 查询时钟树配置
* @param DeviceId 时钟树 ID
* @return 时钟树配置,若不存在则返回 NULL
*/
XClk_Wiz_Config *XClk_Wiz_LookupConfig(u32 DeviceId);

/**
* @brief 初始化时钟树
* @param InstancePtr 时钟树句柄
* @param Config 时钟树配置
* @param EffectiveAddr 时钟树基地址
* @return 时钟树初始化成功返回 XST_SUCCESS,否则返回 XST_FAILURE
*/
u32 XClk_Wiz_CfgInitialize(XClk_Wiz *InstancePtr, XClk_Wiz_Config *Config, UINTPTR EffectiveAddr);

/**
* @brief 设置时钟树输出频率,仅当时钟树只有 1 个时钟输出时有效,时钟树有多个输出时返回 XST_FAILURE。
* 该函数底层通过遍历 DIVCLK_DIVDE、CLKFBOUT 和 CLKOUT0_DIVDE 获取时钟树配置,需要注意
* 1. 仅遍历整数倍频系数
* 2. 不保证输出时钟与设置完全一致,两者的误差保存在 InstancePtr->MinErr 中
* @param InstancePtr 时钟树句柄
* @param SetRate 输出时钟频率(MHz)
* @return 设置成功返回 XST_SUCCESS,否则返回 XST_FAILURE
*/
u32 XClk_Wiz_SetRate(XClk_Wiz *InstancePtr, u64 SetRate);

/**
* @brief 等待时钟锁定
* @param InstancePtr 设备句柄
* @return 时钟锁定返回 XST_SUCCESS,否则返回 XST_FAILURE
*/
u32 XClk_Wiz_WaitForLock(XClk_Wiz *InstancePtr);

仅有单个时钟输出不需要小数倍频 时,使用上述库函数操作比较方便;如果需要 多个时钟输出输出频率较为复杂,建议首先在 Clocking Wizard 配置页面设置所需频率并记录相关参数,然后配置相关寄存器

image-20240912114144819

示例

下面演示两种常见的时钟动态配置示例

  1. 单个时钟输出且不需要小数分频
  2. 多个时钟输出

单时钟输出

输入为 300 MHz 差分时钟,输出单端时钟由 100 MHz 动态调整为 10 MHz

首先配置 Clocking Wizard 的原语为 MMCM,勾选 Dynamic Reconfig,设置 Dynamic Reconfig Interface 为 AXI4Lite

image-20240912161333705

为了直观感受时钟变化,将输出时钟分频 10_000_000 倍后连接至 LED,系统整体框图如下

image-20240912160128366

分频器代码如下

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;

// 1.初始化时钟树
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;
}

// 2.设置输出时钟频率
Status = XClk_Wiz_SetRate(&XClk_Wiz, 10);
if (Status != XST_SUCCESS)
{
xil_printf("[ERROR] Clocking wizard set rate failed\n");
return Status;
}

// 3.加载配置到内部寄存器
XClk_Wiz_WriteReg(XClk_Wiz_Config->BaseAddr, 0x0000025C, XCLK_WIZ_RECONFIG_LOAD | XCLK_WIZ_RECONFIG_SADDR);

// 4.等待时钟锁定
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

image-20240912161338253

设置 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

image-20240912161258361

为了直观感受时钟变化,将输出时钟分频 100_000_000 倍后连接至 LED,系统整体框图如下

image-20240912161733628

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)
{
// 使用默认配置,输出时钟均为 100MHz
Status = XClk_Wizard_Reset(XCLK_WIZARD_BASEADDR);
if (Status != XST_SUCCESS)
{
return Status;
}
xil_printf("[INFO] 100MHz\n");
usleep(3 * 1000 * 1000);

// 配置时钟 1 为 78MHz,时钟 2 为 169MHz
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;
}

// 置位 LOAD/SEN
Xil_Out32(DeviceBaseAddr + 0x25C, XCLK_WIZ_RECONFIG_LOAD);

return XST_SUCCESS;
}

u32 XClk_Wizard_Config(u32 DeviceBaseAddr)
{
u32 Count;

if (!DeviceBaseAddr)
{
return XST_FAILURE;
}

// 设置全局分频系数 DIVCLK_DIVDE 为 25,全局倍频系数 CLKFBOUT_MULT 为 84.5
Xil_Out32(DeviceBaseAddr + 0x200, (500 << 16) | (84 << 8) | 25);
// 配置通道 0 的分频系数为 13(300 / 25 * 84.5 / 13 = 78MHz)
// 配置通道 1 的分频系数为 6 (300 / 25 * 84.5 / 6 = 169MHz)
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