立即注册
查看: 1434|回复: 0

[资料] 做FPGA没有思路?我来帮你!

已绑定手机
发表于 2019-8-17 09:27:18 | 显示全部楼层 |阅读模式 来自 广东省广州市
FPGA设计思想 FPGA设计思想

1.1 看波形图的方法本书的D触发器一章、怎么看FPGA波形一节中,讲述下如何看信号的波形,读者只需要记住一个规则:时钟上升沿看信号,看到的是信号变化前的值。有兴趣的读者,可以返回再细看那一章内容。图 111例如图111,在第4个时钟上升沿看信号a,则是看到a的值为1(变化前);看信号c,看到c的值为0(变化前);在第5个时钟上升沿看信号b,看到b的值为1,看信号c,看到c的值为1。图 112例如图112,在第5个时钟上升沿看信号dout,其值为1;看信号cnt,其值为1,而不是2。以上就是看波形的方法,该方法的由来,可以参考本书的D触发器一章、怎么看FPGA波形一节的内容。当然,使用该方法是有前提的:所有信号都是同步信号;波形是理想的波形。
1.2 至简设计法的四种设计类型学习FPGA,最关键的是学什么?笔者发现,有部分读者将学习的重点放在接口知识、算法原理等理论知识层面。例如,学习串口的时候,把学习重点放在:什么是串口、串口有什么优势、什么时候用到串口等理论。至于串口代码,也是借鉴和模仿,原代码是什么结构,自己设计的代码也要有这种结构,原代码有什么信号,自己也要有这些信号等。如果该串口功能稍加改动,则陷入完全无从下手的状态。每个工程师都有自己的代码风格,甚至有些工程师今天的风格和昨天的风格都会不一样。网络上的代码自然也是良莠不齐,一味靠模仿,能成长为高手,那就奇怪了。明德扬认为,学习FPGA应该是为了提高自己的设计开发能力。学习串口,不是为了懂得这个串口,而是通过这个串口例子,学习其设计思路和方法,以便应用到其他接口上。明德扬认为,设计思路和方法,不应过多过杂,而是形成自己的一套模式。今天学少林铁头功,明天学武当太极,后天学华山剑法,先不说精力问题,能达到精通状态吗?明德扬研发了一套通用的设计方法:至简设计法。至简设计法从宏观上,适应所有的功能设计需求。例如,无论是什么功能,我们都先将其转化成需求波形。然后在此基础上设计模块架构;在模块架构基础上设计信号。这步骤都是通用的、是固化的。至简设计法在微观上,制定了实用的规范。详细到什么时候添加信号;怎么添加信号;添加信号名字是什么等,我们都做了详细的规定。大部分的FPGA设计,明德扬将其归类下面讲述的4种类型。无论多复杂的功能,都是这4种类型的变种。下面通过4个典型案例的设计,来讲述至简设计法。
1.2.1 至简设计法设计类型1案例1:当收到en=1后,dout产生一个宽度为10个时钟周期的高电平脉冲。图113是功能波形图。图 113根据看波形规则,在第3个时钟上沿的时候,看到en==1,根据功能要求,上升沿之后dout就会变为1。10个时钟周期后,即第13个时钟上升沿时,dout将变为0。推理1:从功能要求中,看到数字10,我们就知道要计数,要使用计数器。推理2:10个是指dout==1的次数为10个时钟周期,所以该计数器数的是dout==1的次数,因此看到dout==1时,计数器就会加1。此外,明德扬还制定了计数器要遵守的原则。原则1:初值一定为0。复位后,计数器一定要为0。原则2:数到最后一个时,要及时清0。根据上面2个推理和原则,补充计数器信号cnt,更新后的波形如图114。图 114从功能要求和波形图,我们确认,计数器cnt是对dout==1进行计数,并且一共数10个。为此,在GVIM编辑器中输 入“Jsq”并回车,将出现图115的代码。图 115在第13行,输入dout==1,在第14行代码中,输入10-1,这样就完成了计数器设计,如图 116。图 116add_cnt表示:计数器cnt加1条件。end_cnt表示:计数器数到最后一个,也称之为结束条件。图116第1~第11代码功能:时钟上升沿时,如果计数器加1条件有效,并且是数到最后一个,则计数器清零;如果计数器加1条件有效,但不是最后一个,则计数器就加1;其他时候,计数器就保持不变。那么加1条件,即add_cnt是什么呢?在第13行进行了定义。该行代码表示,dout==1就是计数器的加1条件。那么结束条件,即end_cnt是什么呢?在第14行进行了定义。该行代码表示,数到10个就结束。其中我们关注的是那个数字10,而-1是固定的格式。add_cnt && cnt==10-1,含义是表示“数到第10个的时候”,add_cnt && cnt==x-1表示“数到第 x个的时候”。记住这个规则。end_cnt==1也表示数完了。设计好计数器cnt后,我们就可以设计输出信号dout了。仔细分析dout,该信号有两个变化点:变1和变0。我们分 析原因,dout变1是由于收到en==1;dout变0,则是数到了10个或者是数完了。所以综上所述,dout的代码是:1234567891011always  @(posedge clk or negedge rst_n) beginif(rst_n==1'b0) begindout <= 0 ;endelse if(en==1) begindout <= 1 ;endelse if(end_cnt) begindout <= 0 ;endend至此,我们完成了主体程序的设计,接下来补充module的其他部分。将module的名称定义为my_ex1。并且我们已经知道该模块有4个信号:clk、rst_n、en和dout。为此,代码如下:123456module my_ex1(clk      ,rst_n    ,en       ,dout);其中clk、rst_n和en是输入信号,dout是输出信号,并且4个信号都是1比特的,根据这些信息,我们补充输入输出端口定义。代码如下:1234input    clk     ;input    rst_n   ;input    en      ;output   dout    ;接下来定义信号类型。cnt是用always产生的信号,因此类型为reg。cnt计数的最大值为9,需要用4根线表示,即位宽是4位。add_cnt和end_cnt都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:123reg  [ 3:0]   cnt      ;wire          add_cnt  ;wire          end_cnt  ;dout是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下:1reg           dout     ;至此,整个代码的设计工作已经完成。整体代码如下:123456789101112131415161718192021222324252627282930313233343536373839404142434445module my_ex1(clk      ,rst_n    ,en       ,dout);input    clk     ;input    rst_n   ;input    en      ;output   dout    ;reg [ 3:0]  cnt     ;wire        add_cnt ;wire        end_cnt ;reg         dout    ;always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt <= 0;endelse if(add_cnt) beginif(end_cnt)&nbs p; cnt <= 0;else&nbs p; cnt <= cnt + 1;endendassign add_cnt = (dout==1);assign end_cnt = add_cnt && cnt==10 -1 ;always  @(posedge clk or negedge rst_n) beginif(rst_n==1'b0) begindout <= 0;endelse if(en==1) begindout <= 1;endelse if(add_cnt && cnt==10-1)begindout <= 0;endendendmodule1.2.2 至简设计法设计类型2例2. 当收到en=1后,dout间隔3个时钟后,产生宽度为2个时钟周期的高电平脉冲。图 117如上面波形图所示,在第3个时钟上升沿看到en==1,间隔 3个时钟后,dout变1,再过2个时钟后,dout变0。根据案例1的经验,出现大于1的数字时,就需要计数。我们这里有数字2和3,建议的计数方式如下。图 118当然,其他计数方式最终也能实现功能。但明德扬的总结是上面方式最好,实现的代码将是最简的,其他方式则稍微复杂。接下来判断计数器的加1条件。与案例1不同的是,计数器加1区域如下图阴影部分,但图中没有任何信号来指示此区域 。图 119为此,添加一个名字为“flag_add”的信号,刚好覆盖了阴影部分,如下图。图 120补充该信号后,计数器的加1条件就变为flag_add==1,并且是数5个。代码如下:12345678910111213always @(posedge clk or negedge rst_n) beginif (rst_n==0) begincnt <= 0;endelse if(add_cnt) beginif(end_cnt)&nbs p; cnt <= 0;else&nbs p; cnt <= cnt+1 ;endendassign add_cnt = flag_add==1;assign end_cnt = add_cnt  && cnt == 5-1 ;flag_add有2个变化点,变1和变0。变1的条件是收到en==1,变0的条件是计数器数完了,因此代码如下:1234567891011always  @(posedge clk or negedge rst_n) beginif(rst_n==1'b0) beginflag_add <= 0;endelse if(en==1) beginflag_add <= 1;endelse if(end_cnt) beginflag_add <= 0;endenddout也有2个变化点:变1和变0。变1的条件是“3个间隔之后”,也就是“数到3个的时候”;变0的条件是数完了。代码如下:1234567891011always  @(posedge clk or negedge rst_n) beginif(rst_n==1'b0) begindout <= 0;endelse if(add_cnt && cnt==3-1)begindout <= 1;endelse if(end_cnt) begindout <= 0;endend至此,我们完成了主体程序的设计,接下来是补充module的其他部分。将module的名称定义为my_ex2。并且我们已经知道该模块有4个信号:clk、rst_n、en和dout。为此,代码如下:123456module my_ex2(clk      ,rst_n    ,en       ,dout);其中clk、rst_n和en是输入信号,dout是输出信号,并且4个信号都是1比特的,根据这些信息,我们补充输入输出端口定义。代码如下:1234input    clk     ;input    rst_n   ;input    en      ;output   dout    ;接下来定义信号类型。cnt是用always产生的信号,因此类型为reg。cnt计数的最大值为4,需要用3根线表示,即位宽是3位。add_cnt和end_cnt都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:123reg   [ 2:0]   cnt     ;wire           add_cnt ;wire           end_cnt ;dout是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下:1reg            dout    ;flag_add是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下:1reg            flag_add  ;至此,整个代码的设计工作已经完成。整体代码如下:1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859module my_ex2(clk      ,rst_n    ,en       ,dout);input    clk     ;input    rst_n   ;input    en      ;output   dout    ;reg   [ 2:0]   cnt     ;wire           add_cnt ;wire           end_cnt ;reg            flag_add  ;reg            dout    ;always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt <= 0;endelse if(add_cnt) beginif(end_cnt)&nbs p; cnt <= 0;else&nbs p; cnt <= cnt + 1;endendassign add_cnt = flag_add==1;assign end_cnt = add_cnt && cnt==5-1 ;always  @(posedge clk or negedge rst_n) beginif(rst_n==1'b0) beginflag_add <= 0;endelse if(en==1) beginflag_add <= 1;endelse if(end_cnt) beginflag_add <= 0;endendalways  @(posedge clk or negedge rst_n) beginif(rst_n==1'b0) begindout <= 0;endelse if(add_cnt && cnt==3-1)begindout <= 1;endelse if(end_cnt) begindout <= 0;endendendmodule经过这个案例,我们做一下总结:在设计计数器的时候,如果计数区域没有信号来表示时,可补充一个信号flag_add。
1.2.3 至简设计法设计类型3案例3. 当收到en1=1时,dout产生3个时钟周期的高电平脉冲;当收到en2==1时,dout产生2个周期的高电平脉冲 。下面波形图描述了该功能。图 121图中,第3个时钟上升沿收到en1==1,所以dout变1并且持续3个时钟周期;在第9个时钟上升沿看到en2==1,所以dout变1并且持续2个时钟周期。注意,en1==1和en2==1的出现是没有顺序的。有读者可能会问,如果en1==1和en2==1同时出现,或者说在dout==1期间,出现了en1==1或者en2==1,该怎么办?请不要考虑这种情况,本案例假设永远不会出现该情况。明德扬在模块划分规范时,会要求各个模块之间配合清楚,这有助于简化我们的设计,精简系统。看到大于1的数字,就知道要计数。推荐的计数方式如下:图 122首先,不要用2个计数器分别计两种情况。这是因为这2个计数器都是不同时计数的,是可以合并的。同时,我们可以知道,这两种情况都是计算dout==1的次数。在确认计数器数多少个时,我们遇到了问题。因为这个计数器有时候数到3个就清零(en1==1触发的波形),有时候数到2个就清零(en2==1触发 的波形)。此时,我们建议你用变量x代替,即数到x个。注意,verilog是没有变量的概念的,这个变量,是明德扬提出的一个设计概念,x本质上还是一个信号。引入变量有什么用呢?设计计数器时就方便了,该计数器加1条件是dout==1,数x个就结束,因此代码如下:1234567891011121314always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt <= 0;endelse if(add_cnt) beginif(end_cnt)&nbs p; cnt <= 0;else&nbs p; cnt <= cnt + 1;endendassign add_cnt = dout==1;assign end_cnt = add_cnt && cnt==x-1 ;甚至我们还可以写出dout的代码,dout变1的条件是:en1==1或者en2==1;变0的条件是:计数器数完了。所以代码如下:1234567891011always  @(posedge clk or negedge rst_n) beginif(rst_n==1'b0) begindout <= 0;endelse if(en1==1 || en2==1)begindout <= 1;endelse if(end_cnt) begindout <= 0;endend我们再设计一下变量x,我们知道计数器en1==1触发的时候数3个就清零,en2==1触发的时候数到2个就清零,为此增加一个信号flag_sel来区分这两种情况,flag_sel==0表示是en1==1触发的,flag_sel==1表示是en2==1触发的,波形如下:图 123flag_sel变0的条件是遇到en1==1,flag_sel变1的条件是遇到en2==1,为此flag_sel的代码如下。1234567891011always  @(posedge clk or negedge rst_n) beginif(rst_n==1'b0) beginflag_sel <= 0;endelse if(en1==1) beginflag_sel <= 0;endelse if(en2==1) beginflag_sel <= 1;endend有了flag_sel,我们就好区分x的值了。 flag_sel为0时,x为3(数3个清零);flag_sel为1时,x为2(数2个清零),此时要用组合逻辑设计x,不然会出错的。代码如下:123456always  @(*)beginif(flag_sel==0)x = 3;elsex = 2;end至此,本工程的主体程序已经设计完毕,本题,我们使用了变量x,这是明德扬的至简设计方法中的变量法。主体程序完成后,我们补充模块的其他部分。

等待下文
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

合作/建议

TEL: 19168984579

工作时间:
周一到周五 9:00-11:30 13:30-19:30
  • 扫一扫关注公众号
  • 扫一扫打开小程序
Copyright © 2013-2024 一牛网 版权所有 All Rights Reserved. 帮助中心|隐私声明|联系我们|手机版|粤ICP备13053961号|营业执照|EDI证
在本版发帖搜索
微信客服扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表