问题

  1. 实现一个时钟分频电路,无论是奇数分频、偶数分频还是小数分频

  2. 通过输入任意切换分频,即从一种分频时钟无缝切换到另一种分频时钟。

分类

整数

计数分频

最容易想到的就是行为级描述Verilog通过计数来进行分频操作。

偶分频

观察偶数偶分频的时序图如下(假设分频数为N):

even-clock-div

对于2分频,需要cnt=0时,clk_div<=1cnt=1时,clk_div<=0

对于4分频,需要cnt=0,1时,clk_div<=1cnt=2,3时,clk_div<=0

对于6分频,需要cnt=0,1,2时,clk_div<=1cnt=3,4,5时,clk_div<=0

所以,对于N分频(偶数),

  • clk_div只要在cnt<(N>>1)(小于N/2)时,clk_div<=1

  • clk_div其余时候(大于N/2小于N),clk_div<=0

奇分频

其实奇数分频可以参考偶数分频来做(假设分频数为N),两种方法可以简单的想象为+1法或-1法。

举例而言,假设需要5分频的时钟,则可以通过半周期的4分频时钟+1半周期(取),或是通过半周期的6分频-1半周期(取)来实现;时序图如下:

odd-clock-div

其逻辑就是4+1=56-1=5:

image-20210515195017081

观察奇数分频的时序图,假设5分频,则

+1法取

