b 计数器位选信号 verilog_?HDLBits--(Verilog在线学习)--"105: Count Clock"

6b5b0c3c63837480f5f806a27f04c12f.png

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的信号变化,以及同步复位和使能操作结果。

ba0081c377916573520ea17f0e0e07e8.png

hint: 注意11:59:59 PM 在下一个时钟周期应为12:00:00 AM ;12:59:59 PM 在下一时钟周期应为 01:00:00 PM 。没有00:00:00这种形式。

1.1 正确答案

1   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 电路图

341ffab33285fb2e247c98be9e4b5500.png

f6b3f3b6151af3a2ec31d4a505cdd896.png

b1d1b33eb2c2fc1d4a983b40b869e93b.png

d2ac478ce7e09dc3c9ed09318cfac346.png

9d9e7fae48fd18a0e7fb5c48abcccd7a.png

以上为1.1中代码综合后生成的电路,为清楚表达设计思路、描述设计重点,同时为方便读者查阅,图中略去了部分信号的连接情况,只留下顶层模块和实例化模块间的信号传输关系。若小伙伴想仔细了解该电路的RTL Schematic,可自行复制代码到 ISE 或 Vivado 中查看。

1.3 设计详解

在这一部分,笔者将结合电路图,详细说明该电路的设计流程和设计重点(思路清晰的小伙伴可自行对照代码和综合后电路的对应关系)。

1.3.1 时 /分 /秒计数器及下层模块设计

  • ss_tensss_ones 两模块分别为秒计数器的十位、个位数字计数器,通过实例化 m_s_one_bit_cont module 实现。

其中m_s_one_bit_cont module 为 one-digit BCD 计数器,与常规设计不同的是,此模块增加了zero[3:0] 输入端口,即归零比较值。该端口的设计是为了兼容秒/分计数器个位和十位的不同计数范围(个位0-9;十位0-5),以减少自定义模块个数。

  • mm_tensmm_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小时(半天),因此,直接翻转即可。

最后给两点小建议:

  1. 每个信号,既相互联系,又互相独立。想清楚信号自身的逻辑功能,避免多种信号关联设计,将大大提高代码的写作效率,同时也能避免不必要的bug。
  2. 模块化设计、模块化设计、模块化设计……真香!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值