具有启动、停止、递增和清除功能的秒表项目报告
一、项目需求
1、任务宗旨
·结合数字电路书本知识,深刻理解数字逻辑的功能实现及设计流程
·培养工程化设计理念、规范化的设计流程及解决未知问题的能力
·探索使用行业新工具在项目研发中存在的问题和解决方法
2、任务内容
通过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。
秒表使用四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。
3、实现要求
·在WebIDE环境下进行Verilog代码编程、综合、仿真、生成JED代码并下载到FPGA中进行验证(注:由于WebIDE的性能限制,仿真若在WebIDE环境中报错,可利用Diamond中的仿真功能,仅限于仿真)
·每一个功能模块都通过GPT等大模型(工具不限制)来生成,进行验证、修改后整合在一起实现所需的功能
4、所需知识
·数字电路基础理论
·数字逻辑设计思想
二、平台介绍
1、硬件介绍
STEP小脚丫FPGA学习平台是苏州思得普信息科技公司专门针对FPGA初学者(尤其是学习数字电路的在校同学)打造的一系列性价比最高、学习门槛最低的学习模块系列。板上选用的芯片兼具了FPGA和CPLD的优点,瞬时上电启动,无需外部重新配置FPGA,是学习数字逻辑绝佳的选择。系列中所有板子的大小兼容标准的DIP40封装,尺寸只有52mm x 18mm,非常便于携带,而且能够直接插在面包板上或以模块的方式放置在其它电路板上以即插即用的方式,大大简化系统的设计。
最新推出的STEP-MXO2-LPC在易用性方面做了大幅升级:
使用了USB Type C接口提供板上+5V供电、FPGA的配置,并新增了UART通信的功能,因此无需再通过其它端口与PC进行数据通信
支持U盘模式(连接到上位机的USB端口,上位机自动弹出StepFPGA的U盘盘符)的下载,任何操作系统的电脑 - Windows、Mac OS以及Linux(包括树莓派)都可以在不安装任何驱动程序的情况下,直接将生成的jed配置文件发送到StepFPGA盘中即可完成编程
为配合这款小脚丫FPGA的使用,我们特别升级了Web IDE系统,用户不必再下载安装Diamond软件,即可在任何一款电脑上通过浏览器进行FPGA的编程和编译。图形化的界面使得操作非常直观、便捷
习惯于使用Diamond编程FPGA的用户仍然可以使用Diamond软件进行编译、生成jed文件,只是最终的下载只需要将要下载的文件发送到StepFPGA盘中即可。
2、硬件规范
·核心器件:Lattice LCMXO2-4000HC-4MG132
132脚BGA封装,引脚间距0.5mm,芯片尺寸8mm x 8mm;
上电瞬时启动,启动时间<1ms;
4320个LUT资源, 96Kbit 用户闪存,92Kbit RAM;
2+2路PLL+DLL;
嵌入式功能块(硬核):一路SPI、一路定时器、2路I2C
支持DDR/DDR2/LPDDR存储器;
104个可热插拔I/O;
内核电压2.5-3.3V;
·板载资源:
两位7段数码管;
两个RGB三色LED;
8路用户LED;
4路拨码开关;
4路按键;
·36个用户可扩展I/O(其中包括一路SPI硬核接口和一路I2C硬核接口)
·支持的开发工具思德普开发的Web IDE以及Lattice官方提供的Diamond
·支持MICO32/8软核处理器以及RISC-V软核
·板上集成FPGA编程器,采用U盘的模式
·一路USB Type C接口,可用于给核心板供电、给FPGA下载JED文件以及同上位机通过UART通信
·板卡尺寸52mm x 18mm
三、需求分析
1、秒表功能:通过对时钟进行周期计数完成计数器模块的设计
2、状态实现:通过状态机的设计,完成秒表在启动、停止、递增和清除各个状态下的功能处理
3、按键判断:通过对外部按键输入高低电平的读取,来实现秒表在启动、停止、递增和清除各个状态之间的切换
4、显示驱动:通过对计数器的计数值进行译码,并驱动数码管,来完成最终秒表的展示
四、实现要求
·在WebIDE环境下进行Verilog代码编程、综合、仿真、生成JED代码并下载到FPGA中进行验证(注:由于WebIDE的性能限制,仿真若在WebIDE环境中报错,可利用Diamond中的仿真功能,仅限于仿真)
·每一个功能模块都通过GPT等大模型(工具不限制)来生成,进行验证、修改后整合在一起实现所需的功能
五、代码框图
六、代码及说明
正常状态时,按键的处于高电平,输入为1。当按下按键后,输入变成0。我们可以通过判断由1变成0的这个过程,来得到按键按下的状态。这里需要注意按键需要做个去抖的处理,来保证每次按下只会触发一次。
按键去抖模块:
module key_debounce(
input clk,
input key,
output key_fall
);
reg [16:0] cnt;
always@(posedge clk) begin
if(key)
//没有按下按键时,倒计时器不工作
cnt <= 'd119_999;
else if(~key & (cnt != 0))
//按键按下并稳定后,倒计时开始计时,直到计数值变成0停止
cnt <= cnt - 'd1;
end
reg key_reg;
reg key_dly;
always@(posedge clk) begin
//倒计时结束,此时按键值已稳定,读取此时的按键值
if(~key & (cnt == 0))
key_reg <= 'b0;
else
key_reg <= 'b1;
//将获取的按键值延迟一拍,用于下面的判断
key_dly <= key_reg;
end
//判断前后两个时钟周期的按键值
//前面为低电平,后面为高电平,说明按键从1变成0,按键被按下了
assign key_fall = ~key_reg & key_dly;
endmodule
计数模块:
module cnt_core(
input clk,
input rstn,
input key_start,
input key_stop,
input key_inc,
input key_clc,
output reg [3:0] sec_bit,
output reg [3:0] ms_bit
);
reg [1:0] cnt_status;
//计数器状态机
//停止状态时,开始按键按下,进入计数状态
//计数状态时,停止按键按下,进入停止状态
always@(posedge clk) begin
if(~rstn)
cnt_status <= 'd0;
else if(cnt_status == 0 && key_start)
cnt_status <= 'd1;
else if(cnt_status == 'd1 && key_stop)
cnt_status <= 'd0;
end
reg [20:0] clk_cnt;
//计数状态时,每个时钟上升沿,clk_cnt加1
//时钟周期为12MHz,所以计数到(1200000-1)时完成一次毫秒计数
always@(posedge clk) begin
if(~rstn) begin
clk_cnt <= 0;
end else if(cnt_status == 'd1) begin
if(clk_cnt == 'd1_199_999) begin
clk_cnt <= 0;
end else begin
clk_cnt <= clk_cnt + 'd1;
end
end
end
always@(posedge clk) begin
if(~rstn) begin
ms_bit <= 0;
end else if(clk_cnt == 'd1_199_999) begin
if(ms_bit == 'd9)
ms_bit <= 0;
else
ms_bit <= ms_bit + 'd1;
end else if(cnt_status == 'd0 && key_inc) begin
if(ms_bit == 'd9)
ms_bit <= 0;
else
ms_bit <= ms_bit + 'd1;
end else if(cnt_status == 'd0 && key_clc) begin
ms_bit <= 0;
end
end
always@(posedge clk) begin
if(~rstn) begin
sec_bit <= 0;
end else if((clk_cnt == 'd1_199_999) && (ms_bit == 'd9)) begin
if(sec_bit == 'd9)
sec_bit <= 0;
else
sec_bit <= sec_bit + 'd1;
end else if((cnt_status == 'd0 && key_inc) && (ms_bit == 'd9)) begin
if(sec_bit == 'd9)
sec_bit <= 0;
else
sec_bit <= sec_bit + 'd1;
end else if(cnt_status == 'd0 && key_clc) begin
sec_bit <= 0;
end
end
endmodule
数码管显示模块:
```
module seg_disp(
input [3:0] num_i,
input dp_en,
output reg [8:0] seg_o
);
always@(*) begin
//秒位的数码管会将小数点点亮
if(dp_en) begin
//判断二进制输入值,译码成数码管对应数值
case(num_i)
'd1: seg_o = 'h86; // 1
'd2: seg_o = 'hdb; // 2
'd3: seg_o = 'hcf; // 3
'd4: seg_o = 'he6; // 4
'd5: seg_o = 'hed; // 5
'd6: seg_o = 'hfd; // 6
'd7: seg_o = 'h87; // 7
'd8: seg_o = 'hff; // 8
'd9: seg_o = 'hef; // 9
default: seg_o = 'hbf; // 0
endcase
end else begin
case(num_i)
'd1: seg_o = 'h06; // 1
'd2: seg_o = 'h5b; // 2
'd3: seg_o = 'h4f; // 3
'd4: seg_o = 'h66; // 4
'd5: seg_o = 'h6d; // 5
'd6: seg_o = 'h7d; // 6
'd7: seg_o = 'h07; // 7
'd8: seg_o = 'h7f; // 8
'd9: seg_o = 'h6f; // 9
default: seg_o = 'h3f; // 0
endcase
end
end
endmodule
```
module top(
input clk,
input rstn,
input [3:0] key,
output [8:0] seg0,
output [8:0] seg1
);
wire key_start;
wire key_stop;
wire key_inc;
wire key_clc;
wire [3:0] ms_bit;
wire [3:0] sec_bit;
key_debounce key1_debounce(
.clk(clk),
.key(key[0]),
.key_fall(key_start)
);
key_debounce key2_debounce(
.clk(clk),
.key(key[1]),
.key_fall(key_stop)
);
key_debounce key3_debounce(
.clk(clk),
.key(key[2]),
.key_fall(key_inc)
);
key_debounce key4_debounce(
.clk(clk),
.key(key[3]),
.key_fall(key_clc)
);
cnt_core cnt_core(
.clk(clk),
.rstn(rstn),
.key_start(key_start),
.key_stop(key_stop),
.key_inc(key_inc),
.key_clc(key_clc),
.sec_bit(sec_bit),
.ms_bit(ms_bit)
);
seg_disp ms_disp(
.num_i(ms_bit),
.dp_en(1'b0),
.seg_o(seg0)
);
seg_disp sec_disp(
.num_i(sec_bit),
.dp_en(1'b1),
.seg_o(seg1)
);
endmodule
七、运行结果
八、资源使用说明
本次设计在优化资源的设计下,只占用了2%的寄存器,SLICEs的数量也只有5%,没有使用block RAM。
```
Number of registers: 106 out of 4635 (2%)
PFU registers: 106 out of 4320 (2%)
PIO registers: 0 out of 315 (0%)
Number of SLICEs: 98 out of 2160 (5%)
SLICEs as Logic/ROM: 98 out of 2160 (5%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 47 out of 2160 (2%)
Number of LUT4s: 195 out of 4320 (5%)
Number used as logic LUTs: 101
Number used as distributed RAM: 0
Number used as ripple logic: 94
Number used as shift registers: 0
Number of PIO sites used: 24 + 4(JTAG) out of 105 (27%)
Number of block RAMs: 0 out of 10 (0%)
九、项目总结
1、问题与解决方法
在设计过程中,时序问题经常会导致功能失败或不稳定,可以利用时序约束和时序分析工具来帮助确保时序正确。
按钮按下和释放时可能会产生的机械弹跳会导致意外的按键触发。需要实现按键消抖机制来确保只有有效的按键按下会被识别。
实现一个精确的计时器以确保秒表的计时精度达到要求,需要考虑时钟漂移和延迟对计时精度的影响,并采取相应的补偿措施。
在整个项目中,要确保系统的稳定性和可靠性。进行充分的调试和测试,并考虑添加调试功能,如通过串口输出状态信息来帮助诊断问题。
2、未来展望
在基本功能实现后,可以考虑添加一些额外的功能,如计时器暂停、计时器重置时确认等。可以采用低功耗设计技术和优化算法考虑优化电路设计以减少功耗,延长电池寿命或减少系统发热。可以考虑进一步优化系统性能,如减少延迟、提高刷新率或增加计时器分辨率等。在完成这个项目的过程中,我接触到了很多新的技术和概念,这将持续激励我继续学习和探索数字电路设计、FPGA编程等领域,不断提升自己的技能水平。