题目描述:计时器:在 6 个七段管上分别显示 小时(0-23 或 11)、分(0-59)、秒(0-59),各占 2 个管。外部时钟 50Mhz。可以用按键来产生一个复位信号 key,当按键按下立刻(异步)将时间复位成 0 小时、0 分、0 秒重新开始计时。

目录

  • 1、题目解析
  • 2、综合代码
  • 3、测试代码
  • 4、效果展示
  • 5、代码v2.0
    (提示:其中 2-4为第一版代码,如果是学习的话可以看看,如果只是想要最好的结果,直接跳转到代码 v2.0 即可)

1、题目解析:

分析1:如何实现这个功能?
    我们需要6个7为的寄存器(或者一个7*6 = 42 位的寄存器)来保留每一个时刻的时间,“秒”和“分”的4个计数器的显示分别对应着一个60进制的计数模块,而“时”则对应着一个24位的计数器。

分析2:如果实现每一秒进行一次变化?
    根据系统外部时钟为50 MHz,我们可以写一个计数器,时钟每变化一个周期就记录一次,一直数到50 M时输出一个电平。或者每数到 25M 改变一个让一个输出寄存器里的值发生反转,那么对于这个寄存器,他的周期就是 1Hz 了。

分析3:如何实现 类似00:00:5900:01:00的变化?
     要实现这个逻辑,需要将“秒”对应的进位信号作为“分”的输入信号,当有进位信号的时候才能将分进行计数。“分” 和 “时” 之间也要有类似的进位关系。

2、综合代码

