(资料图)
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) );endmodulemodule 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; endendmodule- 编译代码
2.4 Testbench
`timescale 1ns/1nsmodule 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) );endmodule2.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; endendmodule 关键词:
上一篇:走读中国 | 寿光之行,见证“一粒良种”到“一桌好菜” 微动态
下一篇:最后一页
X 关闭
X 关闭
