明德扬周老师 发表于 2020-5-23 09:48:10

【FPGA至简设计原理与应用】书籍连载10 第三篇FPGA至简设计项目实践 第一章 1位闪烁灯

温馨提示:明德扬2023推出了全新课程——逻辑设计基本功修炼课,降低学习FPGA门槛的同时,增加了学习的趣味性,并组织了考试赢积分活动(点击→了解课程详情)http://www.mdy-edu.com/ffkc/415.html,感兴趣请联系易老师:13112063618(微信同步)


本案例的编号为:000800000012,如果有疑问,请按编号在下面贴子查找答案:MDY案例交流【汇总贴】_FPGA-明德扬科教 (mdy-edu.com)
本文为明德扬原创及录用文章,转载请注明出处!

大家好,近期我们会连载《FPGA至简设计原理与应用》一书,有兴趣的同学可以学习,也希望大家可以对我们的书提出宝贵的意见和建议。

《FPGA至简设计原理与应用》书籍连载索引目录
http://www.fpgabbs.cn/forum.php?mod=viewthread&tid=989


读过的朋友可积极在贴后留言,书籍正式出版时,我们会从留言者中挑选20位幸运读者,幸运读者可获潘老师亲笔签名书籍一本。

注:手机浏览可能格式会乱,建议用电脑端进行浏览。



第三篇 FPGA至简设计项目实践

   第一章1位闪烁灯设计


本文档编号:000800000013
需要看对应的视频,请点击视频编号:000800000162
1.讲解了如何使用至简设计法来时序1位LED灯间隔1秒,亮N秒(其中N=1 .2. …. 9)的功能



第1节 项目背景


    发光二极管(Light EmittingDiode,LED)是一种能够将电能转化为可见光的固态半导体器件,其核心是一个整个被环氧树脂封装起来的半导体晶片。晶片的一端附在一个支架上为负极,另一端连接电源为正极。
    半导体晶片由P型半导体和N型半导体两部分组成,其中P型半导体中空穴占主导地位,而N型半导体的主要部分是电子。当这两种半导体连接起来时,两者之间形成了一个P-N结。当电流经由导线作用于该半导体晶片时,自由电子从N型区扩散到P型区跟空穴复合,其会以光子的形式发出能量,这就是LED灯产生光源的原因。光的波长即为LED的灯光颜色,其由形成P-N结的材料决定,不同的材料可以使LED可以发出不同颜色的光。随着研究的深入,如今LED已经可以直接发出红、黄、蓝、绿、青、橙、紫、白色等多种颜色的光。
    LED起初被用作仪器仪表的指示光源,后来各种光色的LED在交通信号灯和大面积显示屏中得到了广泛应用并产生了很好的经济效益和社会效益。以12英寸的红色交通信号灯为例,美国原本使用的光源是长寿命、低光视效能的140瓦白炽灯,可产生2000流明的白光。但是经红色滤光片后,光损失率高达90%,只剩下200流明的红光。而Lumileds公司采用18个红色LED光源后发现:在产生同样光效的前提下,该类光源包括电路损失在内共耗电14瓦,与传统白炽光光源相比耗能减少了十倍,这一光源的使用为社会做出了巨大贡献。除此之外汽车信号灯也是LED光源的重要应用领域之一。
    而在日常生活中人们往往更需要白色光源以满足照明需求,1998年基于这一需求成功开发了发白光的LED,这种LED将GaN芯片和钇铝石榴石(YAG)封装在一起做成。其中GaN芯片发蓝光(λp=465nm,Wd=30nm),高温烧结制成的含Ce3+的YAG荧光粉受此蓝光激发后发出黄色光射,其峰值为550nm。蓝光LED基片安装在碗形反射腔中,覆盖以混有约200-500nm的YAG树脂薄层。 LED基片发出的一部分蓝光被荧光粉吸收,另一部分蓝光与荧光粉发出的黄光混合,从而可以得到白光。对于InGaN/YAG白色LED,通过改变YAG荧光粉的化学组成和调节荧光粉层的厚度,可以获得色温3500-10000K的各色白光。这种通过蓝光LED得到白光的方法,构造简单、成本低廉、技术成熟度高,因此运用最为广泛。
    基于LED灯的原理,本书制作了1位闪烁灯工程,感兴趣的话可以根据步骤自己进行实操练习。
    从图3.1-1可以看到,至简设计法的教学板一共有8个可以发出绿光的LED灯。图中左边的LED1~LED8是教学板上LED灯的丝印,右边的LED1~LED8_NET为信号线名。此标注是为了更好的理解设计,而在实际的开发板中不显示这些信号线。
    LED灯两端分别连接了3.3V的高电平和LED1~LED8_NET信号线。当LED1~LED8_NET为高电平时,电流无法导通,LED灯不发光;反之,当LED1~LED8_NET是低电平时,电流流通,此时LED灯发光。因此只要控制了信号LED1~LED8_NET的电平情况,就相当于控制了LED灯发光的情况。
                              图3.1-1开发板LED原理图    那么信号线LED1~LED8_NET又连到哪里呢?从下图可以发现这些信号都连接到了FPGA的管脚上。
