三段式有限状态机

article/2025/9/20 0:21:30

    一段式、二段式、三段式状态机是按照书写FSM时使用的always块数目进行划分的,一般而言对于简单的状态机,可以使用一段式,其代码量以及使用资源都最少,但如果状态机较复杂,一段式状态机会对代码维护产生很大的不便,因此多使用便于维护的三段式状态机。下面对几种状态机进行介绍。

一段式状态机

   一段式FSM在一个时序always块中完成所有的状态转移以及输出工作,使用非阻塞赋值,如以下代码

//一段式状态机
module FSM1(
input				clk,
input				rst_n,
input		[3:0]	i,
output	reg	[3:0]	o
);parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;reg	[3:0]	state;
always @(posedge clk or negedge  rst_n)beginif(!rst_n)beginstate	<= S1;o		<= 0;endelse begincase(state)S1: beginstate	<= S2;o		<= i + 1;endS2: beginstate	<= S3;o		<= i + 2;endS3: beginstate	<= S4;o		<= i + 3;endS4: beginstate	<= S1;o		<= i + 4;enddefault: beginstate	<= S1;o		<= 0;endendcaseend
endendmodule

  其仿真结果如下

在这里插入图片描述

二段式状态机

  二段式状态机使用两个always块,实现组合逻辑与时序逻辑的分开,其中第一个always块为时序逻辑,控制状态的更新,第二个always块为组合逻辑,产生下一状态next_state,同时产生状态输出。其输出是基于当前状态state的组合逻辑,代码如下

//二段式状态机
module FSM2(
input				clk,
input				rst_n,
input		[3:0]	i,
output	reg	[3:0]	o
);parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;reg		[3:0]	state;
reg		[3:0]	next_state;always @(posedge clk or negedge  rst_n)begin	//时序逻辑if(!rst_n)beginstate	<= S1;endelse beginstate	<= next_state;end
endalways @(*) begin								//组合逻辑,产生next_state和outputif(!rst_n) beginnext_state	<= S1;o	<= 0;endelse begincase(state)S1: beginnext_state	<= S2;o			<= i + 1;endS2: beginnext_state	<= S3;o			<= i + 2;endS3: beginnext_state	<= S4;o			<= i + 3;endS4: beginnext_state	<= S1;o			<= i + 4;enddefault: beginnext_state	<= S1;o			<= 0;endendcaseend
endendmodule

  仿真结果如下

在这里插入图片描述

  可以看到,其输出与一段式FSM相同,而在状态上,其next_state与一段式FSM的state变化相同

三段式状态机

第三段为基于state的组合逻辑

  我们可以看到,二段式FSM的第二个always块实际上是可以再进行划分的,可以分为生成next_state、产生输出两部分,因此我们直接拆分二段式FSM,获得逻辑输出基于当前状态state的三段式状态机:

//三段式状态机
module FSM3(
input				clk,
input				rst_n,
input		[3:0]	i,
output	reg	[3:0]	o
);parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;reg		[3:0]	state;
reg		[3:0]	next_state;always @(posedge clk or negedge  rst_n)begin	//时序逻辑if(!rst_n)beginstate	<= S1;endelse beginstate	<= next_state;end
endalways @(*) begin								//组合逻辑,产生next_stateif(!rst_n) beginnext_state	<= S1;endelse begincase(state)S1: beginnext_state	<= S2;endS2: beginnext_state	<= S3;endS3: beginnext_state	<= S4;endS4: beginnext_state	<= S1;enddefault: beginnext_state	<= S1;endendcaseend
endalways @(*) begin								//组合逻辑,基于state产生逻辑输出if(!rst_n) begino	<= 0;endelse begincase(state)S1: begino	<= i + 1;endS2: begino	<= i + 2;endS3: begino	<= i + 3;endS4: begino	<= i + 4;enddefault: begino	<= 0;endendcaseend
endendmodule

  由于此三段式FSM仅仅是在写法上将二段式FSM的第二个always块进行了拆分,因此其状态变化以及输出与二段式FSM完全相同,仿真结果如下:

在这里插入图片描述

