目录
在实际的项目工程中,经常需要不同的时钟频率工作,或者在一些笔试面试中,时钟分频也会被问到,因此这篇文章介绍几种常见的时钟分频的案例:偶数分频、奇数分频、任意小数分频。
偶数分频是最常见的分频方式也是最简单的,只需要一个简单的计数器即可,如果要实现4分频的时钟,只需要计数器从0计数到3,然后输出的时钟在计数到1和3的时钟翻转即可。
- //`timescale 100ps / 1ps
- //
- // Engineer:
- // Create Date: 2022/07/24
- // Design Name:
- // Module Name: freq_div_even
- // Tool Versions: Vivado 2021.2
- // Encode: GBK
- // Description: 实现偶数四分频
- // Additional Comments:
- //
- //
-
- module freq_div_even(
- input clk,
- input rst,
- output reg clk_out
- );
- reg [1:0] cnt;
-
- always @(posedge clk or posedge rst) begin
- if (rst) begin
- cnt <= 'd0;
- end
- else if (cnt == 'd3) begin
- cnt <= 'd0;
- end
- else begin
- cnt <= cnt + 'd1;
- end
- end
-
- always @(posedge clk or posedge rst) begin
- if (rst) begin
- clk_out <= 'd0;
- end
- else if (cnt == 'd1) begin
- clk_out <= 'd1;
- end
- else if (cnt == 'd3) begin
- clk_out <= 'd0;
- end
- else begin
- clk_out <= clk_out;
- end
- end
-
- endmodule
- `timescale 10ps / 1ps
- //
- // Engineer:
- // Create Date: 2022/07/24
- // Design Name:
- // Module Name: tb_freq_div_even
- // Tool Versions: Vivado 2021.2
- // Encode: GBK
- // Description:
- // Additional Comments:
- //
- //
-
- module tb_freq_div_even();
-
- reg clk;
- reg rst;
- wire clk_out;
-
- initial begin
- clk = 'd0;
- rst = 'd1;
- #20
- rst = 'd0;
- end
-
- always #10 clk = ~clk;
-
- freq_div_even inst_freq_div_even (
- .clk(clk),
- .rst(rst),
- .clk_out(clk_out)
- );
-
- endmodule

可以看到原时钟为50Mhz,在四分频后时钟为12.5Mhz,符合设计要求。如果要设计八分频甚至更大的分频也是如此操作。

相对于偶数分频,奇数分频要复杂一些,不仅要实现奇数分频而且占空比为50%,这里需要多加一个计数器,一个计数器由输入时钟上升沿触发,另一个计数器由输入时钟下降沿触发,最后将两个计数器的输出做相运算,即可得到奇数分频并且占空比为50%的时钟。
- //`timescale 100ps / 1ps
- //
- // Engineer:
- // Create Date: 2022/07/24
- // Design Name:
- // Module Name: freq_div_odd
- // Tool Versions: Vivado 2021.2
- // Encode: GBK
- // Description: 实现奇数五分频
- // Additional Comments:
- //
- //
-
- module freq_div_odd(
- input clk,
- input rst,
- output wire clk_out
- );
- reg [2:0] cnt1;
- reg [2:0] cnt2;
- reg clk1;
- reg clk2;
-
- always @(posedge clk or posedge rst) begin
- if (rst) begin
- cnt1 <= 'd0;
- end
- else if (cnt1 == 'd4) begin
- cnt1 <= 'd0;
- end
- else begin
- cnt1 <= cnt1 + 'd1;
- end
- end
-
- always @(posedge clk or posedge rst) begin
- if (rst) begin
- clk1 <= 'd0;
- end
- else if (cnt1 < 'd2) begin
- clk1 <= 'd1;
- end
- else begin
- clk1 <= 'd0;
- end
- end
-
- always @(negedge clk or posedge rst) begin
- if (rst) begin
- cnt2 <= 'd0;
- end
- else if (cnt2 == 'd4) begin
- cnt2 <= 'd0;
- end
- else begin
- cnt2 <= cnt2 + 'd1;
- end
- end
-
- always @(negedge clk or posedge rst) begin
- if (rst) begin
- clk2 <= 'd0;
- end
- else if (cnt2 < 'd2) begin
- clk2 <= 'd1;
- end
- else begin
- clk2 <= 'd0;
- end
- end
-
- assign clk_out = clk1 | clk2;
-
- endmodule
- `timescale 10ps / 1ps
- //
- // Engineer:
- // Create Date: 2022/07/24
- // Design Name:
- // Module Name: tb_freq_div_odd
- // Tool Versions: Vivado 2021.2
- // Encode: GBK
- // Description:
- // Additional Comments:
- //
- //
-
- module tb_freq_div_odd();
-
- reg clk;
- reg rst;
- wire clk_out;
-
- initial begin
- clk = 'd0;
- rst = 'd1;
- #20
- rst = 'd0;
- end
-
- always #10 clk = ~clk;
-
- freq_div_odd inst_freq_div_odd (
- .clk(clk),
- .rst(rst),
- .clk_out(clk_out)
- );
-
- endmodule


任意小数分频如果要分的更细一些,可以分成半整数分数和非半整数的小数分频,因为半整数也属于小数,所以这里统一用任意小数分频的设计方法实现。
在设计时也可以选择直接调用 PLL 锁相环实现,比如要实现 6.3 的分频,可以先将源时钟 10 倍频,得到源时钟的 10 倍频率的时钟,再进行 63 分频实现,这样就可以得到 63 分频的时钟。
另外也可以自己设计实现,分频结果 N = M + P,其中 M 为小数分频值得整数部分,P 为小数分频值得小数部分,同时小数可以用分式表示 b/a+b,比如 0.3 可以表示为3/7+3,此时b = 3,则原式可以表示为 N = M + b/a+b,对 M 进行通分得到 N = M(a+b) + b/a+b = [aM + b(M+1)]/a+b,这样就可以列出二元一次方程,6.3分频可以写成分式63/10。
可以得到以下二元一次不等式:
6a + 7b = 63
a + b = 10
解得:a = 7,b = 3
表示先做 7 次 6 分频,再做 3 次 7 分频即可达到 6.3 分频的目的。
- //`timescale 100ps / 1ps
- //
- // Engineer:
- // Create Date: 2022/07/24
- // Design Name:
- // Module Name: freq_div_decimal
- // Tool Versions: Vivado 2021.2
- // Encode: GBK
- // Description: 实现任意小数分频,以6.3分频为例
- // Additional Comments:
- // 先做 7 次 6 分频,再做 3 次 7 分频
- //
- //
-
- module freq_div_decimal(
- input clk,
- input rst,
- output reg clk_out
- );
- reg [3:0] cnt1;
- reg [3:0] cnt2;
-
- always @(posedge clk or posedge rst) begin
- if (rst) begin
- cnt1 <= 'd0;
- end
- else if ((cnt2 < 'd7) &&(cnt1 == 'd5)) begin
- cnt1 <= 'd0;
- end
- else if (cnt1 == 'd6) begin
- cnt1 <= 'd0;
- end
- else begin
- cnt1 <= cnt1 + 'd1;
- end
- end
-
- always @(posedge clk or posedge rst) begin
- if (rst) begin
- cnt2 <= 'd0;
- end
- else if ((cnt2 == 'd9) && (cnt1 == 'd6)) begin
- cnt2 <= 'd0;
- end
- else if ((cnt1 == 'd5) && (cnt2 < 'd7)) begin
- cnt2 <= cnt2 + 'd1;
- end
- else if (cnt1 == 'd6) begin
- cnt2 <= cnt2 + 'd1;
- end
- else begin
- cnt2 <= cnt2;
- end
- end
-
- always @(posedge clk or posedge rst) begin
- if (rst) begin
- clk_out <= 'd0;
- end
- else if ((cnt1 == 'd5) && (cnt2 < 'd7)) begin
- clk_out <= 'd1;
- end
- else if (cnt1 == 'd6) begin
- clk_out <= 'd1;
- end
- else begin
- clk_out <= 'd0;
- end
- end
-
- endmodule
- `timescale 10ps / 1ps
- //
- // Engineer:
- // Create Date: 2022/07/24
- // Design Name:
- // Module Name: tb_freq_div_even
- // Tool Versions: Vivado 2021.2
- // Encode: GBK
- // Description:
- // Additional Comments:
- //
- //
-
- module tb_freq_div_even();
-
- reg clk;
- reg rst;
- wire clk_out;
-
- initial begin
- clk = 'd0;
- rst = 'd1;
- #20
- rst = 'd0;
- end
-
- always #10 clk = ~clk;
-
- freq_div_decimal inst_freq_div_decimal (
- .clk(clk),
- .rst(rst),
- .clk_out(clk_out)
- );
-
- endmodule

可以看到仿真波形,先经过了 7 次的 6 分频,再经过 3 次的 7 分频,最终形成 6.3 分频的时钟输出。