图3.1-2开发板LED信号连接图
    表3.1-1为开发板上实际信号管脚的对应关系,例如信号线LED1连接到了FPGA的AA4管脚上。从表中可以看到LED1~LED8_NET分别与FPGA的8个管脚相连,因而LED1~LED8_NET处于高低电平以及LED灯是否发光最终取决于FPGA管脚的输出。举例说明,如果想要控制LED7号灯,只需调整LED7连接的FPGA管脚AB14的输出就可以了。当输出为高电平时LED7灯灭,当输出为低电平,LED7灯亮。8个LED灯各自对应一个管脚,因此这8个LED灯都是由FPGA独立进行控制。
表3.1-1LED和FPGA管脚关系
教学板丝印信号线FPGA管脚
LED1LED1_NETAA4
LED2LED2_NETAB4
LED3LED3_NETAA5
LED4LED4_NETAB6
LED5LED5_NETAA10
LED6LED6_NETAB13
LED7LED7_NETAB14
LED8LED8_NETAB16
第2节 设计目标    确定设计的功能目标是至简设计法的一大特别之处,只有对设计的功能目标有一定的理解和预期,才能进一步地讨论如何进行代码设计和实现。也就是说,后续设计中每一个步骤都是围绕着设计目标的实现来针对性的展开。如果对于设计目标一知半解,连最终想要什么都无法确定,那么后续设计的思路就难以形成体系化的思考模式,只能一味接收碎片式的知识。设计目标就像大楼的设计图,如果想要掌握完整的案例设计,需要静下心来从明确设计目标开始一步步细细咀嚼,逐步掌握,才可以真正做到事半功倍。因此,在每个设计前本书会确定设计的功能目标,从而更好的完成设计。
    本工程使用1个LED灯(LED1)来实现闪烁灯功能。工程的工作时钟为50MHz,即时钟周期为20ns。当LED1连接的管脚AA4输出低电平时,LED1灯亮,输出高电平时,LED1灯灭。具体功能要求为:隔1秒,亮N秒。N的变化为:1,2,3,……,9秒,之后再次进入循环,如图3.1-3所示。上板后的效果展示可参见图3.1-4。可以登录至简设计法官方网站观看上板后的演示视频:www.mdy-edu.com/xxxx。 图3.1-3 1位闪烁灯功能图
图3.1-4 1位闪烁灯的效果图 第3节 设计实现
    下面会详细地介绍本设计的实现方法并对一些基本的设计理念和思路进行详细地说明。如果已经基本掌握至简设计法理念,且有一定独立实操基础,可以选择直接跳至第五节“简化版步骤分享”按照步骤进行设计实操。当然,还是建议初学者打好基础,按照分析一步步掌握思想原理后再按照操作步骤反复实践练习。
3.1 顶层信号
    新建目录:D:\mdy_book\my_led,并在此目录中新建一个名为my_led.v的文件。用GVIM打开该文件后开始编写代码。在这里建议初学者按照本书提供的路径名和文件名使用,不要进行其它更改。因为在更改过程中可能会出现中文路径、空格路径等非法路径的问题,或者有些文件名更改后会出现报错等现象,而初学者并不能很好地发现并解决这些问题。因此建议先按照要求更名保存,在多次进行工程练习,熟悉了各个步骤后,再进行更名的操作。这个地方不要想当然,按部就班地细心操作就会可以避免后续问题的出现。
    下面来确定顶层信号。由设计目标可知,想要控制1个LED灯亮或灭需要FPGA产生一个信号。假定这一信号为led,将其连接到LED灯上。如果要控制LED灯灭,FPGA将信号led输出为1,控制LED灯亮,FPGA将信号led输出为0。硬件电路图的连接关系如表3.1- 2所示,可以看到本工程中LED1灯连接的FPGA管脚为AA4,对应的FPGA工程信号为led。工程的时钟管脚为G1,对应FPGA工程信号为clk。工程的复位管脚为G1,对应FPGA工程信号为rst_n。由此可见,工程中一共需要三个信号:时钟clk,复位rst_n以及输出信号led。
