2024寒假练-基于小脚丫FPGA实现的多功能秒表
该项目使用了思得普的webIDE,Verilog语言,GPT模型,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:基于Lattice MXO2的小脚丫FPGA核心板,由开始、停止、增量和清除4个按键控制,用数码管显示的秒表。
标签
FPGA
测试
显示
ottoman
更新2024-04-02
北京理工大学
161

1.项目需求

- 使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。

- 四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值;每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间;复位/清除输入强制计数器值为零。

2.需求分析

- 核心板时钟信号clk为12MHz,由一个分频器输出一个10Hz的时钟信号clk1h,以驱动计数器计数。

- 计数器分为个位计数器和十位计数器,clk1h上升沿时个位计数器加一,个位计数器加到9时再加1时重置为0,十位计数器加1。十位加到9时再加1时重置为0。

- 设置4个按钮,分别为开始、停止、增量和重置。

3.实现的方式

将K1,K2,K3,K4键分别设置为开始、停止、增量和清除键。设置防抖模块防止信号抖动。按下开始键且之前没按过start_flag为1,stop_flag为0,可以计数,按下停止键时stop_flag为0,停止计数。按下增量键个位加一。按下清除键清零计数器,停止计数。由一个分频器输出一个10Hz的时钟信号clk1h,clk1h上升沿时计数器个位加一。

4.功能框图

秒表以10Hz的频率计数,通过开始、停止、增量和清除键控制。

5.代码与说明

写代码时我使用了GPT模型,然而它给的代码有时候不靠谱,所以我还得再改,这是我的一些记录

image.png

image.png

以下是代码。

防抖输入模块

核心思路:设置一个按键信号前状态,如果输入按键信号发生变化,即与前状态不相等,则计数器开始计时,计时一定时间后再将输出信号置为现在的输入按键信号,这样可防止信号突然抖动。当重置信号有效时置零计数器与输出s信号

module debounce(
input clk, // Clock input
input rst, // Reset input
input btn_in, // Input button signal
output reg btn_out // Debounced button signal output
);

reg [15:0] cnt; // Counter used for implementing delay

