官方论坛
官方淘宝
官方博客
微信公众号
点击联系吴工 点击联系周老师

3.2 4位闪烁灯设计--明德扬科教(mdy-edu.com)

发布时间:2021-08-22   作者:admin 浏览量:

本文的文档编号:000800000014

1、讲解了如何使用至简设计法来设计实现4位LED灯循环闪烁的功能,具体功能位:间隔1秒,第N盏LED灯亮N秒(其中,N=1.2.3.4)的功能

需要看对应的视频,请点击视频编号:000800000045


篇 FPGA至简设计项目实践


   第二章  4位闪烁灯设计


第1节 项目背景

LED灯的相关理论以及教学板的原理图已经在第一个案例《1位闪烁灯》中进行了比较详细的讲解,这里就不再赘述,如果有需要可以返回上一章进行阅读学习。

第2节 设计目标

开始进行新的设计之前,依旧先要明确本设计的功能目标,这也是本书以及至简设计法的特别之处。通过对上一个案例的学习领悟也可以意识到明确设计目标的重要性,后续设计的每一个步骤都应该根据功能目标有进行针对性的逐步展开。至简设计法的这一方法让每一段代码每一个步骤都有其相应作用。反之,在设计中如果没有明确设计目标,而是按照自己的想法东一锤子、西一榔头,可能最终要花费成倍的时间才能做出同样的成果。所以再次强调,在设计目标确立这一步骤一定不要偷懒,要认真思考一下本次设计最终目标是什么,希望达到什么效果,明确目标后再进行设计实操才能起到事半功倍的效果。

本工程使用4LED灯(LED1~LED4)来实现闪烁灯的功能。具体功能要求为:首先LED11秒,接着亮1秒后变暗;接着LED21秒,亮2秒后变暗;随后LED31秒,亮3秒后变暗;最后LED41秒,亮4秒后变暗;至此一个循环结束,随后按照此规律进行循环往复。也就是说,四个LED灯依次循环闪烁,具体闪烁要求为:隔1秒,亮N秒,N的变化为:1234秒,然后再次进入循环。闪烁灯变化功能图如3.2-1所示,上板后的效果展示可参见3.2-2。可以登录至简设计法官方网站观看上板后的演示视频:old.mdy-edu.com/xxxx
                             

图3.2-14位闪烁灯功能图
   
图3.2-24位闪烁灯效果图
第3节 设计实现

与上个案例相同,下面会详细的按照步骤和思考逻辑进行工程的逐步分析。如果知识掌握比较牢靠、只想练习设计工程,可以跳过此部分,直接进入第五节“简化版步骤分享”进行实操练习。依旧建议初学者不要选择捷径,按照详细思路一步步学习分析打好基础,在掌握思想原理之后,再按照操作步骤反复进行实践。

3.1 顶层信号

新建目录:D:mdy_bookhuxiled,并在该目录中,新建一个名为huxiled.v的文件。用GVIM打开该文件后开始编写代码。这里再强调一下,建议初学者按照本书提供的文件路径以及文件名进行设置,避免不必要的错误。

首先应确定顶层信号。由设计目标可知控制相应LED灯亮或灭需要FPGA产生一个信号并将此信号传送至LED灯。想控制LED灯灭,则令FPGA输出信号为1,想控制LED灯亮,则令FPGA输出信号为0。根据目标可知需要分别控制4LED灯亮或灭,对应地需要FPGA产生4个信号。假定这4个信号分别为led0led1led2led3,将其分别与相应LED灯连接。操作中如果想控制LED0亮,LED1~3灭,则应控制FPGA输出的led0信号为0led1~3信号都为1