表3.1-2信号和管脚关系
器件原理图信号FPGA管脚FPGA工程信号
LED1LED1_NETAA4led
X1SYS_CLKG1clk
K1SYS_RSTAB12rst_n

    将module的名称定义为my_led,在顶层信号代码中需要将与外部相连接的输入/输出信号列出,从而实现信号与管脚的连接,具体顶层信号代码如下:
12345module my_led(clk,rst_n ,   led   );
        随后对信号的输入输出属性进行声明,指出对于FPGA来说这一信号属于输入还是输出,若为输入,声明则为input,若为输出,声明则为output。在本设计中,由于clk是外部晶振输送给FPGA的,因此在FPGA中clk为输入信号input。同样地,rst_n是外部按键给FPGA的信号,因此在FPGA中rst_n也为输入信号input。同时可知led是FPGA输出控制LED1显示的,因此led为输出信号output。其具体声明代码如下:
123input               clk;input               rst_n;output            led    ;
3.2 信号设计


    随后需要对架构进行设计。首先将项目实现功能用波形图表示出来,详细的分析一下设计需求。LED灯的变化规律是暗1秒,亮N秒,其中N的变化为:1、2、3、……、9秒,然后再次从1秒开始循环。将此现象转换为信号,即为信号led=1持续1秒后信号led=0持续N秒,其中N的变化是:1、2、3、……、9秒,如图3.1-5所示。可以看出在第一次亮暗过程中,led=1持续暗了1秒后led=0持续亮了1秒,共计2秒时间。在第二次亮暗过程中,led=1持续暗了1秒后led=0持续亮2秒,共计3秒时间。以此类推,到第9次时,led=1持续1秒后led=0持续了9秒,共计10秒时间。随后以此为规律进行循环往复。
图3.1-51位闪烁灯的波形图
    根据波形图可以完成本项目计数器的架构设计。本项目一共需要两个计数器,其中一个计数器用来计算时间,另一个用来计算次数。本书使用计数器cnt1来记录次数变化,另一个计数器cnt0来记录每一次的工作时间,这样做会方便后续的代码设计。在这里来思考一下,为什么不能只用一个时间计数器,按照2秒、5秒、9秒、14秒……这样增加秒数的方法来进行设计呢?在实际的代码操作中,这样设计虽然只使用了一个计数器,但其计算方法却是非常麻烦的。举个生活中常见的例子,如下图所示,把每一次的循环看做楼层,把每一秒的计数看做门牌号。按照一种计数模式来计数的话,若一楼的门牌号为1、2、3、4、5、6、7、8,二楼的门牌号则为9、10、11、12、13、14、15、16、17、18,后续楼层以此类推。按照这种方式进行计数,如果需要寻找到47号房间,可能需要动脑筋计算很久才可以定位到。反之,使用两种计数模式:一个计数器对应楼层,另一个对应门牌号。那么一楼的门牌号则为一楼的1、2、3、4、5、6、7、8,二楼的门牌号为二楼的1、2、3、4、5、6、7、8、9、10,以此类推。一共有多少层,每一层都有对应的房间号就会一目了然。假如想要寻找六层7号房间就会轻而易举。同样的道理,在本工程设计中使用一个计数器表示次数,另一个计数器   
