Menu Close

Verilog 分频器

在数字系统设计中经常会出现系统时钟频率太高,而在有些模块的设计中往往需要较低的时钟,因此就产生了分频器设计的需求。如I2C 接口在低速时SCL 只有100KHz 的需求,UART在115200波特率的情况下,即使8倍的过采样也只有900多KHz的需求,还有大量的人机界面如数码管的扫描驱动中,往往只有几百Hz。在FPGA及IC设计中,时钟往往是外部提供的,经过内部PLL的变频,可以提供几路不同频率的时钟,但PLL在产生高频时容易,产生低频比较困难,如Xilinx器件的PLL一般最低输出5MHz,低于5MHz的频率,PLL不能提供。Intel-Altera的Cyclone器件最低也只能到1MHz左右,因此更低的频率需要利用分频器产生。分频器有两种提供时钟的方法:

  • 一种是分频后直接由寄存器提供输出时钟,给需要的模块或逻辑电路使用;
  • 另一种不直接提供时钟,而是提供使能的方式驱动下级电路。下面我们分开介绍。

 

1.分频结果直接作为时钟使用

例1:输入时钟100M Hz,设计分频器产生毫秒及秒脉冲时钟。

module  fdiv
(
    input      inclk,  // input clock  --default 100Mhz
    input      rst,    // reset signal
    output reg ms_clk, // output for millisecond
    output reg s_clk   // output for second 
);
 
reg  [16:0] ms_r;
reg  [9:0]  s_r;
 
always@ (posedge inclk or posedge rst)
if(rst) 
begin
    ms_r <= 0;
    ms_clk <= 0;
end
else  
begin
    if(ms_r == 49999) 
    begin
        ms_r <= 0;
        ms_clk <= ~ms_clk;
    end
    else
        ms_r <= ms_r + 1;
end
 //利用分频后的寄存器结果,作为时钟使用。
always@ (posedge ms_clk or posedge rst)
if(rst) 
begin
    s_r <= 0;
    s_clk <= 0;
end
else  
begin
    if(s_r == 499) 
    begin
        s_r <= 0;
        s_clk <= ~s_clk;
    end
    else
        s_r <= s_r + 1;
end
 
endmodule

 

程序分析如下:

  • 第一级从100MHz分频到1000 Hz (1ms),需要除以100000,利用计数器延迟实现,计数器在50000次计数期间保持ms_clk为高,紧跟着50000次计数保持低电平,如此交替进行。对于ms_clk一次高电平+一次低电平为一个时钟周期。而计数器从0-49999计数一个循环正好50000次,因此下面的程序恰好满足一毫秒的关系。

if(ms_r == 49999)

begin //0-49999计数

ms_r <= 0;

ms_clk <= ~ms_clk; //ms_clk反转

end

else

ms_r <= ms_r + 1; //加1计数,ms_clk保持不变

  • 第二级的算法与第一类似,只是时钟由 ms_clk提供。

这种设计方法一般在ASIC设计中比较常见,但在FPGA程序设计中一般很少使用。原因是一般ASIC设计中时钟资源由ASIC设计者在设计阶段安排专门的布线资源,在布局布线时,低延迟的金属线可以布到需要的地方,使得所有参与的寄存器的时钟都能连接到该时钟线上。而FPGA往往是在内部预先设好的时钟线,这些时钟线需要时钟缓冲器(如GBuffer等)才能连接到特定的时钟线上,一般从寄存器输出很难再连接到特定时钟线上。如果不能利用时钟线,那么ms_clk,s_clk在FPGA内部就要经过各种组合逻辑才能到达各个触发器,各个触发器的时钟延迟各不相同,因此逻辑稍微复杂,结果就会难以控制。我们可以认为这种设计在FPGA中,表面上看也是同步设计,实际上行为都是异步的。特别当前级时钟由引用后级的寄存器或后级时钟引用前级时钟的寄存器的值,最终一定是异步时钟之间的关系。分频后的寄存器作为时钟以用如图1所示。

如果在ASIC设计或在FPGA设计中分频后的时钟能布线到特定的时钟线上,利用该分频后的时钟可以简化电路设计。

注:特定时钟线,请参照本文列出的参考文章。

 

%title插图%num

图1

 

  • 时钟的应用

例2:利用s_clk做六十进制BCD码计数器

module BCD_counter60
(
    input            s_clk,   // input clock --default 1hz
    input            rst,     // reset signal
    output reg [3:0] count_l, // output for 0--9
    output reg [3:0] count_h  // output for 0-5
);
 
always@ (posedge s_clk or posedge rst)
if(rst) 
begin
    count_l <= 0;
    count_h <= 0;
