Lesson 5

🕐 数字时钟

🏆 时:分:秒计数正确
✅ Verilator仿真验证通过

📖 实验描述

实现24小时制数字时钟,显示时:分:秒,支持时间设置。

数字时钟是级联计数器的经典应用。 时:分:秒分别由不同模值的计数器实现:秒(0-59)、分(0-59)、时(0-23)。秒计数到59后归零并产生进位驱动分计数器,分类推驱动时计数器。 关键设计:模60计数器和模24计数器的级联。每个计数器到最大值后归零并产生carry信号。 时钟分频:假设输入50MHz时钟,需要分频到1Hz驱动秒计数器。 数字时钟是电子学入门必做项目,从1970年代LED数字表到今天的智能手表,核心计数逻辑从未改变。

🧠 核心概念

📐 电路结构

电路与状态图
数字时钟结构:

  50MHz → 分频器 → 1Hz
                    │
              ┌─────▼─────┐  carry  ┌─────▼─────┐  carry  ┌─────▼─────┐
              │  秒计数器  │───────→│  分计数器  │───────→│  时计数器  │
              │  00-59    │        │  00-59    │        │  00-23    │
              └───────────┘        └───────────┘        └───────────┘
                    │                    │                    │
                  sec[5:0]            min[5:0]            hour[4:0]

📝 设计步骤

  1. 1设计50MHz→1Hz分频计数器
  2. 2实现模60秒计数器(0-59,59→0+carry)
  3. 3实现模60分计数器(同上,秒carry驱动)
  4. 4实现模24时计数器(0-23,分carry驱动)
  5. 523:59:59→00:00:00溢出处理
  6. 6添加时间设置功能(set_hour/set_min)

💻 Verilog实现

digital_clock.svSystemVerilog · Verilator 5.020
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 80
// Digital Clock - 24小时制数字时钟
// 时:分:秒 级联计数器 + 时钟分频
module digital_clock(
    input  wire       clk,       // 50MHz
    input  wire       rst,
    input  wire       set_en,    // 设置使能
    input  wire [4:0] set_hour,  // 设置小时
    input  wire [5:0] set_min,   // 设置分钟
    output reg  [4:0] hour,      // 当前小时
    output reg  [5:0] minute,    // 当前分钟
    output reg  [5:0] second,    // 当前秒
    output wire       sec_pulse  // 1秒脉冲
);

localparam DIV_1HZ = 26'd50_000_000;

reg [25:0] clk_div;
wire sec_carry, min_carry;

assign sec_pulse = (clk_div == DIV_1HZ - 26'd1);

// Clock divider: 50MHz -> 1Hz
always @(posedge clk or posedge rst) begin
    if (rst) begin
        clk_div <= 26'd0;
    end else begin
        if (clk_div >= DIV_1HZ - 26'd1)
            clk_div <= 26'd0;
        else
            clk_div <= clk_div + 26'd1;
    end
end

// Second counter: 0-59
always @(posedge clk or posedge rst) begin
    if (rst) begin
        second <= 6'd0;
    end else if (set_en) begin
        second <= 6'd0;
    end else if (sec_pulse) begin
        if (second == 6'd59)
            second <= 6'd0;
        else
            second <= second + 6'd1;
    end
end

assign sec_carry = sec_pulse && (second == 6'd59);

// Minute counter: 0-59
always @(posedge clk or posedge rst) begin
    if (rst) begin
        minute <= 6'd0;
    end else if (set_en) begin
        minute <= set_min;
    end else if (sec_carry) begin
        if (minute == 6'd59)
            minute <= 6'd0;
        else
            minute <= minute + 6'd1;
    end
end

assign min_carry = sec_carry && (minute == 6'd59);

// Hour counter: 0-23
always @(posedge clk or posedge rst) begin
    if (rst) begin
        hour <= 6'd0;
    end else if (set_en) begin
        hour <= set_hour;
    end else if (min_carry) begin
        if (hour == 5'd23)
            hour <= 5'd0;
        else
            hour <= hour + 5'd1;
    end
end

endmodule

🔬 仿真说明

仿真环境与策略

测试:复位后从00:00:00开始,秒从0计到59→分+1,分从0计到59→时+1,时从0计到23→归零。验证23:59:59→00:00:00。设置功能:set_en=1时正确加载新时间。

✅ 验证结果

Verilator 5.020 仿真通过

本实验所有Verilog代码已通过Verilator编译验证,功能行为正确。测试用例覆盖核心功能路径,确保设计满足规格要求。

⚠️ 常见错误

🌍 真实世界

工业应用

数字时钟是RTC(实时时钟)芯片的基础。PC中的RTC芯片使用32.768kHz晶振,15位分频得到1Hz。智能手表在此基础上增加了闹钟、计时器、日历等功能。

💡 扩展挑战

🚀 自己动手

# 编译并运行
verilator --cc digital_clock.sv --exe digital_clock_tb.cpp --build -j 0
./obj_dir/Vdigital_clock

🎯 试一试

修改参数

尝试修改代码中的关键参数,观察仿真结果变化:

  • 调整位宽,观察不同数据范围
  • 修改初始值/种子,观察不同起始条件
  • 改变时钟分频,测试不同速度

💡 Verilator会在位宽不匹配时给出Warning,这是学习的好机会

添加功能

在现有基础上增加新功能:

  • 添加新的输入信号和控制逻辑
  • 增加状态或修改状态转移条件
  • 扩展输出,增加更多显示信息

🔧 增量开发:每次只改一个地方,验证通过后再改下一个

📋 Verilog速查

语法说明示例
reg [7:0]8位寄存器reg [7:0] data;
wire组合逻辑连线wire valid = cnt > 5;
always @(posedge clk)时序逻辑上升沿触发
always @(*)组合逻辑敏感列表自动推导
localparam局部常量localparam DIV = 50000000;
case多分支选择注意default分支
$display仿真打印不可综合,仅仿真用

📊 性能指标

资源估算(FPGA参考)

本设计在典型FPGA上的资源占用估算:LUT约20-120个,FF约30-100个,无BRAM/DSP依赖(特殊模块除外)。时钟频率可达50-100MHz+。Verilator仿真速度约5-10M周期/秒。

← 上一课 📚 目录 下一课 →