11-计数器

发布时间 2023-05-25 20:42:20作者: Icer_Newer

1.计数器

FPGA中一切与时间有关的电路都会使用到计数器
计数是一种最简单的基本运算,计数器就是实现这种运算的逻辑电路,计数器在数字系统中主要是对脉冲的个数进行计数,以实现测量\计数和控制的功能,同时兼有分频功能.
计数器在数字系统中应用广泛,如电子计算机的控制器中对指令地址进行计数,以便顺序取出下一条指令,在运算器中做乘法,除法运算时记下加法\减法次数;有如在数字仪器中对脉冲的计数等
计数器在FPGA最常用的时序逻辑电路,通过计数器可以知道各个信号之间的关系
计数器一般从0开始计数,计满清0或者是计到一定次数清0

2. FPGA实现

  • 前0.5s处于点亮状态,后0.5s处于熄灭状态

2.1 计数器设计关键

计数器关键:什么时候开始计数,什么时候清零

  • 计数器在复位信号撤销之后,时钟沿到来就可以立即进行计数
  • 计数器清0:计满或者计到需要的值,哪一个值是需要计到的值?比如需要计数1s,就需要知道计数器计多少个数(50M时钟)
  • f = 50MHz = 50000KHz = 50000_000(十进制)Hz,单位时间内信号进行周期性变化的次数,50000_000Hz表示单位时间内进行了50000_000次周期性变化,每次变化所用的时间为t
  • t = 1/f = (1/50000_000) = 20ns -- 时钟周期,计数器每一次计数经过的时间为20ns
  • 计数器需要计数的个数M:M = 1s/20ns = 5*10^7
  • 计数器的最大值是:M-1(从0开始计数)

2.2 模块框图和波形图

  • 计数器是对时钟信号进行计数,所以要有时钟信号,复位信号
  • 引出一路输出信号到LED灯

    波形图绘制非常重要,按照时钟,复位信号,输入,输出的顺序画波形图
  • 设置一个中间变量进行计数,复位信号下赋予初值,复位信号撤销且在上升沿开始计数


    优化
  • 计数到M/2-1之后进行清零,再计数到M/2-1之后再次清0,也可以实现上述前0.5s灯亮,后0.5s灯灭,且每次计数都是M/2
  • 第一种条件下计数到M-1(4999999),转化为二进制需要26bit,优化之后计数到M/2-1(2499999)需要使用25bit,节省资源量

2.3 RTL

module counter
#(
  parameter  CNT_MAX = 25'd24_999_999  // 例化模块的参数传递接口,参数之间加,
)
(

  input wire sys_clk,
  input wire sys_rst_n,
  
  output reg led_out
);
  // 声明计数器变量,位宽25bit
  reg [24:0] cnt;
  
  // 参数定义
  // 计数器的计数最大值
  // parameter可以用与模块内部,也可以用于参数例化模块的参数传递
  parameter  CNT_MAX = 25'd24_999_999; // 普通模块参数定义
  // localparam CNT_MAX = 25'd24_999_999; // 只用于模块内部
  

  counter 
  #(
    .CNT_MAX (100);  // 实例化的时候参数也写在这个位置,并且可以修改参数
  )
  counter_inst1      // 带有参数的模块实例化的模块名称写在参数列表之后
  (
    .sys_clk  (sys_clk),
    .sys_rst_n (sys_rst_n),
    
    .led_out (led_out)
  );

    counter 
  #(
    .CNT_MAX (100);  // 实例化的时候参数也写在这个位置,并且可以修改参数
  )                  // 多个模块实例化可以传递不同的参数
  counter_inst1
  (
    .sys_clk  (sys_clk),
    .sys_rst_n (sys_rst_n),
    
    .led_out (led_out)
  );

endmodule
module counter
#(
  parameter  CNT_MAX = 25'd24_999_999;  // 例化模块的参数传递接口
)
(

  input wire sys_clk,
  input wire sys_rst_n,
  
  output reg led_out
);
  // 声明计数器变量,位宽25bit
  reg [24:0] cnt;

 
  // 计数器变量赋值   
  always @ (posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        cnt <= 25'd0;
      end
    else if(cnt = CNT_MAX)
      begin
        cnt <= 25'b0;
      end
    else
      cnt <= cnt + 25'd1;
  end
  
  // 输出信号赋值
  always@(posedge sys_clk pr negedge sys_clk_n) begin
    if(sys_rst_n == 1'b0)
      led_out <= 1'b0;
    else if(cnt == CNT_MAX)
      led_out <= ~led_out;
    else
      led_out <= led_out;
  end

endmodule
  • 编译代码

2.4 Testbench

`timescale 1ns/1ns

module tb_counter();
  reg sys_clk;
  reg sys_rst_n;
  
  wire led_out;
  
  initial begin
    sys_clk = 1'b1;
    sys_rst_n <= 1'b0;
    #20;
    sys_rst_n <= 1'b1;
  end

  always #10 sys_clk = ~sys_clk;
  
  counter
  #(
    .CNT_MAX (25'd24)
  )
  counter_inst
  (
    .sys_clk (sys_clk),
    .sys_rst_n (sys_rst_n),
    .led_out (led_out)
  );
endmodule


2.5 上板验证


2.6 其他方式实现

  • 当计数器计满之后,添加一个cnt_reg变量,给这个变量的信号产生一个周期的脉冲(拉高一个周期),然后拉低
  • cnt信号正常
  • led_out信号在检测到cnt_flag信号为高电平的时候,会进行一次翻转

  • 使用脉冲标志信号cnt_flag,当计数器计数到M/2-2的时候,在下一个周期(M/2-1)周期上升沿产生一个脉冲,同样在cnt_flag信号拉高的时候led_out进行翻转,这样就能实现led_out和计数信号同步,没有延迟周期

    引入脉冲标志信号cnt_flag,可以简化if中的条件语句,在进行复杂设计的时候可以节省资源
module counter
#(
  parameter  CNT_MAX = 25'd24_999_999;  // 例化模块的参数传递接口
)
(

  input wire sys_clk,
  input wire sys_rst_n,
  
  output reg led_out
);
  // 声明计数器变量,位宽25bit
  reg [24:0] cnt;
  reg        cnt_flag;
 
  // 计数器变量赋值   
  always @ (posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        cnt <= 25'd0;
      end
    else if(cnt = CNT_MAX)
      begin
        cnt <= 25'b0;
      end
    else
      cnt <= cnt + 25'd1;
  end
  

  // 计数器标志位赋值
  always @ (posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
      cnt_flag <= 1'b0;
    else if(cnt ==(CNT_MAX - 25'd1))
      cnt_flag <= 1'b1;
    else 
      cnt_flag <= 1'b0;
    


  
  // 输出信号赋值
  always@(posedge sys_clk pr negedge sys_clk_n) begin
    if(sys_rst_n == 1'b0)
      led_out <= 1'b0;
    else if(cnt_flag == 1'b1)
      led_out <= ~led_out;
    else
      led_out <= led_out;
  end

endmodule