always @(posedge clk or negedge rst) begin
if (!rst) begin
cnt <= 0; // Reset counter
btn_out <= 0; // Reset output signal
end else begin
if (btn_in == btn_out) begin
cnt <= 0; // Reset counter if input matches output
end else if (cnt < 16'hffff) begin
cnt <= cnt + 1; // Start counting if input changes
if (cnt == 16'hfffe) begin
btn_out <= btn_in; // Confirm change after delay
end
end
end
end

endmodule

数码管译码器模块

核心思路:输入个位计数器与十位计数器数字,输出对应数码管显示数字。十位数字要显示小数点

module Segment_led
(
input [3:0] cnt_units,  //cnt_units input
input [3:0] cnt_tens,  //cnt_units input
output [8:0] led_1,  
output [8:0] led_2
);

reg[8:0] mem1 [9:0];
reg[8:0] mem10 [9:0];
initial
    begin
        mem1[0] = 9'h3f;   //  0
        mem1[1] = 9'h06;   //  1
        mem1[2] = 9'h5b;   //  2
        mem1[3] = 9'h4f;   //  3
        mem1[4] = 9'h66;   //  4
        mem1[5] = 9'h6d;   //  5
        mem1[6] = 9'h7d;   //  6
        mem1[7] = 9'h07;   //  7
        mem1[8] = 9'h7f;   //  8
        mem1[9] = 9'h6f;   //  9
        mem10[0] = 9'b010111111;   //  0 with dp
        mem10[1] = 9'b010000110;   //  1 with dp
        mem10[2] = 9'b011011011;   //  2 with dp
        mem10[3] = 9'b011001111;   //  3 with dp
        mem10[4] = 9'b011100110;   //  4 with dp
        mem10[5] = 9'b011101101;   //  5 with dp
        mem10[6] = 9'b011111101;   //  6 with dp
        mem10[7] = 9'b010000111;   //  7 with dp
        mem10[8] = 9'b011111111;   //  8 with dp
        mem10[9] = 9'b011101111;   //  9 with dp
    end

assign led_1 = mem1[cnt_units];
assign led_2 = mem10[cnt_tens];

endmodule

分频器模块

核心思路:核心板时钟信号频率为12MHz,设置一个计数器,当核心板时钟上升沿时计数器加1,当加到600000-1时输出时钟信号反转一次,计数器清零。反转两次为1个周期,所以输出时钟信号周期就是0.1s

module divide(
input clk, // 12MHz main clock input
output reg clkout // 10Hz clock output
);

reg [31:0] counter; // Counter for dividing the clock

always @(posedge clk) begin
if (counter == 5) begin // When the counter reaches 600000-1, output a clock pulse
counter <= 0; // Reset the counter
clkout <= ~clkout; // Toggle the clock signal
end else begin
counter <= counter + 1; // Increment the counter
end
end

endmodule

秒表模块

该模块需要根据10Hz的clk1h计数个位与十位,并且按过开始键才能计数,按过停止键就不能计数,所以需要设置start_flag与stop_flag记录此时是否可以计数。按一下增量键个位加1。个位到9后再加1要向十位进1,十位到9再加1则置零。因为人按键会按一定时间,所以每个时钟上升沿要更新按键前状态为当前按键状态,然后如果之前按键值为0,现在是1,则说明之前没按过键,现在按了,可进行后续操作。按下重置键后所有状态清零。

  • 如果按了开始键且之前没按过,开始计数,将start_flag设1,stop_flag设0
  • 如果按了停止键且之前没按过,停止计数,将stop_flag设1
  • 按照clk1h的自动计数
  • 如果start flag是1且stop flag是0,也就是说之前按过了开始键,则计数
  • 个位到9再加1则置零
  • 个位到9且十位到9时再加1则十位置零
  • 个位到9且十位没到9再加1则十位加1
  • 个位没到9则个位加1
  • 如果增量键按了且之前没按且十位比1大,则个位加1,进位规则如上
module Stopwatch (clk, start_btn, stop_btn, inc_btn, rst, led1, led2);

input clk, start_btn, stop_btn, inc_btn, rst;
output [8:0] led1;
output [8:0] led2;

// Edge detection variables
reg inc_btn_prev = 0; // Previous state of the increment button
reg [3:0] cnt_units; // Define a 3-bit counter, output can be used as input to a 3-to-8 decoder
reg [3:0] cnt_tens; // Define a 3-bit counter, output can be used as input to a 3-to-8 decoder
reg start_flag = 0; // Start flag
reg stop_flag = 0; // Stop flag
reg start_btn_prev = 0; // Previous state of the start button for edge detection
reg stop_btn_prev = 0; // Previous state of the stop button for edge detection
wire clk1h; // Intermediate variable representing the divided clock used as trigger for the counter
wire inc_btn_debounced; // Debounced increment button signal

// Instantiate the decode38 module
Segment_led u1 (
.cnt_units(cnt_units), // Input port connected to cnt, outputs connected to led
.led_1(led1),
.led_2(led2),
.cnt_tens(cnt_tens)
);

// Instantiate the divider module to generate a 10Hz clock signal
divide u2 (
.clk(clk),
.clkout(clk1h)
);

// Instantiate the debounce module
debounce db_inc_btn(
.clk(clk),
.rst(rst),
.btn_in(inc_btn),
.btn_out(inc_btn_debounced)
);

always @(posedge clk or negedge rst) begin
if (!rst) begin
// Reset condition: when the reset signal is active low
start_flag <= 0; // Set start_flag to 0
stop_flag <= 0; // Set stop_flag to 0
start_btn_prev <= 0; // Set previous state of start button to 0
stop_btn_prev <= 0; // Set previous state of stop button to 0
end else begin
start_btn_prev <= start_btn; // Update previous state of start button
stop_btn_prev <= stop_btn; // Update previous state of stop button

if (start_btn && !start_btn_prev) begin
// Start button pressed and was not previously pressed
// Enable the counter to start counting
start_flag <= 1; // Set start_flag to 1 to enable counting
stop_flag <= 0; // Set stop_flag to 0 to disable stopping
end

if (stop_btn && !stop_btn_prev) begin
// Stop button pressed and was not previously pressed
// Stop the counter from counting
stop_flag <= 1; // Set stop_flag to 1 to stop counting
end
end
end
// Automatic counting logic based on clk1h
always @(posedge clk1h or negedge rst) begin
if (!rst) begin
// Reset condition: when the reset signal is active low
cnt_units <= 0; // Set units counter to 0
cnt_tens <= 0; // Set tens counter to 0
inc_btn_prev <= 0; // Set previous state of increment button to 0
end else begin
inc_btn_prev <= inc_btn_debounced; // Update previous state of increment button

if (start_flag && !stop_flag) begin
// Count based on 1Hz clock when start flag is 1 and stop flag is 0
if (cnt_units >= 9) begin
// Increment tens counter when units counter reaches 9
cnt_units <= 0;
if (cnt_tens >= 9)
cnt_tens <= 0; // Reset tens counter when it reaches 9
else
cnt_tens <= cnt_tens + 1; // Increment tens counter by 1
end else
cnt_units <= cnt_units + 1; // Increment units counter by 1
end

if (inc_btn_debounced && !inc_btn_prev && cnt_tens >= 1) begin
// Increment counters when increment button is pressed and tens counter is greater than or equal to 1
if (cnt_units >= 9) begin
// Increment tens counter when units counter reaches 9
cnt_units <= 0;

if (cnt_tens >= 9)
cnt_tens <= 0; // Reset tens counter when it reaches 9
else
cnt_tens <= cnt_tens + 1; // Increment tens counter by 1
end else
cnt_units <= cnt_units + 1; // Increment units counter by 1
end
end
end

endmodule

FPGA资源利用说明

image.png

image.png

我把运行日志给了chatGPT,问它资源利用情况

这是它的答复

image.png

我翻译了以下,资源利用情况是这样的:

1.  寄存器资源利用情况:

    *   总共有4635个寄存器位,其中使用了63个(1%)。

    *   PFU(Programmable Function Unit)寄存器使用了63个(1%)。

    *   PIO(Parallel Input/Output)寄存器未被使用。

2.  SLICE资源利用情况:

    *   总共有2160个SLICE,其中使用了57个(3%)。

    *   其中,作为逻辑/ROM使用的SLICE有49个(3%)。

    *   未用于RAM的SLICE有0个。

    *   作为Carry使用的SLICE有26个(1%)。

3.  LUT4资源利用情况:

    *   总共有4320个LUT4,其中使用了114个(3%)。

    *   其中,作为逻辑LUT使用的有62个,作为分布式RAM使用的有0个,作为Ripple逻辑使用的有52个。

4.  PIO资源利用情况:

    *   总共有105个PIO站点,其中使用了23个(26%),另外4个用于JTAG。

5.  Block RAM资源利用情况:

    *   未使用任何Block RAM(0%)。

6.  GSR资源利用情况:

    *   有1个GSR(Global Set/Reset)实例,使用了1个(100%)。

7.  其他资源利用情况:

    *   未使用EFB、JTAG、Readback、Oscillator等资源。

    *   未使用任何Power Controller、Dynamic Bank Controller、PLLs等资源。


综上所述,该FPGA设计中寄存器、SLICE、LUT4等资源的利用率都比较低,且未使用Block RAM等资源。

6.结果演示

屏幕截图 2024-03-26 175052.png

具体实验结果见视频。按下K1键即开始键后,秒表开始计数,每0.1s个位加一,每1s十位加一。按下K2键即停止键后,秒表停止计数。再按下开始键后继续计数。按下K3键即增量键后秒表个位加一,十位进位正常。按下K4键即清除键后,无论秒表当前状态如何均清零。由此可见该实验完成了项目要求。

附件下载
main.v
implement (9).jed
团队介绍
北京理工大学
团队成员
ottoman
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号