需要cnt=0,1时,clk4_p<=1,cnt=2,3,4时,clk4_p<=0;(占空比4:6

需要clk4_p+1clk4_p半个时钟下降沿触发;

clk_div等于clk4_pclk4_p+1

-1法取与

需要cnt=0,1,2时,clk6_p<=1,cnt=3,4时,clk6_p<=0;(占空比6:4

需要clk6_p-1clk6_p半个时钟下降沿触发;

可以发现这样无法通过clk6_p生成clk6_p-1,因此这里应该先通过下降沿触发的方式构造clk6_p-1,再通过晚半个时钟上升沿触发的方式构造clk6_p;即如下时序图:

minus1

需要cnt=0,1,2时,clk6_n<=1,cnt=3,4时,clk6_n<=0;(占空比6:4

需要clk6_n+1clk6_n半个时钟上升沿触发;

clk_div等于clk6_nclk6_n+1

所以,对于N分频(奇数,假设分频数为N)可以总结规律:

这里只写+1法的规律,-1法同理可得其规律

  • clkN-1_p只要在cnt<(N>>1)(小于N/2)时,clk_div<=1

  • clkN-1_p其余时候(大于N/2小于N),clk_div<=0

  • clkN-1_p+1clk4_p半个时钟下降沿触发;

  • clk_div等于clk4_pclk4_p+1

示例:

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 clk_div #(parameter N = 3)
(
input clk,
input rst,
output div_clk
);

reg [$clog2(N-1):0] cnt;
reg pos_clk;
reg neg_clk;

always@(posedge clk or posedge rst)
begin
if(rst)begin
cnt <= 0;
end
else begin
if(cnt==N-1)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
end

always@(posedge clk or posedge rst)
begin
if(rst)begin
pos_clk <= 1'b0;
end
else begin
if(cnt<(N>>1))begin
pos_clk <= 1'b1;
end
else begin
pos_clk <= 1'b0;
end
end
end

always@(negedge clk or posedge rst)
begin
if(rst)begin
neg_clk <= 1'b0;
end
else begin
neg_clk <= pos_clk;
end
end

assign div_clk = pos_clk | neg_clk;

endmodule

仿真:

wave

通用法

偶数分频其实就是奇数分频计算的其中一个时钟而已,因此可以通过判断奇偶进行分频切换。

假设只是参数化配置为任意分频时钟:

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 clk_div #(parameter N = 3)
(
input clk,
input rst,
output div_clk
);

reg [$clog2(N-1):0] cnt;
reg pos_clk;
reg neg_clk;

always@(posedge clk or posedge rst)
begin
if(rst)begin
cnt <= 0;
end
else begin
if(cnt==N-1)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
end

always@(posedge clk or posedge rst)
begin
if(rst)begin
pos_clk <= 1'b0;
end
else begin
if(cnt<(N>>1))begin
pos_clk <= 1'b1;
end
else begin
pos_clk <= 1'b0;
end
end
end

always@(negedge clk or posedge rst)
begin
if(rst)begin
neg_clk <= 1'b0;
end
else begin
neg_clk <= pos_clk;
end
end

assign div_clk = (N[0]==1'b1) ? (pos_clk | neg_clk) : (pos_clk);

endmodule

如果需要随时可切换,则需要一个输入信号n。需要注意几个点:

  1. 另外为了防止切换时出现异常,应至少等待到上一次分频信号输出完成(即cnt计满)才能进行切换;
  2. 初始化时必须的预设置一个num,否则计数器无法启动;
  3. 以上代码没有考虑N=1的情况,即原时钟输出的情况;

示例:

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
module clk_div #(parameter WIDTH = 4)
(
input clk,
input rst,
input valid,
input [WIDTH-1:0] num_i,
output div_clk
);

reg valid_flag;
reg [WIDTH-1:0] cnt;
reg [WIDTH-1:0] num_r; //store for switch
reg [WIDTH-1:0] num;
reg pos_clk;
reg neg_clk;

//load in
always@(posedge clk or posedge rst)
begin
if(rst)begin
num_r <= 1;
end
else if (valid==1'b1) begin
num_r <= num_i;
end
end

//switch clock and number
always@(posedge clk or posedge rst)
begin
if(rst)begin
valid_flag <= 0;
num <= 1;
end
else begin
if(valid==1'b1)begin
valid_flag <= 1'b1;
end
else if(cnt==num-1)begin
valid_flag <= 1'b0;
num <= num_r;
end
else begin
valid_flag <= valid_flag;
end
end
end

//cnt for clk
always@(posedge clk or posedge rst)
begin
if(rst)begin
cnt <= 0;
end
else begin
if(cnt==num-1)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
end

always@(posedge clk or posedge rst)
begin
if(rst)begin
pos_clk <= 1'b0;
end
else begin
if(cnt<(num>>1))begin
pos_clk <= 1'b1;
end
else begin
pos_clk <= 1'b0;
end
end
end

always@(negedge clk or posedge rst)
begin
if(rst)begin
neg_clk <= 1'b0;
end
else begin
neg_clk <= pos_clk;
end
end

//output
assign div_clk = (num==1) ? clk : ((num[0]==1'b1) ? (pos_clk | neg_clk) : (pos_clk));

endmodule

仿真:

wave_int_div

级联分频

结构级描述应该侧重于考虑级联生成时钟

如果偶数分频系数是2的幂,就可以用2分频器级联得到;例如4分频就是两个2分频级联,用两个2分频器级联得到4分频器,以此类推。同理可得,数分频系数是3的幂,就可以用3分频器级联得到,以此类推。级联可以减少计数器的使用,从而减少电路设计中的组合逻辑大小。

小数

通用法

小数分频的具体的电路实现方法可参考时钟分频系列——分数分频电路的Verilog实现 - 知乎。由于参考文章的作者讲的不够清晰易懂,这里重新叙述一遍其原理,但不再进行电路实现。

举例子最容易理解。例如需要实现一个1.3分频时钟,那么等效于在原时钟进行13个时钟周期,输出的时钟正好完成10个时钟周期。可能的一种时序图如下:

clk-div-m

这里先输出71分频的时钟,再输出32分频的时钟,得到10个时钟正好是原时钟13个时钟的完成时间。

总结上述过程,小数分频满足的条件是:

1 × 7 + 2 × 3=13

7 + 3 = 10

div1 × A + div2 × B = N × 10n+m

A + B = 10n

以上二元一次方程组的整数解即是用两个分频时钟凑小数分频的解法。

其中,

  • div1和div2分别代表分频系数
  • A和B分别代表两个分频时钟的个数
  • 10n代表其为10的幂次,N代表其个数
  • m为实际的小数部分(3),0<m<10n

再以时钟分频系列——分数分频电路的Verilog实现 - 知乎举例,假设分频系数6.432,则可以取10n=1000N=6m=432

下面介绍通用解法:

A = 10n- B代入得:

div1 × (10n - B) + div2 × B = N × 10n+m

整理得:

div1 × 10n + (div2-div1) × B = N × 10n+m

根据等式两边的形式,可以看出此等式一定存在一种解,就是div1 = N,(div2-div1)×B = m;更特殊的话,解可以是div1 = N,div2 = N + 1,B = m

1.3分频的例子中,即是div1 = 1, div2 = 2, B = 3;

6.432分频的例子中,即是div1 = 6, div2 = 7, B = 432

因此,这里存在一种通用解法,取div1 = N , div2 = N + 1, B = m, A = 10n-m 即可。

构造的两个分频时钟为:10-m个分频系数为N的时钟 + m个分频系数为N+1的时钟。

1.3分频的例子中,即是构造7个分频系数为1的时钟 + 3个分频系数为2的时钟。

6.432分频的例子中,即是构造568个分频系数为6的时钟 + 432个分频系数为7的时钟。

衍生

构造方法

由于此问题的本质是解上述二元一次方程组的整数解,因此除了此通用方法之外,肯定存在其他多种解法,只要满足方程式就可以。

  1. 倒退一步

    6.432分频的例子中,此时若不考虑特殊的通用解,往前倒退一步,由公式知:

    div1 = 6,(div2-6)×B = 432

    由于432 = 24 × 33 可分解,因此可以不取div2=7,B=432的解,比如:

    div2-6 = 2,B = 23 × 33 ,即div2=8, B=216也是一种解法,此时构造784个分频系数为6的时钟 + 216个分频系数为8的时钟。

    由于8分频时钟构造上比7分频简单(级联分频),而计数器的使用位宽应相当,因此此种构造方法可能更省资源。

  2. 倒退两步

    6.432分频的例子中,此时若不考虑特殊的通用解,往前倒退两步,由公式知:

    div1 × 10n + (div2-div1) × B = N × 10n+m

    div1 × 1000  + (div2-div1) × B = 6000 + 432

    那么div1完全可以不取6,由于div1为分频系数,因此取4比较合理,这样化简为:

    (div2-4) × B = 2000 + 432

    由于2432 = 27 × 19可分解,由于div2为分频系数,取div2=8,B=608即是一种解法,此时构造392个分频系数为4的时钟 + 608个分频系数为8的时钟。级联分频构造4分频和8分频时钟就更简单了。

  3. 倒退回开始

    6.432分频的例子中,此时若不考虑特殊的通用解,往前倒退回最开始的位置,由公式知:

    div1 × A + div2 × B = N × 10n+m

    A + B = 10n

    div1 × A + div2 × B = 6432

    A + B = 1000

    由于这是通过两个分频时钟凑小数分频的解法,那么其实逻辑上也可以通过更多个分频时钟凑小数分频。

    比如

    div1 × A + div2 × B + div3 × C = 6432

    A + B + C= 1000

    更多的分频自然意味着更多的可能性,当然最终的逻辑资源消耗就得根据结果具体分析了。

分频方法

假设在前一步通过各种构造方法完成了分频时钟的选择,分频的方法也存在多种选择的可能。

1.3分频的例子中,假定已经选择构造7个分频系数为1的时钟 + 3个分频系数为2的时钟。除了一开始示意的时序图:

  1. 先做71分频,再做32分频

    clk-div-m

    还有可能的分频方法是将2分频的时钟放在前面,即如下时序图:

  2. 先做32分频,再做71分频

    clk-div-m2

    还有可能的分频方法是将2分频的时钟穿插在1分频的时钟的中间(混频),即如下时序图:

  3. 32分频穿插在71分频中

    clk-div-m3

    同样的,可以将1分频的时钟穿插在2分频的时钟的中间(混频),即如下时序图:

  4. 71分频穿插在32分频中

    clk-div-m4

根据时钟分频系列——分数分频电路的Verilog实现 - 知乎的说法,

第1、2种情况前后时钟频率不太均匀,因此相位抖动比较大;

第3、4种情况前后时钟频率均匀性稍好,因此相位抖动会减小。

占空比

挖坑占座。主要参考 2020年大疆芯片开发工程师笔试真题解析-单选部分 - 知乎

参考资料

[1] 时钟分频系列——偶数分频/奇数分频/分数分频 - 知乎

[2] 时钟分频系列——分数分频电路的Verilog实现 - 知乎

[3] IC/FPGA校招笔试题分析(二)任意切换的时钟分频电路_Reborn Lee-CSDN博客

[4] Verilog设计分频器(面试必看) - Zhangxianhe - 博客园 (cnblogs.com)

[5] 备战秋招-手撕代码篇-奇数分频

[6] 2020年大疆芯片开发工程师笔试真题解析-单选部分 - 知乎

[7] 常用电路设计——“分频电路” - 知乎 (zhihu.com)