第三段为基于state的时序逻辑

  由于第三段使用的是组合逻辑,因此比较容易出现毛刺,那么很通常的一个想法是将第三个always块变为时序逻辑,通过插入寄存器,实现对毛刺的消除。然而直接将 always(*) 改为 always(posedge clk or negedge rst_n) 就可以了吗?请看下面的代码

//三段式状态机2
module FSM3_2(
input				clk,
input				rst_n,
input		[3:0]	i,
output	reg	[3:0]	o
);parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;reg		[3:0]	state;
reg		[3:0]	next_state;always @(posedge clk or negedge  rst_n)begin	//时序逻辑if(!rst_n)beginstate	<= S1;endelse beginstate	<= next_state;end
endalways @(*) begin								//组合逻辑,产生next_stateif(!rst_n) beginnext_state	<= S1;endelse begincase(state)S1: beginnext_state	<= S2;endS2: beginnext_state	<= S3;endS3: beginnext_state	<= S4;endS4: beginnext_state	<= S1;enddefault: beginnext_state	<= S1;endendcaseend
endalways @(posedge clk or negedge rst_n) begin	//时序逻辑if(!rst_n) begino	<= 0;endelse begincase(state)			//基于当前状态state产生同步时序输出,其输出将出现一拍延迟S1: begino	<= i + 1;endS2: begino	<= i + 2;endS3: begino	<= i + 3;endS4: begino	<= i + 4;enddefault: begino	<= 0;endendcaseend
endendmodule

在这里插入图片描述

  可以看到,其输出将相较于第三段使用组合逻辑的FSM延迟一拍,有些同学在书写三段式FSM时没有注意到这一点,直接将一段式FSM重构为这种三段式FSM,却以为他们的逻辑是一样的,从而导致在逻辑上出现问题。

第三段为基于next_state的时序逻辑

  在二段式FSM的讨论中我们提到过,其next_state的变化才是和一段式FSM相同的,因此在将第三段转换为同步时序逻辑时,不应基于当前状态state,而是应当基于下一状态next_state,代码如下

//三段式状态机3
module FSM3_3(
input				clk,
input				rst_n,
input		[3:0]	i,
output	reg	[3:0]	o
);parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;reg		[3:0]	state;
reg		[3:0]	next_state;always @(posedge clk or negedge  rst_n)begin	//时序逻辑if(!rst_n)beginstate	<= S1;endelse beginstate	<= next_state;end
endalways @(*) begin								//组合逻辑if(!rst_n) beginnext_state	<= S1;endelse begincase(state)S1: beginnext_state	<= S2;endS2: beginnext_state	<= S3;endS3: beginnext_state	<= S4;endS4: beginnext_state	<= S1;enddefault: beginnext_state	<= S1;endendcaseend
endalways @(posedge clk or negedge rst_n) begin	//同步时序逻辑if(!rst_n) begino	<= 0;endelse begincase(next_state)	//使用next_state,此时其输出与一段式FSM相同S1: begino	<= i + 1;endS2: begino	<= i + 2;endS3: begino	<= i + 3;endS4: begino	<= i + 4;enddefault: begino	<= 0;endendcaseend
endendmodule

在这里插入图片描述

  可以看到,此时三段式FSM的输出与一段式FSM相同。

  这里我们发现一个有意思的地方,使用next_state产生输出可以输出提前一拍,正是这提前的一拍将同步时序滞后的一拍给抵消了。那如果我们在第三段使用组合逻辑,但也基于next_state进行输出,就可以将输出提前一拍!

关于状态的定义

  有小伙伴可能会疑惑关于状态的定义部分,为什么要定义为独热的形式?

parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;

  因为这样,由任一状态向其他状态转换时,其出现的不稳定状态都是无效的,这样可以提高系统的稳定性。比如state由S1向S2转换时,可能出现0011或者0000这两种情况,而这两种都是无效状态,很容易辨识剔除。而如果我们使用下面的定义方法

parameter	S1	= 4'b0000;
parameter	S2	= 4'b0001;
parameter	S3	= 4'b0010;
parameter	S4	= 4'b0011;

  当从状态S2向S3转换时,可能出现的0011或者0000,分别对应状态S4、S1,此时将可能导致逻辑错误,尤其是输出是关于state的组合逻辑的时候。