表示时间,这样在后续遇到问题时就可以快速的找到问题代码,而无需逐个推算。图3.1-6门牌号计数示例
    同样是上面的案例,如果想要寻找每一层的固定位置房间,同样可以利用两种计数模式:用cnt0来表示房间号,其范围是0-9,用cnt1来表示楼层号,其范围是0-1。利用这一方法可以通过cnt0和cnt1两个计数器来找到任何一个房间。这样做的另一好处是:如果想找到同一个位置的房间,就可以直接用cnt0来统一进行表示。例如cnt0==4可以统一表示每层楼的四号房间。而在只有房间号这一单一计数模式而没有楼层计数的情况下,如果想表示每层楼的四号房间则需要表示方式为“cnt0==4”、“cnt0=12”,更高楼层以此类推。两种表现形式的难易程度显而易见。
    借鉴同样的思想用于LED灯的设计工程中,根据目标可知需要LED灯第1秒不亮,而后面N秒亮,循环往复。从波形图中也可以看出任何一次亮暗都是在第一秒结束的节点拉低,依据使用两个计数单位的方法,统一将每次循环的第一秒表示为“add_cnt0 && cnt0==50_000_000-1”。
    在这种无限循环的工程中,数据会像滚雪球一样越滚越大,此时只要一个节点出现了问题,就需要花费大量的时间去查找修复。通过增加一个次数计数器的方法将此项目闭合在一个固定的循环中就可以巧妙的避开了这一问题,大大减轻了排查错误的难度。学会这种思考方式后在对项目进行顶层设计时可以更加全面的考虑问题,从而为项目选择最合适的展现形式。
    回到本项目中,本项目采用计数器cnt0来计算时间,如计2秒、3秒等。工作时钟为50MHz,即周期为1s/50MHz=20ns,通过时间换算关系可知2s=2_000_000_000ns,因此当计数器计数到2_000_000_000/20=100_000_000个时就代表着2秒时间到了。按照此方法依次类推,在第2次亮暗控制时计数器数到150_000_000个时代表计时3秒。第4次亮暗控制时数到200_000_000个时代表计时4秒……,第9次时,数到500_000_000个时就表示10秒时间计时结束。
    前文讨论中说明了本设计需要2个计数器,现在就来讨论一下每个计数器的实现方法。至简设计法的设计规则中有讲过,计数器的设计只考虑两个因素:加1条件和计数数量。只要确定好相应逻辑,就能完成计数器代码设计。
    首先来讨论时间计数器cnt0的加1条件:该计数器始终不停地进行计数,因此可以认为其加1条件是一直有效的,可写成:assign add_cnt0==1。
    可能会有同学提出疑问:加1条件的概念是什么?这里以停车位来进行比喻,一般情况下对每个停车位置会进行对应编号,但是如果某个位置上放置了一块石头无法作为停车位时,该位置就不能获得对应的编号。反之则可以认为停车位编号的加1条件就是:对应位置上没有石头,其可以继续进行编号,即assignadd_cnt0 = “没有石头”。因此如果在设计中计数器一直没有阻碍地进行计数工作,就可以认为加1条件是一直有效的。
    下面讨论一下时间计数器cnt0的计数数量:由于第1次到第9次的亮暗控制中每次计数的时间都各不相同,因此可以考虑使用变量法。在设计中变量法的选用逻辑非常简单:当循环周期的个数不同时就可以选择变量法来进行设计。本设计中使用x表示计数器cnt0的计数数量,后续会对x的值进行详细展开。
    进行代码编写这一步骤时,以往都是一行行的输入相应代码。至简设计法在这里提供一个小技巧,在节省编写代码的时间的同时在一定程度上降低了代码的出错率。至简设计法将日常代码中常用到的固定部分制作成模板,进行代码编程时可以调用相应模板后根据逻辑输入对应设计的变量将代码补充完整。这里就使用模板编写计数器代码,学习一下这个炫酷的功能。
    打开GVIM工具,在命令模式下输入“:Mdyjsq”后点击回车就调出了对应模板,如下图所示。之后再将本案例中的变量填到模板里面,就可以得到完整正确的计数器代码。
图3.1-7至简设计法调用计数器代码模板
    按照上文方法,可以得到该时间计数器的代码如下:

1234567891011121314always @(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;   endend
assign add_cnt0 = 1 ;assign end_cnt0 = add_cnt0&&cnt0== x-1;

    同样的,需要确定次数计数器cnt1的加1条件和计数数量:可以看到每当cnt0完成一个周期后计数器cnt1就会加1,因此cnt1的加1条件即为cnt0的结束计数,其代码表示为end_cnt0。此外,从设计目标中可以得知每一周期内次数计数器cnt1的计数数量为9。在这里可以继续调用模板,在GVIM的命令模式下输入“:Mdyjsq”后点击回车调用对应模板后将“add_cnt1”和“end_cnt1”补充完整,得到代码为:

1234567891011121314always @(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;   endend
assign add_cnt1 = end_cnt0;assign end_cnt1 = add_cnt1 &&cnt1==9-1 ;

    确定好两个计数器后来思考一下输出信号led的变化。从设计目标可以得知led有两个变化点:变0和变1。变0的原因均为时间计数器完成到1秒计数,也就是当cnt0数到1_000_000_000/20=50_000_000个时,计时1秒结束,此时LED1灯为灯灭状态。设计目标规定灯暗一秒后亮灯,即此时led信号变为0。同理,变1是因为完成了亮灯的N秒时间,相应计数器完成了计数,即end_cnt0。
    依旧使用至简设计法模板,在GVIM编辑模式下输入“Shixu2”回车,调出模板,得到led信号的代码如下:
1234567891011always   @(posedge clk or negedge rst_n)begin   if(rst_n==1'b0)begin         led <= 1 ;   end   else if(add_cnt0 && cnt0==50_000_000-1)begin         led <= 0 ;   end   else if(end_cnt0)begin         led <= 1 ;   endend

    最后来思考一下计数器cnt0的计数数量x。前文中提到这种循环周期变化的情况要采用变量法来完成设计,下面先同样以楼房门牌号为例来辅助思考。假设楼房的第一层为八个房间,第二层为十个房间,第三层为十五个房间,第四层为二十个房间。如果想要用代码将这种实际情况表示出来,需要使用至简设计法中的组合逻辑模板,在编辑模式下输入“Zuhe”后回车,填写对应条件后得到最终代码如下:
1234567891011121314always   @(*)begin   if(cnt1==0)begin         x = 8 ;   end   else if(cnt1==1)begin         x = 10 ;   end   else if(cnt1==2)begin         x = 15 ;   end   else begin         x = 20 ;   endend

    这里的代码设计需要着重学习一下,可以看到至简设计法写出的代码非常的整洁,逻辑思维一目了然,这种写法可以做到资源最优,并且不会出现BUG,自己实操的时候可以使用这种写法。
    同样的道理,在讨论时间计数器cnt0的时候曾经得出结论:“计数器计数到2_000_000_000/20=100_000_000个时就代表着2秒计时结束。按照此方法依次类推,在第2次亮暗控制时计数器数到150_000_000个时代表3秒计时结束。第4次亮暗控制时数到200_000_000个时代表4秒计时结束……,至第9次时,数到500_000_000个时代表10秒计时结束。”,从中可以看出cnt0的计数数量与cnt1有关。第1次计数数量为100_000_000,第2次计数数量为150_000_000……,按照这样的规律可以将9次亮暗对应的x都表示出来。在GVIM的编辑模式下输入“Zuhe”后回车,将对应条件补充完整后得到x的赋值代码如下:
1234567891011121314151617181920212223242526272829always   @(*)begin   if(cnt1==0)begin         x = 100_000_000 ;   end   else if(cnt1==1)begin         x = 150_000_000 ;   end   else if(cnt1==2)begin         x = 200_000_000 ;   end   else if(cnt1==3)begin         x = 250_000_000 ;   end   else if(cnt1==4)begin         x = 300_000_000 ;   end   else if(cnt1==5)begin         x = 350_000_000 ;   end   else if(cnt1==6)begin         x = 400_000_000 ;   end   else if(cnt1==7)begin         x = 450_000_000 ;   end   else begin         x = 500_000_000 ;   endend

    至此,本设计的主体程序已经完成。回顾一下思考过程可以发现:设计的每一步都是按照设计目标逐步展开,看似是在讨论每个小问题,但其实都是围绕设计目标进行讨论,这也是本书最开始强调制定和理解设计目标的重要性的原因。
3.3 信号定义
    下面需要将module补充完整,首先要做的是定义信号的类型。在判断信号类型时会感到很困惑,搞不清楚如何确定reg和wire。其实大多数的困惑是因为联想的太多,例如认为reg就是寄存器,wire是线,或者认为reg类型会综合成寄存器,wire类型不会综合成寄存器,然而实际上这些与信号是reg型还是wire型都并无关系。至简设计法建议不要进行任何联想,只遵从一个规则“用always实现的是reg型,其他都是wire型”。
    进行信号定义时,除了信号的类型,还需要确定信号的位宽。至简设计法在这里分享一个非常实用的获取信号位宽的技巧:打开计算器,点击“查看”,选择“程序员”模式,在“十进制”下输入数字后就会获得对应的信号位宽。
    cnt0是用always产生的信号,因此类型为reg。根据前文计算可知cnt0计数的最大值为500_000_000,根据至简设计法的实用技巧,打开计算器后在程序员模式十进制下输入500_000_000,如下图所示。可以看出,信号的位宽为29。
图3.1-8通过计算器获取信号位宽
    综上所述cnt0的定义代码如下:
1reg    cnt0;

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

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

    同样的,add_cnt1和end_cnt1也是用assign方式设计的,类型为wire。其值是0或者1,位宽为1。编辑模式下输入“Wire1”调用至简设计法模板,补充完整后得到代码表示如下:
12wire      add_cnt1;wire      end_cnt1;

    led是用always方式设计的,因此类型为reg。led信号值是0或者1,1根线表示即可。编辑模式下输入“Reg1”调用至简设计法模板,补充完整后得到代码表示如下:
1reg         led   ;

    x是用always方式设计的,因此类型为reg。其最大值为500_000_000,需要用29根线表示,即位宽为29。需要注意的是:这里的x是使用组合逻辑设计的,综合结果也不会有寄存器,但是遵循刚刚提到的信号判断原则,x是用always设计出来的,所以依旧使用reg型来进行定义。代码表示如下:
1reg      x       ;

    至此,整个代码的设计工作已经完成。完整版的工程代码如下:
1module my_led(clk,rst_n,led);
input    clk;input    rst_n;output   led   ;

reg cnt0         ;wire   add_cnt0    ;wire   end_cnt0    ;reg    cnt1      ;wire   add_cnt1    ;wire   end_cnt1    ;reg x            ;reg      led         ;

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;   endend
assign add_cnt0 =1 ;assign end_cnt0 = add_cnt0 &&cnt0==x-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;   endend
assign add_cnt1 = end_cnt0;assign end_cnt1 = add_cnt1 &&cnt1==9-1 ;

always   @(posedge clk or negedge rst_n)begin   if(rst_n==1'b0)begin         led<=1;   end   else if(add_cnt0 && cnt0==50000000-1)begin         led<=0;   end   else if(end_cnt0)begin         led<=1;   endend



always   @(*)begin   if(cnt1==0)begin         x=100000000;   end   else if(cnt1==1)begin         x=150000000;   end   else if(cnt1==2)begin         x=200000000;   end   else if(cnt1==3)begin         x=250000000;   end   else if(cnt1==4)begin         x=300000000;   end   else if(cnt1==5)begin         x=350000000;   end   else if(cnt1==6)begin         x=400000000;   end   else if(cnt1==7)begin         x=450000000;   end   else begin         x=500000000;   endend
endmodule


第4节 综合与上板
上一节完成了代码的设计,接下来需要对其进行综合以及上板调试。
4.1 新建工程
    打开软件Quartus Ⅱ,点击File下拉列表中的New Project Wzard...新建工程选项,如图3.1-9所示。 图3.1-9Quartus新建工程界面
    随后会出现图3.1-10所示的Quartus新建工程介绍,直接点击“Next”。 图3.1-10Quartus新建工程介绍
    此时会出现工程文件夹、工程名、顶层模块名设置界面,如图3.1-11所示。注意目录为:D:/mdy_book/my_led,工程名和顶层名为my_led。这里再次进行强调,为了避免初学者使用过程中出现报错情况,强烈建议按照本书的工程名和文件名进行设置,设置完成后点击Next。 图3.1-11QUARTUS新建工程设置名称
    新建工程类型设置选择“Empty project”,如下图所示,然后点击“Next”。 图3.1-12 QUARTUS新建工程类型
图3.1-13QUARTUS添加文件
    文件添加界面如图3.1-13所示,此时添加之前写的“my_led.v”文件,点击右侧的“Add”按钮后文件会在下方显示出来,随后点击“Next”。 图3.1-14QUARTUS选择芯片型号
    芯片型号选择界面如图3.1-14所示,选择“Cyclone ⅣE”,在芯片型号选择处选择“EP4CE15F23C8”,然后点击“Next”。 图3.1-15QUARTUS设置工具界面
    QUARTUS设置工具界面如图3.1-15所示,不必要修改,直接点击“Next”。 图3.1-16QUARTUS新建工程汇总界面
图3.1-16中可以看到新建工程的汇总情况,点击“Finish”,完成新建工程。
4.2 综合
    新建工程步骤完成后,QUARTUS界面如下图所示。 图3.1-17QUARTUS新建工程后界面
    点击编译按钮,可以对整个工程进行编译,编译成功后的界面如图3.1-18所示。 图3.1-18QUARTUS编译后界面
4.3 配置管脚
    下面需要对相应管脚进行配置。如图3.1-19所示,在菜单栏中选中Assignments后选择Pin Planner,随后配置管脚的窗口就会弹出。 图3.1-19QUARTUS配置管脚选项
    在配置窗口最下方中的location一列,按照表3.1-2中最右两列进行FPGA管脚的配置。此处配置管理来源的选择在最开始的管脚配置设计环节中有进行讲解,最终配置的结果见图3.1-20。
表3.1-2信号和管脚关系
器件原理图信号FPGA管脚FPGA工程信号
LED1LED1_NETAA4led
X1SYS_CLKG1clk
K1SYS_RSTAB12rst_n
图3.1-20QUARTUS配置管脚    配置完成后关闭“Pin Planner”,软件自动会保存管脚配置信息。
4.4 再次综合
    再次打开“QUARTUS”软件,在菜单栏中选中“Processing”,然后选择“StartCompilation”,再次对整个工程进行编译和综合,如图3.1-21所示。 图3.1-21QUARTUS编译选项
图3.1-22QUARTUS编译成功标志
当出现图3.1-22时说明编译综合成功。
4.5 连接开发板
    完成编译后开始进行上板调试操作,首先进行开发板的连接。按照图3.1-23中的方式将下载器接入电脑USB接口,接上开发板电源后按下开发板下方蓝色开关。 图3.1-231位闪烁灯开发板连接图
4.6 上板
    单击下图所示的QUARTUS界面中的 按钮,弹出配置界面。 图3.1-24QUARTUS界面
    随后点击add file后添加.sof文件,之后点击“Start”,在“Progress”会显示出进度。 图3.1-25QUARTUS下载程序界面
如图3.1-25所示,“Progress”进度条中提示成功后即可在开发板上观察到相应的现象。可以看到开发板上的LED灯开始闪烁,并且按照亮一秒灭一秒,亮两秒灭一秒的规律进行循环闪烁。当观察到开发板上的LED灯可以照常闪烁,并且在亮九秒灭一秒后又回到亮一秒灭一秒的循环时就可以判断此次实验成功。如果出现LED灯不亮或者不能按照规律进行闪烁等情况时,需要回头思考检查问题的出现点,建议可以多进行几遍操作,不要粗心,找到错误点后进行改正。
第5节 简化版步骤分享
为了便于有基础的同学操作学习,本书也准备了简化版的操作步骤。当然对于初学者来说,掌握了各个部分原理后也可以按照下列方法进行重复性的实验操作练习,加强实操能力。
5.1 设计实现

5.1.1顶层信号
    新建目录:D:\mdy_book\my_led。在该目录中新建一个名为my_led.v的文件,用GVIM打开后开始编写代码。
    确定顶层信号。此工程信号和管脚关系如下表所示。表3.1-2 信号和管脚关系
器件原理图信号FPGA管脚FPGA工程信号
LED1LED1_NETAA4led
X1SYS_CLKG1clk
K1SYS_RSTAB12rst_n
    写出顶层信号代码:
12345module my_led(clk   ,rst_n   ,   led   );
声明输入输出属性:
123input               clk;input               rst_n;output            led    ;


5.1.2信号设计
    进行架构设计。根据设计需求画出设计的波形图: 图3.1- 41位闪烁灯的波形图
    设计计数器架构,对表示计算时间的计数器cnt0进行代码设计:

1234567891011121314always @(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;   endend
assign add_cnt0 = 1 ;assign end_cnt0 = add_cnt0&&cnt0== x-1;

    对表示计算次数的计数器cnt1进行代码设计:

1234567891011121314always @(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;   endend
assign add_cnt1 = end_cnt0;assign end_cnt1 = add_cnt1 &&cnt1==9-1 ;
    设计led信号代码:
1234567891011always   @(posedge clk or negedge rst_n)begin   if(rst_n==1'b0)begin         led <= 1 ;   end   else if(add_cnt0 && cnt0==50_000_000-1)begin         led <= 0 ;   end   else if(end_cnt0)begin         led <= 1 ;   endend

    设计表示计数器cnt0的循环次x的代码:
1234567891011121314151617181920212223242526272829always   @(*)begin   if(cnt1==0)begin         x = 100_000_000 ;   end   else if(cnt1==1)begin         x = 150_000_000 ;   end   else if(cnt1==2)begin         x = 200_000_000 ;   end   else if(cnt1==3)begin         x = 250_000_000 ;   end   else if(cnt1==4)begin         x = 300_000_000 ;   end   else if(cnt1==5)begin         x = 350_000_000 ;   end   else if(cnt1==6)begin         x = 400_000_000 ;   end   else if(cnt1==7)begin         x = 450_000_000 ;   end   else begin         x = 500_000_000 ;   endend
主体程序完成后将module补充完整。
5.1.3信号定义
    首先,对信号进行类型定义,其中cnt0信号定义如下:
1reg    cnt0;
    add_cnt0和end_cnt0信号定义如下:
12wire      add_cnt0;wire      end_cnt0;
    cnt1信号定义如下:
1reg    [ 3:0]cnt1;
    add_cnt1和end_cnt1信号定义如下:
12wire      add_cnt1;wire      end_cnt1;
    led信号定义如下:
1reg         led   ;
    x信号定义如下:
1reg      x       ;
        至此,整个代码的设计工作完成,后续应对代码进行编译综合以及上板查看现象。
5.2 综合与上板


5.2.1新建工程
    打开Quartus Ⅱ,点击File下拉列表中的New Project Wzard...新建工程选项。
图3.1-9Quartus新建工程界面
点击“Next”: 图3.1-10Quartus新建工程介绍
如下图所示设置工程文件夹、工程名、顶层模块名后点击“Next”。 图3.1-11QUARTUS新建工程设置名称
选择“Empty project”后点击“Next”。 图3.1-12QUARTUS新建工程类型
点击“Add”选项-后添加“my_led.v”文件后点击“Next”。 图3.1-13QUARTUS添加文件
在“Device family”选项中选择“Cyclone ⅣE”,“Available devices”选项中选择“EP4CE15F23C8”,随后点击“Next”。 图3.1-14QUARTUS选择芯片型号
直接点击“Next”。 图3.1-15QUARTUS设置工具界面
点击“Finish”后完成新建工程。 图3.1-16QUARTUS新建工程汇总界面
5.2.2综合
新建工程后界面如图3.1-17所示,点击“编译”按钮,编译成功后界面如图3.1-18所示。 图3.1-17QUARTUS新建工程后界面
图3.1-18QUARTUS编译后界面
5.2.3配置管脚
在菜单栏点击“Assignments”后点击“Pin Planner”,随后会弹出配置管脚的窗口。 图3.1-19QUARTUS配置管脚选项
在配置窗口“location”配置管脚,配置完成关闭“Pin Planner”即可自动保存配置信息。 图3.1-20QUARTUS配置管脚
5.2.4再次综合
菜单栏中选择“Processing”后点击“Start Compilation”再次进行综合。 图3.1-21QUARTUS编译选项
出现QUARTUS编译成功标志即表示此次编译成功。 图3.1-22QUARTUS编译成功标志
5.2.5连接开发板
将下载器接入电脑USB接口,开发板接上电源后按下蓝色开关。 图3.1-23 1位闪烁灯开发板连接图
5.2.6上板
打开QUARTUS界面后单击“”图标。 图3.1-24QUARTUS界面
点击“add file”添加“.sof”文件,随后点击“Start”。“Progress”会显示进度,进度条显示“100%”为成功,可在开发板观察此时现象。 图3.1-25QUARTUS下载程序界面
第6节 扩展练习
    至此整个1位闪烁灯设计已经分享完毕,这里只是展示了一个案例,已经掌握原理后可以试着进行设计的扩展延伸。比如在同样的闪烁灯设计中更改闪烁时间再进行上板尝试,举一反三,既考察了自己的知识掌握程度,又强化了自己的设计能力。鼓励同学们对此设计进行扩展性的思考和讨论,有更好的思路可以前往至简设计法论坛进行交流。



求教了 发表于 2020-12-14 00:15:51

非常好的学习资料,谢谢

求教了 发表于 2020-12-15 00:30:00

今日打卡,坚持,加油

daydayup 发表于 2021-1-28 14:10:59

加油,加油
页: [1]
查看完整版本: 【FPGA至简设计原理与应用】书籍连载10 第三篇FPGA至简设计项目实践 第一章 1位闪烁灯