1.如何进行小数分频
一般情况下我们会用到三种分频:偶数分频、奇数分频和小数分频。偶数分频最容易实现,且肯定能保证百分之五十占空比;奇数分频比较容易实现,如果追求百分之五十占空比的话,会麻烦一些;小数分频相对来说比较麻烦,本文主要结合8.7分频讲解小数分频实现原理。
由于时钟是不可分的,所以我们不可能像普通分频那样,8.7个输入时钟对应一个输出时钟,于是我们转换思路,用87个输入时钟对应10个输出时钟。同样由于时钟不可分,我们需要使用两个不同的分频时钟进行拼接,凑齐87个时钟,所以模块的输出时钟必然不可能是均匀的。
由于
8
<
8.7
<
9
8 < 8.7<9
8
<
8.7
<
9
所以我们选择若干个8分频和9分频时钟拼接出10个输出时钟,使这10个输出时钟对应87个输入时钟。那么在输出的10个时钟中,有多少个是8分频的,又有多少个是9分频的呢?设有x个8分频时钟,y个9分频时钟,那么
8
x
+
9
y
=
87
x
+
y
=
10
8x+9y=87\\ x+y=10
8
x
+
9
y
=
87
x
+
y
=
10
解得
x
=
3
y
=
7
x=3\\ y=7
x
=
3
y
=
7
所以有3个8分频时钟和7个9分频时钟。
所以最终我们得到这样一种效果:
同理,如果分频系数是别的小数,比如7.482,那么我们需要7分频时钟和8分频时钟进行组合,7482个输入时钟对应1000个输出时钟,可列方程组如下:
7
x
+
8
y
=
7482
x
+
y
=
1000
7x+8y=7482\\ x+y=1000
7
x
+
8
y
=
7482
x
+
y
=
1000
解之得:
x
=
518
y
=
482
x=518\\ y=482
x
=
518
y
=
482
也就是说需要512个7分频时钟和482个8分频时钟,对应输入的7482个时钟。
2.Verilog实现
上面对小数分频原理进行了解释,接下来以8.7分频为例对代码进行介绍。
首先我们必然需要一个计数器对输入时钟进行计数,计数范围为0-86共87个输入时钟。
parameter M_N = 8'd87;
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期
reg[7:0]clk_count;
always @(posedge clk_in, negedge rst) begin
if(rst == 0)begin
clk_count <= 0;
end
else begin
clk_count <= clk_count==M_N-1 ? 0 : clk_count+1;
end
end
同时对于输出的时钟,我们需要确定何时为高电平,何时为低电平,所以需要引入另一个计数器,对每个输出的时钟进行计数。比如此时是8分频时钟,计数范围0-7,计数器在0-3时,输出高电平,4-7时,输出低电平。当9分频时,0-3输出高电平,4-8输出低电平。
reg[7:0]cnt;
always @(posedge clk_in, negedge rst) begin
if(rst == 0)begin
cnt <= 0;
end
else if(div_class)begin
cnt <= cnt==(div_e-1) ? 0 : cnt+1;
end
else begin
cnt <= cnt==(div_o-1) ? 0 : cnt+1;
end
end
由于8分频时钟和9分频时钟,计数范围是不同的,一个0-7,一个0-8,所以我们引入一个变量,区分此时是8分频还是9分频。
wire div_class = clk_count < c89 ? 1 : 0;
最终就是模块的输出。右移一位是除以2的整数除法,无需多言。
reg clk_out_r;
always @(posedge clk_in, negedge rst) begin
if(rst == 0)begin
clk_out_r <= 0;
end
else if(div_class)begin
clk_out_r <= (cnt < div_e>>1) ? 1 : 0;
end
else begin
clk_out_r <= (cnt < div_o>>1) ? 1 : 0;
end
end
assign clk_out = clk_out_r;
完整Verilog代码。
`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[7:0]clk_count;
reg[7:0]cnt;
always @(posedge clk_in, negedge rst) begin
if(rst == 0)begin
clk_count <= 0;
end
else begin
clk_count <= clk_count==M_N-1 ? 0 : clk_count+1;
end
end
wire div_class = clk_count < c89 ? 1 : 0;
always @(posedge clk_in, negedge rst) begin
if(rst == 0)begin
cnt <= 0;
end
else if(div_class)begin
cnt <= cnt==(div_e-1) ? 0 : cnt+1;
end
else begin
cnt <= cnt==(div_o-1) ? 0 : cnt+1;
end
end
reg clk_out_r;
always @(posedge clk_in, negedge rst) begin
if(rst == 0)begin
clk_out_r <= 0;
end
else if(div_class)begin
clk_out_r <= (cnt < div_e>>1) ? 1 : 0;
end
else begin
clk_out_r <= (cnt < div_o>>1) ? 1 : 0;
end
end
assign clk_out = clk_out_r;
//*************code***********//
endmodule
3.testbench及仿真结果
testbench较为简单。
`timescale 1ns/1ns
module div_M_N_tb(
);
reg clk_in;
reg rst;
wire clk_out;
initial begin
clk_in <= 0;
rst <= 0;
#20 rst <= 1;
end
always begin
#5 clk_in <= ~clk_in;
end
div_M_N dut(
.clk_in(clk_in),
.rst(rst),
.clk_out(clk_out)
);
endmodule
仿真效果如图所示。