目录
前言
最近又到了招聘季,招聘就离不开笔试面试,而在笔试面试过程中经常出现的一道题就是写出分频电路的verilog代码(以及电路图?),因此在这篇文章里,我们使用一个公式将所有分频电路套进去,以便于在考场上和仿真工具中轻松实现分频电路。
题目
实现2/3/4/5分频电路,给出一个模板:
module clk_div(reset,clk_in,clk_out)
input reset ;
input clk_in ;
output clk_out ;
//#功能实现模块
endmodule
二分频电路
第一步:画波形图
分频电路是将原始信号频率分成频率更小的衍生信号,信号频率变为二分之一,也就意味着周期变为2倍,因此直接将原本的2个周期所占用的时间现在变为一个周期,即可以生成一个二分频的信号。之所以采用波形图是因为波形图的直观性便于理解和思考,相对于抽象的代码和难以理解的电路图来说。
第二步:写状态机
按照三段式状态机的标准写法(以往文章有过介绍),写一个序列发生器,产生我们想要的信号。在二分频电路里,我们实际上是想实现重复的01信号的产生,这就是最简单的序列发生器。
//#功能实现模块
//三段式状态机
//状态声明
parameter s0 = 0 ;
parameter s1 = 1 ;
reg cs,ns
always@(posedge clk_in) begin
if (reset)
cs <= s0;
else
cs <= ns;
end
//状态改变
always@(*) begin
case(cs)
s0: ns = s1 ;
s1: ns = s0 ;
end
//输出
always@(*) begin
case(cs)
s0: clk_out = 0 ;
s1: clk_out = 1 ;
end
在二分频电路中产生的是循环的01信号,所以状态机只需要2个状态。二分频电路就这样产生了,接下来看三分频。
三分频电路——1/3占空比
第一步:画波形图
如上图所示,三分频电路即就是将原始clk信号三个周期的时间,变为新信号clk_out_3的一个周期。我们常见的周期信号都是占空比1/2的信号,但是在上图中我们可以看到三分频电路可以分为两种,一种就是1/2占空比(1.5,1.5?),而另一种就是1/3占空比的三分频信号(001,010,100)。按照本文的公式,后者这种规则的循环序列信号很容易产生。由易向难,我们先看1/3占空比的三分频电路。
第二步:写状态机
//#功能实现模块
//三段式状态机
//状态声明
parameter s0 = 0 ;
parameter s1 = 1 ;
parameter s2 = 2 ;
reg [1:0]cs,ns
always@(posedge clk_in) begin
if (reset)
cs <= s0;
else
cs <= ns;
end
//状态改变
always@(*) begin
case(cs)
s0: ns = s1 ;
s1: ns = s2 ;
s2: ns = s0 ;
end
//输出
always@(*) begin
case(cs)
s0: clk_out = 0 ;
s1: clk_out = 0 ;
s2: clk_out = 1 ;
end
要实现三分频电路,那么要产生的序列就是001或者010或100,此时状态机有三个状态,所以需要2位寄存器。简单的1/3占空比三分频电路实现后,现在来看1/2占空比的三分频电路。
三分频电路——1/2占空比
第一步:画波形图
如上图所示,clk_out_3_2即为上面的1/3占空比电路,而现在我们要生成1/2占空比的三分频电路clk_out_3,那么我们就需要在下降沿控制原始信号。但是我们无法使用下降沿和上升沿同时去控制一根信号,所以我们可以使用clk_in的下降沿产生一个010序列信号clk_out_3_1。接下来将clk_out_3_1和clk_out_3_2信号进行或[ | ]操作,即可得到1/2占空比的3分频信号clk_out_3。
第二步:写状态机
//#功能实现模块
//三段式状态机
//上升沿触发
//状态声明
parameter s0 = 0 ;
parameter s1 = 1 ;
parameter s2 = 2 ;
reg [1:0]cs,ns
always@(posedge clk_in) begin
if (reset)
cs <= s0;
else
cs <= ns;
end
//状态改变
always@(*) begin
case(cs)
s0: ns = s1 ;
s1: ns = s2 ;
s2: ns = s0 ;
end
//输出
always@(*) begin
case(cs)
s0: clk_out_1 = 0 ;
s1: clk_out_1 = 0 ;
s2: clk_out_1 = 1 ;
end
//三段式状态机
//下降沿触发
//状态声明
reg [1:0]cts,nts
always@(negedge clk_in) begin
if (reset)
cts <= s0;
else
cts <= nts;
end
//状态改变
always@(*) begin
case(cts)
s0: nts = s1 ;
s1: nts = s2 ;
s2: nts = s0 ;
end
//输出
always@(*) begin
case(cts)
s0: clk_out_2 = 0 ;
s1: clk_out_2 = 1 ;
s2: clk_out_2 = 0 ;
end
//final out
clk_out = clk_out_1 | clk_out_2 ;
4/5/6/7…分频电路
根据上面的模式,我们想实现一个任意分频任意占空比的分频电路已经不是难事了,只要画出波形图,仔细分析想要实现的功能,然后剩下的就只是套用公式了。
第一步:画波形图
四分频电路波形图如下,用2分频电路再2分频即可得到,只需要再次例化二分频电路模块,不用修改模块内部代码即可实现。亦可直接用原始信号写一个具有4种状态的状态机,实现0011序列发生器。当然如果需要实现什么1/4占空比,3/8占空比,那也是被这套公式所支持的。
如下图所示,5分频电路其实就是3分频电路的扩展。使用上升沿实现00011的序列发生器,再使用下降沿实现00110的序列发生器,最后将产生的两个2/5占空比的5分频电路或起来,即可得到1/2占空比的5分频信号clk_out_5。奇分频电路的实现最重要的一点就是使用下降沿触发来实现那0.5,将多出来的这个1分成2个0.5给高电平和低电平。
总结
总的来说,实现奇偶分频电路,只需要套用上述公式,即可轻松得到任意分频任意占空比的分频信号。
最后再次总结一下公式:
1.画波形图 :偶数分频只需要数好周期数,即可找到中间点。而奇数分频可以先实现简单的接近中点的分频,例如1/3(1.5/3)占空比分频,2/5(2.5/5)占空比分频等等,然后将这个接近1/2占空比的信号利用下降沿平移半个原始信号的周期,即可出现那缺少的0.5,最后再通过或[ | ]得到最终的1/2占空比的信号。
2.写状态机:使用状态机实现序列发生器,通过观察波形图得到想要的是什么序列,然后依据本文中的代码示例状态机的格式和写法进行扩展,主要是状态机的状态个数。
希望这篇文章能够让你对奇偶分频增加更多的了解,欢迎指出错误和互相交流。