模块名称: timer
主要功能 :通过配置输入端口有3个重要参数:来设置定时器模块实现 一次计数(定时)还是循环计数(定时)功能
CNT_ENABLE : 定时器使能开关(一次计数时,发送一个时钟周期的高电平脉冲信号,循环定时时,直接拉高)
MODE : =0 单次定时器计数 =1 循环定时计数
CNT_ARR[31:0] : 计数器重装载值,每次计数器的计数值计数到该位置以后,重新开始计数
输出有两个信号:
Full_flag : 计数器计满标志信号.
ARR[31:0] : 实时(当前)计数器的计数值
实现(设计)流程:
1、使用一个always 语句,判断是在 单次计数模式 还是循环计数模式,判断进入使能模式下后,
在循环模式下:判断是否 CNT_ENABLE =1 ,是的条件下,进行正常计数,到达最大值是计数器清零,重新开始计数。
在单次计数模式下: 我们为了让计数器进行单次的计数,设置了一个计数寄存器标志信号(oneshot),当标志信号位 1 时,计数器正常机器,计数器清零保持该状态。
2、标志信号(oneshot)使用一个always块语句来判断,当MODE =0 时,如果CNT_ENABLE =1 ,设置 oneshot =1 ,如果计数器但前值==(CNT_ARR[31:0] 定时装载值-1),oneshot =0 ,停止计数,其余时刻保持该状态即可。
优势: 一个模块可以实现两个功能,模块复用的时候可以更加的方便。
- timer.v 文件
//通用定时器模块
//描述:
//通过定义 timer 里面的 input 的数值
// 得到 output 里面的数值,这样就可以得到我们想要的输出(2个 分别是 CNT_NOW 和 Full_Flag 两个输出信号)
// 时钟和复位信号是输入信号,一般由系统给定
// CNT_ARR、MODE、CNT_ENABLE 可以自己给定固定值或者通过外部的输入给定一个值(信号)
module timer(
Clk , //时钟
Rst_n , //复位信号
CNT_ARR , //定时器重装载值(类似与32的重装载值)
MODE , // Mode : 1 循环定时 Mode : 0 单次定时
CNT_ENABLE , //CNT_ENABLE : 1 定时器计数器使能,开始计数
//CNT_ENABLE : 0 定时器计数不使能,停止计数
CNT_NOW , //实时定时器计数器
Full_Flag //计数完成标志位
);
// 定义:
input Clk ;
input Rst_n;
input [31:0]CNT_ARR ; //通用32位定时计数器
input MODE ;
input CNT_ENABLE;
output [31:0]CNT_NOW ; //当前32位定时计数器的值
output reg Full_Flag ; //计数完成标志位
//自己模块定义的信号
reg [31:0]cnt; //定义一个32位的计数器
reg oneshot ; //定义一个 单次计数值计满 触发信号
wire Full_Flag_r; //Full_Flag_r 用于延时Full_Flag 的一个周期
assign CNT_NOW = cnt; //定时器的值
assign Full_Flag_r = (CNT_NOW == CNT_ARR - 1)?1'b1:1'b0;
//第1个 always 块
always@(posedge Clk)
Full_Flag <= Full_Flag_r;
//第2个 always 块
always@(posedge Clk or negedge Rst_n)
if(!Rst_n) //复位信号到来,数据清零(qingling)
cnt <=0;
else if(MODE == 1'b1)begin //判断是否是循环模式
if((CNT_ENABLE == 1'b1) && (cnt < CNT_ARR)) //判断CNT_ENABLE 是否启动,且计数值是否小于重装载值
cnt <= cnt + 1'b1; //cnt++
else //否则数据清零
cnt <= 0;
end
else if(!MODE)begin //判断是否是单次计数模式
if(oneshot) //oneshot信号为1,表示满足一次计数条件
cnt <= cnt + 1'b1;
else
cnt <= 0;
end
//第3个 always 块
always@(posedge Clk or negedge Rst_n)
if(!Rst_n) //复位信号清零
oneshot <= 1'b0;
else if(!MODE)
begin //判断是在一次计数模式下
if(CNT_ENABLE == 1'b1) //判断CNT_ENABLE 是否启动
oneshot <= 1'b1;
else if(CNT_NOW == CNT_ARR - 1) //判断计数值是否等于重转载值
oneshot <= 1'b0;
else //保持不变
oneshot <= oneshot;
end
else//在循环模式下,oneshot =0 ;
oneshot <= 1'b0;
endmodule
- timer_tb.v 文件
`timescale 1ns/1ns
`define clk_period 20
// 定时器定时模块测试源文件
module timer_tb; // 定义的测试模块的名称,一般命名 是后面加 _tb,表示这是测试模块
// 测试模块的输入输出 定义: input 类型 --> reg 类型替换
// output 类型 --> wire 类型替换
reg Clk ;
reg Rst_n;
reg [31:0]CNT_ARR ; //通用32位定时计数器
reg MODE ;
reg CNT_ENABLE;
wire [31:0]CNT_NOW ; //当前32位定时计数器的值
wire Full_Flag ; //计数完成标志位
timer timer0(
.Clk(Clk) , //时钟
.Rst_n(Rst_n) , //复位信号
.CNT_ARR(CNT_ARR) , //定时器重装载值(类似与32的重装载值)
.MODE(MODE) , // Mode : 1 循环定时 Mode : 0 单次定时
.CNT_ENABLE(CNT_ENABLE) , //CNT_ENABLE : 1 定时器计数器使能,开始计数 //CNT_ENABLE : 0 定时器计数不使能,停止计数
.CNT_NOW(CNT_NOW) , //实时定时器计数器
.Full_Flag(Full_Flag) //计数完成标志位
);
/*********** 定义初始化测试信号 ************/
initial Clk = 1; //定义时钟信号
always #(`clk_period/2) Clk = ~Clk;
initial begin //执行一次,叫 initial 加入 begin
//初始化所有输入信号
Rst_n = 0;
CNT_ARR = 0;
MODE = 0;
CNT_ENABLE = 0;
#(`clk_period*20 + 1);
//复位信号 Rst_n = 1; 模块正式开始工作
Rst_n = 1;
#(`clk_period*20);
//设置预设值为1000,模式为循环定时模式
CNT_ARR = 1000;
MODE = 1;
#(`clk_period*20); //设置为循环模式,延时20个时钟周期
CNT_ENABLE = 1; //开始启动计数定时器
#(`clk_period*12000);
CNT_ENABLE = 0; //关闭计数定时器
#(`clk_period*20);
//设置预设值为600,模式为循环定时模式
CNT_ARR = 600;
MODE = 1;
#(`clk_period*20);
CNT_ENABLE = 1;
#(`clk_period*8000);
CNT_ENABLE = 0;
#(`clk_period*20);
//设置预设值为1000,模式为单次定时模式
CNT_ARR = 1000;
MODE = 0;
#(`clk_period*20); // CNT_ENABLE = 1; #(`clk_period) CNT_ENABLE = 0; 表示给定的是一个脉冲信号,进入的是单次计数模式
CNT_ENABLE = 1;
#(`clk_period);
CNT_ENABLE = 0;
#(`clk_period*1200);
//设置预设值为600,模式为单次定时模式
CNT_ARR = 600;
MODE = 0;
#(`clk_period*20); // CNT_ENABLE = 1; #(`clk_period) CNT_ENABLE = 0; 表示给定的是一个脉冲信号,进入的是单次计数模式
CNT_ENABLE = 1;
#(`clk_period);
CNT_ENABLE = 0;
#(`clk_period*1200);
$stop;
end
endmodule