
Verilog HDLBits--Count Clock
这篇文章主要讲述HDLBits的基础练习中,有关 Verilog 时钟计数器的问题。计数器的常用功能之一就是数字时钟,而在二进制存储的FPGA中,十进制的显示问题往往成为每个使用者都需要解决的问题。
笔者在使用常规 one-digit BCD编码计数器将各位(如分钟的个位、十位等)分开单独显示 方法后,固执地设计了一种十进制计数直接转BCD显示的方法。后者在本题中应用时虽有些小瑕疵,但不失为该类问题的一种规范化思路,尤其对十进制转BCD编码类问题,具有重要的参考意义。为不让文章显得冗长复杂,本文将只对第一种方法进行介绍,第二种方法将在下一篇详细分析。
0. HDLBits简介
有关该Verilog在线学习网站的安利,见本专栏第一篇文章。
1. Problem105: Count Clock
要求: 设计一个满足12小时计时的时钟系列计数器(包括am/pm指示)。该计数器由一个fast-running clk驱动,每当时钟需要增加计时时,ena信号则会发生脉冲信号(如:每秒一次)。 reset信号复位时钟到12:00 AM。pm信号为0时指示AM,为1时指示PM。hh、mm、ss都为两个BCD digits(8 bits),分别表示小时(01-12)、分钟(00-59)、秒(00-59)。reset 比 enable信号优先级更高,即使未使能,也要执行复位操作。 下面的时序图展示了时钟从11:59:59 AM 到12:00:00 PM的信号变化,以及同步复位和使能操作结果。

hint: 注意11:59:59 PM 在下一个时钟周期应为12:00:00 AM ;12:59:59 PM 在下一时钟周期应为 01:00:00 PM 。没有00:00:00这种形式。
1.1 正确答案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
801 module top_module( 2 input clk, 3 input reset, 4 input ena, 5 output pm, 6 output [7:0] hh, 7 output [7:0] mm, 8 output [7:0] ss); 9 wire ena_ss_tens, ena_mm_ones, ena_mm_tens, ena_hh; //enable signals for every single bit 10 wire ss_zero; 11 //ss ones bit 12 m_s_one_bit_cont ss_ones(.clk(clk), .reset(reset), .ena(ena), .zero(4'b1001), .one(ss[3:0])); 13 //ss tens bit 14 assign ena_ss_tens =(ss[3:0]==4'd9)? 1'b1 : 1'b0; 15 m_s_one_bit_cont ss_tens(.clk(clk), .reset(reset), .ena(ena & ena_ss_tens), .zero(4'b0101), .one(ss[7:4])); 16 //mm ones bit 17 assign ss_zero = ss[7:4]==4'd5 & ss[3:0]==4'd9; 18 assign ena_mm_ones = (ss_zero) ? 1'b1: 1'b0; 19 m_s_one_bit_cont mm_ones(.clk(clk), .reset(reset), .ena(ena & ena_mm_ones), .zero(4'b1001), .one(mm[3:0])); 20 //mm tens bit 21 assign ena_mm_tens =(mm[3:0]==4'd9 & ss_zero)? 1'b1 : 1'b0; 22 m_s_one_bit_cont mm_tens(.clk(clk), .reset(reset), .ena(ena & ena_mm_tens), .zero(4'b0101), .one(mm[7:4])); 23 //hh two bits 24 assign ena_hh = (mm[7:4]==4'd5 & mm[3:0]==4'd9 & ss_zero) ? 1'b1 : 1'b0; 25 h_two_bit_cont hh_2bits(.clk(clk), .reset(reset), .ena(ena & ena_hh), .hh(hh)); 26 //pm 27 always@(posedge clk) 28 if (reset) 29 pm = 1'b0; 30 else if(hh=={4'd1, 4'd1} & mm=={4'd5, 4'd9} & ss=={4'd5, 4'd9} ) 31 pm = ~pm; 32 else 33 pm = pm; 34 endmodule 35 //hour counter: 1-12 36 module h_two_bit_cont ( 37 input clk, 38 reset, 39 ena, 40 output[7:0] hh); 41 reg[3:0] one_dec;// decimal number 42 always@(posedge clk) begin 43 if (reset)begin 44 hh = {4'b0001, 4'b0010}; 45 one_dec = 4'b1100; 46 end 47 else if(~ena)begin 48 hh = hh; 49 one_dec = one_dec; 50 end 51 else if (one_dec == 4'b1100) begin 52 hh = {4'b0000, 4'b0001}; 53 one_dec = 4'b0001; 54 end 55 else 56 one_dec = one_dec + 1'b1; 57 if(one_dec >= 4'b1010) 58 hh = {4'b0001, one_dec - 4'd10}; 59 else 60 hh = {4'b0000, one_dec}; 61 end 62 endmodule 63 //minute and second one-digit counter 64 module m_s_one_bit_cont ( 65 input clk, 66 reset, 67 ena, 68 input[3:0] zero,//return 'zero' bit 69 output[3:0] one); 70 always@(posedge clk) 71 if (reset) 72 one = 4'b0000; 73 else if(~ena) 74 one = one; 75 else if (one == zero) 76 one = 4'b0000; 77 else 78 one = one + 1'b1; 79 endmodule
题目解析:
正如题目名称:计数时钟,实质就是用于时钟显示的计数器设计。由于FPGA内部变量都是二进制存储,因此像数字时钟、秒表等的十进制数字显示问题成为设计者值得思考的一类问题。目前,人们普遍解决该类问题的方法是利用one-digit BCD编码计数器巧妙地将各个位上的数字分割开来单独显示。我想这也是HDLBits作者在前两题:第103-104题就已经传达给读者的设计思想。
而深受软件编程思路影响的童鞋,这里可能会有疑问:既然是将时钟的各个位分割开来,为什么不直接使用“ / ”、“ % ”语句来的更简单些呢?认真了解过Verilog语法的伙伴们都清楚,在所有Verilog的语法中,大约只有30%可用于综合电路的设计,其余70%只能用于testbench编写。而“ / ”、“ % ”也归于70%那一类。
代码分析:
1. 设计one-digit BCD模块(代码第64-79行-- module m_s_one_bit_cont),通过调用该模块,实现“分”、“秒”计数器个位、十位(不涉及百位)数字的单独定义。
2. 考虑到“时”计数器本质上属十二进制,不能与“分”、“秒”计数器同用一种BCD模块,因此,代码第36-62行对“时”计数器(module h_two_bit_cont)进行了单独设计。
3. top_module中,实例化下层模块,设计各模块enable信号电路及pm 指示电路。
设计重点:
- “分”、“秒”时钟计数器 one-digit BCD模块设计;
- “时”计数器(十二进制)显示模块设计; - 各个位计数器使能信号设计 ;
注:
代码第12行,时钟的秒计数器,个位使能信号ena直接设计为top_module的ena输入信号,依靠时钟上升沿的驱动按需计数,以推动整个时钟计数器的运行。
- pm指示信号的电路设计;
1.2 电路图





