分频器是数字系统设计中最常见的基本电路之一。所谓分频,就是把输入信号的频率变成成倍数地低于输入频率的输出信号。
分频器分为偶数分频器和奇数分频器,和计数器非常类似,有时候可以说就是一个东西。就像上一节计数器中的时钟信号和led_out。
一、偶分频
和第七节,计数器是差不多的就不再进行单独讲解。 这里就简单实现一个8-分频器吧
1、Visio画图
2、代码
module divider_eight(
input wire sys_clk,
input wire sys_rst_n,
output reg clk_flag
);
reg [2:0] cnt;
//给cnt赋值,计数器什么时候清零,什么时候增加
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
cnt<=3'd0;
else if(cnt == 3'd7)
cnt<=3'd0;
else
cnt<=cnt+3'd1;
//给clk_flag赋值
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
clk_flag <= 1'b0;
else if(cnt == 3'd6)
clk_flag<=1'b1;
else
clk_flag<=1'b0;
endmodule
3、仿真
仿真代码:
`timescale 1ns/1ns
module tb_divider_eight();
reg sys_clk;
reg sys_rst_n;
wire clk_flag;
//赋初值
initial
begin
sys_clk=1'b1;
sys_rst_n=1'b0;
#20
sys_rst_n=1'b1;
end
initial
begin
$timeformat(-9,0,"ns",6);
$monitor("@time %t:clk_flag=%b",$time,clk_flag);
end
always #10 sys_clk=~sys_clk;
divider_eight divider_eight_inst(
.sys_clk (sys_clk),
.sys_rst_n(sys_rst_n),
.clk_flag (clk_flag)
);
endmodule
仿真波形如图:
至于这里为什么要用一个新的clk_flag,当计数器到达最大值的时候保持一个时钟周期的高电平,是为了和系统时间同步,而不是自己单独的去定义一个变量来记录时间。这种方法叫做降频方法。
二、奇分频
仅就实现分频功能来讲,奇分频和偶分频的差别还是很大的,奇数分频相对于偶数分频要复杂一些,并不是简单的用计数器计数就可以实现。将一个系统时钟进行5分频的奇数分频功能,可以用于将高频的时钟降低为低频的时钟使用。
1、采用分频的方法
由于这里是5分频,不可能计数到2.5,所以只能计数0-4;
通过观察可以发现,第一种是采用上升沿采样,第二种是采用下降沿采样,两者占空比都不是50%。这两种都不是我们想要的时钟信号的波形。而第三种才是我们想要的输出信号的波形。通过观察我们可以得到:第一种波形和第二种波形在组合逻辑下进行或运算可以得到第三种波形(占空比为50%的5分频信号)。那么第一种波形和第二种波形就可以作为中间变量来得到我们的目标波形。
(1)代码
module divider_five(
input wire sys_clk,
input wire sys_rst_n,
output wire clk_out
);
reg [2:0] cnt;
reg clk_1;
reg clk_2;
//cnt赋值
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 3'd0;
else if(cnt == 3'd4)
cnt <= 3'd0;
else
cnt <= cnt + 3'd1;
//clk_1赋值,这里是上升沿采样
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
clk_1 <= 1'b0;
else if(cnt == 3'd2)//等于2的时候拉高
clk_1 <= 1 'b1;
else if(cnt == 4)//等于4的时候拉低
clk_1 <= 1'b0;
else//既不是2也不是4的时候保持原来的值
clk_1 <= clk_1;
//clk_2赋值,这里是下降沿采样
always@(negedge sys_clk or negedge sys_rst_n)//这里敏感列表不一样
if(sys_rst_n == 1'b0)
clk_2 <= 1'b0;
else if(cnt == 3'd2)//等于2的时候拉高
clk_2 <= 1 'b1;
else if(cnt == 4)//等于4的时候拉低
clk_2 <= 1'b0;
else//既不是2也不是4的时候保持原来的值
clk_2 <= clk_2;
//clk_out赋值,取clk_1和clk_2的逻辑或运算,使用组合逻辑进行赋值就不会延迟一个周期
assign clk_out = (clk_1 | clk_2);
endmodule
对应的rtl代码综合出来的rtl视图如图:
(2)仿真
仿真代码:
`timescale 1ns/1ns
module tb_divider_five();
reg sys_clk;
reg sys_rst_n;
wire clk_out;
//赋初值
initial
begin
sys_clk=1'b1;
sys_rst_n=1'b0;
#20
sys_rst_n=1'b1;
end
initial
begin
$timeformat(-9,0,"ns",6);
$monitor("@time %t:clk_out=%b",$time,clk_out);
end
always #10 sys_clk=~sys_clk;
divider_five divider_five_inst
(
.sys_clk (sys_clk),
.sys_rst_n(sys_rst_n),
.clk_out (clk_out)
);
endmodule
仿真波形如图:
(3)绑定管脚和上板验证
2、采用降频的方法
采用降频的方法的意思其实就是,当计数达到我们所想要得到的数之后,给一个脉冲信号,之后开始重新计数。而采用分频的方法的话,就是在我们计数目标时间内,一半为高电平一半为低电平。这就是这两种方法的区别。
(1)VIsio画图
(2)代码
module divider_five(
input wire sys_clk,
input wire sys_rst_n,
output reg clk_flag
);
reg [2:0] cnt;
/* reg clk_1;
reg clk_2; */
//cnt赋值
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 3'd0;
else if(cnt == 3'd4)
cnt <= 3'd0;
else
cnt <= cnt + 3'd1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
clk_flag <= 1'b0;
else if(cnt==3'd3)
clk_flag<=1'b1;
else
clk_flag<=1'b0;
endmodule
(3)仿真
仿真代码:
`timescale 1ns/1ns
module tb_divider_five();
reg sys_clk;
reg sys_rst_n;
wire clk_flag;
//赋初值
initial
begin
sys_clk=1'b1;
sys_rst_n=1'b0;
#20
sys_rst_n=1'b1;
end
initial
begin
$timeformat(-9,0,"ns",6);
$monitor("@time %t:clk_flag=%b",$time,clk_flag);
end
always #10 sys_clk=~sys_clk;
divider_five divider_f_inst
(
.sys_clk (sys_clk),
.sys_rst_n(sys_rst_n),
.clk_flag (clk_flag)
);
endmodule
仿真波形:
这里就不再上班验证测量频率了。