Verilog奇数分频(个人总结)
奇数分频器跟偶数分频器一样,当计数器的值等于分频系数(加1或者减1)的一半或等于分频系数时,时钟信号翻转。奇数分频器分频原理如下图:
上图的分频系数是3,用一个计数器在上升沿计数,每次计数到1翻转一次,每次计数到3再翻转一次,然后周期重复得到信号clkp1,它的周期就是clk的3倍,但是它的占空比不是50%(占空比就是clk为高的时间占整个时钟周期的百分比)。奇数分频想通过计数器直接分频出占空比是50%的时钟是不可能的,必须要通过中间的临时波形,做一些逻辑“与”“或”的动作才能得到占空比50%的分频时钟。
方法一作逻辑“与”操作
以下代码是个人的一个总结,实现了7分频,要想实现其他奇数分频,改变div数值即可(相比其他的,还是比较容易看懂理解的)。
module frequency_div
#(parameter div = 6)
(
input clk,
input rst_n,
output clk_div
);
//奇数分频情况
reg [3:0]cnt;
reg clk_test;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 4'b0;
end
else if(cnt == div)
cnt <= 4'b0;
else begin
cnt <= cnt + 1;
end
end
reg clk_1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
clk_1 <= 1'b0;
end
else if(cnt == div/2 +2)//注意个人写法,下文有分析
clk_1 <= 1'b0;
else if(cnt == 4'd1)
clk_1 <= 1'b1;
end
reg clk_2;
always@(negedge clk or negedge rst_n)begin
if(!rst_n)begin
clk_2 <= 1'b0;
end
else if(cnt == div/2 +2)
clk_2 <= 1'b0;
else if(cnt == 4'd1)
clk_2 <= 1'b1;
end
assign clk_div = clk_1 & clk_2;
endmodule
代码中只需要定义clk_1和clk_2就行(注意上升沿触发和下降沿触发的区别),若要修改分频系数,改变div数值即可。
上述文中cnt == div/2 +2和cnt == 4’d1是对“奇数分频器跟偶数分频器一样,当计数器的值等于分频系数(加1或者减1)的一半或等于分频系数时,时钟信号翻转”的解释。至于为什么不选择cnt == div/2 +1和cnt == 4’d0,下文仿真会有解释。
先贴出tb文件代码:
`timescale 1ns / 1ps
module tb_frequency_div(
);
reg clk;
reg rst_n;
wire clk_div;
initial begin
clk = 1'b0;
rst_n = 1'b0;
#9
rst_n = 1'b1;
end
always #10 clk = ~clk;
frequency_div u_frequency_div(
.clk (clk),
.rst_n (rst_n),
.clk_div (clk_div)
);
endmodule
选择cnt == div/2 +1和cnt == 4’d0的仿真:
选择cnt == div/2 +2和cnt == 4’d1的仿真:
可以发现复位之后,第一种有一段缺失,效果较差,第二种更完整一些,故选择第二种。这边对于这段代码做出解释,防止阅读难以理解。
方法二做逻辑“或”操作
相比逻辑与操作更加容易理解与实现一些
实现5分频代码:
module fre_div (
input clk,
input rst_n,
output clk_div
);
reg clk_div1;
reg clk_div2;
reg [2:0]cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 3'd0;
end
else if(cnt != 3'd4)begin
cnt <= cnt + 1'b1;
end
else begin
cnt <= 3'd0;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
clk_div1 <= 1'b0;
end
else if (cnt == 3'd0) begin
clk_div1 <= ~clk_div1;
end
else if (cnt == 3'd2) begin
clk_div1 <= ~clk_div1;
end
else begin
clk_div1 <= clk_div1;
end
end
always @(negedge clk or negedge rst_n) begin
if (!rst_n) begin
clk_div2 <= 1'b0;
end
else begin
clk_div2 <= clk_div1;
end
end
assign clk_div = clk_div1 | clk_div2;
endmodule
tb文件
`timescale 1ns/100ps
module tb_fre_div ();
reg clk;
reg rst_n;
wire clk_div;
initial begin
clk = 0;
rst_n = 0;
#10
rst_n = 1;
end
always #10 clk = ~clk;
fre_div u_fre_div(
.clk (clk),
.rst_n (rst_n),
.clk_div (clk_div)
);
endmodule
仿真结果
可以观察到逻辑或的规律是:计数cnt计到想要分频的n-1次(从0开始),clk_div1在cnt为0时候翻转一次,然后在(n-1)/2的时候再翻转一次,然后clk_div2在下降沿采样clk_div1,最后将两个信号取或操作即可。