end
else 
begin
    if(count_l == 9) 
    begin
        count_l <= 0;
        if(count_h == 5) count_h <= 0;
        else             count_h <= count_h + 1;
    end
    else 
    begin
        count_l <= count_l + 1;
    end
end
 
endmodule

 

 

2.时钟的使能输出

时钟使能输出不同于上面谈到的分频后直接做时钟,而是利用分频后的寄存器输出做下级电路的使能信号,参与下级的组合逻辑。例3中ms_en作为使能端使用的等效电路如图2所示。

%title插图%num

图2

 

例3:输入时钟100M Hz,设计分频器产生毫秒及秒脉冲使能。

module  fdiv
(
    input      inclk, // input clock  --default 100Mhz
    input      rst,   // reset signal
    output reg ms_en, // output for millisecond
    output reg s_en   // output for second  
);
 
reg [16:0] ms_r;
reg [9:0]  s_r;
 
always@ (posedge inclk or posedge rst)
if(rst) 
begin
    ms_r <= 0;
    ms_en <= 0;
end
else  
begin
    ms_en <= 0;
    if(ms_r == 99999) 
    begin
        ms_r <= 0;
        ms_en <= 1'b1;
    end
    else
        ms_r <= ms_r + 1;
end
 
always@ (posedge inclk or posedge rst)
if(rst) 
begin
    s_r <= 0;
    s_en <= 0;
end
else  
begin
    s_en <= 1'b0;
    if(ms_en )
        if(s_r == 999) 
        begin
            s_r <= 0;
            s_en <= 1'b1;
        end
        else 
            s_r <= s_r + 1;
end
 
endmodule

 

程序分析,例3中的程序与例1的程序不同,虽然都是分频,但例3中ms_en采用的是脉冲输出,其脉冲宽度为本时钟的单个时钟周期的窄脉冲。我们用它来做使能信号,而不是作为时钟信号使用,从ms_en到s_en都是同步电路设计(都采用inclk作为时钟)。当然计数器的计数最大值也发生了变化,如计数最大值由49999变成99999。后级的应用程序也不同。

例4:利用s_clk做六十进制BCD码计数器

module BCD_counter60
(
    input            inclk,   // input clock --default 100MHz
    input            rst,     //reset signal
    input            s_en,
    output reg [3:0] count_l, //output for 0--9
    output reg [3:0] count_h  //output for 0-5
);
 
always@ (posedge inclk or posedge rst)
if(rst) 
begin
    count_l <= 0;
    count_h <= 0;
end
else 
begin
    if(s_en)
        if(count_l == 9) 
        begin
            count_l <= 0;
            if(count_h == 5) count_h <= 0;
            else             count_h <= count_h + 1;
        end
        else 
        begin
            count_l <= count_l + 1;
        end
end
 
endmodule

 

3.时钟使能分频输出的优缺点

优点

  • 所有逻辑都在同一个时钟域下,时钟可以使用特定的(dedicated)时钟线,整体电路在同步时钟域下工作, 时序约束,布线都比较容易。综合布线后的时序报告文件比较准确。而分频后的寄存器作为时钟使用,整体电路将工作在多个时钟下,其结果将是异步时钟信号,在不同时钟域下的信号引用, 必须考虑异步信号的同步处理。
  • 整体电路工作在特定的时钟线下,由于特定的时钟线的驱动能力(fan-out)强,因此在负载较重时不用考虑时钟质量下降带来的影响。而分频后的寄存器作为时钟使用,一般来说,驱动能力弱,容易影响时钟质量,可能会给整体电路带来不可预估的影响。
  • 在负载较重的情况下,时钟使能可以复制,方便布局布线的调整。而分频后的寄存器作为时钟使用时,在复制后会产生更多的异步时钟,使系统设计更加复杂。
  • 相位控制上,时钟分频使能输出相对于分频后的寄存器作为时钟使用更加灵活。

缺点

  • 相对于分频后的寄存器作为时钟使用的情况,时钟分频使能输出增加一级逻辑
  • 相对于分频后的寄存器作为时钟使用的情况,在使用时钟分频使能输出时,后级的代码书写繁琐,例如状态机中每一个状态需要使用if语句判断使能,在状态机程序设计中可能会增加额外的状态。

总结:在ASIC程序设计中,上面的两种方法都可以选用。至于FPGA设计,如果可以将内部逻辑重新布线到特定的时钟线上,这两种方法也都可以使用。不过,在FPGA的开发中推荐使用时钟分频使能输出的方式。

关于更多关于Xilinx 7系列相关的时钟资源,参照下列IC知识库文章:

 

 

 

Posted in FPGA, FPGA, Verilog, Verilog

发表评论

相关链接