【Verilog 笔记 01】某个时钟分频代码实例的分析

近期在 AD9226 的驱动代码中见到了一个有关时钟分频的处理,觉得比较有意思,稍作了一些学习,故在此记录。

代码

模块的驱动代码如下:

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 ad9226_read (
input clk, // 输入时钟 一般由 PLL 产生 260M
input rst_n,
input [12:0] IO_data, // 从ADC模块 IO 上输入的数据 最高位采集 OTR 标志
output reg [12:0] ADC_data, // 输出的采样数据 最高位输出 OTR 标志
output reg clk_driver // 用于驱动ADC模块的时钟
);

`define clkOutPeriod 4 // 模块驱动时钟分频系数,clk_driver = clk/4 = 260M/4 = 65M

reg [3:0] clkCnt;

always @(posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
clkCnt <= 4'd0;
end
else if(clkCnt == (`clkOutPeriod - 1))
begin
clkCnt <= 4'd0;
end
else
begin
clkCnt <= clkCnt + 4'd1;
end
end

always @(posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
clk_driver <= 1'd0;
ADC_data <= 12'd0;
end
else if(clkCnt == `clkOutPeriod/2-1)
begin
clk_driver <= 1'd1;
ADC_data <= IO_data;
end
else if(clkCnt == `clkOutPeriod-1)
begin
clk_driver <= 1'd0;
ADC_data <= ADC_data;
end
else
begin
clk_driver <= clk_driver;
ADC_data <= ADC_data;
end
end

endmodule

模块的总体功能可从上述代码看出。

模块接收由 PLL 产生的时钟信号,并将其 4 分频成 clk_driver 输出,用于给 AD9226 提供时钟信号。

在驱动时钟的上升沿,模块将 AD9226 输出的 IO_data 更新到 ADC_data 中。

分析

为了实现分频,我们看到代码首先定义了一个计数器 clkCnt,以下部分的代码控制了其在 0000 ~ 0003 之间循环计数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
always @(posedge clk or negedge rst_n) 
begin
if (!rst_n)
begin
clkCnt <= 4'd0;
end
else if(clkCnt == (`clkOutPeriod - 1))
begin
clkCnt <= 4'd0;
end
else
begin
clkCnt <= clkCnt + 4'd1;
end
end

注意到,逻辑语句是 clkCnt == (`clkOutPeriod - 1)clkOutPeriod 是 4,之所以在逻辑语句中需要减一,是因为计数默认从 0 开始,减一以后正好数到 3。而从 0 ~ 3 是 4 个数,恰好实现了以 4 为周期的计数。

代码中还有几个 clkOutPeriod 都进行了减一,也是出于这个考虑。

接下来,代码依照 clkCnt 的值进行了相应处理,以实现分频。

1
2
3
4
5
6
7
8
9
10
11
12
....   
else if(clkCnt == `clkOutPeriod/2-1)
begin
clk_driver <= 1'd1;
ADC_data <= IO_data;
end
else if(clkCnt == `clkOutPeriod-1)
begin
clk_driver <= 1'd0;
ADC_data <= ADC_data;
end
....

`clkOutPeriod/2-1 的值是 1,正好是计数周期的一半,此时 clk_driver 产生上升沿,ADC 数据被读入;

`clkOutPeriod-1 的值是 3,正好是计数周期的一半,此时 clk_driver 产生下降沿,ADC 数据保持。

结合以下时序仿真图,就比较明确了:在 rstn 信号高电平期间,clk_driver 确为输入时钟的 4 分频(图一),IO_dataclk_driver 的上升沿被更新至 ADC_data (图二)。