计数器¶
计数器规则¶
计数器规则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_”。