基于FPGA的有限状态机浅析
前言:状态机大法好,状态机几乎可以实现一切时序逻辑电路。
有限状态机(Finite State Machine, FSM),根据状态机的输出是否与输入有关,可分为Moore型状态机和Mealy型状态机。Moore型状态机输出仅仅与现态有关和Mealy型状态机不仅与现态有关,也与输入有关,所以会受到输入的干扰,可能会产生毛刺(Glith)的现象,所以我们通常使用的是Moore型状态机。
状态机的编码,二进制编码(Binary),格雷码编码(Gray-code),独热码(One-hot)。不同的编码方式是防止在状态转移中发生突变,使得状态转移更为稳定,系统更加可靠,但是通常情况下我们直接采用的是二进制进行编码,除非系统对稳定性和状态编码有特殊要求。
状态机的描述,一段式、二段式、三段式。
一段式状态机,将组合逻辑和时序逻辑混合在一起,这样的写法对于逻辑简单的状态机来说还是可以使用的,但是对于复杂的逻辑就不推荐了,如果状态复杂也会容易出错,而且一个always块中信号太多也不利于维护和修改。
//状态参数声明
parameter S0 = 'b0000,
S1 = 'b0001,
s2 = 'b0010;
//FSM one segment
reg [:] state;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
state <= S0;
else begin
case(state)
S0:
S1:
S2:
.
.
.
default:
endcase
end
end
两段式状态机也是一种常用的写法,它把组合逻辑和时序逻辑区分出来,第一段负责状态的转移,第二段是组合逻辑赋值,但是这种写法的缺点是,组合逻辑较容易产生毛刺等常见问题,关于组合逻辑较容易产生毛刺原因,下文会提到。
//状态参数声明
parameter S0 = 'b0000,
S1 = 'b0001,
s2 = 'b0010;
//FSM two segment
reg [:] pre_state;
reg [:] next_state;
//--------------------------------------
//FSM one
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
pre_state <= S0;
else
pre_state <= next_state;
end //FSM two
always @(*)begin
case(pre_state)
S0:
S1:
S2:
.
.
.
default:;
endcase end
三段式状态机就可以较好的解决一段二段的不足,我也是比较推荐的写法,第一段采用时序逻辑负责状态转移,第二段组合逻辑负责数据赋值,第三段时序逻辑负责输出,代码层次清晰,容易维护,时序逻辑的输出解决了两段式写法中组合逻辑的毛刺问题。但是资源消耗会多一些,此外,三段式从输入到输出会比一段式和二段式延迟一个时钟周期。在书写状态机的时候,一定要事先设计好状态转移图,将所有的状态都考虑到,避免状态进入死循环,或者跳到偏离态。
//状态参数声明
parameter S0 = 'b0000,
S1 = 'b0001,
s2 = 'b0010;
//FSM three segment
//--------------------------------------
//FSM one
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
pre_state <= S0;
else
pre_state <= next_state;
end //FSM two
always @(*)begin
case(pre_state)
S0:
S1:
S2:
.
.
.
default:;
endcase
end //FSM three
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
dout <= 'b0;
else begin
case(pre_state)
S0:
S1:
S2:
.
.
.
default:;
endcase
end
end
如下图,我通过一个实例来说明一下状态机的使用。下面是一个序列检测状态转移图,检测是的使1101这个序列,我们给这个序列的检测序列是11101 1101这一串数据。在这个序列检测器中,我们允许使用重复位。也就是说,前一个“1101”最后一位的1可以作为后一个“1101”序列的起始位。如果不允许重复为位,只需要将S4到S2的转移替换成S4到S1即可。