硬件电路图的连接关系如下表所示,可以看到本工程中LED0连接的FPGA管脚为AA4,对应FPGA工程信号为led0LED1连接的FPGA管脚为AB4,对应FPGA工程信号为led1LED2连接的FPGA管脚为AA5,对应FPGA工程信号为led2LED3灯连接的FPGA管脚为AB8,对应的FPGA工程信号为led3。工程的时钟管脚为G1,对应FPGA工程信号为clk,复位管脚为AB12,对应FPGA工程信号为rst_n。由此可见,本工程一共需要六个信号:时钟信号clk、复位信号rst_n以及四个LED灯的控制信号led0led1led2led3

3.2-1信号和管脚关系
  
器件
  
原理图信号
FPGA管脚
FPGA工程信号
  
LED0
  
LED1_NET
AA4
led0
  
LED1
  
LED2_NET
AB4
led1
  
LED2
  
LED3_NET
AA5
led2
  
LED3
  
LED4_NET
AB8
led3
  
X1
  
SYS_CLK
G1
clk
  
K1
  
SYS_RST
AB12
rst_n

基于此可以完成顶层信号代码的编写,将module的名称定义为huxiled。在顶层信号代码中需要将与外部相连接的输入/输出信号列出,从而实现信号与管脚的连接,其具体代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
module huxiled(
  
clk  ,
  
rst_n  ,
  
    led0    ,
  
    led1    ,
  
    led2    ,
  
     led3
  
     );

随后对信号的输入输出属性进行声明,指出对于FPGA来说这一信号属于输入还是输出,若为输入声明则为input,若为输出声明则为output。在本设计中由于clk是外部晶振输送给FPGA的,因此对于FPGA来说clk为输入信号input。同样地,rst_n是外部按键给FPGA的,在FPGA中也为输入信号input。同时可知led0led1led2led3四个信号是FPGA产生以实现对相应四个LED灯的控制,因此led0led1led2led3为输出信号output。根据这些信息将输入输出端口定义补充完整,其代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
input               clk  ;
  
input               rst_n  ;
  
  
output              led0   ;
  
output              led1   ;
  
output              led2   ;
  
output              led3   ;
3.2 信号设计

在进行架构设计前再次分析一下功能需求,首先 LED11秒,亮1秒变暗;接着LED21秒,亮2秒后变暗;随后LED31秒,亮3秒后变暗;最后LED41秒,亮4秒后变暗,如此循环往复。分析需求可以看出:当一个LED灯亮时,其余LED灯都属于灭的状态。如果将四个LED灯看成一个整体循环,可以把功能需求翻译成:LED0复位后,灭1秒,亮1秒后灭12秒;LED1复位后,灭3秒,亮2秒后灭9秒;LED2复位后,灭6秒,亮3秒后灭5秒;LED3,灭10秒后亮4秒,四个LED灯以此为规律循环往复。

下面将以上逻辑翻译成信号来进行理解:
复位后,信号led0=1持续1秒,接着让led0=0持续1秒,然后让led0=1持续12秒,循环往复。
复位后,信号led1=1持续3秒,接着让led1=0持续2秒,然后让led1=1持续9秒,循环往复。
复位后,信号led2=1持续6秒,接着让led2=0持续3秒,然后让led2=1持续5秒,循环往复。
复位后,信号led3=1持续10秒,接着让led3=0持续4秒,循环往复。
以此为规律的信号波形图如下图所示。
图3.2-34位闪烁灯波形图

从波形图中可看出信号led0~led3的变化单位最小是1秒,并且4个信号都是以14秒为一个周期进行循环往复。这样的情况下,至简设计法的思路是使用2个计数器,其中1个计数器用来确定1秒的时间,另1个计数器用来计1秒的次数从而确定一个循环即14秒的时间。这两个计数器的使用为led0~led3的变化时间确定了标准。

本设计中将计算1秒次数的计数器命名为cnt0。至简设计法的设计规则中有讲过,计数器的设计只考虑两个因素:加1条件和计数数量。只要确定好相应逻辑,就能完成计数器代码设计。本工程的工作时钟是50MHz,时钟周期即20ns,当计数器计数到1_000_000_000/20=50_000_000个的时候就意味着1秒时间计时结束。该计数器始终不停地进行计数,因此可以认为其加1条件是一直有效的,可写成:assignadd_cnt0==1

