计数器

计数器规则

计数器规则1: 计数器逐一考虑三要素–初值、加1条件、结束条件

任何计数器都有三个要素:初值、加1条件、结束值

  • 初值:计数器的默认值或者开始计数的值

  • 加1条件:计数器执行加1条件

  • 结束值:计数器计数周期的最后一个值 设计计数器,要逐一考虑这三个要素,一般是先考虑初值,再考虑加1条件,最后再考虑结束值。

计数器规则2:计数初值必须为0

计数器的默认值和开始值一定要为0。这是我们规范的统一要求。我们知道一般编程语言计数都是从0开始的,0表示第1个,1表示第2个,。。。这里我们也参考这种做法,计数器都从0开始计数。

所有计数器都统一从0开始计数,有助于我们阅读理解、方便使用,从而不用从头看具体代码,就能清楚这个数值的含义。

计数器规则3:使用某一计数值,必须同时满足加1条件

计数器从0开始计数,计数器的默认值,也就是复位值也是0。当计数器值为0时,如何判断这是计数器的第一个值还是还没开始计数的默认值呢?

可以通过加1条件来判断。当加1条件无效时,计数器值为0表示未开始计数的默认值;当加1条件有效时,计数器值为0表示计数器的第1个值。以此类推,当cnt==x-1时,不表示数到x;只有当cnt==x-1时,并且加1条件有效时,才表示数到x。

例如:当加1条件为add_cnt,且 add_cnt && cnt==4时,表示计数到5个;而当 add_cnt==0 && cnt==4时,不表示计数到5个。

计数器规则4:结束条件必须同时满足加1条件,且结束值必须是 x-1 的形式。

计数器的结束条件必须同时满足加1条件。例如假设要计数5个,那么结束值是4,但是结束条件不是 cnt==4 而是 add_cnt && cnt==4。因为 cnt==4 不表示计数到5个,只有 add_cnt && cnt==4 时,才表示计数到 5 个。

为了更好地阅读代码,我们这里规定结束值必须是 x-1 的形式,即 add_cnt && cnt==4 要写成 add_cnt && cnt==5-1。这里的“5”表示希望计算的个数,“-1”则是固定格式。有了这个约定后,计数的边界就很明确了。

计数器规则5:当取某个数时,assign 形式必须为:(加1条件) && (cnt==计数值-1)

当要从计数器取某个数时,例如要取计数器的第5个点,就很容易写成cnt==5-1,这是不正确的。正确的写法时:(加1条件)&&(cnt==计数值-1),如 add_cnt && cnt==5-1。

计数器规则6:结束后必须回到0

每轮计数周期结束后,计数器变回0,这是为了使计数器能够循环重复计数。

计数器规则7:若需要限定范围,则推荐使用“>=”和“<”两种符号

设计时,考虑边界值通常要花费一些心思,而且容易出错。为此,我们约定:若需要限定范围,则推荐使用“>=”和“<”两种符号。例如要取前8个数,那么就取 cnt>=0 && cnt<8。注意,一定是“大与或等于”和“小于”符号,而不使用“大与”和“小于或等于”符号。

该规则参考编程里的for循环语句。假如要循环 8 次,for 循环的条件通常会写成“i==0; i<8; i++”,前面的0表示开始值,后面的8表示循环次数。当然,也可以写成“i==0; i<=7; i++”,但是这数字的意义就实在令人费解了,虽然知道7是从8-1得来的,但多一个“-1”的思考,就纯属画蛇添足了。

计数器规则8:设计步骤

设计步骤: 先写计数器的always段,条件用名字代替;然后用assign写出加1条件;最后用assign写出结束条件

我们的计数器模版代码包括三段。

第一段,写出计数器的 process/always 段

VHDL

