数字电路设计许多模块都是由计数器与分频器组成的,例如 PWM 脉宽调制、频率计等。分频逻辑也往往通过计数逻辑完成。本文主要对偶数分频、奇数分频、半整数分频进行简单的总结。
偶数分频
采用触发器反向输出端连接到输入端的方式,可构成简单的 2 分频电路。
以此为基础进行级联,可构成 4 分频,8 分频电路。
电路实现如下图所示,用 Verilog 描述时只需使用简单的取反逻辑即可。
如果偶数分频系数过大,就需要对分频系数 N 循环计数,进行分频。在计数周期达到分频系数中间数值 N/2 时进行时钟翻转,可保证分频后时钟的占空比为 50%。因为是偶数分频,也可以对分频系数中间数值 N/2 进行循环计数。
偶数分频的 Verilog 描述举例如下。
1 | module even_div #(parameter N=10) ( |
2 | input clk, |
3 | input rstn, |
4 | output clk_div2, |
5 | output clk_div4, |
6 | output clk_div10 |
7 | ); |
8 | |
9 | // 2 分频 |
10 | reg clk_div2_r; |
11 | always @(posedge clk or negedge rstn) begin |
12 | if(!rstn) begin |
13 | clk_div2_r <= 'b0; |
14 | end |
15 | else begin |
16 | clk_div2_r <= ~clk_div2_r; |
17 | end |
18 | end |
19 | assign clk_div2 = clk_div2_r; |
20 | |
21 | // 4 分频 |
22 | reg clk_div4_r; |
23 | always @(posedge clk_div2 or negedge rstn) begin |
24 | if (!rstn) begin |
25 | clk_div4_r <= 'b0; |
26 | end |
27 | else begin |
28 | clk_div4_r <= ~clk_div4_r; |
29 | end |
30 | end |
31 | assign clk_div4 = clk_div4_r; |
32 | |
33 | // N/2 计数 |
34 | reg [3:0] cnt; |
35 | always @(posedge clk or negedge rstn) begin |
36 | if(!rstn) begin |
37 | cnt <= 'b0; |
38 | end |
39 | else if(cnt == (N/2-1)) begin |
40 | cnt <= 'b0; |
41 | end |
42 | else begin |
43 | cnt <= cnt + 1; |
44 | end |
45 | end |
46 | // 输出时钟 |
47 | reg clk_div10_r; |
48 | always @(posedge clk or negedge rstn) begin |
49 | if (!rstn) begin |
50 | clk_div10_r <= 'b0; |
51 | end |
52 | else if(cnt == (N/2 - 1)) begin |
53 | clk_div10_r <= ~clk_div10_r; |
54 | end |
55 | end |
56 | assign clk_div10 = clk_div10_r; |
57 | endmodule |
仿真结果如下:
奇数分频
奇数分频如果不要求占空比为 50%,可按照偶数分频的方法进行分频。即计数器对分频系数 N 进行循环计算,然后根据计数值选择一定的占空比输出分频时钟。
如果奇数分频输出时钟的高低电平只差一个 cycle ,则可以利用源时钟双边沿特性并采用”与操作”或”或操作”的方式将分频时钟占空比调整到 50%。
或操作调整占空比
实现思路:
假设实现N分频(N为奇数,占空比为50%),在输入时钟上升沿下计数到 N-1,同时在 0 和 (N-1)/2 处时钟信号1翻转;在输入时钟下降沿下计数到 N-1,同时在 0 和 (N-1)/2 处时钟信号2翻转;最后信号1和信号2进行相或。
三分频和五分频电路:
verilog实现:
下面是一个9分频的例子:
1 | module odd_div_or #(parameter N = 9) ( |
2 | input rstn, clk, |
3 | output clk_div |
4 | ); |
5 | // 计数器 |
6 | reg [3:0] cnt; |
7 | always @(posedge clk or negedge rstn) begin |
8 | if(!rstn) begin |
9 | cnt <= 'b0; |
10 | end |
11 | else if(cnt == N-1) begin |
12 | cnt <= 'b0; |
13 | end |
14 | else begin |
15 | cnt <= cnt + 1'b1; |
16 | end |
17 | end |
18 | |
19 | // 在上升沿,计数器==(N-1)/2, N-1 翻转 |
20 | reg clk_p_r; |
21 | always @(posedge clk or negedge rstn) begin |
22 | if(!rstn) begin |
23 | clk_p_r <= 1'b0; |
24 | end |
25 | else if((cnt_p == (N-1)/2) || (cnt_p == (N-1))) begin |
26 | clk_p_r <= ~clk_p_r; |
27 | end |
28 | end |
29 | |
30 | // 在下降沿,计数器==(N-1)/2, N-1 翻转 |
31 | reg clk_n_r; |
32 | always @(negedge clk or negedge rstn) begin |
33 | if(!rstn) begin |
34 | clk_n_r <= 1'b0; |
35 | end |
36 | else if((cnt == (N-1)/2) || (cnt == (N-1))) begin |
37 | clk_n_r <= ~clk_n_r; |
38 | end |
39 | end |
40 | |
41 | assign clk_div = clk_p_r | clk_n_r; |
42 | |
43 | endmodule |
小数分频
实现思路:
假设实现N.5分频(N为整数),在输入时钟上升沿下计数到 2N,同时在 0 和 N+1 处时钟信号1翻转;在输入时钟下降沿下计数到 2N,同时在 0 和 N 处时钟信号2翻转;最后信号1和信号2进行相与。
例1.5分频:
1 | module div_3p2 #(parameter N=1) ( |
2 | input clk, rstn |
3 | output clk_div |
4 | ); |
5 | reg [2:0] cnt_p; |
6 | reg [2:0] cnt_n; |
7 | reg clk_p_r, clk_n_r; |
8 | |
9 | //上升沿计数 |
10 | always @(posedge clk or negedge rstn) begin |
11 | if(!rstn) begin |
12 | cnt_p <= 'b0; |
13 | end |
14 | else if(cnt_p == 2*N) begin |
15 | cnt_p <= 'b0; |
16 | end |
17 | else begin |
18 | cnt_p <= cnt_p + 1'b1; |
19 | end |
20 | end |
21 | //上升沿分频 |
22 | always @(posedge clk or negedge rstn) begin |
23 | if(!rstn) begin |
24 | clk_p_r <= 1'b0; |
25 | end |
26 | else if(cnt_p == N+1 || cnt_p == 0) begin |
27 | clk_p_r <= ~clk_p_r; |
28 | end |
29 | end |
30 | |
31 | //下降沿计数 |
32 | always @(negedge clk or negedge rstn) begin |
33 | if(!rstn) begin |
34 | cnt_n <= 'b0; |
35 | end |
36 | else if(cnt_n == 2*N) begin |
37 | cnt_n <= 'b0; |
38 | end |
39 | else begin |
40 | cnt_n <= cnt_n + 1'b1; |
41 | end |
42 | end |
43 | //下降沿分频 |
44 | always @(negedge clk or negedge rstn) begin |
45 | if(!rstn) begin |
46 | clk_n_r <= 1'b1; |
47 | end |
48 | else if(cnt_n == N || cnt_n == 0) begin |
49 | clk_n_r <= ~clk_n_r; |
50 | end |
51 | end |
52 | assign clk_div = clk_n_r & clk_p_r; |
53 | endmodule |
分数分频
小数分频电路可以转化为特定分频比电路设计问题。如19/9分频,意味着在输入时钟clk_in的19个周期内,输出需产生9个脉冲。因为19/9 = 2.11…, 因此可以用2分频和3分频配合实现,设待分频时钟的19个周期内共有x个二分频时钟周期,y个三分频时钟周期,则有:
x+y=9
2x+3y=19
解得x=8,y=1。即只要在待分频时钟的19个周期内控制输出8个二分频时钟周期和1个三分频时钟周期即可。具体代码思路:
1)首先一个总的计数器,在0-18循环;
2)其次设计两个分别生成2分频和3分频的计数器,根据总计数器的数值范围分别在0-1和0-2循环;
3)最后是波形生成逻辑,根据总计数器和2、3分频计数器的数值控制输出脉冲翻转生成期望分频比的时钟。
19/9的分频电路代码如下,其余分频比也可参考:
1 | // M/N小数分频,M为分子,N为分母 |
2 | module Dec_Freq_Div_M_N( |
3 | input clk, |
4 | input rstn, |
5 | output clk_out |
6 | ); |
7 | |
8 | reg [5:0] cnt; |
9 | reg [3:0] cnt_a; |
10 | reg [3:0] cnt_b; |
11 | reg clk_out_reg; |
12 | assign clk_out = clk_out_reg; |
13 | |
14 | // div_a和div_b分别为根据前述公式计算出来的基准分频系数 |
15 | // change为2、3分频时钟的切换点 |
16 | parameter M = 6'd19; |
17 | parameter change = 6'd16; |
18 | parameter div_a = 5'd2; |
19 | parameter div_b = 5'd3; |
20 | |
21 | //总计数器 |
22 | always @(posedge clk or negedge rstn) begin |
23 | if(!rstn) |
24 | cnt <= 6'b0; |
25 | else begin |
26 | if(cnt == M - 1'b1) |
27 | cnt <= 6'b0; |
28 | else |
29 | cnt <= cnt + 1'b1; |
30 | end |
31 | end |
32 | |
33 | //产生2、3分频的计数器 |
34 | always @(posedge clk or negedge rstn) begin |
35 | if(!rstn) begin |
36 | cnt_a <= 4'b0; |
37 | cnt_b <= 4'b0; |
38 | end |
39 | else if(cnt<=change-1'b1) begin |
40 | cnt_b <= 4'd0; |
41 | if(cnt_a == div_a - 1'b1) |
42 | cnt_a <= 4'd0; |
43 | else |
44 | cnt_a <= cnt_a + 1'b1; |
45 | end |
46 | else if(cnt>change-1'b1) begin |
47 | cnt_a <= 4'd0; |
48 | if(cnt_b == div_b - 1'b1) |
49 | cnt_b <= 4'd0; |
50 | else |
51 | cnt_b <= cnt_b + 1'b1; |
52 | end |
53 | |
54 | //输出时钟产生逻辑 |
55 | always @(posedge clk or negedge rstn) begin |
56 | if(!rstn) |
57 | clk_out_reg <= 1'b0; |
58 | else if(cnt<change) begin |
59 | if(cnt_a == 4'd0 || cnt_a == div_a/2) |
60 | clk_out_reg <= ~clk_out_reg; |
61 | else |
62 | clk_out_reg <= clk_out_reg; |
63 | end |
64 | else if(cnt>=change) begin |
65 | if(cnt_b == 4'd0 || cnt_b == (div_b - 1'b1)/2) |
66 | clk_out_reg <= ~clk_out_reg; |
67 | else |
68 | clk_out_reg <= clk_out_reg; |
69 | end |
70 | end |
71 | |
72 | endmodule |