2024年寒假练 - 基于Lattice MXO2的小脚丫FPGA核心板设计具有启动、停止、递增和清除功能的秒表
该项目使用了Lattice MXO2的小脚丫FPGA核心板,实现了秒表的设计,它的主要功能为:启动、停止、递增和复位。
标签
FPGA
Esther
更新2024-04-01
北京理工大学
239

注:

视频已上传至哔哩哔哩平台,

账号:Esther陈小飞,

视频标题:2024年寒假练-基于Lattice MXO2的小脚丫FPGA核心板设计具有启动、停止、递增和清除功能的秒表

 

一、 项目需求

1. 任务名称

2024年寒假练-——基于Lattice MXO2的小脚丫FPGA核心板(Type C接口)设计具有启动、停止、递增和清除功能的秒表。

2. 任务宗旨

A. 结合数字电路书本知识,深刻理解数字逻辑的功能实现及设计流程;

B. 培养工程化设计理念、规范化的设计流程及解决未知问题的能力;

C. 探索使用行业新工具在项目研发中存在的问题和解决方法。

3. 任务内容

通过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。

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

秒表使用四个按钮输入:开始、停止、增量和清除(重置)。

A. 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次);

B. 停止输入使计数器停止递增,但使数码管显示当前计数器值;

C. 每次按下按钮时,增量输入都会导致显示值增加一次(0.1),无论按住增量按钮多长时间;

D. 复位/清除输入强制计数器值为零。

4. 实现要求

A. 在WebIDE环境下进行Verilog代码编程、综合、仿真、生成JED代码并下载到FPGA中进行验证。

B. 每一个功能模块都通过GPT等大模型(工具不限制)来生成,进行验证、修改后整合在一起实现所需的功能。

5. 基础知识

A. 数字电路基础理论

B. 数字逻辑设计思想

二、 需求分析

该任务有四个经按键输入的信号,分别为“复位”、“增量”,“开始”,“结束”。

设计的主要思路为通过四个输入量控制两个计数器“cnt_ge”和“cnt_shi”进行0.0~9.9之间的循环计数,并将两个计数器的计数值映射为两个七段数码管的显示输出。

三、 实现的方式

程序采用以状态机为主体的设计,状态机的两种状态:

State = 1'd0 :空闲状态;

State = 1'd1 : 计数状态。

初始状态为空闲状态,计数器不进行计数;

任意时刻按下复位键可将状态机复位至空闲状态,且计数器清零;

初始状态下,按下开始键,可将状态机调整至计数状态,计数器从当前计数值开始计数,且计数到9.9时自动恢复为0.0持续计数;

计数状态下,按下停止键,可将状态机调整至空闲状态,计数器保持当前计数值;

状态机空闲状态下,可通过增量键以计数值+0.1s/次的步进对计数器进行计数值累加;

对“增量”、“开始”、“结束”输入进行下降沿抓取处理,保证按键效果与按键时长无关。

使用GPT工具协助工程的设计:

 

注:GPT给出的建议以及示例代码还需仿真验证及修改调试,最终表现为本实验报告中最终展示的版本。

四、 功能框图

 

五、 代码及说明

注:此处说明使用中文,附件中代码注释按要求使用英文。

1. 顶层模块

module time_counter
(
    clk             ,
    rst             ,
    increment       ,
    start           ,
    stop            ,
    seg_led_1       ,
    seg_led_2
);

    input           clk;                //输入时钟
    input           rst;                //输入复位
    input           increment;          //输入增量
    input           start;              //输入开始
    input           stop;               //输入结束
    output  [8:0]   seg_led_1;          //输出数码管十位