http://chatgpt.dhexx.cn/article/i7FD7i3B.shtml

相关文章

状态机,从细节出发(一段式、两段式、三段式,moore型、mealy型)

目录 前言 一、状态机要素 二、状态机描述方法 1、一段式描述方法 2、两段式描述方法 3、三段式描述方法 三、关系 1、一段式与三段式 2、两段式与三段式 3、三种FSM描述方法比较表 四、状态机的种类 1、Moore型状态机 2、Mealy型状态机 3、注意点 五、举例 1、…

时间复杂度与空间复杂度分析(递归与非递归比较)

时间复杂度&#xff1a; 一般情况下&#xff0c;算法中基本操作重复的次数就是问题规模n的某个函数f&#xff08;n&#xff09;&#xff0c;进而分析f&#xff08;n&#xff09;随n的变化情况并确定T&#xff08;n&#xff09;的数量级。这里用‘o’来表示数量级&#xff0c;给…

数据结构:时间复杂度空间复杂度(递归)

转载文章 时间复杂度&#xff1a; 一般情况下&#xff0c;算法中基本操作重复执行的次数是问题规模n的某个函数f(n)&#xff0c;进而分析f(n)随n的变化情况并确定T(n)的数量级。这里用"O"来表示数量级&#xff0c;给出算法的时间复杂度。 T(n)O(f(n)); 它表示随着问…

递归理解以及时间复杂度计算

一.复杂度分析&#xff1a; 可以理解为递归的深度就是空间复杂度&#xff0c;时间复杂度就是O(T*depth),其中&#xff34;是每个递归函数的时间复杂度&#xff0c;depth是递归深度&#xff0e; #空间复杂度O(1) def sum1_(n):res 0for i in range(n1):resireturn res#递归 空…

递归的时间与空间复杂度

一、 递归的时间复杂度 递归算法的时间复杂度 递归次数 \times 每次递归的时间复杂度。 递归次数&#xff1a;可以通过画递归树&#xff0c;数递归树的节点数&#xff0c;得到递归次数。 二、递归的空间复杂度 递归算法的空间复杂度 递归深度 \times 每次入栈的空间…

【转】Tomcat相关面试题,看这一篇就够了!

转自公众号&#xff1a;Java思维导图 Tomcat相关的面试题出场的几率并不高&#xff0c;正是因为如此&#xff0c;很多人忽略了对Tomcat相关技能的掌握。下面这篇文章整理了Tocmat相关的系统架构&#xff0c;介绍了Server、Service、Connector、Container之间的关系&#xff0c…

10道Mybatis经典面试题,赶快上车吧!⚡⚡⚡⚡

1.Mybatis中#{}和${}的区别是什么&#xff1f; 1.1 #{}方式能够很大程度防止sql注入&#xff08;安全&#xff09;&#xff1b; ${}方式无法防止Sql注入。 1.2 在JDBC能使用占位符的地方&#xff0c;最好优先使用#{}&#xff1b; 在JDBC不支持使用占位符的地方&#xff0c;就…

mybatis面试题 一

一、MyBatis工作原理&#xff1f; 1、 创建SqlSessionFactory 2、 通过SqlSessionFactory创建SqlSession 3、 通过sqlsession执行数据库操作 4、 调用session.commit()提交事务 5、 调用session.close()关闭会话 1&#xff09;读取 MyBatis 配置文件&#xff1a;mybatis-c…

Java面试题Tomcat的优化经验

来源&#xff1a;传智论坛 Tomcat作为Web服务器&#xff0c;它的处理性能直接关系到用户体验&#xff0c;下面是几种常见的优化措施&#xff1a; 一、掉对web.xml的监视&#xff0c;把jsp提前编辑成Servlet。有富余物理内存的情况&#xff0c;加大tomcat使用的jvm的内存 二、服…

Tomcat相关面试题,看这篇就够了!保证能让面试官颤抖!