可能会有同学提出疑问:加1条件的概念是什么?这里以停车位来进行比喻,一般情况下对每个停车位置会进行对应编号,但是如果某个位置上放置了一块石头无法作为停车位时,该位置就不能获得对应的编号。反之则可以认为停车位编号的加1条件就是:对应位置上没有石头,其可以继续进行编号,即assignadd_cnt0 = “没有石头。因此如果在设计中计数器一直没有阻碍地进行计数工作,则可以认为加1条件是一直有效的。

以往进行代码编写这一步骤时都是一行行的输入相应代码。至简设计法在这里提供一个小技巧,在节省编写代码的时间的同时在一定程度上降低了代码的出错率。至简设计法将日常代码中常用到的固定部分制作成模板,进行代码编程时可以调用相应模板后根据逻辑输入对应设计的变量将代码补充完整。此处就可以用模板编写计数器代码,下面带领同学们一起来学习一下这个炫酷的功能。

打开GVIM工具,在命令模式下输入“:Mdyjsq”,点击回车后调出了对应模板,如下图所示。之后再将本案例中的变量填到模板里面,就可以得到完整正确的计数器代码。

3.2-4至简设计法调用计数器代码模板

按照上文方法调出计数器模板后,将本案例中的变量填到模板里面,得到计数器cnt0的完整代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
always @(posedge clk or negedge rst_n)begin
  
     if(!rst_n)begin
  
         cnt0 <= 0;
  
     end
  
     else if(add_cnt0)begin
  
         if(end_cnt0)
  
            cnt0 <= 0;
  
         else
  
            cnt0 <= cnt0 + 1;
  
     end
  
end
  
  
assign add_cnt0 = 1 ;
  
assign end_cnt0 = add_cnt0 &&  cnt0== 50_000_000-1 ;

同样的,需要确定用于计时14秒计数器的加1条件和计数数量,将该计数器命名为cnt1。该计数器的值每隔1秒进行加1操作,因此其加一条件为cnt0结束,用代码表示即为end_cnt0。根据设计目标可知计数器cnt1用于确定周期时间,闪烁灯的一个周期是14秒,因此该计数器的计数数量应为14
此处可以继续调用代码模板,在GVIM的命令模式下输入“:Mdyjsq”,点击回车调出计数器模板后将“add_cnt1”和“end_cnt1”补充完整,得到代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
always @(posedge clk or negedge rst_n)begin  
  
     if(!rst_n)begin
  
         cnt1 <= 0;
  
     end
  
     else if(add_cnt1)begin
  
         if(end_cnt1)
  
            cnt1 <= 0;
  
         else
  
            cnt1 <= cnt1 + 1;
  
     end
  
end
  
  
assign add_cnt1 = end_cnt0;
  
assign end_cnt1 = add_cnt1 &&  cnt1==14-1 ;

确定好两个计数器的代码后请同学们思考一下四个led输出信号的变化。根据设计目标可知,LED灯具有两种状态:亮和灭。对应地,四个led输出信号也两个变化点:变0和变1。下面依次对四个led信号变化的原因进行判断,可以结合3.2-3中的波形进行思考。首先来确定led0,根据设计目标可以发现:led00的原因是计数器完成了1秒计时,即当add_cnt1 && cnt1==1-1时,时间过去了一秒,led0的值变为0来控制LED0点亮。而led01的原因则是计数器完成2秒时间计时,即add_cnt1 && cnt1==2-1时,led0的值变为1从而控制LED0熄灭。在GVIM的编辑模式下输入“Shixu2”回车,调用至简设计法模板,将代码补充完整后得到led0信号的代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
         led0 <= 1 ;
  
     end
  
     else if(add_cnt1 && cnt1==1-1)begin
  
         led0 <= 0 ;
  
     end
  
     else if(add_cnt1 && cnt1==2-1)begin
  
         led0 <= 1 ;
  
     end
  