module timer(// 控制输入input reset,  // 重置信号, 当其值为低电平时,发生重置input set,    // 置位标致位,当输入高电平时进行输入input clk50,    // 时钟信号input[1:0] setp,   // set_position 选择要输入的位置 00,表示秒位, 01 表示分位, 10表示时位,其余报错input[5:0] setv,   // set_value 输入的数值// 输出的 6个七段管output reg[6:0] second1,output reg[6:0] second2,output reg[6:0] munite1,output reg[6:0] munite2,output reg[6:0] hous1,output reg[6:0] hous2
);wire set1, set2, set3;    // 对应每一个管子的设置
wire car1, car2, carr3;   // 对应每一个管子的输出进位标志
wire clk1;// 接收每个计数器的计数器值
wire[3:0] num1,num2,num3,num4,num5,num6;// 接受每一个七段管的输出值
wire[6:0] tub1,tub2,tub3,tub4,tub5,tub6;divclk1hz clock1(reset,clk50,clk1);// 定义秒
counter60 c60_1(.clk(clk1),.in(1),.reset(reset),.set(set1),.inv(setv),.carry(car1),.out1(num1),.out2(num2)
);// 定义分
counter60 c60_2(.clk(clk1),.in(car1),.reset(reset),.set(set2),.inv(setv),.carry(car2),.out1(num3),.out2(num4)
);// 定义时
counter24 c24_2(.clk(clk1),.in(car2),.reset(reset),.set(set3),.inv(setv),.carry(car3),.out1(num5),.out2(num6)
);// 将计数器输出的数值信号转化为七段管的亮暗状态
int2tube i1( .in(num1), .out(tub1) );
int2tube i2( .in(num2), .out(tub2) );
int2tube i3( .in(num3), .out(tub3) );
int2tube i4( .in(num4), .out(tub4) );
int2tube i5( .in(num5), .out(tub5) );
int2tube i6( .in(num6), .out(tub6) );// 以下代码的作业就是链接各个模块
// 这里加入reset 的检测是为了异步处理
always @(posedge clk1 or negedge reset) beginif(!reset) beginsecond1 <= 8'b0000001;second2 <= 8'b0000001;munite1 <= 8'b0000001;munite2 <= 8'b0000001;hous1 <= 8'b0000001;hous2 <= 8'b0000001;end else beginsecond1 <= tub1;second2 <= tub2;munite1 <= tub3;munite2 <= tub4;hous1 <= tub5;hous2 <= tub6;end
end// 设置置位信号
assign set1 = (set==1)?((setp == 0)?1:0):0;
assign set2 = (set==1)?((setp == 1)?1:0):0;
assign set3 = (set==1)?((setp == 2)?1:0):0;
endmodule/** 60位计数器 */
module counter60(input clk,     // 时钟信号input in,      // 输入信号input reset,   // 重置信号, 当其值为低电平时,发生重置input set,     // 置位标致位,当输入高电平时进行输入input[5:0] inv,// 置位信号output reg carry,  // 进位信号output reg[3:0] out1,  // 输出十位output reg[3:0] out2   // 输出个位
);reg[5:0] inner;  // 内置计数器
always @(posedge clk or posedge set or negedge reset) beginif(!reset) begin       // 检测是否要进行重置 低电平有效inner <= 0;carry <= 0;out1 <= 0;out2 <= 0;end else if(set) begin // 检测是否要进行置数 高电平有效inner <= inv;out1 <= inv / 10;out2 <= inv % 10;end else beginif(in) begin // 没有重置和置数时,in为高电平时进行加数if(inner < 58) begin  // 如果没到58就继续数inner <= inner + 1;end else if(inner == 58)begin // 到58结束时输出进位信号,这样在59结束时(新的1秒开始)就可以carry <= 1;        // 并且进行进位inner <= inner + 1;end else begin      // 如果到了59, 那么下一时刻就要进行从 0 开始数 inner <= 0;carry <= 0;endendout1 <= inner / 10;out2 <= inner % 10;end
end
endmodule/** 24位计数器 */
module counter24(input clk,     // 时钟信号input in,      // 输入信号input reset,   // 重置信号, 当其值为低电平时,发生重置input set,     // 置位标致位,当输入高电平时进行输入input[5:0] inv,// 置位信号output reg carry,  // 进位信号output reg[3:0] out1,  // 输出十位output reg[3:0] out2   // 输出个位
);reg[4:0] inner;  // 内置计数器
always @(posedge clk or posedge set or negedge reset) beginif(!reset) begin       // 检测是否要进行重置 低电平有效inner <= 0;carry <= 0;out1 <= 0;out2 <= 0;end else if(set) begin // 检测是否要进行置数 高电平有效inner <= inv;out1 <= inv / 10;out2 <= inner % 10;end else beginif(in) begin  // 没有重置和置数时,in为高电平时进行加数if(inner < 23) begin  // 如果没到59就继续数inner <= inner + 1;carry <= 0;end else begin      // 如果到了59, 那么下一时刻就要进行从 0 开始数 inner <= 0;carry <= 1;       // 并且进行进位endendout1 <= inner / 10;out2 <= inner % 10;endend
endmodule/** 将数值信号转化为七段管的亮暗状态 */
module int2tube(input[3:0] in,output reg[6:0] out
);always @(in) begincase (in)0: out <= 8'b0000001;1: out <= 8'b0011111;2: out <= 8'b0010010;3: out <= 8'b0000110;4: out <= 8'b1001100;5: out <= 8'b0100100;6: out <= 8'b0100000;7: out <= 8'b0001111;8: out <= 8'b0000000;9: out <= 8'b0000100;endcase
endendmodule/** 分频模块,每数到25M改变一个让一个输出寄存器clk1里的值发生反转 */
module divclk1hz(reset,clk50,clk1);
input clk50,reset;   //clk50 为输入50Mhz 信号,reset 为复位信号
output reg clk1;     // 新产生的1hz 信号
integer i=0;         //50Mhz 频率下,周期计数器
always@(posedge clk50) beginif(!reset) begin i=0;clk1 = 0;end else beginif(i==30) begin i=0; clk1=~clk1;  // 25000000 这里的i应该=25M,但是为了更方便的展示效果,我将i的值改为了30,这样七段管里的数值会改变的更快。endelse i=i+1;end
end
endmodule

3、测试代码

