一、项目需求
1.以小脚丫FGPA核心板上的四个按钮作为输入,分别作为开始、停止、递增和清除按钮。
2.使用小脚丫FPGA核心板上段两个七段数码管作为输出,实现一个两位数秒表,从0.0秒计时到9.9秒后翻转,0.1秒为精确度。
3.开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。
4.在WebIDE环境下进行Verilog代码编程、综合、仿真、生成JED代码并下载到FPGA中进行验证(注:由于WebIDE的性能限制,仿真若在WebIDE环境中报错,可利用Diamond中的仿真功能,仅限于仿真)。
5.每一个功能模块都通过GPT等大模型(工具不限制)来生成,进行验证、修改后整合在一起实现所需的功能。
二、需求分析
1. 输入设备:
使用小脚丫FPGA核心板上的四个按钮,分别为开始、停止、递增和清除按钮。
开始按钮:启动秒表,使计数器开始递增。
停止按钮:停止秒表的递增,但仍显示当前计数器值。
递增按钮:每按下一次,使计数器值增加一次,无论按钮按下时间多长。
清除按钮:强制将计数器值清零。
2. 输出设备:
使用小脚丫FPGA核心板上的两个七段数码管作为输出。
实现一个两位数秒表,精确到0.1秒,范围为0.0秒到9.9秒。
显示当前计数器值。
3. 逻辑控制:
使用10Hz时钟速率递增计数器,即每0.1秒计数一次。
开始按钮启动秒表后,计数器开始递增。
停止按钮停止计数器递增,但仍显示当前计数器值。
递增按钮每按下一次,使计数器值增加一次。
清除按钮强制将计数器值清零。
4. 开发环境:
在WebIDE环境下进行Verilog代码编程、综合、仿真、生成JED代码并下载到FPGA中进行验证。
如果在WebIDE环境中仿真出现问题,可以使用Diamond中的仿真功能进行调试。
5. 模块设计:
需要设计按钮输入模块,用于检测四个按钮的按下状态。
需要设计计时器整数分频模块,用于实现0.1秒的计时功能。
需要设计数码管驱动模块,用于控制七段数码管显示。
需要设计逻辑控制模块,根据按钮输入和计时器状态进行逻辑控制。
6. 验证和调试:
使用GPT等工具对每个功能模块进行验证和调试。
确保每个模块的功能和逻辑正确后,将它们整合在一起,以实现所需的功能。
基于以上需求,可以开始在实际操作中逐步设计和实现每个模块,并在仿真和实际FPGA验证中进行测试和调试。
三、实现方式
这两段代码共同构成了一个简单的数字秒表的实现,它利用了Verilog HDL的特性来模拟秒表的行为,并通过两个7段数码显示器显示秒表的计数值。下面是它们工作原理的详细描述:
1.LED模块
这个模块主要参考来自小脚丫提供的示例项目中的驱动数码管,只不过示例项目中只有一位七段数码管被驱动,因而需要根据本项目需求,更改为两位七段数码管的驱动。
首先,`LED`模块负责将秒表的数字(`segdata1`和`segdata2`)转换成适合7段显示器显示的形式。每个输入数字对应一个特定的9位编码,这些编码通过控制每个7段显示器上的LED来显示相应的数字。`seg_1`和`seg_2`输出数组包含了这些编码,其中包括了每个段(A到G)、小数点(DP)和数码管选通信号(DIG)的控制位。
- `segdata1`和`segdata2`输入分别代表个位和十位上的数字。
- 模块内部有两个9位宽的寄存器数组`seg_1`和`seg_2`,用于存储每个数字对应的7段编码。例如,`seg_1[0] = 9'hbf;`表示数字0在第一个显示器上的显示编码。
- 通过`assign`语句,将输入的数字映射到对应的7段编码上,从而驱动数码显示器。
2.top模块
top模块的生成来自chatgpt,首先提出设计的top模块应该包含的按键功能和整数分频时钟信号需求。
再对LED模块进行实例化
根据chatgpt所给的代码中存在的偏差进行沟通修改。
对最后得到代码根据实际需求,将按键改成下降沿触发,并使increment按键在停止状态下按下无论按住多久都只增加0.1秒,以及整数分频部分存在错误需要进行修改,最后得到完整代码见附件。
实现方法:
时钟分频:模块通过一个计数器将12MHz的输入时钟分频到100Hz的 tick
。这个 tick
作为秒表的时间基础,允许它每0.1秒递增时间。
下降沿检测:模块使用边沿检测逻辑来识别控制按钮的状态变化。当对应的按钮释放(而非按下)时,start_falling
、stop_falling
、increment_falling
和 clear_falling
信号会变为活跃状态,这是因为这些按钮是在下降沿触发的。
控制逻辑:一个 always
过程块包含主要的控制逻辑,响应按钮按压并相应地更新时间。当秒表正在运行时,tick
信号驱动计时。单独的秒和十分之一秒计数器根据需要递增。increment_triggered
标志用来确保增量按钮每按一次只增加一次时间。
显示逻辑:模块实例化了一个 LED
模块(在代码片段中未给出),推测该模块驱动两个7段数码管来显示秒和十分之一秒。
状态维持:秒表通过运行标志和秒、十分之一秒的计数器来维持其状态。running
标志表明秒表是否在积极地计数。
通过这种方式,该设计模拟了一个基本的秒表功能,包括启动、停止、递增计数和清零操作,同时使用两个7段显示器来显示当前的秒表值。
四、功能框图
1.硬件框图
2.软件框图
五、代码及说明
本次设计一共分为两个模块:top.v和LED.v。
- LED.v模块
这个模块参考自小脚丫平台官方提供的数码管驱动示例代码,我将示例代码中的一个数码管更改为两个数码管,同时考虑到实际使用中个位显示小数点,十分位不显示,对两个数码管的赋值进行了修改。
// The initial block populates the seg_1 and seg_2 arrays with the segment encoding for digits 0 through 9.
initial begin
// Encoding for the first display, including DP (Decimal Point) for appropriate visual representation.
seg_1[0] = 9'hbf; // '0' with decimal point
seg_1[1] = 9'h86; // '1'
seg_1[2] = 9'hdb; // '2'
seg_1[3] = 9'hcf; // '3'
seg_1[4] = 9'he6; // '4'
seg_1[5] = 9'hed; // '5'
seg_1[6] = 9'hfd; // '6'
seg_1[7] = 9'h87; // '7'
seg_1[8] = 9'hff; // '8'
seg_1[9] = 9'hef; // '9'
// Encoding for the second display, designed for the representation of digits without the decimal point.
seg_2[0] = 9'h3f; // '0'
seg_2[1] = 9'h06; // '1'
seg_2[2] = 9'h5b; // '2'
seg_2[3] = 9'h4f; // '3'
seg_2[4] = 9'h66; // '4'
seg_2[5] = 9'h6d; // '5'
seg_2[6] = 9'h7d; // '6'
seg_2[7] = 9'h07; // '7'
seg_2[8] = 9'h7f; // '8'
seg_2[9] = 9'h6f; // '9'
end
这段是将输入的四位数据连续赋值到数码管驱动信号上,输出数码管信号
// Continuous assignments map the 4-bit input (segdata1 and segdata2) to the corresponding 9-bit segment control output.
assign seg_led_1 = seg_1[segdata1]; // Mapping for the first display.
assign seg_led_2 = seg_2[segdata2]; // Mapping for the second display.
- top.v模块
这个top模块是一个秒表的实现,包含启动、停止、递增和清除功能。它还驱动两个数码管来显示秒表的计数值。以下是模块功能的分析和代码的英文注释。
这段是top模块的原始输入时钟信号clk(12MHz)进行整数分频得到10Hz信号,实现0.1秒跳变一次的秒表功能
// Clock division to 10Hz for timing
reg [23:0] counter_clk = 0;
wire tick;
always @(posedge clk) begin
if (counter_clk >= 1199999)
counter_clk <= 0; // Reset the divider counter
else
counter_clk <= counter_clk + 1; // Increment the divider counter
end
assign tick = (counter_clk == 0); // Generate a tick every 0.1 seconds (10Hz)
添加了四个寄存器(start_reg
, stop_reg
, increment_reg
, clear_reg
),它们在每个时钟周期保存按钮的状态。这是为了检测按钮的下降沿。创建了四个下降沿检测信号(start_falling
, stop_falling
, increment_falling
, clear_falling
)。这些信号在检测到对应按钮的下降沿时变为高。
添加了一个标志increment_triggered
来指示增量按钮是否已被触发。在按键按下(即下降沿检测到)后,如果秒表当前处于停止状态,并且 increment_triggered
标志未被设置,则允许执行递增操作。如果 increment
按钮被释放,increment_triggered
标志将被重置,准备下一次按钮按下的检测。
reg running = 0; // Indicates if the stopwatch is running
reg increment_triggered = 0; // Flag to indicate if increment has been triggered
// Registers to detect falling edge
reg start_reg = 0;
reg stop_reg = 0;
reg increment_reg = 0;
reg clear_reg = 0;
// Falling edge detection
wire start_falling = start_reg && !start;
wire stop_falling = stop_reg && !stop;
wire increment_falling = increment_reg && !increment;
wire clear_falling = clear_reg && !clear;
// Update the registers for falling edge detection
always @(posedge clk) begin
start_reg <= start;
stop_reg <= stop;
increment_reg <= increment;
clear_reg <= clear;
end
在这段中实现了秒表的状态转换,用senconds和tenths对个位和十分位进行计数。
running表示秒表的运行状态标志,检测到start的下降沿即转为1表示运行,而只有当不运行时(running = 0)增量键才能使秒表递增0.1s。
// Control logic for the stopwatch
always @(posedge clk) begin
if (clear_falling) begin
// Reset logic
running <= 0;
seconds <= 0;
tenths <= 0;
end
else if (stop_falling) begin
// Stop the stopwatch
running <= 0;
end
else if (start_falling) begin
// Start the stopwatch
running <= 1;
end
当running = 0且增量标志increment_triggered置0时增量按钮下降沿increment_falling才能触发增量功能,并且在完成一次增量后增量标志increment_triggered需要置1保证按键一次只能产生一次增量。
同时在计数时,需要注意tenths和seconds的进位。
else if (increment_falling && !running && !increment_triggered) begin
// Increment logic, only if stopwatch is stopped and increment not already triggered
increment_triggered <= 1; // Set the flag to indicate increment has been triggered
if (tenths == 9) begin
tenths <= 0;
if (seconds == 9)
seconds <= 0; // Reset if at 9.9 seconds
else
seconds <= seconds + 1; // Increment seconds
end
else begin
tenths <= tenths + 1; // Increment tenths
end
end
else if (tick && running) begin
// Timing logic
increment_triggered <= 0; // Clear the increment trigger flag when running
if (tenths == 9) begin
tenths <= 0;
if (seconds == 9)
seconds <= 0; // Reset if at 9.9 seconds
else
seconds <= seconds + 1; // Increment seconds
end else begin
tenths <= tenths + 1; // Increment tenths
end
end
// Reset the increment trigger flag if increment is not being pressed
if (!increment) begin
increment_triggered <= 0;
end
end
这段对LED模块进行实例化,驱动数码管输出信号
// Instantiate the LED module to drive the 7-segment displays
LED display(
.segdata1(seconds),
.segdata2(tenths),
.seg_led_1(seg_led_1),
.seg_led_2(seg_led_2)
);
六、仿真波形图
首先是对top模块的仿真,其仿真文件testbench由chatgpt生成:
按下start键后,开始计数,且每seg_led_2变化到第10次seg_led_1进一位变化一次:
按下stop后停止计时
按下increment后十分位递增一位,按下clear后两位都清零(此处由于个位为零故而无变化)
接着是对LED模块的仿真,testbench也由chatgpt生成,不同的四位输入信号经过译码在两个七段数码管上输出信号。
七、FPGA的资源利用说明
由chatgpt根据run.log生成,分析如下:
这段日志文件记录了一个使用Lattice Diamond软件进行FPGA设计的综合、布局与布线(Place and Route, P&R)以及时序分析的过程。该过程涉及到的FPGA芯片型号是Lattice的MachXO2系列中的LCMXO2-4000HC,封装类型为CSBGA132,速度等级为5。以下是对资源利用分析、警告信息和时序分析的总结:
### 资源利用情况
- **寄存器利用率**:使用了38个寄存器位,总共4635个,利用率接近0%。
- **SLICE资源利用**:使用了40个SLICE,总共2160个,利用率为2%。
- **LUT4资源利用**:使用了54个作为逻辑LUTs(查找表),总共4320个,逻辑LUT利用率约为1.25%。
- **I/O使用情况**:使用了23个加上4个JTAG的I/O端口,总共105个,利用率为25.7%。
- **时钟资源**:有一个主要时钟源(clk_c),且有四个时钟使能信号。
- **Block RAM**(BRAM)和**Digital Signal Processing**(DSP)模块未使用。
### 警告和错误信息
- 多个警告指出了在`top.v`文件中的某些表达式的大小被截断以适应目标大小,这可能是设计中的潜在问题。
- `LED.v`文件中有两个警告,指出`seg_1`和`seg_2`网络没有驱动器,这意味着这些信号可能没有被正确定义或连接。
- 有关时钟`clk_c`的警告,指出其驱动器位于非专用引脚上,可能会导致延迟或抖动问题。
### 时序分析
- **目标频率**:200 MHz,实际达到的最高频率为90.522 MHz,这表明设计未能满足其性能目标。需要进行优化以达到或超过目标频率。
- **时序错误**:报告显示有508条路径未满足设置时间要求,累计负时序裕度为1883526,这是一个指示设计远远未能满足时序要求的明确信号。
### 综合建议
- **设计优化**:鉴于大量的时序错误和低资源利用率,建议对设计进行优化,特别是针对那些未能满足时序要求的路径。
- **检查警告**:解决所有警告,特别是那些关于信号大小截断和缺少驱动器的警告,这可能会影响到设计的功能性和性能。
- **重新设计**:可能需要重新考虑设计的某些方面,特别是那些涉及到高频操作的部分,以确保能够满足时序要求。
这个资源利用和时序分析的总结提供了对设计的性能和潜在问题的初步了解,为后续的设计优化和调整提供了方向。
八、主要遇到的困难和解决方法
在这个寒假之前,我唯一一次接触FPGA是在课内实验中,在已知顶层文件和管脚分配的情况下撰写功能实现代码,包括译码器、加法器,后面加大难度制作计算器。但在本次项目中,我是第一次只能根据需求完整自己设计模块,并完成仿真。虽然有chatgpt的辅助,但对于我这个FPGA小白来说还是比较难上手。感谢电子森林提供的WebIDE平台,与Vivado比起来编译运行时间大大降低,可以直接在平台内进行管教分配,并连接到核心板上观察功能实现情况,这大大节省了我设计的时间,毕竟本小白刚开始实在是遇到许多编译错误。
在好不容易过了编译以后,我又发现fpga映射后,按四个按键核心板没有反应,于是又反复检查代码,最后发现是chatgpt给的代码中时钟分频有问题,我应该自行调整,同时按键初始值应该为1,当变为0时表明按下按键。
在核心板上实现功能后,仿真又成为让我头疼的另一个问题。我并没有用过modelsim,更不会编写testbench文件,于是继续求助chatgpt和CSDN论坛。但是在编译通过后,我的top模块仿真波形并没有变化,于是我又查询网络、反复修改尝试,最终才发现是自己在testbench中设置的时钟信号周期有问题,同时不同的按键按下间隔过短。这才得到最终的仿真结果。总之真的是收获颇多
九、未来的计划或建议
虽然在这个项目中我遇到了许多问题,也花费不少时间解决,这让我多少感觉自己在fgpa上缺少天赋。但是最终敲完这个报告,我感觉自己心中最多的还是满足和成就感。在大四的毕设我可能会选择fgpa开发方向进行更多的历练,也会继续参加更多fgpa活动,也会推荐更多同学参加之后电子森林举办的活动。