FPGA设计中,最重要的设计思想就是状态机的设计思想!状态机的本质就是对具有逻辑顺序时序规律的事件的一种描述方法,它有三个要素:状态、输入、输出:状态也叫做状态变量(比如可以用电机的不同转速作为状态),输出指在某一个状态的特定输出,输入指状态机中进入每个状态的条件。根据状态机的输出是否和输入有关,可分为摩尔(Moore)型状态机和米勒型(Mealy)状态机:摩尔型状态机的输出只取决于当前状态,而米勒型状态机的输出不仅取决于当前状态,还与当前输入有关。通常,我们描述状态机有三种方法:状态转移图、状态转移表、HDL描述,状态转移图直观,设计用,而HDL语言方便描述,实现时用。

  那么,如何用HDL描述一个好的状态机呢?主要有以下四点:安全、稳定性高速度快面积小设计清晰;在描述过程中,我们会引用两个新的verilog语法:localparam描述参数(等价于parameter)以及用task/endtask将输出功能块封装,增强代码可读性;描述状态机的关键是要描述清楚状态机的三大要素:如何进行状态转移?每个状态的输出?状态输出是否和输入条件相关?通常有三种写法,下面通过一个实例说明;

实例.检测“Hello”序列状态机

  1、功能:在输入一串字符中检测“Hello”序列,检测到后将led状态进行翻转;

  2、根据设计的FSM状态转移图(visio绘制):

  3、一段式描述法:在一个always块里既描述状态转移,又描述状态的输入和输出;

  verilog代码如下:

//检测“Hello”后led状态翻转
module check_hello(input  clk,         //50M时钟信号input  rst,         //低电平复位input  [7:0]asci,   //字符输入output reg  led     //控制led
);//状态寄存器reg  [4:0]NS;     //nextstate//状态独热编码localparam CHECK_H   = 5'b0_0001,CHECK_e     = 5'b0_0010,CHECK_la  = 5'b0_0100,CHECK_lb  = 5'b0_1000,CHECK_o   = 5'b1_0000;//一段式状态机always@(posedge clk,negedge rst)if(!rst)beginNS  <= CHECK_H;led <= 1'b1;    //led熄灭endelse begin    case(NS)CHECK_H:beginled <= 1'b1; if(asci == "H")NS <= CHECK_e;elseNS <= CHECK_H;endCHECK_e:beginled <= 1'b1; if(asci == "e")NS <= CHECK_la;elseNS <= CHECK_H;endCHECK_la:beginled <= 1'b1; if(asci == "l")NS <= CHECK_lb;elseNS <= CHECK_H;endCHECK_lb:beginled <= 1'b1; if(asci == "l")NS <= CHECK_o;elseNS <= CHECK_H;endCHECK_o:beginif(asci == "o")led <= 1'b0; elseled <= 1'b1; NS <= CHECK_H;end//排除任意情况,增强FSM安全性default:beginNS  <= CHECK_H;led <= 1'b1;endendcaseend
endmodule

  testbench测试文件如下:

`timescale 1ns/1ps`define    clk_period 20module check_hello_tb();reg  clk;         //50M时钟信号reg  rst;         //低电平复位reg  [7:0]asci;   //字符输入wire  led;        //控制led//例化测试模块check_hello  check_hello_test(.clk(clk),         //50M时钟信号.rst(rst),         //低电平复位.asci(asci),       //字符输入.led(led)          //控制led);//产生50M时钟信号initial clk = 1;always #(`clk_period / 2)clk <= ~clk;//开始测试initial beginrst  = 0;          //系统复位asci = 3'bx;#(`clk_period * 2);rst = 1;#(`clk_period);asci = "H";#(`clk_period);asci = "e";#(`clk_period);asci = "l";#(`clk_period);asci = "l";#(`clk_period);asci = "o";#(`clk_period);asci = "2";#(`clk_period);asci = "e";#(`clk_period);asci = "h";#(`clk_period);asci = "l";    #(`clk_period);$stop;end
endmodule

  测试结果如下,可以看到,刚开始输入数据是任意数据,FSM为CHECK_H状态,led输出高电平,保持熄灭;当检测到Hello序列时,led输出低电平,状态翻转;

  该测试中隐藏了一个重要的问题,我们在编写testbench的时候,将数据变化与时钟上升沿对齐,但在实际中数据变化会产生滞后,所以这时候我们为了更好地模拟实际情况,编写testbench就有一定的技巧:将输出变化延迟1-2ns,所以我们将testbench中的这行代码进行修改:

initial beginrst  = 0;          //系统复位asci = 3'bx;#(`clk_period * 2);  rst = 1;#(`clk_period);    //将这行修改为  #(`clk_period + 1); 将整体数据改变时间较时钟上升沿滞后1ns观察asci = "H";

  然后再运行仿真观察波形,可以看到,这次的波形更好地说明了实际情况,因为我们编写的一段式FSM整体在一个时序逻辑中,所以FSM只在时钟上升沿检测数据变化,也就是说,在第一个时钟内发生的变化,下一个时钟沿才能检测到:

  状态转移图如下,可以看到按照预定设计执行:

  综合出来的电路图如下,可以看到FSM实现的重点在于状态寄存器,耗费资源很少,由综合报告也可看出:

  

  在一段式描述方法中可以看到,虽然一个alaways块就可以解决问题,但描述不清晰,不利于维护修改,并且不利用附加约束,不利于综合其和布局布线器对设计的优化;

  4、两段式描述法:一个always块描述状态转移,另一个always块描述状态判断转移条件

  verilog代码如下:

//检测“Hello”后led状态翻转module check_hello(input  clk,         //50M时钟信号input  rst,         //低电平复位input  [7:0]asci,//字符输入output reg led     //控制led
);//状态寄存器reg  [4:0]NS;     //nextstatereg  [4:0]CS;    //currentstate//状态独热编码localparam CHECK_H   = 5'b0_0001,CHECK_e   = 5'b0_0010,CHECK_la  = 5'b0_0100,CHECK_lb  = 5'b0_1000,CHECK_o   = 5'b1_0000;//两段式状态机//第一个always块描述状态转移always@(posedge clk,negedge rst)if(!rst)CS  <= CHECK_H;elseCS  <= NS;        //状态转移到下一状态//第二个always块描述状态输出以及判断状态转移        always@(CS,asci)case(CS)CHECK_H:beginled = 1'b1;if(asci == "H")NS <= CHECK_e;elseNS <= CHECK_H;endCHECK_e:beginled = 1'b1;if(asci == "e")NS <= CHECK_la;elseNS <= CHECK_H;endCHECK_la:beginled = 1'b1;if(asci == "l")NS <= CHECK_lb;elseNS <= CHECK_H;endCHECK_lb:beginled = 1'b1;if(asci == "l")NS <= CHECK_o;elseNS <= CHECK_H;endCHECK_o:beginif(asci == "o")led = 1'b0;elseled = 1'b1;NS <= CHECK_H;enddefault: beginNS  <= CHECK_H;led = 1'b1;endendcase
endmodule

  用之前修改后的testbench测试文件进行测试,测试结果如下,可以看到,两段式状态机描述结果和之前一段式描述并没有差异:

  综合后状态转移图如下,与之前也没有差异:

  再查看综合后的电路图,与之前有了较大的差异,可以看到,这次led输出采用组合逻辑输出,之前采用一个带有使能端的D触发器输出:

  再来分析一下资源占用情况,在上次设计中,总共占用了14个LE,6个寄存器资源,而这次占用了12个LE,5个寄存器资源,因为两段式描述更清晰,便于软件进行分析优化,所以设计也更加优良:

  

  接下来我们再次人为上演上一个testbench中的错误,将数据改变与时钟上升沿对齐,测试波形如下:

  可以看出来,因为我们描述输出采用组合逻辑,所以这样的测试是错误的,不仅NS与CS变化没有同步,而且状态也没有翻转,但在实际中,数据变化与时钟上升沿有可能会同时发生,显然这个设计就会出错。所以为了从根本上避免这种情况,一般在输出部分插入一级额外的时钟信号,用来保证信号稳定性,所以我们引入接下来的三段式FSM描述。

  

  5、三段式描述法:一个always块采用时序逻辑描述状态转移,一个always块采用组合逻辑判断状态转移条件,一个always块采用时序逻辑描述状态输出

  verilog代码如下:

//检测“Hello”后led状态翻转module check_hello(input  clk,         //50M时钟信号input  rst,         //低电平复位input  [7:0]asci,//字符输入output reg led     //控制led
);//状态寄存器reg  [4:0]NS;     //nextstatereg  [4:0]CS;    //currentstate//状态独热编码localparam CHECK_H   = 5'b0_0001,CHECK_e   = 5'b0_0010,CHECK_la  = 5'b0_0100,CHECK_lb  = 5'b0_1000,CHECK_o   = 5'b1_0000;//三段式状态机//第一个always块描述状态转移always@(posedge clk,negedge rst)if(!rst)CS  <= CHECK_H;elseCS  <= NS;        //状态转移到下一状态//第二个always块判断状态转移        always@(CS,asci)case(CS)CHECK_H:beginif(asci == "H")NS <= CHECK_e;elseNS <= CHECK_H;endCHECK_e:beginif(asci == "e")NS <= CHECK_la;elseNS <= CHECK_H;endCHECK_la:beginif(asci == "l")NS <= CHECK_lb;elseNS <= CHECK_H;endCHECK_lb:beginif(asci == "l")NS <= CHECK_o;elseNS <= CHECK_H;endCHECK_o:beginNS <= CHECK_H;enddefault: beginNS  <= CHECK_H;endendcase//第三个always块描述状态输出always@(posedge clk,negedge rst)if(!rst)led <= 1'b1;    //led熄灭else begincase(CS)CHECK_H:        led <= 1'b1;CHECK_e:    led <= 1'b1;CHECK_la:led <= 1'b1;CHECK_lb:led <= 1'b1;CHECK_o:  if(asci == "o")led <= 1'b0;elseled <= 1'b1;default:led <= 1'b1;endcaseend
endmodule

  testbench依然采用之前修改后的测试文件(数据整体延迟时钟1ns)进行测试,结果如下,测试结果和之前相同:

  再次人为进行查错,将数据与时钟对齐进行测试,结果如下:

  结果是不是很令人惊喜^_^,可以看到当数据变化与时钟上升沿对齐的时候,结果依然正确,这是因为状态输出不是组合逻辑,而是时序逻辑,在输出前面插入了一级时钟信号就有效的解决了问题,所以一般描述FSM时选用三段式描述法描述;

  再来看看状态转移图,与之前两种描述也没有差异:

  综合后的电路如下,可以验证之前说的,在输出前插入了一级时钟信号:

  再来对比一下资源占用,可以看到三段式描述法中和了前面两种描述方法的优点,总共耗费了12个LE,6个寄存器资源,虽然多了一个寄存器资源,却有效的避免了重大错误,这也暗示了一个数字设计里最重要的思想,中和设计思想有的时候可以需要性能的提升,但在提升性能的同时也会增加资源占用,设计面积增大,所以两者中和往往是最优秀的设计;

  

  

  至此,一个完整的示例就设计实现完成了,最后再进行几点补充:

  1、因为这个示例中输出只有led,所以我们采用了直接写在FSM描述里面,如果输出较多,可以利用task/endtask将输出进行封装;

  2、在整个设计中,不管是任何一种描述方式,只要case,就会写入default选项,这一点也一直在设计过程中没有提到;

   default选项是必须要写入的,这样就符合了刚开始提到的状态机评判标准最重要的一点——安全性,因为不管我们采用二进制编码还是one-hot编码,在实际应用中可能会由于其他因素(比如噪声)产生突变,这时候就会进入default选项,然后重新启动状态机,可谓优点多多;

  3、这一点也是最重要的一点,状态机设计不是一种具体的事物,比如说verilog语法就是固定的,它更多的是一种对具有逻辑规律和时序逻辑事件的一种描述思想,所以,即使前面提到了一段式,两段式,三段式FSM描述方法,在实际中,如果需要,我们可以分离出来4个always块,5个always块等等,这里的一段式,两段式,三段式反映的只是一种设计思想,希望在以后的数字设计中有更多的体会!

    

  

