HDLBits题目链接

题目要求

  • 构建一个带有AM/PM指示标志的12小时时钟,clk输入为1 pulse/sec。
  • 复位输入reset将时钟复位至AM 12:00,优先级高于使能输入enable
  • PM指示标志pm为0时表示AM,1表示PM。
  • hh,mm,ss的数据表示方式为BCD码。

题目分析

首先,12小时制与24小时制的所表示的时间段有如下对应规则:

12HR24HR
12:00AM – 11:59AM0:00 – 11:59
12:00PM – 11:59PM12:00 – 23:59

所以,其实咱们不用做这个对应,只需要傻傻计时以及维护pm指示标志即可。

由于时分秒的计时范围分别为1-12,0-59,因此可以设计两个BCD计时模块,分别维护1-12的计时和pm标志,以及0-59的计时。

写出两模块分别如下:

module cnt12
(
    input clk,
    input reset,
    input ena,
    output reg [7:0] counter,
    output reg pm
);
    // 初始为上午12时
    initial pm = 1'b0;
    initial counter = 8'h12;
    
    always @(posedge clk) begin
        // reset高优先级
        if (reset) begin
            counter <= 8'h12;
            pm <= 1'b0;
        end
        else begin
            if (ena) begin
                if (counter == 8'h11) begin
                    // 该改变PM标志了,要不等8'h112做非阻塞赋值就会有一个执行周期的延迟
                    pm <= !pm;
                    counter <= counter + 8'h1;
                end
                else if (counter == 8'h12) begin
                    // 计时循环结束
                    counter <= 8'h1;
                end
                else if (counter == 8'h9) begin
                    // 这里十位最高为1,仅需if判断即可,不需要使用vector操作
                    counter <= 8'h10;
                end
                else begin
                    // 计数+1
                    counter <= counter + 8'h1;
                end
            end
        end
    end

endmodule


module cnt60
(
    input clk,
    input reset,
    input ena,
    output reg [7:0] counter
);

    always @(posedge clk) begin
        // reset高优先级
        if (reset) begin
            counter <= 8'h0;
        end
        else begin
            if (ena) begin
                if (counter == 8'h59) begin
                    // 计时循环结束
                    counter <= 8'h0;
                end
                else if (counter[3:0] == 4'd9) begin
                    // 这里进位使用vector操作更方便,不需要大量if判断
                    counter[7:4] <= counter[7:4] + 4'd1;
                    counter[3:0] <= 4'd0;
                end
                else begin
                    // 计数+1
                    counter[3:0] <= counter[3:0] + 4'd1;
                end
            end
        end
    end

endmodule

接下来,在顶层模块中例化两个计时模块:

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_mm, ena_hh;
    
    // 分钟进位:59秒+1
    assign ena_mm = ena & (ss == 8'h59);
    // 小时进位:59分59秒+1,使用ena_mm进行简化
    assign ena_hh = ena_mm & (mm == 8'h59);
    
    cnt60 u_cnt60_ss(clk, reset, ena, ss);
    cnt60 u_cnt60_mm(clk, reset, ena_mm, mm);
    cnt12 u_cnt12_hh(clk, reset, ena_hh, hh, pm);

endmodule