output  [8:0]   seg_led_2;          //输出数码管个位

    wire            clk10hz;            //经分频模块输出10Hz时钟,对应计数器分辨率0.1s
    reg     [8:0]   seg     [9:0];
    reg     [3:0]   cnt_ge;             //个位
    reg     [3:0]   cnt_shi;            //十位
    reg     [1:0]   shiftstart;         //上升沿检测
    reg     [1:0]   shiftstop;          //上升沿检测
    reg     [1:0]   shiftincrement;     //上升沿检测
    reg             state;              //状态机

    // 例化分频器模块,产生一个10Hz时钟信号    
    IntegerDivider #(.WIDTH(32),.N(1200000))
    IntegerDivider_inst
    (
        .clk(clk),
        .rst_n(rst),
        .clkout(clk10hz)
    );

    // 检测信号上升沿
    always @(posedge clk10hz or negedge rst) begin
        if(!rst == 1) begin
            shiftstart <= 2'b11;
            shiftstop <= 2'b11;
            shiftincrement <= 2'b11;
        end
        else begin
            shiftstart <= {shiftstart[0], start};
            shiftstop <= {shiftstop[0], stop};
            shiftincrement <= {shiftincrement[0], increment};
        end
    end

    // 状态机
    always @ (posedge clk10hz or negedge rst) begin
    if(!rst == 1) begin
        state <= 0;
        cnt_ge <= 4'd0;
        cnt_shi <= 4'd0;
    end
    else begin
        case(state)
            //IDLE 空闲状态
            1'd0 : begin
                if(shiftincrement == 2'b10) begin
                    if(cnt_shi==9 && cnt_ge==9) begin
                        cnt_shi <= 0;
                        cnt_ge <= 0;
                    end
                    else if(cnt_ge==9) begin
                        cnt_shi <= cnt_shi + 1;
                        cnt_ge <= 0;
                    end
                    else begin
                        cnt_ge <= cnt_ge + 1;
                    end
                end
                else if(shiftstart == 2'b10) begin
                    state <= 1'd1;
                end
                else begin
                    cnt_shi <= cnt_shi;
                    cnt_ge <= cnt_ge;
                end
            end
            //COUNTER 计数状态
            1'd1 : begin
                if(shiftstop == 2'b10) begin
                    state <= 1'd0;
                end
                else begin
                    if(cnt_shi==9 && cnt_ge==9) begin
                        cnt_shi <= 0;
                        cnt_ge <= 0;
                    end
                    else if(cnt_ge==9) begin
                        cnt_shi <= cnt_shi + 1;
                        cnt_ge <= 0;
                    end
                    else begin
                        cnt_ge <= cnt_ge + 1;
                    end
                end
            end
            default : state <= 0;
        endcase
    end
end

    //数码管输出映射
    initial
    begin
        seg[0] = 9'h3f;    //  0
        seg[1] = 9'h06;    //  1
        seg[2] = 9'h5b;    //  2
        seg[3] = 9'h4f;    //  3
        seg[4] = 9'h66;    //  4
        seg[5] = 9'h6d;    //  5
        seg[6] = 9'h7d;    //  6
        seg[7] = 9'h07;    //  7
        seg[8] = 9'h7f;    //  8
        seg[9] = 9'h6f;    //  9
    end

    //输出数码管十位,使能小数点DP
    assign seg_led_1 = seg[cnt_shi] + 9'b10000000;
    //输出数码管个位
    assign seg_led_2 = seg[cnt_ge];

endmodule

2.  时钟分频模块

module IntegerDivider ( clk,rst_n,clkout);
        input   clk,rst_n;
//输入信号,其中clk连接到FPGA的C1脚,频率为12MHz
        output  clkout;
//输出信号,可以连接到LED观察分频的时钟
        //parameter是verilog里常数语句

parameter   WIDTH   = 3;
//计数器的位数,计数的最大值为 2**WIDTH-1
parameter   N   = 5;
//分频系数,请确保 N < 2**WIDTH-1,否则计数会溢出

reg     [WIDTH-1:0] cnt_p,cnt_n;
//cnt_p为上升沿触发时的计数器,cnt_n为下降沿触发时的计数器
reg         clk_p,clk_n;
//clk_p为上升沿触发时分频时钟,clk_n为下降沿触发时分频时钟

    //上升沿触发时计数器的控制
always @ (posedge clk or negedge rst_n )
//posedge和negedge是verilog表示信号上升沿和下降沿
//当clk上升沿来临或者rst_n变低的时候执行一次always里的语句
        begin
            if(!rst_n)
                cnt_p<=0;
            else if (cnt_p==(N-1))
                cnt_p<=0;
            else cnt_p<=cnt_p+1;            
//计数器一直计数,当计数到N-1的时候清零,这是一个模N的计数器
        end

//上升沿触发的分频时钟输出,如果N为奇数得到的时钟占空比不是50%;如果N为偶数得到的时钟占空比为50%
         always @ (posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                clk_p<=0;
            else if (cnt_p<(N>>1))          
//N>>1表示右移一位,相当于除以2去掉余数
                clk_p<=0;
            else
                clk_p<=1;              
//得到的分频时钟正周期比负周期多一个clk时钟
        end

        //下降沿触发时计数器的控制          
    always @ (negedge clk or negedge rst_n)
        begin
            if(!rst_n)
                cnt_n<=0;
            else if (cnt_n==(N-1))
                cnt_n<=0;
            else cnt_n<=cnt_n+1;
        end

        //下降沿触发的分频时钟输出,和clk_p相差半个时钟
    always @ (negedge clk)
        begin
            if(!rst_n)
                clk_n<=0;
            else if (cnt_n<(N>>1))  
                clk_n<=0;
            else
                clk_n<=1;                
//得到的分频时钟正周期比负周期多一个clk时钟
        end

        assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;      
//条件判断表达式
//当N=1时,直接输出clk
//当N为偶数也就是N的最低位为0,N(0)=0,输出clk_p
//当N为奇数也就是N最低位为1,N(0)=1,输出clk_p&clk_n。正周期多所以是相与

endmodule

六、 仿真波形图

1. 时钟分频模块仿真文件代码:

module tb_IntegerDivider(
);
    reg clk;        // 输入时钟信号
    reg rst_n;      // 复位信号(低有效)
    wire clkout;     // 输出分频后的时钟信号
    initial clk = 1;
    always #4 clk = ~clk;

    initial begin
    rst_n = 1;
    #40
    rst_n = 0;
    #40
    rst_n = 1;
    end

IntegerDivider #
(
    .your_divisor(12)
)

IntegerDivider_inst
(
    .clk(clk),
    .rst_n(rst_n),
    .clkout(clkout)
);

endmodule

2. 时钟分频模块仿真波形

分频系数=3

 

分频系数=10

 

3. 顶层模块仿真代码

module tb_time_counter(
);
    reg           clk;                //输入时钟
    reg           rst;                //输入复位
    reg           increment;          //输入增量
    reg           start;              //输入开始
    reg           stop;               //输入结束
    wire    [3:0]   mark_cnt_ge;
    wire    [3:0]   mark_cnt_shi;
    wire            mark_state;
    initial clk = 1;
    always #4 clk = ~clk;

    initial begin
    rst = 1;
    #40
    rst = 0;
    #40
    rst = 1;
    increment = 1;
    start = 1;
    stop = 1;
    #40
    increment = 0;
    #40
    increment = 1;
    #40
    increment = 0;    
    #40
    increment = 1;
    #40
    start = 0;
    #1200
    stop = 0;
    end

time_counter time_counter_inst
(
    .clk(clk),
    .rst(rst),
    .increment(increment),
    .start(start),
    .stop(stop),
    .seg_led_1(),
    .seg_led_2(),
    .mark_cnt_ge(mark_cnt_ge),
    .mark_cnt_shi(mark_cnt_shi),
    .mark_state(mark_state)
);

endmodule

注:对于顶层模块仿真时,为方便抓取波形(原分频系数为1200000,仿真波形不容易观察),顶层模块未调用时钟分频模块,而是直接使用仿真输入时钟clk;为方便观察计数原理是否正确,引出计数器mark_cnt_ge和mark_cnt_shi作为输出,取代原七段数码管显示输出。基于以上原因,进行顶层文件仿真时对其进行部分修改:

 

4.  顶层模块仿真波形

 

七、 FPGA的资源利用说明

 

八、 遇到的主要难题及解决方法

GPT作为该实验推荐的工具,在实际使用中,并不能完全符合实验要求,也无法直接通过仿真测试及上板验证,还是需要加深自己本身对于任务的理解,借助GPT提供的思路,梳理出模块架构,不断仿真调试、不断修改优化,直至成功完成试验任务。

九、 未来的计划或建议等

作为硬件FPGA逻辑新手,2024寒假在家练项目确实对我逻辑开发的能力大有裨益,在不断调试的过程中,我的经验值不断增加,期待今后在小脚丫STEP-MXO2-LPC开发板上探索更多可能。

十、 程序附件清单

1. time_counter.v

2. IntegerDivider.v

3. time_counter_for_tb.v

4. tb_time_counter.v

5. tb_IntegerDivider.v

6. implement .jed

十一、 对照成绩构成自评注释 

 

十二、实验成功实物照片

 

附件下载
time_counter.v
IntegerDivider.v
time_counter_for_tb.v
tb_IntegerDivider.v
tb_time_counter.v
implement .jed
团队介绍
团队只包含陈奕霏个人。 北京理工大学20级本科生陈奕霏,就读于电子信息实验班。
团队成员
Esther
评论
0 / 100
查看更多
猜你喜欢
2024年寒假练-基于Lattice MXO2的小脚丫FPGA核心板设计具有启动,停止,递增和清除功能的秒表该项目使用了WebIDE环境,Verilog语言,基于Lattice MXO2的小脚丫FPGA核心板,实现了一个具有启动,停止,递增和清除功能的秒表的设计,它的主要功能为:0.0s到9.9s的一个秒表。可通过按键控制其实现正向计时,暂停计时,计时递增一次,清零功能,并通过板载数码管显示出来。。
胡道扬
342
2024年寒假练-基于Lattice MXO2的小脚丫FPGA核心板设计具有启动、停止、递增和清除功能的秒表该项目使用了Lattice MXO2的小脚丫FPGA核心板,实现了秒表的设计,它的主要功能为:启动、停止、递增和清除功能。
水波不兴
236
2024年寒假练 -基于Lattice MXO2的小脚丫FPGA核心板 -具有启动、停止、递增和清除功能的秒表该项目使用了Lattice MXO2的小脚丫FPGA核心板,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:通过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。 秒表使用四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。。
zzkuner
290
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号