Tomcat相关的面试题出场的几率并不高&#xff0c;正式因为如此&#xff0c;很多人忽略了对Tomcat相关技能的掌握&#xff0c;下面这一篇文章最早发布在知识星球&#xff0c;整理了Tomcat相关的系统架构&#xff0c;介绍了Server、Service、Connector、Container之间的关系&…

【Tomcat专题】简单认识一下Tomcat总体架构

文章目录 什么是Tomcat&#xff1f;Tomcat的主要工作Tomcat总体架构连接器容器 请求定位流程 什么是Tomcat&#xff1f; 在Tomcat官方网站上是这样介绍的。 The Apache Tomcat software is an open source implementation of the Jakarta Servlet, Jakarta Server Pages, Jaka…

四张图带你了解Tomcat系统架构--让面试官颤抖的Tomcat回答系列!

俗话说&#xff0c;站在巨人的肩膀上看世界&#xff0c;一般学习的时候也是先总览一下整体&#xff0c;然后逐个部分个个击破&#xff0c;最后形成思路&#xff0c;了解具体细节&#xff0c;Tomcat的结构很复杂&#xff0c;但是 Tomcat 非常的模块化&#xff0c;找到了 Tomcat最…

Tomcat常见面试题

1、tomcat有哪些组件&#xff1f; 2、tomcat有哪些Connector&#xff1f; http ajp 3、tomcat的Valve的作用是什么&#xff1f; 给每一个虚拟主机定义访问日志 4、servlet的生命周期&#xff1f; Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过…

【金三银四】Tomcat面试题(2021最新版)

目录 前言 1、Tomcat的缺省端口是多少&#xff0c;怎么修改&#xff1f; 2、tomcat 有哪几种Connector 运行模式(优化)&#xff1f; 3、Tomcat有几种部署方式&#xff1f; 4、tomcat容器是如何创建servlet类实例&#xff1f;用到了什么原理&#xff1f; 5.tomcat 如何优化…

Tomcat面试题(2020最新版)

文章目录 Tomcat是什么&#xff1f;Tomcat的缺省端口是多少&#xff0c;怎么修改tomcat 有哪几种Connector 运行模式(优化)&#xff1f;Tomcat有几种部署方式&#xff1f;tomcat容器是如何创建servlet类实例&#xff1f;用到了什么原理&#xff1f;Tomcat工作模式Tomcat顶层架构…

「面试必背」Tomcat面试题(收藏)

「面试必背」Tomcat面试题&#xff08;建议收藏&#xff09; 2022-04-27 16:31java柚子茶 前言 在工作中&#xff0c;作为 Java 开发的程序员&#xff0c;Tomcat 服务器是大家常用的&#xff0c;也是很多公司现在正在用的。但是&#xff0c;在系统并发量比较大的情况下&…

Tomcat面试题(总结最全面的面试题)

Tomcat是什么&#xff1f; Tomcat 服务器Apache软件基金会项目中的一个核心项目&#xff0c;是一个免费的开放源代码的Web 应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发和调试JSP 程序的首…

【面试】Tomcat面试题

文章目录 Tomcat是什么&#xff1f;Tomcat的缺省端口是多少&#xff0c;怎么修改怎么在Linux上安装Tomcat怎么在Linux部署项目Tomcat的目录结构类似Tomcat&#xff0c;发布jsp运行的web服务器还有那些&#xff1a;tomcat 如何优化&#xff1f;tomcat 有哪几种Connector 运行模式…

Linux面试问题---常用命令

Linux面试问题---常用命令 1、cd命令 用于切换当前目录&#xff0c;参数是要切换到的目录的路径。 Cd /root/Documents #切换到/root/Documents目录Cd ./path 切换到当前目录下的path目录 Cd ../path 切换到上层目录下的path目录 2、ls命令 查看文件与目录的命令 3、grep…

Linux命令面试突击

Linux 命令常见面试题总结。 其它面试知识点突击整理&#xff1a; 序号文章1Java基础面试突击2JVM面试突击3设计模式面试突击4并发编程面试突击5消息队列Kafka面试突击6Redis面试突击7计算机网络面试突击8Spring面试突击9Dubbo面试突击10MyBatis面试突击11操作系统面试突击12…