12-hour clock using six BCD digits

本文最后更新于:Tuesday, October 6th 2020, 11:32 am

problem link:https://hdlbits.01xz.net/wiki/Count_clock

Background

时钟是每个人每天都会用到的工具,那怎么用电子电路搭建一个12-hour(带有am/pm指示)的时钟呢?

Problem description

选择一系列合适的计数器。你的计数器统一被快时钟(fast-running clock)驱动, 还带有一个脉冲使能输入,该脉冲会在任何需要时钟加1的时候到来(i.e.,每秒来一次)

  • reset:重置时钟到12:00:00 AM
  • pm is 0 for AM and 1 for PM
  • hh:mm:ss:都各自代表两位BCD(用四位二进制表达一位十进制)数;
  • hh(hours):01-12;mm(minutes):00-59;ss(seconds):00-59
  • resetenable有更高的权限,即使在使能信号无效的时候也可以值位。
  • 👇:从11:59:59 AM 到12:00:00 PM翻转和同步复位,使能的行为。

mark

Hint:Note that 11:59:59 PM(晚上转钟) advances to 12:00:00 AM, and 12:59:59 PM(中午快1点了) advances to 01:00:00 PM. There is no 00:00:00.

Template

1
2
3
4
5
6
7
8
9
10
11
// Module Declaration
module top_module(
input clk,
input reset,
input ena,
output pm,
output [7:0] hh,
output [7:0] mm,
output [7:0] ss);

endmodule

Answer

① 1位BCD计数器

  1. 复位到0:为分钟,秒计数器服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /************** 1位BCD计数器,复位到0;实现0-9的循环 ***************/
    module bcdreset0 (
    input clk,
    input reset, // Synchronous active-high reset
    input enable,
    output [3:0] q);
    always @ (posedge clk) begin
    if(reset) q <= 4'h0;
    else if(!enable) q <= q;
    else if(q == 4'h9) q <= 4'h0;
    else q <= q + 4'h1;
    end
    endmodule
  2. 复位到2:为小时低位服务(x->2)[⭐此处有特殊情况]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /************************* 1位BCD计数器,复位到2;实现0-9的循环 *************************/
    /************** 个位为2时:当高位为1,下一次应该是1;但是当高位为0,下一次为3 **************/
    module bcdreset2 (
    input clk,
    input reset, // Synchronous active-high reset
    input flag, // 指示当前为特殊情况,即为12的时候
    input enable,
    output [3:0] q);
    always @ (posedge clk) begin
    if(reset) q <= 4'h2;
    else if(!enable) q <= q;
    else if(flag) q <= 4'h1; // 特殊情况
    else if(q == 4'h9) q <= 4'h0;
    else q <= q + 4'h1;
    end
    endmodule
  3. 复位到1:为小时高位服务(x->1)[⭐此处有特殊情况]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /*********** 1位BCD计数器,复位到2;实现0-1的循环 *************/
    /*********** 每次使能到来,小时高位要么从0->1;要么从1->0;逻辑对应代码第11行***********/
    module bcd_zero_one(
    input clk,
    input reset,
    input enable,
    output [3:0] q);
    always @ (posedge clk) begin
    if (reset) q <= 4'h1; // (复位为12; 高位为1)
    else if (!enable) q <= q;
    else q[0] <= ~q[0]; // 0变为1,1变为0(0000->0001)
    end
    endmodule

    ② 两位BCD计数器

  4. 秒,分钟计数器:实现0-59循环;复位为0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**************** 两位BCD(8-bit)数表示0-59 ;循环到59时能不能下一个计数到0 **************/
    module zero2fifty_nine(
    input clk,
    input reset,
    input ena,
    output [7:0] ss);
    wire ena_ten, reset_ten;
    assign reset_ten = (((ss == 8'h59) & ena) | reset)? 1'd1:1'd0;⭐// 当计数到59且下一个使能到来时,把高位置为到0;因为低位肯定回到0;如果不reset,那么肯定为60
    assign ena_ten = ((ss[3:0] == 4'h9) & ena)?1'd1:1'd0; ⭐ // 个位到9(重点:必须使能到来)才能使能十位(假设现在为29分:00秒,分钟的十位使能必须在60s后才会到来;但是不并上使能,那么下一秒将是39分:01秒
    decade_counter one (clk, reset, ena, ss[3:0]); // 4位二进制表示个位的0-9
    decade_counter ten (clk, reset_ten, ena_ten, ss[7:4]); // 4位二进制表示十位的0-9
    endmodule

  5. 小时计数器:实现1-12循环,复位为12

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /************* 两位BCD(8-bit)数表示1-12;循环到12时能不能下一个计数到1 ***************/
    /* ⭐(flag12) 小时个位为2时:当高位为1(12),下一次应该是1;但是当高位为0(02),下一次为3 */
    module one2twelve(
    input clk,
    input reset,
    input ena,
    output [7:0] hh);
    wire ena_ten, flag12;
    assign ena_ten = (ena & ((hh == 8'h9)|(hh == 8'h12)))? 1'd1:1'd0; // 小时高位的改变信号(09->10;12->01)
    assign flag12 = (hh == 8'h12); // 指示当前为12
    BCDreset2 one (clk, reset, flag12, ena, hh[3:0]); // 4位二进制表示个位的0-9
    BCDzero_one ten (clk, reset, ena_ten, hh[7:4]); // 4位二进制表示十位的0-1
    endmodule

    ③ 实例化三个②

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module top_module(
input clk,
input reset,
input ena,
output pm,
output [7:0] hh,
output [7:0] mm,
output [7:0] ss);
wire ena_hr, ena_min, ena_pm;
one2twelve hour (clk, reset, ena_hr, hh);
zero2fifty_nine minite(clk, reset, ena_min, mm);
zero2fifty_nine second(clk, reset, ena, ss);
assign ena_min = (ss==8'h59)?1'd1:1'd0; // 当59s时才使能分钟计数器;
assign ena_hr = ((mm == 8'h59) & (ss == 8'h59))? 1'd1: 1'd0; // 当59分59s才使能小时计数器
assign ena_pm = ena_hr & (hh == 8'h11); // 当11时59分59s才能改变pm⭐
always @ (posedge clk) begin
if (reset) pm <= 1'd0;
else if(ena_pm) pm <= ~pm; // 每当时钟转到11:59:59pm都会变换一次
else pm <= pm;
end
endmodule

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!