process(clk,rst_n)
begin
  if(rst_n = '0')then
    cnt <= 0;
  elsif(clk'event and clk = '1')then
    if(加1条件)then
      if(结束条件)then
        cnt <= 0;
      else
        cnt <= cnt + 1;
      end if;
    end if;
  end if;
end process;

verilog

always @(posedge clk or negedge rst_n) begin
   if(!rst_n) begin
      cnt <= 0;
   end
    else if(加1条件) begin
        if(结束条件)
    cnt <= 0;
      else
    cnt <= cnt + 1;
   end
end

大家有没有发现上述模版的特点?这个模版只需要填两项内容:加一条件和结束条件。如果为加1条件和结束条件定义一个信号名,例如 add_cnt 和 end_cnt,则代码变成:

VHDL

process(clk,rst_n)
begin
  if(rst_n = '0')then
    cnt <= 0;
  elsif(clk'event and clk = '1')then
    if(add_cnt )then
      if(end_cnt)then
        cnt <= 0;
      else
        cnt <= cnt + 1;
      end if;
    end if;
  end if;
end process;

verilog

always @(posedge clk or negedge rst_n) begin
   if(!rst_n) begin
      cnt <= 0;
   end
   else if(add_cnt) begin
      if(end_cnt)
    cnt <= 0;
      else
    cnt <= cnt + 1;
   end
end

第二段,用组合逻辑写出加1条件

在此阶段,只需要想好一个点,就是计数器的加1条件。假设计数器的加1条件为 a==2,则代码如下:

VHDL

add_cnt <=  a=2;--add 1

verilog

assign add_cnt = a==2;//add 1

第三段,用组合逻辑写出结束条件

在此阶段,只需要想好一个点,就是计数器的结束值。参考计数器规则5的要求,结束条件的形式一定是:(加1条件)&&(cnt==计数值-1).假设计数器要计数10个,则代码如下:

VHDL

end_cnt <= add_cnt and (cnt =  10-1);--end

verilog

assign end_cnt = add_cnt && cnt == 10-1; //end

至此,就完成了计数器代码的设计。总结一下这段代码的特点:每次值考虑一件事,按这要求去做,就非常容易完成代码设计。

以下是我们完整的模版:

VHDL

signal cnt : integer range 0 to  ;--max number
signal add_cnt : boolean;
signal end_cnt : boolean;

process(clk,rst_n)
begin
  if(rst_n = '0')then
    cnt <= 0;
  elsif(clk'event and clk = '1')then
    if(add_cnt )then
      if(end_cnt)then
        cnt <= 0;
      else
        cnt <= cnt + 1;
      end if;
    end if;
  end if;
end process;

add_cnt <=  ;--add 1   dout_tmp='1'
end_cnt <= add_cnt and (cnt =  -1);--end

以上模版中,只需要补充三个地方,

signal cnt : integer range 0 to  ;--在 to 后面补充计数器的最大计数范围
add_cnt <=  ;--补充加1条件
end_cnt <= add_cnt and (cnt =  -1);--补充计数器数多少数

verilog

reg [  :0]   cnt    ;
wire         add_cnt;
wire         end_cnt;

always @(posedge clk or negedge rst_n) begin
   if(!rst_n) begin
      cnt <= 0;
   end
   else if(add_cnt) begin
      if(end_cnt)
    cnt <= 0;
      else
    cnt <= cnt + 1;
   end
end

assign add_cnt = ;//condition: add 1
assign end_cnt = add_cnt && cnt ==  -1; //End condition, last value

以上模版中,只需要补充三个地方,

reg [  :0]   cnt    ;//补充计数器的最大计数范围
assign add_cnt = ;//补充加1条件
assign end_cnt = add_cnt && cnt ==  -1; //补充计数器数多少数

计数器规则9:加1条件必须与计数器严格对齐,其它信号一律向计数器对齐

我们设计出计数器,但一般计数器不是最终的目的,最终的目的是输出各种信号。设计计数器是为了方便产生这些输出信号(包括中间信号),并能从计数器获取变化条件。例如:信号dout在计数到6时拉高,则其变1的条件是:add_cnt && cnt==6-1。

假设有两个信号:dout0在计数到6时拉高;dout1在计数到7时拉高。一种做法是dout0变1的条件是add_cnt && cnt==6-1,dout1变1的条件是dout0==1。这个dout1就是间接与计数器对齐。这是非常不好的方法。这里我们建议一律向计数器对齐,dout1变1的条件应该为add_cnt && cnt==7-1。

计数器规则10:命名必须符合规范

比如:add_cnt 表示加1条件;end_cnt 表示结束条件

如无特别说明,计数器的命名都要符合规范,加1条件的前缀为 “add_”,结束条件的前缀为 “end_”。