|
分频器是指使输出信号频率为输入信号频率整数分之一的电子电路。在许多电子设备中如电子钟、频率合成器等,需要各种不同频率的信号协同工作,常用的方法是以稳定度高的晶体振荡器为主振源,通过变换得到所需要的各种频率成分,分频器是一种主要变换手段。
早期的分频器多为正弦分频器,随着数字集成电路的发展,脉冲分频器(又称数字分频器)逐渐取代了正弦分频器。 下面以Verilog HDL 语言为基础介绍占空比为50%的分频器。 1、偶分频 偶分可以看到,clk的上升沿,采样到cnt=2的时候,就翻转,采样到0和1的时候,保持。这样就可以做到一半高电平,一半低电平。频电路指的是分频系数为 2、4、6、8 ... 等偶数整数的分频电路,我们可以直接进行分频。 例如下面 divider.v 中,对输入时钟进行6分频,即假设clk 为 50MHz ,分频后的时钟频率为 (50/6) MHz。程序如下: 设计代码: - //rtl
- module divider(
- clk,
- rst_n,
- clk_div
- );
- input clk;
- input rst_n;
- output clk_div;
- reg clk_div;
- parameter NUM_DIV = 6;
- reg [3:0] cnt;
- always @(posedge clk or negedge rst_n)
- if(!rst_n) begin
- cnt <= 4'd0;
- clk_div <= 1'b0;
- end
- else if(cnt < NUM_DIV / 2 - 1) begin
- cnt <= cnt + 1'b1;
- clk_div <= clk_div;
- end
- else begin
- cnt <= 4'd0;
- clk_div <= ~clk_div;
- end
- endmodule
- //tb
- module divider_tb();
- reg clk;
- reg rst_n;
- wire clk_div;
- parameter DELY=100;
- divider U_divider(
- .clk (clk ),
- .rst_n (rst_n ),
- .clk_div(clk_div)
- );
- always #(DELY/2) clk=~clk;//产生时钟波形
- initial begin
- $fsdbDumpfile("divider_even.fsdb");
- $fsdbDumpvars(0,U_divider);
- end
- initial begin
- clk=0;rst_n=0;
- #DELY rst_n=1;
- #((DELY*20)) $finish;
- end
- endmodule
可以看到,clk的上升沿,采样到cnt=2的时候,就翻转,采样到0和1的时候,保持。这样就可以做到一半高电平,一半低电平。 2、奇分频
由于奇分频需要保持分频后的时钟占空比为 50% ,所以不能像偶分频那样直接在分频系数的一半时使时钟信号翻转(高电平一半,低电平一半)。
在此我们需要利用输入时钟上升沿和下降沿来进行设计。
接下来我们设计一个 5 分频的模块,设计思路如下:
采用计数器 cnt1 进行计数,在时钟上升沿进行加 1 操作,计数器的值为 0、1 时,输出时钟信号 clk_div 为高电平;计数器的值为2、3、4 时,输出时钟信号 clk_div 为低电平,计数到 5 时清零,从头开始计数。我们可以得到占空比为 40% 的波形 clk_div1。
采用计数器 cnt2进行计数,在时钟下降沿进行加 1 操作,计数器的值为 0、1 时,输出时钟信号 clk_div 为高电平;计数器的值为2、3、4 时,输出时钟信号 clk_div 为低电平,计数到 5 时清零,从头开始计数。我们可以得到占空比为 40% 的波形 clk_div2。
clk_div1 和clk_div2 的上升沿到来时间相差半个输入周期,所以将这两个信号进行或操作,即可得到占空比为 50% 的5分频时钟。程序如下:
设计代码:
- //rtl
- module divider(
- clk,
- rst_n,
- clk_div
- );
- input clk;
- input rst_n;
- output clk_div;
- reg clk_div;
- parameter NUM_DIV = 5;
- reg[2:0] cnt1;
- reg[2:0] cnt2;
- reg clk_div1, clk_div2;
- always @(posedge clk or negedge rst_n)
- if(!rst_n)
- cnt1 <= 0;
- else if(cnt1 < NUM_DIV - 1)
- cnt1 <= cnt1 + 1'b1;
- else
- cnt1 <= 0;
- always @(posedge clk or negedge rst_n)
- if(!rst_n)
- clk_div1 <= 1'b1;
- else if(cnt1 < NUM_DIV / 2)
- clk_div1 <= 1'b1;
- else
- clk_div1 <= 1'b0;
- always @(negedge clk or negedge rst_n)
- if(!rst_n)
- cnt2 <= 0;
- else if(cnt2 < NUM_DIV - 1)
- cnt2 <= cnt2 + 1'b1;
- else
- cnt2 <= 0;
- always @(negedge clk or negedge rst_n)
- if(!rst_n)
- clk_div2 <= 1'b1;
- else if(cnt2 < NUM_DIV / 2)
- clk_div2 <= 1'b1;
- else
- clk_div2 <= 1'b0;
- assign clk_div = clk_div1 | clk_div2;
- endmodule
3.任意占空比的任意分频
在verilog程序设计中,我们往往要对一个频率进行任意分频,而且占空比也有一定的要求这样的话,对于程序有一定的要求。
现在在前面两个实验的基础上做一个简单的总结,实现对一个频率的任意占空比的任意分频。
比如: FPGA系统时钟是50M Hz,而我们要产生的频率是880Hz,那么,我们需要对系统时钟进行分频。很容易想到用计数的方式来分频:50000000/880 = 56818。
显然这个数字不是2的整幂次方,那么我们可以设定一个参数,让它到56818的时候重新计数就可以实现了。程序如下: - //rtl
- module div(
- clk,
- rst_n,
- clk_div
- );
- input clk,rst_n;
- output clk_div;
- reg clk_div;
- reg [15:0] counter;
- always @(posedge clk or negedge rst_n)
- if(!rst_n)
- counter <= 0;
- else if(counter==56817)
- counter <= 0;
- else
- counter <= counter+1;
- assign clk_div = counter[15];
- endmodule
|