基于FPGA的分频器设计

描述

数电基础

板载晶振提供的时钟信号频率是固定的,不一定满足需求,因此需要对基准时钟进行分频。要得到更慢的时钟频率可以 分频 ,要得到更快的时钟频率可以 倍频 。我们有两种方式可以改变频率,一种是 锁相环 (PLL,后面章节会讲解),另一种是用 Verilog代码描述。

用Verilog代码描述的往往是分频电路,即 分频器 。分频就是输出信号的频率是输入信号的1/n。原理是,输入信号为计数脉冲,每n个脉冲输出就翻转一次。就可以看作是对输入信号的“分频”。十进制的计数器对应十分频,如果是二进制的计数器那就是二分频,还有四进制、八进制、十六进制等等以此类推。

设计规划

实现6分频,第一种方法是仅实现分频,第二种方法是降频:

Verilog

Verilog

时钟信号周期为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为低电平,溢出时取反。

Verilog

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。

Verilog

编写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得到的波形

Verilog

方法2得到的波形

Verilog

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分