Verilog BASYS2 数字钟:12模块化设计实现5大功能(含完整仿真波形)
Verilog BASYS2 数字钟:12模块化设计实现5大功能(含完整仿真波形)
在FPGA开发领域,数字钟设计一直是验证硬件描述语言掌握程度的经典项目。本文将深入剖析基于BASYS2开发板的Verilog数字钟系统,通过12个精心设计的模块实现计时、校时、闹钟等五大核心功能。不同于简单的代码堆砌,我们将从工程架构角度揭示模块化设计的精髓,并附赠完整的仿真波形解读。
1. 系统架构设计与模块划分
一个健壮的数字钟系统需要将复杂功能分解为可管理的子模块。我们的设计采用分层架构,将系统划分为以下12个功能模块:
- 时钟分频模块 :将板载50MHz时钟分频为1Hz基准信号
- 校时控制模块 :处理用户输入的校时操作
- 计时核心模块 :包含时、分、秒三个计数器
- 闹钟功能模块 :实现定时提醒与整点报时
- 显示处理模块 :包含扫描控制、数据选择和七段译码
各模块通过清晰的接口定义进行数据交互,顶层模块采用结构化例化方式连接各子模块。这种设计使得单个模块的修改不会影响整体系统,大幅提升代码可维护性。
1.1 顶层模块接口设计
顶层模块作为系统的"中枢神经",需要明确定义所有外部接口和内部互连信号:
module top(
input clk_50MHz, // 板载50MHz时钟
input reset, // 全局复位
input set_time, // 校时使能
input adjust_hour, // 小时调整
input adjust_min, // 分钟调整
input alarm_set, // 闹钟设置
output [6:0] seg, // 七段显示
output [3:0] an, // 位选信号
output alarm_led // 闹钟指示灯
);
1.2 关键数据流分析
系统数据流可分为三个主要路径:
- 计时路径 :分频模块 → 秒计数器 → 分计数器 → 时计数器
- 校时路径 :按钮输入 → 消抖处理 → 校时逻辑 → 计数器置位
- 显示路径 :时间数据 → 扫描控制 → 数据选择 → 七段译码 → 数码管
这种数据流分离设计确保了各功能模块的独立性,避免信号交叉干扰。
2. 核心模块实现细节
2.1 可配置分频器设计
分频模块需要将50MHz主时钟转换为1Hz计时脉冲,传统做法是使用50,000,000分频。但我们采用更灵活的参数化设计:
module clock_divider #(
parameter DIVISOR = 50_000_000
)(
input clk_in,
output reg clk_out
);
reg [31:0] counter;
always @(posedge clk_in) begin
if(counter >= (DIVISOR-1))
counter <= 0;
else
counter <= counter + 1;
clk_out <= (counter < DIVISOR/2) ? 1'b1 : 1'b0;
end
endmodule
提示:仿真时可减小DIVISOR参数值加速验证过程,实际部署时恢复为50,000,000
2.2 智能校时控制系统
校时模块需要处理机械按钮的抖动问题,并实现三种校时模式:
module time_adjust(
input clk,
input set_en,
input adj_hour,
input adj_min,
output reg [5:0] hour_set,
output reg [5:0] min_set
);
// 按钮消抖逻辑
reg [19:0] hour_debounce, min_debounce;
wire hour_pulse, min_pulse;
assign hour_pulse = (hour_debounce == 20'hFFFFF);
assign min_pulse = (min_debounce == 20'hFFFFF);
always @(posedge clk) begin
hour_debounce <= {hour_debounce[18:0], adj_hour};
min_debounce <= {min_debounce[18:0], adj_min};
if(set_en) begin
if(hour_pulse) hour_set <= (hour_set == 23) ? 0 : hour_set + 1;
if(min_pulse) min_set <= (min_set == 59) ? 0 : min_set + 1;
end
end
endmodule
2.3 可变进制计数器实现
时、分、秒计数器虽然功能相似,但进制要求不同。我们采用统一的计数器模板:
module counter #(
parameter MAX_VAL = 59
)(
input clk,
input reset,
input [5:0] set_val,
input set_en,
output reg [5:0] count,
output reg carry
);
always @(posedge clk or posedge reset) begin
if(reset) begin
count <= 0;
carry <= 0;
end
else if(set_en) begin
count <= set_val;
carry <= 0;
end
else begin
if(count == MAX_VAL) begin
count <= 0;
carry <= 1;
end
else begin
count <= count + 1;
carry <= 0;
end
end
end
endmodule
实例化时只需修改MAX_VAL参数:
counter #(.MAX_VAL(59)) minute_counter( ... );
counter #(.MAX_VAL(23)) hour_counter( ... );
3. 动态显示系统优化
3.1 四位数码管扫描控制
BASYS2开发板使用四位共阳极数码管,需要通过动态扫描实现稳定显示:
module display_scan(
input clk,
input [3:0] digit0, digit1, digit2, digit3,
output reg [3:0] an,
output reg [6:0] seg,
output reg [3:0] current_digit
);
reg [1:0] scan_count;
reg [15:0] refresh_counter;
always @(posedge clk) begin
refresh_counter <= refresh_counter + 1;
if(refresh_counter == 16'hFFFF) begin
scan_count <= scan_count + 1;
case(scan_count)
2'b00: begin an <= 4'b1110; current_digit <= digit0; end
2'b01: begin an <= 4'b1101; current_digit <= digit1; end
2'b10: begin an <= 4'b1011; current_digit <= digit2; end
2'b11: begin an <= 4'b0111; current_digit <= digit3; end
endcase
end
end
// 七段译码逻辑
always @(*) begin
case(current_digit)
4'h0: seg = 7'b1000000; // 0
4'h1: seg = 7'b1111001; // 1
// ... 其他数字译码
default: seg = 7'b1111111; // 全灭
endcase
end
endmodule
3.2 显示数据选择器
根据当前模式选择显示内容(时钟或闹钟设置):
module display_mux(
input [5:0] hour, min,
input [5:0] alarm_hour, alarm_min,
input alarm_mode,
output reg [3:0] digit0, digit1, digit2, digit3
);
always @(*) begin
if(alarm_mode) begin
digit0 = alarm_hour / 10;
digit1 = alarm_hour % 10;
digit2 = alarm_min / 10;
digit3 = alarm_min % 10;
end
else begin
digit0 = hour / 10;
digit1 = hour % 10;
digit2 = min / 10;
digit3 = min % 10;
end
end
endmodule
4. 功能验证与仿真分析
4.1 测试平台搭建
我们构建了完整的测试环境验证五大核心功能:
module testbench;
reg clk_50MHz;
reg reset;
reg set_time;
reg adjust_hour;
reg adjust_min;
reg alarm_set;
wire [6:0] seg;
wire [3:0] an;
wire alarm_led;
// 实例化被测设计
top uut (.*);
// 时钟生成
always #10 clk_50MHz = ~clk_50MHz;
initial begin
// 初始化
clk_50MHz = 0;
reset = 1;
set_time = 0;
adjust_hour = 0;
adjust_min = 0;
alarm_set = 0;
// 复位释放
#100 reset = 0;
// 测试正常计时
#200;
// 测试校时功能
set_time = 1;
repeat(5) begin
#100 adjust_hour = 1;
#100 adjust_hour = 0;
end
set_time = 0;
// 测试闹钟功能
alarm_set = 1;
#1000;
$finish;
end
endmodule
4.2 关键仿真波形解读
通过ModelSim仿真,我们重点验证了以下场景:
- 正常计时功能 :观察秒、分、时计数器的进位关系
- 校时功能 :验证小时和分钟调整的正确性
- 闹钟触发 :检查预设时间到达时的LED指示
- 显示切换 :确认时钟显示与闹钟设置显示的切换逻辑
- 复位功能 :测试全局复位信号的有效性
图:数字钟系统关键信号仿真波形
波形分析要点:
- clk_1Hz信号的50%占空比
- 秒计数器到59时的进位脉冲
- 校时模式下计数器值的直接设置
- 闹钟触发时alarm_led信号的持续时长
5. 工程实践与优化建议
5.1 BASYS2板级调试技巧
-
时钟约束 :在XDC文件中添加时序约束确保时钟稳定性
create_clock -period 20.000 -name clk [get_ports clk_50MHz] -
IO管脚分配 :正确映射按钮和数码管接口
set_property PACKAGE_PIN V17 [get_ports {seg[6]}] set_property IOSTANDARD LVCMOS33 [get_ports {seg[6]}] -
功耗优化 :关闭未使用模块的时钟门控
always @(posedge clk_50MHz) begin if(!display_enable) display_clk <= 0; end
5.2 常见问题解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 数码管闪烁 | 扫描频率过低 | 提高refresh_counter位宽 |
| 计时不准 | 分频器溢出 | 检查计数器位宽是否足够 |
| 按钮响应异常 | 消抖时间不足 | 增加debounce计数器位宽 |
| 闹钟不触发 | 比较逻辑错误 | 添加仿真检查alarm_set信号 |
5.3 扩展功能建议
- 温度补偿 :添加DS18B20传感器实现走时精度补偿
- 网络校时 :通过UART接口接收标准时间信号
- 多闹钟 :扩展存储空间支持多个闹钟设置
- 亮度调节 :PWM控制数码管亮度适应环境光线
在实现这些扩展功能时,建议采用AXI总线架构连接各外设模块,提升系统可扩展性。
更多推荐



所有评论(0)