end

下面来思考输出信号led1的变化原因。同理,led1的值变0的原因是计数到3秒时间,即add_cnt1 &&cnt1==3-1时,led1的值变为0从而控制LED1点亮。led11的原因则是计数到5秒时间,即add_cnt1 &&cnt1==5-1时,led1的值变为1从而控制LED1熄灭。打开GVIM,在编辑模式下输入“Shixu2”回车后调用至简设计法模板,将代码补充完整后得到led1信号的代码如下所示:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
         led1 <= 1 ;
  
     end
  
     else if(add_cnt1 && cnt1==3-1)begin
  
         led1 <= 0 ;
  
     end
  
     else if(add_cnt1 && cnt1==5-1)begin
  
         led1 <= 1 ;
  
     end
  
end

同样的逻辑来思考输出信号led2的变化。led20的原因是计数到6秒时间,也就是add_cnt1 &&cnt1==6-1时,led2的值变为0从而控制LED2点亮。led21的原因则是数到9秒时间,即add_cnt1 &&cnt1==9-1时,led2的值变为1从而控制LED2熄灭。打开GVIM,在编辑模式下输入“Shixu2”回车,调用至简设计法模板,将代码补充完整后得到led2信号的代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
         led2 <= 1 ;
  
     end
  
     else if(add_cnt1 && cnt1==6-1)begin
  
         led2 <= 0 ;
  
     end
  
     else if(add_cnt1 && cnt1==9-1)begin
  
         led2 <= 1 ;
  
     end
  
end

最后确定输出信号led3的变化。led30的原因是计数到10秒时间,也就是add_cnt1 &&cnt1==10-1时,led2的值变为0从而控制LED2点亮。变1的原因是计数到14秒时间,即add_cnt1 && cnt1==14-1,也就是end_cnt1时,led3的值变为1从而控制LED2熄灭。在GVIM的编辑模式下输入“Shixu2”回车,调用至简设计法模板,将代码补充完整后得到led0信号的代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
         led3 <= 1 ;
  
     end
  
     else if(add_cnt1 && cnt1==10-1)begin
  
         led3 <= 0 ;
  
     end
  
     else if(end_cnt1)begin
  
         led3 <= 1 ;
  
     end
  
end

至此,主体程序已经完成。如果对四个输出信号代码的设计还存有疑虑,可以结合波形图再次着重了解一下设计目标,重新整理思路,完全吸收设计目标后再进行代码编写。反复领会吸收一定会有所收获。

3.3 信号定义

下面需要将module补充完整,首先要做的是定义信号的类型。在这里再次强调,在进行regwire类型判断的时候,总会有多余的联想,比如认为reg就是寄存器,wire是线;或者认为reg类型会综合成寄存器,wire类型不会综合成寄存器。实际上这些与信号是reg型还是wire型都没有关系。至简设计法建议不要进行任何联想,只遵从一个规则:“用always实现的是reg型,其他都是wire型”。

进行信号定义时,除了信号的类型还需要确定信号的位宽。至简设计法在这里分享一个非常实用的获取信号位宽的技巧:打开计算器,点击“查看”,选择“程序员”模式,在“十进制”下输入数字后就会获得对应的信号位宽。

3.2-5通过计算器获取信号位宽

cnt0是用always产生的信号,因此类型为regcnt0计数的最大值为50_000_000。如3.2-5所示,通过计算器可以得知该信号需要用26根线表示,即位宽是26位。代码表示如下:
1
reg     [25:0]  cnt0  ;

同样的,cnt1也是用always产生的信号,因此类型为regcnt1计数的最大值为8,需要用4根线表示,即位宽是4位。编辑状态下输入“Reg4”调用至简设计法模板,补充完整后得到代码表示如下:
1
reg     [ 3:0]  cnt1  ;

