verilog时序单元计数器

发布时间 2023-07-22 10:59:26作者: luckylan

计数器

①时序电路的行为决定了其只能通过always 块语句实现,通过关键词“posedge”和“negedge”来捕获时钟信号的上升沿和下降沿。在always 语句块中可以使用任何可综合的标志符。

②在描述时序电路的always 块中的reg 型信号都会被综合成寄存器,这是和组合逻辑电路所不同的。

③时序逻辑中推荐使用非阻塞赋值“<=”.

④时序逻辑的敏感信号列表只需要加入所用的时钟触发沿即可,其余所有的输入和条件判断信号都不用加入,这是因为时序逻辑是通过时钟信号的跳变沿来控制的。

环形计数器

环形计数器的规则是利用一个移位寄存器右移实现,N位的环形计数器能计数的个数为N;也就是说,有N个有效的状态;

8级移位寄存器构成的环形计数器,能有8个有效状态;

总结:环形也是基于移位寄存器的计数器,对于n个移位寄存器构成的计数器,只有n个有效状态。

扭环计数器

扭环计数器规则:

扭环计数器又成约翰逊计数器,也是有移位寄存器构成,但是它与环形计数器不同的是将最低位取反后移位到最高位,约翰逊计数器的长度为N,因为移位寄存器串行输入端的信号是从反向端 ~Q取得的。经过N个时钟后,计数器的状态与初始状态刚好相反,必须再经过N个时钟后才能回到扭环原态。

可见有8个有效状态;总结:N位的扭环计数器有2N个有效状态。

,即将FFn-1的输出Qn-1接到FF0的输入端D0

计数器,顾名思义就是在时钟的节拍下进行计数.

2.1.3 N位计数器

一个简单的N位计数器的代码如下所示,这个计数器从0计数到 -1(共计数了 个数,也就是N位计数器)

module count#(parameter N=8)(
input clk,
input clear,
output[N-1:0] cnt_Q
);
reg[N-1:0] cnt;
assign cnt_Q = cnt;
always@(posedge clk)

  if(clear)
    cnt <= 'h0; //同步清 0,高电平有效
  else
    cnt <= cnt+1'b1; //加法计数
endmodule

上述描述的计数器通过 clear 信号清除计数值,然后下一周期开始加 1 计数;当计数器计到能够存储的最大数值时, 例如本例为 8 个 1,即 8'hff 就会自动回到 0,然后开始下一轮计数。

k计数器

如果想要实现 0~k 范围内计数,其中k ≠ 2^N ,可以将 always 语句修改为:

module count#(parameter N=8)(
input clk,
input clear,
output[N-1:0] cnt_Q
);
reg[N-1:0] cnt;
assign cnt_Q = cnt;
always@(posedge clk)
    if(clear)
        cnt <=  'h0; //同步清 0,高电平有效
    else if(cnt==K)
        cnt <= 'h0;
    else
        cnt <= cnt+1'b1; //加法计数
endmodule

不规则的计数器

这是一个53计数器, 采用计到53后产生异步复位的办法实现清0, 产生毛刺是必然的。

module count#(parameter N=6)(
input clk,
output[N-1:0] cnt_Q
);
reg[N-1:0] cnt;
assign cnt_Q = cnt;
wire  CLRN=~(Q==6’b110101);
always@(posedge clk or negedge CLRN)
    if(~CLRN)
        cnt <=  'h0; //同步清 0,高电平有效
    else
        cnt <= cnt+1'b1; //加法计数
endmodule

上面异步复位的办法实现清0,不仅容易产生毛刺,然而最严重的是, 当计数器所有bit或相关bit均在翻转时, 电路有可能出错, 例如: 计数器从“ 110011”- >“110100”,由于电路延时的原因,中间会出现“ 110101”状态,导致计数器误清0。采用同步清0的办法,不仅可以有效地消除毛刺,而且能避免计数器误清0。电路如下图所示。

module count#(parameter N=6)(
input clk,
input clear,
output[N-1:0] cnt_Q
);
reg[N-1:0] cnt;
assign cnt_Q = cnt;
always@(posedge clk)
  if(cnt==6‘b110100)
        cnt <= 'h0;
    else
        cnt <= cnt+1'b1; //加法计数
endmodule

2.1.6 递增递减计数器

前面是累加计数,下面是一个既可以递增也能递减,且具备初始值装载和复位的计数器,代码如下所示:

module updown_count#(parameter N=8)(
  input clk,
  input clear,
  input load,
  input up_down,
  input [N-1:0] preset_D,
  output[N-1:0] cnt_Q
);
reg[N-1:0] cnt;
assign cnt_Q = cnt;
always@(posedge clk)
  if(clear)
    cnt <= 'h0; //同步清 0,高电平有效
  else if(load)
     cnt <= preset_D; //同步预置
  else if(up_down)
     cnt <= cnt+1;    //加法计数
  else
     cnt <= cnt-1;    //减法计数
endmodule

带并行预置及进位输出计数器

module counter_4(
output reg [3:0]A_count, //数据输出
output   C_out,//进位输出
input [3:0]data_in,//数据输入
input count,//高电平有效的计数
input load, //高电平有效的预置
input clk,
input resetn //
);
assign C_out=count&&(~load)&&
(A_count==4’b1111);
always@(posedge clk or negedge resetn)
if(~resetn)   A_count<=4’b0000;
else if (load)  A_count<=data_in;
else if(count)  A_count<= A_count+1’b1;
else       A_count<= A_count;
endmodule

Q<=#2 ~Q:延时符号放在了非阻塞符号的右边,这种形式的时延称为内在赋值时延,将Q取反,然后延迟,再赋给Q。

计数器的用途

(1)基本的计数功能与分频

计数器的基本功能顾名思义就是计数了,用来计数,产生某个信号等等。利用这个功能,可以实现信号的分频,具体会在后面的分频电路中进行描述。

(2)看门狗

计数器其实就可以设计成看门狗。在初始状态时,看门狗电路首先装载一个大数;当状态机或者程序开始运行后,看门狗开始倒计数。如果状态机或程序运行正常,每隔一段时间应发出指令或信号让看门狗重新装载一个大的初始值,并再次开始倒计数。如果看门狗减到 0 就认为程序或状态机没有正常工作,就需要强制整个系统复位。

上面的第二处改进的计数器电路描述就是一个看门狗电路,只要加上 cnt==0 作为看门复位状态即可;而 load 信号则是状态机或软件给出的喂狗动作。

(3)特殊的有限状态机

当状态机要求没有那么严格的时候,这个时候就可以用计数器的计数值当做状态机的状态,计数增加或者减少就是改变状态。

(4)对计数器的译码

对计数器译码,可能由于竞争冒险产生毛刺。如果后级采用了同步电路,我们完全可以对此不予理会。如果对毛刺要求较高,推荐采用Gray编码(PLD)或One-hot编码(FPGA)的计数器, 一般不要采用二进制码.具体描述中,我们可以用状态机来描述,而利用逻辑综合工具来编码。

采用时钟检测信号,出现变化即为上升/下降沿。 当被测信号与时钟相关时,可以不用第一个触发器。