数字IC学习之四(状态机)

  数字系统中有数据流密集型,这种设计一般控制流程不是很复杂,还有另一种极端,其控制流程很多。我想控制器就好比一个电路系统中的指挥中心,当你阅读完《Verilog HDL高级数字设计》这本书的时候就会深有感触,全书中每个设计都用状态机来充当控制器,而且书中的例子较为复杂,比如UART以及RISC等等,如果你真正理解了这些例子,就会发现状态机的妙处,它就像一个指挥中心,时刻接收前线战况,以及根据前线战况来发出准确的命令,让一切运行的井井有条,对于状态机,《Verilgo HDL数字系统设计》(夏宇闻)这本书给出了详细的介绍,提出了两种米利状态机的模板,而且《Verilog HDL高级数字设计》前期部分也系统介绍了状态机,以及相应的ASMD图,已经讲述的非常详细了。

  但是这里我还是要用一个小小的例子来说说状态机,一方面推荐教材中关于状态机的论述是较为复杂的,另一方面,也是我提出一个简单例子分析的主要目的,就像在前面几个部分提到的,由于组合逻辑有着延迟,寄存器存在着建立保持时间,而且寄存器本身还有延迟,再加上Verilog事件队列以及EDA软件的波形采样时刻,这些种种叠加起来,足以让人焦头烂额,如果你把这些问题统统考虑进去,有时候会很头疼,推荐教材中没有涉及如何协调这些因素,所以我希望能以状态机作为一个典型例子(状态机确实可以看成是一个经典的例子)深入电路细节来联系Verilog代码和真实电路,注意,我们这里假设已经按照前面几个篇章推荐的完成了相应教材的学习。

  设计说明:电路平时不工作,处于等待状态,等待输入i_Start信号高电平有效,电路开始进入工作状态,一旦i_Start信号变为无效低电平状态,则电路停止工作并且复位到初始状态,工作状态为:2比特计数器从00开始递增,每周期增加1,并且当其为10时,下一周期变为00,也就是一个自然二进制的加法计数器,模为3。

  计数操作为:00 -> 01 -> 10 -> 00 -> 01 -> 10 -> 00 ... ... 

  二比特信号命名为o_Counter(注意这是一个2比特寄存器)

  到此,你可以尝试自己设计一下电路(不用状态机),那么我可以肯定你的思路是混乱的,你将会去思考如何将i_Start信号嵌入到计数器逻辑中进行控制,这仅仅是一个极其简单的例子,如果控制信号和系统行为远复杂于此的情况下,你几乎会被电路的分支弄的焦头烂额而无从下手,如何将控制信号嵌入到逻辑中,如何考虑控制信号的无效状态,等等都是你需要考虑的,下面我们假设你已经经历了思考如何将i_Start信号处理的过程,即使可能你很厉害一下子就想出了如何处理,但是复杂性提升的情况下,我不认为你可以快速解决这个问题。

  下面我们来用状态机实现,如果你希望控制一件事,比如你把自己当成了指挥官,来控制一个烧水的行为,那么你必须时刻关注热水器的状态,它是否给你发出了烧水完毕的信号呢,如果你去关注它给你反馈的水是否已经沸腾的信号,而且盲目的去关闭电源取出水,那么要么是水已经烧开很长时间了,要么水没有烧开,所以我们可以看出烧水器状态信号的重要性,《高级设计》(《Verilog HDL高级数字设计》)中的例子无疑不是将电路的状态反馈给控制器,让控制器时刻知道电路已经处于什么状态,另一方面,控制器理所当然的要向电路发出各种控制命令,好,让我们看看下面的代码(代码采用《高级设计》中的模板),顺便指出书中有很多在不同的if分支对于同一个变量赋值的情况,这种对于可综合模型中是要注意禁止的,可以想象,如果两个if语句给同一个变量赋值,那么你到底是赋哪个。

  值得注意的是以上描述方式中c_Flush信号的优先级是大于c_Incr的,如果二者同时为1,那么将会执行清零操作而不是加一,这一点没有任何问题,但是逻辑层面你必须注意到这种情况,你必须确认是否会出现两个控制信号同时为1的情况,并且如果可能出现,它对功能有什么影响,好在本设计中,这两个信号永远不可能同时有效,除非电路故障了。

  接下来我们分析下这个代码,其中c_Flush是一个控制信号,显然是由控制器发出的(从控制器模板中赋值的),它的作用可以清楚的看到,就是如果它有效那么将会清零r_Counter,而c_Incr也是一个控制信号,它有效则r_Counter会进行加一操作,那么什么时候应该发出这些命令呢?也就是对应的什么时候应该去关闭电源取出烧好的水呢?这就要看烧水器反馈给你的信号了,这两个信号可以类比此设计中的i_Start和c_ContIsTop,我们拿c_ContIsTop来举例,通过分析case(state)中分支以及if分支,你可以发现,当在WORKING状态下c_ContIsTop信号有效时,控制器将会发出c_Flush信号将r_Counter清零,这就准确的实现了当计数器为10的下一个周期不至于增加到11,而是按照预想的返回00,那么任何大型的设计都可以借照此处的原理,可以给控制器发送状态信号,并且控制器会根据这个状态及时准确的发出控制信号来完成预设的任务,从这个例子中你体会到控制器的指挥中枢作用了吗?如果你没有体会到,不要着急,《高级设计》中有大量的例子,你会逐渐明白,如果没有控制器,那将会多么混乱不堪。

  好了,状态机给设计带来的便利已经有了相当大的体会了,接下来我要说的是如何将以上这段代码同实际电路联系起来,这里说的联系是指你必须具备的一种条件映射,也就是明白了某种协议,你可以毫无压力的解决控制器内部的case以及if分支,而且当你看到别人的代码时你可以迅速在脑中展现它反映的电路,或者是体现的某种协议。实际上,你头脑中反映出的电路波形已经是高度简化理想化的了,而且实际上这种波形(EDA软件给出的那种波形)也是非常有效的,你没有必要去思考实际电路波形中的过渡区域,甚至是寄存器的延迟都不需要考虑,但是,在你非常熟练之前,还是必须要十分清楚实际上考虑到寄存器延迟、寄存器建立保持时间以及组合延迟之后是什么样子的,因为,如果你不清楚这些,当你的电路出现问题时,你可能找不出到底哪里出了问题,尤其是逻辑的细节问题。即使考虑实际情况,也没必要考虑过渡,我们认为波形变化依然是直上直下的。

  或许你看了我的分析之后也不太清楚我说的是什么,不要紧,随着你阅读推荐的书籍,或者自己发现的好书,你会慢慢明白这些,并且随着实践的增加,你会越来越熟练,甚至到最后无须去花费更多的精力去理会这么多繁杂的因素。

  我下面来画出上面代码所表示的电路,注意其中的组合逻辑都是用组合逻辑云来表示,至于这个图画的正确与否,你可以来自己想一想:

  上图中标有r_Counter和state的地方为两个寄存器(可能比较小看的不是很清楚),还有c_ContIsTop的来源我给落下了,你自己找找它的来源吧,注意对于组合逻辑来说,只有经过黄色组合逻辑云才会有时间消耗(延迟),连线本身不具有延迟,连线的节点都用大写字母表示,而控制信号和状态信号c_Flush、c_Incr还有c_ContIsTop和i_Start都已经标出,我们假设i_Start为外部寄存器输入,那么我们来看下面的一些波形例子:

  首先我们来弄清楚寄存器的延迟和寄存器的建立保持时间和寄存器本身的延迟行为,我们假设在建立时间保持时间窗内,寄存器输入信号不变(变化的情况将导致亚稳态,在后续跨时钟域中会有介绍):

  上图中注意黑色部分时寄存器输入端的约束弧(输入时序约束,也就是输入信号必须在这一窗口保持不变),而红色部分为寄存器时钟端到Q端的延迟(就是时钟上升沿开始到Q端出现采样到的数据的延迟),需要指出的是这两个时间没有任何关系,它们可以一个比另一个大,也可能小,完全独立的时间概念,当然了,你说如果D信号在寄存器采样窗口改变了值会怎么样,那将进入亚稳态,输出Q会变为规定的0和1电平中的某个值,将会导致后续组合电路彻底乱掉,所以这也是综合中的静态时序分析工具所必须保证的,接下来说综合的时候将会用到这个图。

  那么我们如果用这种时序图来分析问题,当电路很简单时可能会比较方便,但是当电路比较复杂时,画出复杂的时序图,本身协议就比较复杂,如果再考虑到这么多窗口,以及寄存器的延迟,那么协议本身的时序关系将会被这些杂乱的无关信息所覆盖,所以,无论是我们交流还是EDA工具给出的波形,都是不是这样的,而是如下的简化形式:

  所以,从这个图中,我们明白,它本身并不能代表真实的电路时序,(D的变化已经同时钟上升沿重叠了,必然会产生亚稳态),而且Q的有效数据的产生也是在时钟上升沿处,也就是说寄存器本身的延迟为0,那是不可能的,所以说,这种图是对实际的一种简化,我们需要明白其内在的一种联系,D在时钟沿处变化,我们要明白,寄存器采样的是clk沿之前的D值,而这种近似不仅仅体现在寄存器,还体现在组合逻辑,真实电路中组合逻辑是有很大延迟的,但是在仿真波形以及我们的讨论中,是将其默认为0理想化了。明白这些内在的变化,近似的本质,才能建立起真实电路与理想波形进而同Verilog代码之间产生起扎实的联系,从此,随着实践的联系,将会越来越熟练。

  好了,让我们采用这种寄存器的理想模型(毕竟采样窗口和延迟都很小,到可以忽略不计),但是我们对组合逻辑却采用真实的电路模型(有一定延迟)来讨论几种上述例子中的状态机的波形,熟悉下状态机的操作吧!

 

  我们来适当分析下上述的波形当然所有的寄存器像上面说的,已经理想化了两个时间(一个采样窗口一个寄存器延迟),里面的延迟ab,我们可以从电路原理图(上述带着逻辑云的那副图),其延迟为从C到F的延迟,因为C为寄存器的输出,当其输出在时钟沿变为2'b10的时候,这个时候,它需要同2'b10进行比较,比较强本身是有一定延迟的,那么这个延迟就是上述时序图中的ab,但是在EDA软件中,ab延迟为0,也就是c_ContIsTop这个信号会在时钟上升沿处跳变,这样的近似情况的本质你必须明白,它是认为组合逻辑理想为0的情况,而上述时序图中的A是原理图中r_Counter的输入,它是经过了一段组合逻辑后产生的结果,所以,它必然会有一定的延迟,我们看周期1,r_Counter为2'b00,这个时候i_Start为1'b1,这导致一段延迟之后c_Incr为1'b1(延迟dc),c_Incr为1之后,它会同当前的r_Counter寄存器输出进行运算,同样是组合逻辑必然会产生延迟,在一定延迟之后其变为2'b01,如图所示,它本身是经过了一段组合逻辑之后的结果,所以在周期1到周期2的那个时钟上升沿处,它不会立即改变它的值。

  如果你不是很熟悉我上面的时序分析,要建立一个电路与代码之间的清晰的联系可能有些困难,当然了,如果你觉得不必建立这种精确的底层联系,也可以设计出好的电路,也许这是可能的,但是一旦你认为自己由于不是很清楚上面的问题而导致bug无法修正或者细节逻辑分析时混乱不堪,那么不妨过来看看这个例子。

  好了,对于状态机以及Verilog HDL代码同实际电路的联系就说到这里,这篇的上半部分算是对《高级设计》的一次抛砖引玉,用一个不怎么费力的小小设计来体会状态机的魅力,而且同时作为一个看《高级设计》的衔接,下半部分本人在思考之前已经学过了相关的电路和代码知识,经过一些非常头疼的思考得出的结果,如果你也看过了前面的推荐的教材,而导出我说的不对,那么欢迎留言。

  

猜你喜欢

转载自www.cnblogs.com/HIT1719292537/p/9195680.html
今日推荐