add_cnt0end_cnt0都是用assign方式设计的,因此类型为wire。其值是0或者1,用1根线表示即可,即位宽为1。编辑模式下输入“Wire1”调用模板,补充完整后得到代码表示如下:
1
  
2
wire      add_cnt0  ;
  
wire      end_cnt0  ;

add_cnt1end_cnt1也是用assign方式设计的,因此类型为wire。其值是0或者1,用1根线表示即可,即位宽为1。编辑模式下输入“Wire1”调用模板,补充完整后得到代码表示如下:
1
  
2
wire      add_cnt1  ;
  
wire      end_cnt1  ;

led0led1led2led3是用always方式设计的,因此类型为reg。其值是0或者1,用1根线表示即可。编辑模式下输入“Reg1”调用模板,补充完整后得到代码表示如下:
1
  
2
  
3
  
4
reg         led0     ;
  
reg         led1     ;
  
reg         led2     ;
  
reg         led3     ;
整个代码的设计工作至此已经完成,完整的工程代码如下所示:
1
module huxiled(
  
clk  ,
  
rst_n  ,
  
      led0        ,
  
      led1        ,
  
      led2        ,
  
      led3           
  
);
  
  
input        clk   ;
  
input        rst_n   ;
  
output       led0     ;
  
output       led1     ;
  
output       led2     ;
  
output       led3     ;
  
  
reg   [25:0]  cnt0    ;
  
wire         add_cnt0;
  
wire         end_cnt0;
  
  
reg   [ 3:0]  cnt1    ;
  
wire         add_cnt1;
  
wire         end_cnt1;
  
reg          led0    ;
  
reg          led1    ;
  
reg          led2    ;
  
reg          led3    ;
  
  
  
always @(posedge clk or negedge rst_n)begin
  
     if(!rst_n)begin
  
         cnt0 <= 0;
  
     end
  
     else if(add_cnt0)begin
  
         if(end_cnt0)
  
            cnt0 <= 0;
  
         else
  
            cnt0 <= cnt0 + 1;
  
     end
  
end
  
  
assign add_cnt0 = 1;
  
assign end_cnt0 = add_cnt0 &&  cnt0==50_000_000 - 1 ;
  
  
always @(posedge clk or negedge rst_n)begin  
  
     if(!rst_n)begin
  
         cnt1 <= 0;
  
     end
  
     else if(add_cnt1)begin
  
         if(end_cnt1)
  
            cnt1 <= 0;
  
         else
  
            cnt1 <= cnt1 + 1;
  
    end
  
end
  
  
assign add_cnt1 = end_cnt0;
  
assign end_cnt1 = add_cnt1 &&  cnt1==14-1 ;
  
  
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
         led0 <= 1;
  
     end
  
     else if(add_cnt1 && cnt1==1-1)begin
  
         led0 <= 0;
  
     end
  
     else if(add_cnt1 && cnt1==2-1)begin
  
         led0 <= 1;
  
     end
  
end
  
  
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
         led1 <= 1;
  
     end
  
     else if(add_cnt1 && cnt1==3-1)begin
  
         led1 <= 0;
  
     end
  
     else if(add_cnt1 && cnt1==5-1)begin
  
         led1 <= 1;
  
     end
  
end
  
  
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
         led2 <= 1;
  
     end
  
     else if(add_cnt1 && cnt1==6-1)begin
  
         led2 <= 0;
  
     end
  
     else if(add_cnt1 && cnt1==9-1)begin
  
         led2 <= 1;
  
     end
  
end
  
  
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
         led3 <= 1;
  
     end
  
     else if(add_cnt1 && cnt1==10-1)begin
  
         led3 <= 0;
  
     end
  
     else if(end_cnt1)begin
  
         led3 <= 1;
  
     end
  
end
  
  
endmodule
第4节 综合与上板

4.1 新建工程

打开软件Quartus Ⅱ,点击“File”下拉列表中的New Project Wzard...新建工程选项,如下图所示。
图3.2-6Quartus新建工程

