(原创声明:该文是作者的原创,面向对象是FPGA入门者,后续会有进阶的高级教程。宗旨是让每个想做FPGA的人轻松入门,作者不光让大家知其然,还要让大家知其所以然!每个工程作者都搭建了全自动化的仿真环境,只需要双击top_tb.bat文件就可以完成整个的仿真(前提是安装了modelsim),降低了初学者的门槛。如需整个工程请留言(微信Blue23Light),不收任何费用,但是仅供参考,不建议大家获得资料后从事一些商业活动!)
前面几课的时间都是在讲计数器模块,相信大家掌握的都差不多了,那这节课我们开启一个新的模块——状态机。为什么要用状态机呢?这要从硬件设计的并行思维讲起,FPGA芯片只要一上电,内部的所有always模块开始开始并行执行。但是对于实际的工程应用中,往往需要设计硬件来实现一些具有顺序的工作,必须一些算法的实现,在比如一些寄存器的配置,就需要用到状态机的思想。
状态机是一种用于处理具有前后顺序的事件的计算机模型,包含现态、条件、动作和次态四个要素,它可以将一个复杂的控制流程分解成多个互相独立的状态,从而简化设计过程并提高了系统的可靠性和性能。
现态:系统当前所处的状态。比如早晨起来你还在家里,这就是你当前的状态;
输入:一般指外部事件,当一个外部事件发生后,状态机便会根据状态转移函数发生响应。 比如早晨7点30分是每天出门上班的时间。输入条件满足,状态就要开始转变。
次态:根据现态、输入、状态转移条件所得到的状态机将要跳转至的新状态。其中,“次态”是相对于“现态”而言的,一旦被跳转后,“次态”就转变成新的“现态”了。比如早晨7点30分去上班,去上班就是次态。但是7点30分后,你在上班的路上,此时去上班就变成了现态。
输出:由现态或现态和输入共同决定的,但是有时某一状态或者某一输入改变,输出并不一定会发生变化,带来的仅仅是状态迁移而已。比如输出是在公司工作,状态从家里变成去上班的路上,都没有达到输出的结果,此时输入不变。等到了公司开始工作,那输出就有变化了。
状态机有两种类型,一种是摩尔(Moore)型状态机:下一状态只由当前状态决定 ;另一种是米勒(Mealy) 型状态机:下一状态不但与当前状态有关,还与当前输入值有关 。
如下所示Moore型状态机, 最左侧是次态组合逻辑电路,通过将状态输出反馈到次态组合逻辑电路,在和输入信号决定,下一状态是什么状态。中间部分是时序电路,就是一个触发器,将下一状态的值给到现在的状态。最右侧是组合输出逻辑,通过判断当前的状态,得到输出。
如下所示Mealy型状态机,结构和Moore型状态机类似,只是最右侧的输出逻辑不仅与当前的状态有关,还有输入有关。
在实际的应用中,Mealy型状态机的输出与输入有关,输出信号很容易出现毛刺,所以工程设计一般采用Moore型状态机。但是Moore型状态机最终的输出是组合逻辑,还是有产生毛刺的风险,目前输出逻辑大多采用时序逻辑进行实现。
一般来说,在FPGA中状态机有多种写法,常见的有一段式、两段式和三段式状态机。
一段式状态机:所有的状态变化以及输出变化都写在一个always块中,在该always块中既描述状态的同步转移,又描述状态的输入条件和输出。采用一段式状态机会让结构和逻辑看起来比较混乱,不推荐使用。
两段式状态机:用两个always块来描述状态机。其中一个模块采用同步时序逻辑描述状态转移,另一个模块采用组合逻辑判断状态转移条件。它需要两个状态——现态和次态,然后通过现态和次态的转换来实现时序逻辑。输出采用组合逻辑。也不太推荐使用。
三段式状态机:用三个always块来描述状态机。其中一个模块采用同步时序逻辑描述状态转移,另一个模块采用组合逻辑判断状态转移条件(注意和两段式的区别)。第三个模块描述状态的输出(既可以用组合逻辑也可以用时序逻辑,建议使用时序逻辑)。
所以对于比较复杂的状态机建议采用三段式状态机的设计,比较简单的状态机采用改进的一段式状态机,即用一个always块描述状态的同步转移,但是用一个模块单独描述状态的输出(建议使用时序逻辑)。
下面通过一个简单的工程让大家在直观上理解一下状态机的运行。工程要求:系统时钟是100MHz,低电平复位,一根输入线上的电平会随机跳动,请检测出数字序列1011001。需求很明确,就是把线上特定的序列检测出来,线上的状态是串行变化的,所以用状态机实现是比较合适的。
那怎么定义状态呢?一般都会有一个IDLE状态,就是系统的初始状态,还有就是S1,S10,S101,S1011,S10110,S101100,S101101这几个状态,根据输入信号进行切换。比如开始检测上线的信号是1,那状态就由IDLE转变成S1,如果下一个时刻检测到的线上的信号是0,那状态就有S1转变成S10,如果这个时刻检测到的线上的信号是1,那状态就有S1转变成IDEL,以此类推。一般用状态转移图来表示,很直观明了。如下所示,pi输入,po是输出,根据输入确定状态机的跳转,并标明输出的值。
有了状态转移图,那FPGA代码的设计就很简单了,由于本状态机很简单,我们采用改进的一段状态机进行设计,下面讲解一下。pi是输入的随机电平信号,po是检测到数字序列1011001后的输出信号,po拉高表明检测到了期望的序列。定义了7bit的状态寄存器state,用来进行7个状态的切换,状态参数用的独热码进行定义,独热码的好处就是任意状态的切换指挥有一位变化,降低了数据变化出错的机率。
然后根据状态图,通过输入pi的变化设计状态机的跳转即可,这儿需要注意一点,用了case语句一定要有default,就是一个默认的状态。目前的状态机简单,可以穷尽所有的状态,如果对于复杂的状态机,如果某些状态跳转没有描述,没有default语句,状态机可能就会卡死;而有了default语句,遇到未知的跳转条件就会跳转到这个默认的状态。
最后用时序逻辑设计了输出po,po拉高,就说明检测到了期望的序列。
最后看一下仿真结果,如下所示:下图是检测到期望的1011001序列的一段仿真图,设计的功能是正确的。
文章来源:https://www.toymoban.com/news/detail-812658.html
今天又熟悉了一个新的模块——状态机。如果能把状态机和计数器合理的组合,那就能设计出来更多的功能。前期的uart模块只能发送和接收单byte的数据,那如果发送和接收多byte的数据呢?那就要用上状态机了,我们下节课好好讲解。文章来源地址https://www.toymoban.com/news/detail-812658.html
到了这里,关于孩子都能学会的FPGA:第八课——状态机的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!