首先,从输出状态S0开始检测,当S0检测到1时跳到S1,否则跳回S0,S1检测到1状态跳到S2,否则跳回S0,S2检测到0状态跳到S3,否则还停留在S2状态,因为这里我们的检测序列允许重复位,所以S1检测到的1与S2检测到的1保留,不舍弃作为一下组1101的前两位,所以只需要继续检测下一位数据即可。S3、S4的状态一次类推。这里举着个例子是为了说明状态机的状态跳转,在我们实际的设计中这种情况也是会遇到的。
在使用状态机来描述时序电路的时候,首先应该做的是画出状态转移图,然后根据状态跳转来描述代码,最后便会事半功倍。这段序列检测的代码我也贴出来。当然这只是序列检测的一个应用了,我前面也说了状态机机会可以实现一切的时序电路。如果你遇到实在不好解决的设计,那么这个时候,你就可以考虑一下使用状态机了。
module state(
input mclk,
input rst_n,
input din,
output reg dout;
); parameter s0 = 'b000,
s1 = 'b001,
s2 = 'b010,
s3 = 'b011,
s4 = 'b100;//状态
//此为三段式状态机,还有一段式状态机,二段式状态机
reg [:] present_state, next_state;
//用摩尔状态机设计1101序列检测器
//状态寄存器
always @(posedge mclk or negedge rst_n)
begin
if(!rst_n)
present_state <= s0;
else
present_state <= next_state;
end //状态转换模块
always @(*)
begin
case(present_state)
s0: if(din==)
next_state = s1;
else
next_state = s0;
s1: if(din==1)
next_state = s2;
else
next_state = s0;
s2: if(din==0)
next_state = s3;
else
next_state = s2;
s3: if(din==)
next_state = s4;
else
next_state = s0;
s4: if(din==)
next_state = s0;
else
next_state = s2;
default: next_state = s0;
endcase
end always @(posedge clk or negedge rst_n)begin
if(!rst_n)
dout <= 'b0;
else if(present_state ==s4)
dout <= 'b1;
else
dout <= 'b0;
end endmodule
在状态机的设计中,一段式状态机用时序逻辑,二段式状态机第一段用时序逻辑,第二段用组合逻辑,三段式状态机第一段用时序逻辑,第二段用组合逻辑,第三段用时序逻辑。我在设计的时候,尝试把第二段写成时序逻辑,最终结果并没有影响,时序逻辑随时钟变化,组合逻辑是直接赋值,所以在第三段状态机进行输出时,输出结果肯定是稳定的,但是这样会限制fmax。如果用时序逻辑的主频率过高的话,可能不如第二段组合逻辑赋值来的稳定,这里就还需要考虑到时序分析了,暂且不谈。这里还需要提的是使用三段式状态机相较于一段二段式,会延迟一个时钟周期输出,就是因为第三段使用了时序逻辑的缘故。
既然谈状态机的时候,说到了组合逻辑会产生毛刺的现象,那么这里就顺便整理一下,为什么组合逻辑会产生毛刺,组合逻辑的冒险与竞争分析。
竞争(Competition)在组合逻辑电路中,某个输入变量通过两条或两条以上的途径传到输出端,由于每条途径延迟时间不同,到达输出门的时间就有先有后,这种现象称为竞争。把不会产生错误输出的竞争的现象称为非临界竞争。把产生暂时性的或永久性错误输出的竞争现象称为临界竞争。
冒险(risk)信号在器件内部通过连线和逻辑单元时,都有一定的延时。延时的大小与连线的长短和逻辑单元的数目有关,同时还受器件的制造工艺、工作电压、温度等条件的影响。信号的高低电平转换也需要一定的过渡时间。由于存在这两方面因素,多路信号的电平值发生变化时,在信号变化的瞬间,组合逻辑的输出有先后顺序,并不是同时变化,往往会出现一些不正确的尖峰信号,这些尖峰信号称为"毛刺"。如果一个组合逻辑电路中有"毛刺"出现,就说明该电路存在冒险
竞争冒险(Competition risk)产生原因:由于延迟时间的存在,当一个输入信号经过多条路径传送后又重新会合到某个门上,由于不同路径上门的级数不同,或者门电路延迟时间的差异,导致到达会合点的时间有先有后,从而产生瞬间的错误输出。
首先看下面这个电路,使用了两个逻辑门,一个非门和一个与门,本来在理想情况下F的输出应该是一直稳定的0输出,但是实际上每个门电路从输入到输出是一定会有时间延迟的,这个时间通常叫做电路的开关延迟。而且制作工艺、门的种类甚至制造时微小的工艺偏差,都会引起这个开关延迟时间的变化。


实际上如果算上非门的延迟的话,那么F最后就会产生毛刺。信号由于经由不同路径传输达到某一汇合点的时间有先有后的现象,就称之为竞争,由于竞争现象所引起的电路输出发生瞬间错误的现象,就称之为冒险,所以在设计中我们要注意避免这个现象,最简单的避免方法是尽量使用时序逻辑同步输出。
这篇状态机和组合逻辑的冒险竞争就聊到这里,下次我们接着说时序逻辑的冒险竞争。
参考资料:百度百科,冒险竞争、《FPGA设计技巧与案例开发详解》、《FPGA数字逻辑设计教程——Verilog》、《深入浅出玩转FPGA》等网络文章。