`timescale 10 ns/ 1 ns
module timer_vlg_tst();
reg clk50;
reg reset;
reg set;
reg [1:0] setp;
reg [5:0] setv;wire [6:0]  hous1;
wire [6:0]  hous2;
wire [6:0]  munite1;
wire [6:0]  munite2;
wire [6:0]  second1;
wire [6:0]  second2;    timer i1 ( .clk50(clk50),.hous1(hous1),.hous2(hous2),.munite1(munite1),.munite2(munite2),.reset(reset),.second1(second1),.second2(second2),.set(set),.setp(setp),.setv(setv)
);initial begin // 初始化外部时钟 clk50 = 0; // 重置一遍timer模块reset = 1; #5 reset =0; #5 reset =1;set = 0;// 准备将秒位设置到55秒setv = 33; setp = 0;
// 等待2100 个时间单位,观察时间变化# 2100;// 进行数值修改set = 1;// 修改完毕,结束修改信号#1 set = 0;// 运行900 时间单位,观察效果,进行复位#900 reset = 0;// 复位信号归位#5 reset = 1;// 运行900个单位以后停止运行#900 $stop;endalways begin#1 clk50 =~ clk50;
endendmodule

4、效果展示

异步复位信号的效果:可以看到在reset信号进入赋值以后,6个七段管的值都恢复到0000001 对应数值0

同步置数的效果:,当set型号进入高点平,改变所设置的时间的值(将“秒”数值置为33),0000110 对应数值为3。

5、代码 v2.0

5.1 第一版代码存在的问题

对于上述代码,我们可以看到,在数字发生复位或者置位以后会有很长时间的延时,小编在之后的学习中了解到这个是由于寄存器的使用会导致信号延时,这道题中,七段管的输出信号要经过计数器计数寄存器inner, 个位十位寄存器out1out2 最后在经过 int2tube数值转七段输出的out 寄存器,得到输出结果,这会让我们的输入信号执行的效果出现严重延时。针对这个问题,在代码 v2.0 中就以减少中间寄存器为目标,开始了优化。

5.2 综合代码

在综合代码中,我们去除了大量reg, 只保留下最基本的计数器计数寄存器inner,其他都用逻辑电路进行链接。

module timer(// 控制输入input reset,     // 重置信号, 当其值为低电平时,发生重置input set,       // 置位标致位,当输入低电平时进行输入input clk50,     // 时钟信号input showtype,  // 显示方式,是24小时还是12小时。0为24 | 1为12input[1:0] setp, // set_position 选择要输入的位置, 00表示秒位, 01 表示分位, 10表示时位,其余报错input[5:0] setv, // set_value 输入的数值input set_cp,    // set_clock_position, 要输入的闹钟值,1时位, 0分位,   其他无效input set_c,     // 闹钟置位标致位,当输入低电平时进行输input close,     // 关闭闹钟和整点报时// 输出的 6个七段管output[6:0] second1,output[6:0] second2,output[6:0] munite1,output[6:0] munite2,output[6:0] hous1,output[6:0] hous2,// 输出的闹钟信号output led,output reg err
);// 寄存器变量reg[11:0] clock_reg;  // 用于调整闹钟报时的时间reg[5:0] noise;       // 用于表示闹钟的持续时间reg cancel_clock;     // 用于表示用户是否已取消闹钟reg[16:0] time_reg;   // 记录当前时间// 中间变量,为了方便计算wire[3:0] num5,num4,num3,num2,num1,num0;// 加速变量, 对于运算中重复出现的表达,我们将组合电路模块话wire[11:0] _n1;   // 表示60*分钟位 + 秒位wire[4:0] _n2;    // 表示小时位// 利用函数转化计数器输出的function[6:0] int2tube;input[3:0] _in;case (_in)0: int2tube = 8'b0000001;1: int2tube = 8'b0011111;2: int2tube = 8'b0010010;3: int2tube = 8'b0000110;4: int2tube = 8'b1001100;5: int2tube = 8'b0100100;6: int2tube = 8'b0100000;7: int2tube = 8'b0001111;8: int2tube = 8'b0000000;9: int2tube = 8'b0000100;endcaseendfunction// 初始化寄存器initial beginclock_reg <= 12'b10_0000_00_0000; // 不设置闹钟noise <= 6'b0;end// 定义分频模块divclk1hz clock1(.reset(reset), .clk50(clk50), .clk1(clk1));// 内部计时寄存器控制always @(posedge clk1 or negedge set or negedge reset) begin// 复位信号if(!reset) begintime_reg <= 17'b0;end// 置位信号else if(!set) begincase (setp)2'b00: time_reg <= num0*36000 + num1*3600 + num2*600 + num3*60 + setv;2'b01: time_reg <= num0*36000 + num1*3600 + setv*60  + num4*10 + num5;2'b10: time_reg <= setv*3600  + num2*600  + num3*60  + num4*10 + num5;default: time_reg <= time_reg;endcaseend// 正常情况else if(clk1) begintime_reg <= (_n2==23 && _n1==12'b1110_0000_1111)? 17'b0:time_reg + 1; // 23:59:59 清零endend// 设置闹钟时间always @(negedge set_c) beginif(set_cp) begin  // 如果是时位时clock_reg[11:6] <= setv;end else begin   // 否则当分位时clock_reg[5:0] <= setv;endend// 铃声开启检测always @(posedge clk1 ,negedge close) beginif (!close) beginnoise <= 0;cancel_clock = 1; // 防止闹钟在一分钟内多次响起endelse begin// 当正在响铃时if(noise) beginnoise <= noise - 1;if(noise == 1) cancel_clock = 1; // 防止闹钟在一分钟内多次响起end // 当触发闹钟时,这里规定闹钟优先级大于整点报时else if(clock_reg[11:6]==num0*8+num0*2+num1 && clock_reg[5:0 ]==num2*8+num2*2+num3) beginif(cancel_clock==0) begin noise <= 30; // 规定闹钟响半分钟err <= 1;endend// 当整点时else if(_n1==0) beginnoise <= 5;endelse begincancel_clock = 0;endend end// 铃声开启检测always @(clk1) beginend// 计算加速assign _n1 = time_reg%3600;assign _n2 = time_reg/3600;// 让计数器模块的输出转变成系统七段管输出assign num5 = time_reg%10;assign num4 = time_reg%60/10;assign num3 = _n1/60%10;assign num2 = _n1/600;assign num1 = showtype?(_n2%12%10):(_n2%10); // 考虑12小时表示和24时表示assign num0 = showtype?(_n2%12/10):(_n2/10); // 考虑12小时表示和24时表示// 让计数器模块的输出转变成系统七段管输出assign second2 = int2tube(num5);assign second1 = int2tube(num4);assign munite2 = int2tube(num3);assign munite1 = int2tube(num2);assign hous2   = int2tube(num1);assign hous1   = int2tube(num0);// 闹钟输出assign led = noise?0:1;endmodule/** 分频模块,每数到25M改变一个让一个输出寄存器clk1里的值发生反转 */
module divclk1hz(reset,clk50,clk1);input clk50,reset;   //clk50 为输入50Mhz 信号,reset 为复位信号output reg clk1;     // 新产生的1hz 信号integer i=0;         //50Mhz 频率下,周期计数器always @(posedge clk50 or negedge reset) beginif(!reset) begini = 0;clk1 = 0;end else beginif(i==30) begin i=0; clk1=~clk1;  // 25000000 这里的i应该=25M,但是为了更方便的展示效果,我将i的值改为了30,这样七段管里的数值会改变的更快。endelse begini=i+1;endendend
endmodule

5.4 效果展示

波形变化细节
版本1波形:

版本2波形:

可以看在版本2中,我们在置数以后,不用等待很长时间就可以看到清零的效果,但仍然存在一些问题,这里是由于分频模块第一次计数开始到产生上升沿只有半个周期,所以在半秒(理论上)以后就进入了下一秒,感兴趣的朋友可以再对代码进行修改,调整一下分频模块,让第一次尽可能准。(欢迎大家评论补充)
文本输出效果

EDA设计(verilog)—— 七段管时钟相关推荐

  1. 【Verilog】跨时钟域设计Clock Domain Crossing Design(Multi cycle path formulation with feedback acknowledge)

    上次写了跨时钟域设计MCP公式不带反馈的实现[Verilog]跨时钟域设计Clock Domain Crossing (CDC) Design(MCP formulation without feed ...

  2. 秒表设计(Verilog)--quartus13

    秒表设计(Verilog)–quartus13 本次实验的目的在于: (1) 掌握利用硬件描述语言设计计数器.分频电路.译码电路的方法: (2) 掌握利用例化语句设计顶层电路的方法. 实验内容 秒表设 ...

  3. (106)FPGA面试题-Verilog编写50MHz时钟激励

    1.1 FPGA面试题-Verilog编写50MHz时钟激励 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-Verilog编写50MHz时钟激励: 5) ...

  4. (30)FPGA原语设计(单端时钟转差分时钟)

    (30)FPGA原语设计(单端时钟转差分时钟) 1.1 目录 1)目录 2)FPGA简介 3)Verilog HDL简介 4)FPGA原语设计(单端时钟转差分时钟) 5)结语 1.2 FPGA简介 F ...

  5. 参加集成电路EDA设计精英挑战赛的体会

    2020年11月27-29日,2020(第二届)集成电路EDA设计精英挑战赛在南京举行. 带队参加比赛过程中,听了"开源EDA助力产学融合论坛"的各路大咖发言介绍,收获多多.感谢比 ...

  6. 简易计时闹钟设计verilog语言实现

    简易计时闹钟设计verilog语言实现 任务描述: 用电路或verilog语言实现以下功能 简易计时闹钟: 有四位数码管,前两位计分钟,表示00~99分钟, 后面两位记秒,值为00~59秒. 有三个按 ...

  7. 数字电子钟设计制作——数字逻辑课程设计 Verilog HDL CPLD

    目的: 1.进一步掌握数字电子技术的理论知识,培养工程设计能力和综合分析问题.解决问题的能力: 2.基本掌握常用电子电路的一般设计方法,提高电子电路的设计和实验能力: 3.掌握复杂可编程逻辑器件CPL ...

  8. 太原理工大学荣获2020(第二届)集成电路EDA设计精英挑战赛一等奖

    11月29日,"2020年(第二届)集成电路EDA设计精英挑战赛"总决赛颁奖典礼在南京市江北新区ICisC人才实训基地隆重举行.在本次比赛中,太原理工大学由2018级计科专业高子博 ...

  9. 第十三届蓝桥杯《EDA设计与开发》赛后总结

    目录 一,赛前准备 1.立创培训第一题 2.立创培训第二题 3.立创培训第三题 4.自选训练第一题 5.自选训练第二题 二,省赛 1.第12届蓝桥杯湖北省赛试题 2.第13届蓝桥杯山东省赛试题 3.第 ...

  10. AD20和立创EDA设计(4)PCB设计

    (1)本文主要介绍如何将从立创EDA导出的原理图,在AD20进行PCB设计. (2)需提前观看:AD20和立创EDA设计(3)微调原理图和原理图检查: (3)邀请加入嵌入式社区,您可以在上面发布问题, ...

最新文章

  1. 页面 table 可编辑的实现
  2. 10种常用排序算法实现
  3. 2.vue 安装教程
  4. Android之SwipeRefreshLayout嵌套RecyclerView遇到的坑
  5. linux 信号_Linux中的信号处理机制 [四]
  6. lower_bound()和upper_bound()
  7. CompletionService VS ExecutorService
  8. 2014年5月30日
  9. 一步一步写算法(之 算法总结)
  10. 下载CentOS镜像
  11. 使用frp实现将内网映射到公网 无需花生壳
  12. Linux 系统如何更改主机名
  13. 【Iftop】实时监控流量工具
  14. jsp分页功能的位置有可能会影响到翻页时的查询条件
  15. 【2020.11.16】堆栈传参、堆栈平衡
  16. win10如何升级win11
  17. php 音频上传之ogg格式,如何快速将MP3格式转化成ogg格式
  18. Excel中3个超级好用的条件求和的函数
  19. echarts 图例 两行展示
  20. 二分类问题中的评价指标

热门文章

  1. etl mysql报错_etl工具
  2. True/False Positive True/False Negative (真/假阳性, 真/假阴性)
  3. 云上未来,数智导航:阿里云研究院报告合集
  4. keras-yolov3训练及测试详解
  5. Jamie's Contact Groups ——(一对多)二分图多重最大匹配
  6. P1035 [NOIP2002 普及组] 级数求和
  7. 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log Server.鏈嶅姟鍣ㄧ増鏈�:
  8. OpenCV C++ imread填写路径下有图片却读不出 求助!!!!!!
  9. php不做手术会怎么样,薇娅做手术上热搜!这种病年轻人高发,有人治了三年还没治好...
  10. IDEA中使用JUnit5(单元测试框架)