随后会出现图3.2-7所示的Quartus新建工程介绍,直接点击“Next”:
图3.2-7Quartus新建工程介绍

此时出现的是工程文件夹、工程名、顶层模块名设置界面,如图3.2-8所示。注意目录为:D:/mdy_book/huxiled,工程名和顶层名为huxiled。这里再次进行强调,为了避免初学者使用过程中出现报错情况,强烈建议按照本书的工程名和文件名进行设置,设置完成后点击Next。
图3.2-8QUARTUS新建工程设置名称

新建工程类型设置选择“Empty project”,如下图所示,然后点击“Next”。
图3.2-9QUARTUS新建工程类型

文件添加界面如图3.2-10所示,点击右侧的“Add”按钮,选择已经写好的“huxiled.v”文件,此时文件会在下方显示出来,随后点击“Next”。
图3.2-10QUARTUS添加文件

芯片型号选择界面如图3.2-11所示,选择“Cyclone ⅣE”,在芯片型号选择处选择“EP4CE15F23C8”,之后点击“Next”。
图3.2-11QUARTUS选择芯片型号

图3.2-12为QUARTUS设置工具界面,不必做任何修改,直接点击“Next”即可。
图3.2-12QUARTUS设置工具界面

新建工程的汇总情况如图3.2-13所示,点击“Finish”完成新建工程。
图3.2-13QUARTUS新建工程汇总界面

4.2 综合

新建工程步骤完成后,QUARTUS界面如下图所示。
图3.2-14QUARTUS新建工程后界面
点击编译按钮,可以对整个工程进行编译,编译成功后的界面如图3.2-15所示。
图3.2-15QUARTUS编译后界面

4.3 配置管脚

下面需要对相应管脚进行配置。如图3.2-16所示,在菜单栏中选中Assignments后选择Pin Planner,随后配置管脚的窗口就会弹出。
图3.2-16QUARTUS配置管脚选项

在配置窗口最下方中的“location”一列,参考信号和管脚关系,按照表3.2-1中最右两列配置好FPGA管脚。此处配置管理来源的选择在最开始的管脚配置设计环节中进行了讲解,最终配置的结果见图3.2-17。配置完成后,关闭“Pin Planner”,软件自动会保存管脚配置信息。
表3.2-1信号和管脚关系
  
器件
  
原理图信号
FPGA管脚
FPGA工程信号
  
LED0
  
LED1_NET
AA4
led0
  
LED1
  
LED2_NET
AB4
led1
  
LED2
  
LED3_NET
AA5
led2
  
LED3
  
LED4_NET
AB8
led3
  
X1
  
SYS_CLK
G1
clk
  
K1
  
SYS_RST
AB12
rst_n
图3.2-17QUARTUS配置管脚

4.4 再次综合

再次打开“QUARTUS”软件,在菜单栏中选中“Processing”,然后选择“Start Compilation”,再次对整个工程进行编译和综合,如下图所示。
图3.2-18QUARTUS编译选项

当出现如下图所示的编译成功标志时,说明已经编译综合成功。
图3.2-19QUARTUS编译成功标志
4.5 连接开发板

完成编译后开始进行上板调试操作,按照下图的方式将下载器接入电脑USB接口,接上开发板电源后按下开发板下方蓝色开关,硬件连接完毕。
图3.2-204位闪烁灯开发板连接图
4.6 上板

如下图所示,单击QUARTUS界面中的 ,弹出配置界面。
图3.2-21QUARTUS界面
随后点击“add file”添加“.sof”文件,点击“Start”,在“Progress”中会显示出进度。
图3.2-22QUARTUS下载程序界面

如图3.2-22所示,当“Progress”进度条中提示成功即可在开发板上观察到相应的现象。如果按照步骤正确完成了设计,此时会看到四个LED灯轮流开始闪烁,且每次持续的时间按照增加一秒的规律,四个LED灯依次闪烁循环往复,若观察到这一现象则可以判断此次设计成功。如果无法正常显示或者未按设计闪烁的情况,则需要回过头来进行排查错误。如果反复思考后依然无法找到错误,建议静下心来重新操作一遍,熟能生巧,多次实践后自然而然就可以迅速发现问题并且解决问题。

