Abstract: Verilog 小数分频

原理简述

单纯的 FPGA 硬件无法进行小数的运算,计数几分之几个时钟周期也是不现实的

因此,实现小数分频的方法是使用若干整数分频的组合来达到和小数分频一样的效果。小数可以化为最简分数的形式$M/N$,表示在输入时钟的 M 个周期内输出 N 个时钟周期,就相对于小数分频。需要注意的是,这样得到的分频时钟是不均匀的且占空比不为 $50\%$

以 8.7 分频为例,小数 8.7 可以化为分数 $87/10$,且这个分数不能化简,也就是需要在 87 个输入时钟内生成 10 个输出时钟。接下来需要取定组成分频时钟的整数分频系数,一般在小数分频系数周围取,这里是 8.7 分频,则取 8 和 9 为整数分频系数,因为 $7<8.7<8$

现在就可以确定,若干个 8 分频和若干个 9 分频组成输出的 10 个时钟周期,且这 10 个周期要在 87 个输入时钟周期内输出,由此得出以下方程组:

$$\begin{equation} \begin{cases} x+y=10\\ 8x+9y=87 \end{cases} \end{equation}$$

解出 x=3, y=7。就是说需要 3 个 8 分频时钟和 7 个 9 分频时钟,另外,如果先输出 8 分频时钟,则在 24 个输入时钟后输出 9 分频时钟

示例代码

题目见:牛客网 Verilog 专题 VL41

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
`timescale 1ns/1ns

module div_M_N(
input wire clk_in,
input wire rst,
output wire clk_out
);
parameter M_N = 8'd87;
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期
//*************code***********//

reg [6:0] cyc_clk; // 输入时钟计数
reg [3:0] clk_cnt; // 时钟分频计数
reg div_flag; // 分频标志,0为8分频,1为9分频
reg clk_out_tmp;

always @(posedge clk_in or negedge rst) begin // 输入时钟计数
if (~rst) begin
cyc_clk <= 'b0;
end
else begin
cyc_clk <= (cyc_clk==M_N-1)? 'b0 : cyc_clk+'b1;
end
end

always @(posedge clk_in or negedge rst) begin // 分频时钟计数器
if (~rst) begin
clk_cnt <= 'b0;
end
else begin
if (~div_flag) begin
clk_cnt <= (clk_cnt==div_e-1)? 'b0 : clk_cnt+'b1;
end
else begin
clk_cnt <= (clk_cnt==div_o-1)? 'b0 : clk_cnt+'b1;
end
end
end

always @(posedge clk_in or negedge rst) begin // 分频系数切换
if (~rst) begin
div_flag <= 'b0;
end
else begin
div_flag <= (cyc_clk==(M_N-1) || cyc_clk==c89-1)? ~div_flag : div_flag;
end
end

always @(posedge clk_in or negedge rst) begin // 生成分频时钟
if (~rst) begin
clk_out_tmp <= 'b0;
end
else begin
if (~div_flag) begin
clk_out_tmp <= (clk_cnt<=((div_e>>2)+1));
end
else begin
clk_out_tmp <= (clk_cnt<=((div_o>>2)+1));
end
end
end

assign clk_out = clk_out_tmp;

//*************code***********//
endmodule