【FPGA入门教程】(七)FSM(Finite State Machine,有限状态机)设计相关推荐

  1. 有限状态机FSM(finite state machine) 二

    有限状态机FSM(finite state machine) 二 延续上一篇继续有限状态机 上一篇中的状态切换判断是在每一个状态类的 OnExecute 方法中各种 if else 硬编码逻辑 当状态 ...

  2. 有限状态机FSM(Finite State Machine)及实现方式介绍(转)

    原文:https://www.cnblogs.com/barrywxx/p/12860573.html 一.为什么引入有限状态机? 最近做一个项目,项目中很多实体(Entity),每个实体都有很多状态 ...

  3. 有限状态机FSM(finite state machine) 一

    有限状态机FSM(finite state machine) 一 有限状态机又称有限自动状态机,它拥有有限数量的状态,每个状态代表不同的意义,每个状态可以切换到 零-多 个状态.任意时刻状态机有且只能 ...

  4. 计算机态,(计算机)有限态自动机,FSM(finite state machine),音标,读音,翻译,英文例句,英语词典...

    补充资料:ω-有限自动机 ω-有限自动机 ω-finite state automata 1094·.一youx一anz}dongJ-..有限自动机(.一rinite state automata)一 ...

  5. 【决策状态机FSM(finite state machine)梳理】

    背景:写论文需要,对需要有限状态机部分进行整理.论文内容是关于自动驾驶避撞决策. 参考Junior: The Stanford Entry in the Urban Challenge的有限状态机 1 ...

  6. 有限状态机 FSM——Finite State Machine

    有限状态机 1.状态机的结构 2.Mealy状态机和Moore状态机 3.用Verilog来描述可综合的状态机 实例 序列检测器 ADC采样控制电路 按键消抖 1.状态机的结构 其中F和G是两个有关状 ...

  7. 证明与计算(7): 有限状态机(Finite State Machine)

    什么是有限状态机(Finite State Machine)? 什么是确定性有限状态机(deterministic finite automaton, DFA )? 什么是非确定性有限状态机(nond ...

  8. 《计算机组成与CPU设计实验》5有限状态机的Verilog HDL描述(Finite State Machine,FSM)

    多数控制逻辑都可以用有限状态机描述 状态机 状态机是组合逻辑和时序逻辑的特殊组合 时序逻辑用于存储状态 组合逻辑用于产生次态和产生输出 状态的数量是有限的,故称为有限状态机(Finite State ...

  9. FSM(Finite State Machine,有限状态机)设计

    有限状态机(Finite State Machine, FSM),根据状态机的输出是否与输入有关,可分为Moore型状态机和Mealy型状态机.Moore型状态机输出仅仅与现态有关和Mealy型状态机 ...

  10. 图像设定阈值二值matlab,“图像类型转换II——使用“导入、导出和转换”中的“阈值法”转换为二值图像”,MATLABImageProcessingToolbox,入门教程,七,之...

    1 imbinarize函数 在 [MATLAB Image Processing Toolbox 入门教程二]快速入门之"亮度校正"和"目标识别" 中我们已经 ...

最新文章

  1. c++ clr编译dll在c#调用时出现“试图加载不正确的格式”“找不到dll”错误的解决...
  2. poj2362 DFS+剪枝
  3. ALV TREE学习笔记
  4. kafka记录及面试题
  5. src漏洞挖掘|一个谎言需要无数谎言来弥补
  6. NFS4文件锁机制探秘
  7. 管理全局包、缓存和临时文件夹
  8. orm2 中文文档 3.3 模型钩子
  9. 增强光学系统设计 | Zemax 全新 22.2 版本产品现已发布!
  10. 使用pip出现报错:Could not find a version that satisfies the...No matching distribution distributio...
  11. 所处网络导致虚拟机的域名解析失败
  12. TZC Python编程入门教程 ————题解
  13. 国家开发银行软件测试工资待遇,一名省直公务员告诉你绝对准确的国家开发银行员工收入,与省厅公务员作详细对比...
  14. MFC 登陆界面 创建地方
  15. 【中秋快乐】如何用three.js实现我的太空遐想3D网页
  16. Web测试如何让IT门外汉更好的入门篇
  17. 白杨SEO:软文怎么写?怎么才能写好推广软文,有哪些技巧?
  18. Java poi 在 Excel中生成统计图
  19. ramfs, rootfsinitramfs
  20. HTML ins 标签

热门文章

  1. 2022高教社杯 国赛数学建模 B题思路
  2. 关于算法学习的总结和感悟
  3. 基于深度神经网络的股票多因子预测模型
  4. Room 使用及初步分析
  5. 369、Java中级24 -【Spring】 2020.08.26
  6. 桌面远程控制计算机名字和,Win7系统远程控制其他计算机桌面教程
  7. 2019 ICPC 上海站网络赛 K.Peekaboo (圆上整点)
  8. T-Code (Controlling)
  9. java jca_Java加密体系结构(JCA)参考指南
  10. ReadyInterview