第5节 简化版步骤分享

同样这里也会分享简化版的步骤,方便复习以及反复实操。

5.1 设计实现

5.1.1 顶层信号

新建目录:D:mdy_bookhuxiled。在该目录中,新建一个名为“huxiled.v”的文件,并用GVIM打开,开始编写代码。
确定顶层信号。工程信号和管脚关系如下图:
表3.2-1信号和管脚关系
  
器件
  
原理图信号
FPGA管脚
FPGA工程信号
  
LED0
  
LED1_NET
AA4
led0
  
LED1
  
LED2_NET
AB4
led1
  
LED2
  
LED3_NET
AA5
led2
  
LED3
  
LED4_NET
AB8
led3
  
X1
  
SYS_CLK
G1
clk
  
K1
  
SYS_RST
AB12
rst_n
写出顶层信号代码:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
module  huxiled(
  
clk  ,
  
rst_n  ,
  
    led0    ,
  
    led1    ,
  
    led2    ,
  
    led3
  
    );

声明输入输出属性:
1
  
2
  
3
  
4
  
5
  
6
  
7
input               clk  ;
  
input               rst_n  ;
  
  
output              led0   ;
  
output              led1   ;
  
output              led2   ;
  
output              led3   ;
5.1.2 信号设计
首先进行架构设计。根据设计需求得出波形图如下所示。
图3.2-34位闪烁灯波形图

设计计数器架构,表示计时1秒的计数器cnt0代码如下所示:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
always  @(posedge clk or negedge rst_n)begin
  
    if(!rst_n)begin
  
        cnt0 <= 0;
  
    end
  
    else if(add_cnt0)begin
  
        if(end_cnt0)
  
            cnt0 <= 0;
  
        else
  
            cnt0 <= cnt0 + 1;
  
    end
  
end
  
  
assign  add_cnt0 = 1 ;
  
assign  end_cnt0 = add_cnt0 && cnt0== 50_000_000-1 ;

表示计时一次循环(14秒)的计数器cnt1代码如下所示:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
always  @(posedge clk or negedge rst_n)begin
  
    if(!rst_n)begin
  
        cnt1 <= 0;
  
    end
  
    else if(add_cnt1)begin
  
        if(end_cnt1)
  
            cnt1 <= 0;
  
        else
  
            cnt1 <= cnt1 + 1;
  
    end
  
end
  
  
assign  add_cnt1 = end_cnt0;
  
assign  end_cnt1 = add_cnt1 && cnt1==14-1 ;