以上为1.1中代码综合后生成的电路,为清楚表达设计思路、描述设计重点,同时为方便读者查阅,图中略去了部分信号的连接情况,只留下顶层模块和实例化模块间的信号传输关系。若小伙伴想仔细了解该电路的RTL Schematic,可自行复制代码到 ISE 或 Vivado 中查看。
1.3 设计详解
在这一部分,笔者将结合电路图,详细说明该电路的设计流程和设计重点(思路清晰的小伙伴可自行对照代码和综合后电路的对应关系)。
1.3.1 时 /分 /秒计数器及下层模块设计
- ss_tens 、ss_ones 两模块分别为秒计数器的十位、个位数字计数器,通过实例化 m_s_one_bit_cont module 实现。
其中m_s_one_bit_cont module 为 one-digit BCD 计数器,与常规设计不同的是,此模块增加了zero[3:0] 输入端口,即归零比较值。该端口的设计是为了兼容秒/分计数器个位和十位的不同计数范围(个位0-9;十位0-5),以减少自定义模块个数。
- mm_tens 、mm_ones 则为分计数器的十位、个位数字计数器,原理同秒计数器。
- hh_2bits 模块则是为十二进制的时计数器单独设计的 two-digit BCD 计数器。不同于分/秒计数器的各位分开定义,时计数器的计数范围要求1--12,归零比较值为12,即在下一个ena信号到来时,时计数器复位为1。
注:
相比 one-digit BCD 设计,two-digit BCD 需要额外的十进制计数器进行时钟计数,然后直接将该十进制数字转换成BCD编码形式。该类十进制计数直接转BCD显示的思想同样适用于分、秒计数器设计,笔者特地私下做了一番验证,将在下一篇文章中详细说明(附在本篇中显得过于冗长)。
1.3.2 各ena及pm指示信号电路设计
- ss_ones_ena = top_module_ena //秒-个位ena信号
- ss_tens_ena =top_module_ena & (ss_ones==4'd9) //秒-十位ena信号
- mm_ones_ena = top_module ena & (ss=={4'd5 , 4'd9})
- mm_tens_ena = top_module ena & (mm_ones == 4'd9) & (ss=={4'd5 , 4'd9})
- hh_ena = top_module ena & (mm == {4'd5 , 4'd9}) & (ss == {4'd5 , 4'd9})
注:
各模块ena信号需同时考虑到 top_module 的 ena 信号,这是容易被忽略的一点。
- pm指示信号,则根据题目hint部分的提示即可完成设计——11:59:59 PM 在下一个时钟周期应为12:00:00 AM。每个 11:59:59 时刻,pm进行一次翻转。一开始会多想,总觉得没这么简单。但只要记住一天一半的时间是AM,另一半的时间是PM,而时钟每计到 11:59:59,则为12小时(半天),因此,直接翻转即可。
最后给两点小建议:
- 每个信号,既相互联系,又互相独立。想清楚信号自身的逻辑功能,避免多种信号关联设计,将大大提高代码的写作效率,同时也能避免不必要的bug。
- 模块化设计、模块化设计、模块化设计……真香!
最后
以上就是谨慎果汁最近收集整理的关于b 计数器位选信号 verilog_?HDLBits--(Verilog在线学习)--"105: Count Clock"的全部内容,更多相关b内容请搜索靠谱客的其他文章。
发表评论 取消回复