数电基础
板载晶振提供的时钟信号频率是固定的,不一定满足需求,因此需要对基准时钟进行分频。要得到更慢的时钟频率可以 分频 ,要得到更快的时钟频率可以 倍频 。我们有两种方式可以改变频率,一种是 锁相环 (PLL,后面章节会讲解),另一种是用 Verilog代码描述。
用Verilog代码描述的往往是分频电路,即 分频器 。分频就是输出信号的频率是输入信号的1/n。原理是,输入信号为计数脉冲,每n个脉冲输出就翻转一次。就可以看作是对输入信号的“分频”。十进制的计数器对应十分频,如果是二进制的计数器那就是二分频,还有四进制、八进制、十六进制等等以此类推。
设计规划
实现6分频,第一种方法是仅实现分频,第二种方法是降频:
时钟信号周期为1格,输出信号周期为6格,因此频率为原来的1/6,也就是6分频。
方法一:只需要让计数器从0计数到2,就让clk_out输出信号取反。
方法二:方法一得到的新时钟信号和真正的时钟信号有区别,在高速系统中 不稳定 。因为在FPGA中凡是时钟信号都要连接到全局时钟网络上,它能够使时钟信号到达每个寄存器的时间都尽可能相同,以保证更低的时钟偏斜(Skew)和抖动(Jitter)。用分频的方式产生的clk_out信号并没有连接到全局时钟网络上,但sys_clk则是由外部晶振直接通过管脚连接到了FPGA的专用时钟管脚上,自然就会连接到全局时钟网络上。因此我们可以产生一个clk_flag标志信号,从0计数到5,就变高电平,下一个时钟电平变为低电平并维持5个时钟间隔。和方法1对比,相当于把clk_out的上升沿信号变成了clk_flag的脉冲电平信号。虽然需要多使用一些寄存器资源但是能使系统更加稳定。
编写代码
module divider_six
(
input wire sys_clk , //系统时钟50MHz
input wire sys_rst_n ,
output reg clk_out
)
;
reg [1:0] cnt;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 2'b0;
else if(cnt == 2'd2)
cnt <= 2'b0;
else
cnt <= cnt + 1'b1;
//clk_out:6分频50%占空比输出
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
clk_out <= 1'b0;
else if(cnt == 2'd2)
clk_out <= ~clk_out;
endmodule
我们观察cnt和clk_out的变化条件:计数器发生改变的条件有两个,一个是时钟上升沿,一个是复位有效(复位下降沿)。计数器发生的改变有两个,要么+1要么清零。清零条件有两个:复位和溢出。因此第一个always块中有三个判断条件:复位和溢出时清零,其他的时候+1。
clk_out的变化条件:时钟上升沿和复位有效(复位下降沿)。复位时clk_out为低电平,溢出时取反。
module divider_six
(
input wire sys_clk , //系统时钟50MHz
input wire sys_rst_n ,
output reg clk_flag
);
reg [2:0] cnt;
//cnt:计数器从0到5循环计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 3'b0;
else if(cnt == 3'd5)
cnt <= 3'b0;
else
cnt <= cnt + 1'b1;
//clk_flag:脉冲信号指示6分频
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
clk_flag <= 1'b0;
else if(cnt == 3'd4)
clk_flag <= 1'b1;
else
clk_flag <= 1'b0;
endmodule
和方法1相似,区别在于输出的变化不同,从0计数到4输出变为1,否则为0。cnt溢出的条件是计数到5,输出的变化是计数到4。
编写testbench
`timescale 1ns/1ns
module tb_divider_six();
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
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50MHz
always #10 sys_clk = ~sys_clk;
//--------------------divider_sixht_inst--------------------
divider_six divider_six_inst
(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.clk_out (clk_out ) //output clk_out
);
endmodule
两种方法的testbench代码一样,除了输出是clk_out还是clk_flag。
对比波形
如果波形没有出来,可以在modelsim通过view的transcript查看错误。
方法1得到的波形
方法2得到的波形
全部0条评论
快来发表一下你的评论吧 !