计数器是一种在数字电子系统中常见的逻辑电路,用于记录输入脉冲或时钟信号的数量。计数器的作用是在输入信号发生变化时,将一个内部计数值递增或递减。通常用于测量时间、频率、脉冲数量等。
常见的计数器有:
递增计数器(Up Counter): 每次输入一个时钟脉冲时,计数器的值递增。当达到最大计数值时,计数器可以溢出,重新从零开始。
递减计数器(Down Counter): 每次输入一个时钟脉冲时,计数器的值递减。当达到最小计数值时,计数器可以溢出,重新从最大值开始。
同步计数器(Synchronous Counter): 计数操作与时钟信号同步进行。所有触发器在同一时钟边沿触发。
异步计数器(Asynchronous Counter): 计数操作不一定与时钟信号同步。不同阶段的触发器可以在不同时钟边沿触发。
可编程计数器: 允许用户根据需要编程计数器的初始值、最大值、最小值等参数。
环形计数器(Ring Counter): 一种特殊的计数器,只有一个触发器处于“1”的状态,其余为“0”,而且这个“1”在每个时钟脉冲中移动。
本文按照明德杨的框架详细阐述如何编写计数器Verilog代码。
在构建计数器时,只需要考虑三部分,即初值,计数条件和结束值。初值最好设为0,计数值为计数个数减1。在明德杨规范中,计数条件写为aa_cnt,结束计数条件为add_cnt&&cnt==x-1。即当计数到x且满足计数条件时结束计数,增加add_cnt这一条件的目的在于区分复位值和初始默认值(在初始时认为计数条件不满足,即逻辑值为0)。同时明德杨还规定在限定范围时,最好使用>=和<符号,避免对于边界值的考虑。例如要取前6个数,则cnt>=0&&cnt<6,从5开始取10个数,cnt>=5&&cnt<10+5。
以下题为例:
要求计数器从0到9之间计数,上升沿触发,同步复位,复位到0。计数模块的代码编写如下:
always@(posedge clk)
begin
if(reset)
q<=4'b0000;
else if(add_cnt)//计数条件
begin
if(end_cnt)//计数结束条件
q<=4'b0000;
else
q<=q+1;
end
end
接下来对其中的计数条件和结束计数条件作描述
always@(posedge clk)
begin
add_cnt=1'b1;
end
assign end_cnt=add_cnt&&(q==10-1);
大致中文翻译为:
创建一组适用于12小时制时钟(带有AM/PM指示器)的计数器。您的计数器由一个快速运行的时钟信号(clk)驱动,每当时钟应该递增时(即每秒一次),ena信号会脉冲一次。
reset信号将时钟复位为12:00 AM。pm信号用于表示AM为0,PM为1。hh、mm和ss分别表示小时(01-12)、分钟(00-59)和秒(00-59),每个都是用二进制编码十进制(BCD)表示。在不启用时,复位具有更高的优先级,并且可以在未启用时发生。
以下时序图显示了从上午11:59:59滚动到下午12:00:00的行为,以及同步复位和启用的行为。
要实现时钟计数,则需要构建三个计数模块,时、分、秒。其中每个计数模块中要分为十位和个位,个位的计数范围为0~9,十位的计数范围为0~6,综合来看,分、秒的计数范围都为0~59,时的计数范围为0~11。
秒的个位计数条件为使能信号ena脉冲,即add_s_ones=ena。结束条件为end_s_ones=add_s_ones&&(s_ones==4'd9)。十位计数条件为add_s_tens=end_s_ones,结束条件为end_s_tens=add_s_tens&&(s_tens==4'd5)。即当秒钟的个位计数到9时,复位到0,同时十位计数1;
分的个位计数条件为add_m_ones=end_s_tens,结束条件为end_m_tens=add_m_ones&&(s_ones==4'd9)。十位的计数条件为add_m_tens=end_s_ones,结束条件为end_m_tens=add_m_tens&&(m_tens==4'd5);
对于时位的设计稍显复杂。当时位是0时,个位可以由0增加到9,个位再复位到0,同时十位增加到1,当时位是1,个位只能由0增加到2,然后十位个位都复位到0。因此十位为0 时,个位的结束条件为end_h_ones_0=add_h_ones&&(h_ones==4'd9),十位为1时个位的结束条件为end_h_ones_1=add_h_ones && ((h_ones == 4'd2) && (h_tens == 4'd1))。当个位为2十位为1时,结束十位为1 的状态,因此有end_h_tens_1 = add_h_tens && end_h_ones_1。当个位为9时,结束十位为0的状态,因此有end_h_tens_0 =add_h_tens && end_h_ones_0。
reg pm_temp;//区分上午下午
reg [3:0] s_ones;//秒的个位
reg [3:0] s_tens;//秒的十位
reg [3:0] m_ones;//分的个位
reg [3:0] m_tens;//分的十位
reg [3:0] h_ones;//时的个位
reg [3:0] h_tens;//时的十位
wireadd_s_ones;//秒的个位开始计数条件
wireend_s_ones;//秒的个位结束计数条件
wireadd_s_tens;//秒的十位开始计数条件
wireend_s_tens;//秒的十位结束计数条件
wireadd_m_ones;//分的个位开始计数条件
wireend_m_ones;//分的个位结束计数条件
wireadd_m_tens;//分的十位开始计数条件
wireend_m_tens;//分的十位结束计数条件
wireadd_h_ones;//时的个位开始计数条件
wireend_h_ones;//时的个位结束计数条件
wireadd_h_tens;//时的十位开始计数条件
wireend_h_tens;//时的十位结束计数条件
wire end_h_ones_0;//时的十位为0个位为9时结束计数
wire end_h_ones_1;//时的十位为1个位为2时结束结束
wire end_h_tens_0;//在个位为9时,结束十位为0的状态
wire end_h_tens_1;//在个位为2十位为1时,结束十位为1的状态
wirepm_ding;//区分上午下午
2、秒钟代码块实现//秒的个位
always @(posedge clk)
begin
if(reset)
s_ones <= 4'b0;
else if(add_s_ones)
begin
if(end_s_ones)
s_ones <= 4'b0;
else
s_ones <= s_ones + 4'b1;
end
end
assign add_s_ones=ena;
assign end_s_ones=add_s_ones&&(s_ones==4'd9);
//秒的十位
always@(posedge clk)
begin
if(reset)
s_tens<=4'b0;
else if(add_s_tens)
begin
if(end_s_tens)
s_tens<=4'd0;
else
s_tens<=s_tens+4'b1;
end
end
assign add_s_tens=end_s_ones;
assign end_s_tens=add_s_tens&&(s_tens==5);
3、分钟代码块实现//分的个位
always @(posedge clk)
begin
if(reset)
m_ones <= 4'b0;
else if(add_m_ones)
begin
if(end_m_ones)
m_ones <= 4'b0;
else
m_ones <= m_ones + 4'b1;
end
end
assign add_m_ones = end_s_tens;
assign end_m_ones = add_m_ones && (m_ones == 4'd9);
//分的十位
always @(posedge clk)
begin
if(reset)
m_tens <= 4'b0;
else if(add_m_tens)
begin
if(end_m_tens)
m_tens <= 4'b0;
else
m_tens <= m_tens + 4'b1;
end
end
assign add_m_tens = end_m_ones;
assign end_m_tens = add_m_tens && (m_tens == 4'd5);
4、时钟的实现//时钟个位的实现
always @(posedge clk)
begin
if(reset)
h_ones <= 4'd2;
else if(add_h_ones)
begin
if(end_h_ones_0)
h_ones <= 4'b0;//如果十位为0,则复位到0
else if(end_h_ones_1)
h_ones <= 4'b1;//如果十位为1,则复位到1,出现11:59:59
else
h_ones <= h_ones+4'b1;
end
end
assign add_h_ones=end_m_tens;
assign end_h_ones_0=add_h_ones&&(h_ones==4'd9);
assign end_h_ones_1=add_h_ones && ((h_ones == 4'd2) && (h_tens == 4'd1));
//时钟十位的实现
always @(posedge clk)
begin
if(reset)
h_tens <= 4'd1;
else if(add_h_tens)
begin
if(end_h_tens_0)
h_tens <= 4'b1;
else if(end_h_tens_1)
h_tens <= 4'b0;
end
end
assign add_h_tens = end_m_tens;
assign end_h_tens_1 = add_h_tens && end_h_ones_1;//在个位为2十位为1时,结束十位为1的状态
assign end_h_tens_0 =add_h_tens && end_h_ones_0;//在个位为9时,结束十位为0的状态
5、上午下午的区分本部分参考另一位大佬的代码@日拱一卒_未来可期
//上午下午的区分
always@(posedge clk)
begin
if(reset)
pm_temp <= 1'b0;
else if(pm_ding)
pm_temp <= ~pm_temp;
end
assign pm_ding = h_tens == 4'd1 && h_ones == 4'd1 && end_m_tens;
6、输出的实现always@(*)
begin
pm=pm_temp;
s={s_tens,s_ones};
m={m_tens,m_ones};
h={h_tens,h_ones};
end
7、完整代码module top_module(
input clk,
input reset,
input ena,
output pm,
output [7:0] hh,
output [7:0] mm,
output [7:0] ss);
reg pm_temp;//区分上午下午
reg [3:0] s_ones;//秒的个位
reg [3:0] s_tens;//秒的十位
reg [3:0] m_ones;//分的个位
reg [3:0] m_tens;//分的十位
reg [3:0] h_ones;//时的个位
reg [3:0] h_tens;//时的十位
wireadd_s_ones;//秒的个位开始计数条件
wireend_s_ones;//秒的个位结束计数条件
wireadd_s_tens;//秒的十位开始计数条件
wireend_s_tens;//秒的十位结束计数条件
wireadd_m_ones;//分的个位开始计数条件
wireend_m_ones;//分的个位结束计数条件
wireadd_m_tens;//分的十位开始计数条件
wireend_m_tens;//分的十位结束计数条件
wireadd_h_ones;//时的个位开始计数条件
wireend_h_ones;//时的个位结束计数条件
wireadd_h_tens;//时的十位开始计数条件
wireend_h_tens;//时的十位结束计数条件
wire end_h_ones_0;//时的十位为0个位为9时结束计数
wire end_h_ones_1;//时的十位为1个位为2时结束结束
wire end_h_tens_0;//在个位为9时,结束十位为0的状态
wire end_h_tens_1;//在个位为2十位为1时,结束十位为1的状态
wirepm_ding;//区分上午下午
//秒的个位
always @(posedge clk)
begin
if(reset)
s_ones <= 4'b0;
else if(add_s_ones)
begin
if(end_s_ones)
s_ones <= 4'b0;
else
s_ones <= s_ones + 4'b1;
end
end
assign add_s_ones=ena;
assign end_s_ones=add_s_ones&&(s_ones==4'd9);
//秒的十位
always@(posedge clk)
begin
if(reset)
s_tens<=4'b0;
else if(add_s_tens)
begin
if(end_s_tens)
s_tens<=4'd0;
else
s_tens<=s_tens+4'b1;
end
end
assign add_s_tens=end_s_ones;
assign end_s_tens=add_s_tens&&(s_tens==5);
//分的个位
always @(posedge clk)
begin
if(reset)
m_ones <= 4'b0;
else if(add_m_ones)
begin
if(end_m_ones)
m_ones <= 4'b0;
else
m_ones <= m_ones + 4'b1;
end
end
assign add_m_ones = end_s_tens;
assign end_m_ones = add_m_ones && (m_ones == 4'd9);
//分的十位
always @(posedge clk)
begin
if(reset)
m_tens <= 4'b0;
else if(add_m_tens)
begin
if(end_m_tens)
m_tens <= 4'b0;
else
m_tens <= m_tens + 4'b1;
end
end
assign add_m_tens = end_m_ones;
assign end_m_tens = add_m_tens && (m_tens == 4'd5);
//时钟个位的实现
always @(posedge clk)
begin
if(reset)
h_ones <= 4'd2;
else if(add_h_ones)
begin
if(end_h_ones_0)
h_ones <= 4'b0;//如果十位为0,则复位到0
else if(end_h_ones_1)
h_ones <= 4'b1;//如果十位为1,则复位到1,出现11:59:59
else
h_ones <= h_ones+4'b1;
end
end
assign add_h_ones=end_m_tens;
assign end_h_ones_0=add_h_ones&&(h_ones==4'd9);
assign end_h_ones_1=add_h_ones && ((h_ones == 4'd2) && (h_tens == 4'd1));
//时钟十位的实现
always @(posedge clk)
begin
if(reset)
h_tens <= 4'd1;
else if(add_h_tens)
begin
if(end_h_tens_0)
h_tens <= 4'b1;
else if(end_h_tens_1)
h_tens <= 4'b0;
end
end
assign add_h_tens = end_m_tens;
assign end_h_tens_1 = add_h_tens && end_h_ones_1;//在个位为2十位为1时,结束十位为1的状态
assign end_h_tens_0 =add_h_tens && end_h_ones_0;//在个位为9时,结束十位为0的状态
//上午下午的区分
always@(posedge clk)
begin
if(reset)
pm_temp <= 1'b0;
else if(pm_ding)
pm_temp <= ~pm_temp;
end
assign pm_ding = h_tens == 4'd1 && h_ones == 4'd1 && end_m_tens;
//输出
always@(*)
begin
pm=pm_temp;
ss={s_tens,s_ones};
mm={m_tens,m_ones};
hh={h_tens,h_ones};
end
endmodule
8、仿真波形图 复位和秒钟###本文参考《明德杨大道至简的至简设计方法》,和@日拱一卒_未来可期的一篇博客,HDLBits答案(11)_Verilog计数器-CSDN博客。如有侵权,请联系删除。
相关知识
【fpga里Verilog语言的小知识点】
谈谈Verilog和SystemVerilog简史,FPGA设计是否需要学习SystemVerilog
实数比较指令示例
【免费】基于Javaweb的网上花店系统的设计与实现+jsp资源
用功能指令改变计数器C0的设定值,当X1、X0=00时设定值为10,当X1、X0
绿色医疗医学PPT模板.zip资源
彩灯控制电路设计
P=闭环系统稳定的充要条件
一种智能化成蚊密度监测设备的制作方法
δ的求法若f#(x,y)=M为常数
网址: 计数器的Verilog实现 https://m.huajiangbk.com/newsview893376.html
上一篇: Mpg计算器 |
下一篇: Vmware中虚拟化Intel |