一、任务需求
任务名称为具有启动、停止、递增和清除功能的秒表,要求通过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。
使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。秒表使用四个按钮输入:开始、停止、增量和清除(重置)。开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次);停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。
二、需求分析与实现方式
任务要求利用FPGA核心板制作一款具有启动、停止、递增和清除功能的秒表,可以利用核心板上12MHZ时钟信号来产生10HZ的时钟信号,从而可以根据此时钟信号实现相应的秒表正常计数功能,然后通过七位数码管来显示当前的计时情况,利用一个按键来控制计时的开始与暂停;增量按键为异步输入信号,暂停状态下每次按下后抬起时进行增量,一次增量为一个周期的信号(0.1s),无论按下多长时间均只会增量一次,且内部置有一个单独的变量进行检测来保障仅增量一次;重置按键直接将数码管上显示的数字清零,但不会改变当前计时状态,无论是暂停还是运行中均可进行重置。相应的功能框图如下所示。
三、代码及说明
代码直接在在WebIDE环境下进行Verilog编程,十分方便。
1.变量声明部分
input clk;
input hold;
input rst;
input inc;
output [8:0] seg_led1,seg_led2;
parameter WIDTH = 24; //计数器的位数,计数的最大值为 2**WIDTH-1
parameter N = 600000; //分频系数,请确保 N < 2**WIDTH-1,否则计数会溢出
reg clkout;
reg [WIDTH-1 : 0] cnt_p; // 计数器,用于计数分频数
reg[6:0] seg [9:0];
reg [3:0] cnt_ge; //个位
reg [3:0] cnt_shi; //十位
reg hold_flag; //按键标志位
reg inc_flag;
initial
begin
seg[0] = 7'h3f; // 0
seg[1] = 7'h06; // 1
seg[2] = 7'h5b; // 2
seg[3] = 7'h4f; // 3
seg[4] = 7'h66; // 4
seg[5] = 7'h6d; // 5
seg[6] = 7'h7d; // 6
seg[7] = 7'h07; // 7
seg[8] = 7'h7f; // 8
seg[9] = 7'h6f; // 9
end
三个输入分别为开始暂停、增量以及重置对应的按键,输出用于七位数码管对应的显示。
还定义了一系列分别用于开始暂停标志、分频器、计数所需的时钟信号和变量。
2.分频器部分
此部分代码块由GPT模型生成并改编,这是GPT生成的代码块
module Divider(
input wire clk, // 输入时钟
output wire clk_out // 输出分频后的时钟
);
reg [1:0] count; // 计数器,用于计数分频数
always @(posedge clk) begin
if (count == 2'b11) begin
count <= 2'b00; // 当计数达到分频数时,重置计数器
clk_out <= ~clk_out; // 翻转输出时钟
end else begin
count <= count + 1; // 继续计数
end
end
endmodule
下面是改编的代码段
always @ (posedge clk or negedge rst ) //posedge和negedge是verilog表示信号上升沿和下降沿
//当clk上升沿来临或者rst变低的时候执行一次always里的语句
begin
if(!rst)
cnt_p<=0;
else if (cnt_p==(N-1))
begin
cnt_p<=0;
clkout <= ~clkout; // 翻转输出时钟
end
else begin
cnt_p<=cnt_p+1; //计数器一直计数,当计数到N-1的时候清零,这是一个模N的计数器
end
end
MXO2小脚丫开发板上自带有一个12MHZ的时钟信号,利用一个模N计数器,通过时钟触发计数器计数,当计数到N-1时清零并翻转输出时钟信号,从而根据设定的N值来得到10HZ的时钟信号。
3.开始暂停计数和重置部分
always @ (negedge hold)
if(!rst==1)
hold_flag <= 1;
else
hold_flag <= ~hold_flag;
always @ (posedge clkout or negedge rst)
begin
if (!rst == 1) begin
cnt_ge <= 4'd0;
cnt_shi <= 4'd0;
end
else if(hold_flag == 1)begin
cnt_ge <= cnt_ge;
cnt_shi <= cnt_shi;
end
else if(cnt_shi==9 && cnt_ge==9) begin
cnt_shi <= 0;
cnt_ge <= 0;
end
else if(cnt_ge==9)begin
cnt_ge <= 4'd0;
cnt_shi <= cnt_shi+1;end
else
cnt_ge <= cnt_ge+1;
end
通过按键hold触发产生一个hold_flag信号来控制计时的开始与暂停,当该信号为1时暂停并显示当前计时数字,且初始状态为1即一开始即为暂停状态,当该信号翻转后进行计时,计时分为个位和小数点后一位,根据10HZ的时钟信号,每0.1s小数点后一位显示数字加一,当该位数字为9时当前周期的操作为个位显示数字加一然后该位数字归零,从而实现进位操作。当个位和小数点后一位均为9时该周期操作为两位同时清零并保持计时。重置信号检测优先级最高,当检测重置信号为低电平时直接异步清零,将个位与小数点后一位直接置零,但不改变当前的计数状态,若原先是暂停则置零后仍保持暂停,计数中同理。
4.增量部分
此部分代码也是由GPT编写并改编,这是GPT生成得代码块
module Increment_Display(
input clk, // 时钟信号
input rst, // 复位信号
input increment, // 增量触发信号,按键下降沿触发
output reg [3:0] display // 数码管显示的值
);
reg increment_flag; // 增量触发标志
always @(posedge clk or posedge rst) begin
if (rst) begin
display <= 4'b0000; // 复位时显示值为 0
increment_flag <= 1'b0; // 复位时清除增量触发标志
end else begin
if (increment && !increment_flag) begin
// 按键下降沿触发时,增加显示值
if (display == 4'b1001)
display <= 4'b0000; // 当显示值达到 9 时重新从 0 开始
else
display <= display + 1;
increment_flag <= 1'b1; // 设置增量触发标志,防止连续触发
end else if (!increment) begin
increment_flag <= 1'b0; // 松开按键时清除增量触发标志
end
end
end
endmodule
GPT给出的描述为
这个 Verilog 模块描述了一个简单的异步电路,其中:
clk
是时钟信号,用于同步电路操作。rst
是复位信号,当复位信号为高时,数码管显示的值被重置为 0。increment
是增量触发信号,当按键下降沿触发时,该信号为高,表示需要增加显示值。display
是数码管显示的值,用 4 位二进制表示。
在 always
块中,根据时钟信号和复位信号,以及增量触发信号的状态,更新数码管显示的值。当按键下降沿触发并且增量触发标志为低时,显示的值增加一,并且设置增量触发标志以防止连续触发。当松开按键时,增量触发标志被清除,以便下次按键触发。
改编后的代码如下
else begin
if (inc && !inc_flag) begin
// 按键上升沿触发时,增加显示值
if(cnt_ge==9)begin
cnt_ge <= 4'd0;
cnt_shi <= cnt_shi+1;end
else
cnt_ge <= cnt_ge+1;
inc_flag <= 1'b1; // 设置增量触发标志,防止连续触发
end else if (!inc) begin
inc_flag <= 1'b0; // 松开按键时清除增量触发标志
end
增量原理是按键上升沿触发使得十分位数字加一,若十分位数字为9则进位,并且内部设置一个增量触发标志,该增量触发标志原理如GPT描述一般,用于防止连续触发,并且每次操作会将其清除。
代码将增量模块置于正常计数模块部分中reset重置部分后检测开始暂停操作前,以便在暂停状态下进行增量操作。
5.FPGA的资源利用说明
如图为FPGA的管脚映射
run.log文件中的资源利用说明为
Design Summary:
Number of registers: 35 out of 4635 (1%)
PFU registers: 35 out of 4320 (1%)
PIO registers: 0 out of 315 (0%)
Number of SLICEs: 36 out of 2160 (2%)
SLICEs as Logic/ROM: 36 out of 2160 (2%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 13 out of 2160 (1%)
Number of LUT4s: 72 out of 4320 (2%)
Number used as logic LUTs: 46
Number used as distributed RAM: 0
Number used as ripple logic: 26
Number used as shift registers: 0
Number of PIO sites used: 22 + 4(JTAG) out of 105 (25%)
Number of block RAMs: 0 out of 10 (0%)
Number of GSRs: 1 out of 1 (100%)
EFB used : No
JTAG used : No
Readback used : No
Oscillator used : No
Startup used : No
POR : On
Bandgap : On
Number of Power Controller: 0 out of 1 (0%)
Number of Dynamic Bank Controller (BCINRD): 0 out of 6 (0%)
Number of Dynamic Bank Controller (BCLVDSO): 0 out of 1 (0%)
Number of DCCA: 0 out of 8 (0%)
Number of DCMA: 0 out of 2 (0%)
Number of PLLs: 0 out of 2 (0%)
Number of DQSDLLs: 0 out of 2 (0%)
Number of CLKDIVC: 0 out of 4 (0%)
Number of ECLKSYNCA: 0 out of 4 (0%)
Number of ECLKBRIDGECS: 0 out of 2 (0%)
四、演示视频
五、难题与解决方案以及未来的计划及建议
此次项目遇到的主要问题在实现秒表增量的部分,当时遇到了FPGA中经典的多重驱动问题,两个操作同时改变一个变量的值导致FPGA映射报错,解决方案尝试过用两个变量最后求和进行,但考虑到要分别进位,得到的效果并不理想,最后采用了if else语句来解决,在非暂停状态增量操作会使当前计数状态暂停并显示当前计数,增量完成后会进行增量并恢复计数,在暂停状态可直接进行增量,并且不会对暂停状态产生影响。
本次“寒假在家一起练”活动让我加深了对数字电路以及Verilog的认识和理解,也让我对于FPGA有了一定的学习,我很感谢电子森林能给我这个学习机会,也希望在未来能够通过电子森林平台来进行更多的项目学习与开发。