转载请注明出处:NingHeChuan(宁河川)
个人微信订阅号:开源FPGA
如果你想及时收到个人撰写的博文推送,可以扫描左边二维码(或者长按识别二维码)关注个人微信订阅号
知乎ID:NingHeChuan
微博ID:NingHeChuan
原文地址:http://www.cnblogs.com/ninghechuan/p/7898297.html
基于FPGA的有限状态机浅析的更多相关文章
- FPGA经典:Verilog传奇与基于FPGA的数字图像处理原理及应用
一 简述 最近恶补基础知识,借了<<Verilog传奇>>,<基于FPGA的嵌入式图像处理系统设计>和<<基千FPGA的数字图像处理原理及应用>& ...
- 基于FPGA的飞机的小游戏
基于FPGA的飞机的小游戏 实验原理 该实验主要分为4个模块,采用至上而下的设计方法进行设计.由50M的晶振电路提供时钟源,VGA显示控制模块.图形显示控制模块.移动模块的时钟为25M,由时钟分频电路 ...
- 基于FPGA的音频信号的FIR滤波(Matlab+Modelsim验证)
1 设计内容 本设计是基于FPGA的音频信号FIR低通滤波,根据要求,采用Matlab对WAV音频文件进行读取和添加噪声信号.FFT分析.FIR滤波处理,并分析滤波的效果.通过Matlab的分析验证滤 ...
- 基于FPGA的图像去噪
目录 结构图 其中FPGA 控制模块为核心,通过它实现视频图像数据的获取.缓存.处理和控制各模块间通讯[1].由CCD 相机对目标成像,高速图像数据由camera link 实时传输[2],经信号转换 ...
- 基于FPGA的线阵CCD图像测量系统研究——笔记
本文是对基于FPGA的线阵CCD图像测量系统研究(作者:高尚)的阅读笔记 第一章绪论 1. 读读看 读了前面的摘要依然没有看懂作者要做什么.接着往下读....终于看到了一个字眼“基于机器视觉的图像测量 ...
- 基于FPGA的按键扫描程序
最近在学习FPGA,就试着写了个按键扫描的程序.虽说有过基于单片机的按键扫描处理经验,对于按键的处理还是有一些概念.但是单片机程序的编写通常都采用C写,也有用汇编,而FPGA却是采用VHDL或者Ver ...
- 基于FPGA的DW8051移植(三)
总结一下问题: 1) http://www.cnblogs.com/sepeng/p/4137405.html 基于FPGA的DW8051移植(一)里面用modelsim观测波形发现程序进入了ida ...
- 基于FPGA的红外遥控解码与PC串口通信
基于FPGA的红外遥控解码与PC串口通信 zouxy09@qq.com http://blog.csdn.net/zouxy09 这是我的<电子设计EDA>的课程设计作业(呵呵,这个月都拿 ...
- 基于FPGA的OLED真彩色动态图像显示的实现
源:基于FPGA的OLED真彩色动态图像显示的实现 作为第3代显示器,有机电致发光器件(Organic Light Emitting Diode,OLED)由于其主动发光.响应快.高亮度.全视角.直流 ...
随机推荐
- Django实现用户密码重置
使用Django内置的认证视图实现简单的通过邮箱重置密码的功能版本:django 1.11 在django.contrib.auth.views中提供了四个类视图用于密码重置 class Passwo ...
- ASP.NET MVC 学习笔记 1
1. 什么是ASP.Net MVC ASP.Net MVC是一种开发Web应用程序的工具(is a web application development framework),采用Model-Vie ...
- KindEditor文件上传成功前端显示上传失败
一.使用kindeditor 上传图片 ,根据kindeditor 要求返回了相应的数据, 但是kindeditor 插件显示上传失败!!! 解决方法: 各个版本位置可能不同!!! 1.修改kinde ...
- SPARK 学习笔记一
1.Spark基于内存进行运算 2.Spark核心是SprintContext,是程序的入口 3.RDDs是提供抽象的数据集,通过RDDS可以方便对分布在各个节点的数据进行计算,而且忽略细节 4.RD ...
- Loadrunner Http接口Get/Post方法性能测试脚本解析
最近使用LoadRunner 11进行了一次完整的Http WEB接口性能测试,下面介绍下Http接口Get/Post方法性能测试脚本通用编写方法. 1. Http接口性能测试基本流程 首先定义了一个 ...
- macOs升级到10.13.1Beta || JAVA升级到最新版之后PhpStorm菜单栏问题
macOs升级到10.13.1Beta || JAVA升级到最新版之后PhpStorm菜单栏会消失,估计不止出现在PhpStorm,一系列jetbrains的产品可能都会有这个问题,包括eclipis ...
- 一个简单的makefile文件编写
下午闲来无聊,就打开很久没动过的linux系统想熟悉熟悉在linux上面编译代码,结果一个makefile文件搞到晚上才搞定,哈哈! 先把代码简单贴上来,就写了一个冒泡排序: sort.h: #ifn ...
- python matplotlib 图表局部放大
import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes ...
- 聊聊Java的字节码
本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃 巴山楚水凄凉地,二十三年弃置身.怀旧空吟闻笛赋,到乡翻似烂柯人.沉舟侧畔千帆过,病树前头万木春 ...
- 直方图均衡化C++实现
直方图均衡化在图像增强方面有着很重要的应用.一些拍摄得到的图片,我们从其直方图可以看出,它的分布是集中于某些灰度区间,这导致人在视觉上感觉这张图的对比度不高.所以,对于这类图像,我们可以通过直方图均衡 ...