设计led0信号代码:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always  @(posedge clk or negedge rst_n)begin
  
    if(rst_n==1'b0)begin
  
        led0 <= 1 ;
  
    end
  
    else if(add_cnt1 &&  cnt1==1-1)begin
  
        led0 <= 0 ;
  
    end
  
    else if(add_cnt1 &&  cnt1==2-1)begin
  
        led0 <= 1 ;
  
    end
  
end

设计led1信号代码:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always  @(posedge clk or negedge rst_n)begin
  
    if(rst_n==1'b0)begin
  
        led1 <= 1 ;
  
    end
  
    else if(add_cnt1 &&  cnt1==3-1)begin
  
        led1 <= 0 ;
  
    end
  
    else if(add_cnt1 &&  cnt1==5-1)begin
  
        led1 <= 1 ;
  
    end
  
end

设计led2信号代码:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always  @(posedge clk or negedge rst_n)begin
  
    if(rst_n==1'b0)begin
  
        led2 <= 1 ;
  
    end
  
    else if(add_cnt1 &&  cnt1==6-1)begin
  
        led2 <= 0 ;
  
    end
  
    else if(add_cnt1 &&  cnt1==9-1)begin
  
        led2 <= 1 ;
  
    end
  
end

设计led3信号代码:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always  @(posedge clk or negedge rst_n)begin
  
    if(rst_n==1'b0)begin
  
        led3 <= 1 ;
  
    end
  
    else if(add_cnt1 &&  cnt1==10-1)begin
  
        led3 <= 0 ;
  
    end
  
    else if(end_cnt1)begin
  
        led3 <= 1 ;
  
    end
  
end
       主体程序完成后将module补充完整。

5.1.3 信号定义
对信号进行类型定义,其中cnt0 信号定义如下:
1
reg    [28:0]   cnt0  ;
add_cnt0和 end_cnt0 信号定义如下:
1
  
2
wire     add_cnt0   ;
  
wire     end_cnt0   ;
cnt1信号定义如下:
1
reg    [ 3:0]   cnt1  ;
add_cnt1和 end_cnt1 信号定义如下:
1
  
2
wire     add_cnt1   ;
  
wire     end_cnt1   ;
led0、led1、led2、led3信号定义如下:
1
  
2
  
3
  
4
reg         led0     ;
  
reg         led1     ;
  
reg         led2     ;
  
reg         led3     ;
至此,整个代码的设计工作完成,后续应对代码进行编译综合以及上板查看现象。

5.2 综合与上板

5.2.1 新建工程

打开Quartus Ⅱ,点击File下拉列表中的New Project Wzard...新建工程选项。
图3.2- 6Quartus新建工程
点击“Next”。
图3.2-7Quartus新建工程介绍
此时会出现工程文件夹、工程名、顶层模块名设置界面,完成设置后点击“Next”。
图3.2-8QUARTUS新建工程设置名称
选择“Empty project”后点击“Next”。

图3.2- 9QUARTUS新建工程类型
点击右侧的“Add”按钮后添加“huxiled.v”文件,点击“Next”。
图3.2-10QUARTUS添加文件
在“Device family”选项中选择“Cyclone ⅣE”,“Available devices”选项下选择“EP4CE15F23C8”,随后点击“Next”。
图3.2-11QUARTUS选择芯片型号
直接点击“Next”。
图3.2-12QUARTUS设置工具界面
点击“Finish”,完成新建工程。
图3.2-13QUARTUS新建工程汇总界面
5.2.2 综合

新建工程后界面如下图所示,点击“编译”按钮。
图3.2-14QUARTUS新建工程后界面
编译成功界面如下图所示。
图3.2-15QUARTUS编译后界面
5.2.3 配置管脚

在菜单栏点击“Assignments”后点击“Pin Planner”随后会弹出配置管脚的窗口。
图3.2-16QUARTUS配置管脚选项
在配置窗口“location”配置管脚,配置完成关闭“Pin Planner”即可自动保存配置信息。
图3.2-17QUARTUS配置管脚
5.2.4 再次综合

打开“QUARTUS”软件,在菜单栏中选择“Processing”,点击“Start Compilation”后再次进行综合。
图3.2-18QUARTUS编译选项
出现 QUARTUS 编译成功标志则表示此次编译成功。
图3.2-19QUARTUS编译成功标志
5.2.5 连接开发板

将下载器接入电脑 USB 接口,开发板接上电源后按下蓝色开关。
图3.2-204位闪烁灯开发板连接图
5.2.6 上板

打开 QUARTUS 界面后单击“  ”图标。
图3.2-21QUARTUS界面

点击“add file”添加.sof文件,随后点击“Start”。“Progress”会显示当前进度,当进度条显示“100%”为成功,可在开发板观察此时现象。
图3.2-22QUARTUS下载程序界面

第6节 扩展练习

至此整个4位闪烁灯设计就分享完毕了,这里也可以基于此例展开思考,可以自己尝试更改LED亮灯时间或者四个LED灯的亮灯顺序。尝试进行更多的设计,也欢迎有更好思路和想法的同学前往至简设计法论坛进行交流讨论。
   拓展阅读