数字电路设计之奇数分频
奇数分频几乎是初学逻辑电路设计时,必然会遇到的一个问题。假如对时钟的占空比没有要求,相信任何一个初学者都可以立马写出分频的RTL代码。而奇数分频的难点就在于对50%占空比的处理,其核心思想就在于要学会利用寄存器的不同捕获边沿进行分频操作。
假设奇数分频的值为2N+1,为了实现占空比为50%的奇数分频,需要用到一个计数器和2路分频信号,第1路根据计数器的值在上升沿分频,即分别在计数器走到N-1和2N时,进行时钟信号的反转;而第2路则根据计数器的值在下降沿分频,同样是在计数器走到N-1和2N时,进行时钟信号反转。因为,一个时钟的上升沿和下降沿之间的时间差正好是50%的时钟周期,所以将两路信号进行或逻辑运算,就可以实现50%占空比的奇数分频。
以5分频为例,其时序示意图如下:
如图所示,5分频的情况下,计数器计数范围0~4,clk_div1是第1路分频信号,以上升沿为时钟有效沿,在计数值走到1和4时反转,此时占空比为:2/5 = 40%;而clk_div2是第2路分频信号,以下降沿为时钟有效沿,同样在计数值走到0和2时反转,占空比也为: 2/5 = 40%。clk_div_out是最终输出的时钟信号,由clk_div1和clk_div2相或得到,满足占空比50%的要求。
奇数分频的RTL代码如下:
module clk_div
#(parameter DIV = 3)
(
input clk , // 输入时钟
input rst_n , // 输入复位信号,低有效
output clk_div_out // 输出分频时钟信号
);
parameter WIDTH = $clog2(DIV) ; // 分频计数器位宽计算
parameter HALF_DIV = (DIV-1)/2 ; // 半分频计数
reg [WIDTH-1:0] cnt ; // 分频计数器
reg clk_div1 ; // 上升沿分频
reg clk_div2 ; // 下降沿分频
assign clk_div_out = clk_div1 | clk_div2 ; // 上升沿与下降沿分频信号,相或输出最终 50% 占空比时钟
//*************** 分频计数 *******************
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
cnt <= 0;
end
else begin
if (cnt == DIV - 1'b1) begin
cnt <= 0;
end
else begin
cnt <= cnt + 1'b1;
end //
end
end
//************** 上升沿分频 ******************
always @ (posedge clk or negedge rst_n) begin
if (~rst_n) begin
clk_div1 <= 0;
end //
else begin
if (cnt == HALF_DIV-1) begin
clk_div1 <= 1'b0;
end //
else if (cnt == DIV-1) begin
clk_div1 <= 1'b1;
end //
else begin
clk_div1 <= clk_div1 ;
end //
end //
end //
//************** 下降沿分频 *******************
always @ (negedge clk or negedge rst_n) begin
if (~rst_n) begin
clk_div2 <= 0;
end //
else begin
if (cnt == HALF_DIV-1) begin
clk_div2 <= 1'b0;
end //
else if (cnt == DIV-1) begin
clk_div2 <= 1'b1;
end //
else begin
clk_div2 <= clk_div2 ;
end //
end //
end //
endmodule
上述代码适用于任意奇数分频,只要给定分频值DIV,其中计数器位宽WIDTH可以通过verilog函数clog2获得,其主要功能是实现求2的对数的功能,并对结果向上取整。
当DIV = 3时,代码的RTL图如下所示:
其实奇数分频的方式很简单,只要利用好触发器下降沿,采用或逻辑,与逻辑还是异或逻辑,都可以得到相应的50%占空比分频信号,只要根据相应的逻辑操作转换相应的信号电平即可,这个可以留给读者自己去探索。