“硬件设计很讲究并行设计思想,虽然用Verilog描述的电路大都是并行实现的,但是对于实际的工程应用,往往需要让硬件来实现一些具有一定顺序的工作,这就要用到状态机思想。什么是状态机呢?简单的说,就是通过不同的状态迁移来完成一些特定的顺序逻辑。硬件的并行性决定了用Verilog描述的硬件实现(臂如不同的always语句)都是并行执行的,那么如果希望分多个时间完成一个任务,怎么办?也许可以用多个使能信号来衔接多个不同的模块,但是这样做多少显得繁琐。状态机的提出会大大简化这一工作。”——特权同学《深入浅出玩转FPGA》

  一、状态机分类:

  1.Moore型:状态机的状态变化仅和当前状态有关(特权同学《深入浅出玩转FPGA》);时序逻辑电路的输出只取决于当前状态(夏宇闻《Verilog数字系统设计》)。设计高速电路时常用此类状态机,把状态变化直接用作输出。

  2.Mealy型:状态机的状态变化不仅与当前的状态有关,还取决于当前的输入条件(特权同学《深入浅出玩转FPGA》);时序逻辑的输出不但取决于状态还取决于输入(夏宇闻《Verilog数字系统设计》)。平常使用较多的是此类状态机。

  “其实这几种状态机之间,只要做一些改变,便可以从一种形式转变为另一种形式。把状态机精确的分为这类或那类,其实并不重要,重要的是设计者如何把握输出的结构能满足设计的整体目标,包括定时的准确性和灵活性。”——夏宇闻《Verilog数字系统设计》

  二、状态机编码:

   状态机的参数定义采用的都是独热码,和格雷码相比,虽然独热码多用了触发器,但所用组合电路可以省一些,因而使电路的速度和可靠性有显著提高,而总的单元数并无显著增加。采用独热编码后有了多余的状态,就有一些不可达到的状态。为此在case语句的最后需要增加default分支向。这可以用默认项表示该项,也可以用确定项表示,以确保回到初始状态。一般综合器都可以通过综合指令的控制来合理地处理默认项。

  三、实例分析

  状态机一般有三种不同的写法,即一段式、两段式和三段式的状态机写法,他们在速度、面积、代码可维护性等各个方面互有优劣,不要对任何一种写法给出“一棍子打死”的定论。手头上刚好有一个状态机的例子,借此记录一下三种状态机的Verilog写法。

  要求:

  售货机里有价值4元的脉动饮料,支持1元和2元硬币。请设计一个状态机,检测投入的硬币,当累计投入币值大于等于脉动价格时,售货机自动找零并弹出1瓶脉动饮料。硬币和商品都是一个一个的进出,不会出现一次性投很多个硬币弹出很多瓶脉动的情况。

信号 含义
clk 时钟信号
rst_n 复位信号
in 输入信号,币值,有1和2两种,投钱
out 输出信号,币值,有1和2两种,找零
out_vld 输出信号,脉动,为1则输出1瓶脉动

  状态转移图:

  根据要求,我们先把状态转移图画出来,绘画软件:Visio,如果没有安装也可以用wps自带应用的“流程图”功能:

  1. 一段式状态机

  只定义一个转移状态:state,总体结构是一段always时序逻辑,用于描述状态转移和输出。由于是时序逻辑能够自动保持,所以可以省略else。但建议在初始状态时(例如下文的S0),else处赋一下初始值。

 //======================================================================
// --- 名称 : FSM_1
// --- 作者 : xianyu_FPGA
// --- 日期 : 2018-12-15
// --- 描述 : 售货机练习,采用一段式状态机
//====================================================================== module FSM_1
//---------------------<端口声明>---------------------------------------
(
input clk ,
input rst_n ,
input [:] in ,
output reg [:] out ,
output reg out_vld
);
//---------------------<信号定义>---------------------------------------
reg [:] state ;
//---------------------<状态机参数>-------------------------------------
localparam S0 = 'b0001 ;
localparam S1 = 'b0010 ;
localparam S2 = 'b0100 ;
localparam S3 = 'b1000 ; //----------------------------------------------------------------------
//-- 状态机第1段
//----------------------------------------------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state <= S0;
out <= ;
out_vld <= ;
end
else begin
case(state)
S0: begin
if(in==)begin
state <= S1;
end
else if(in==)begin
state <= S2;
end
else begin
out <= ;
out_vld <= ;
end
end
S1: begin
if(in==)begin
state <= S2;
end
else if(in==)begin
state <= S3;
end
end
S2: begin
if(in==)begin
state <= S3;
end
else if(in==)begin
state <= S0;
out_vld <= ;
end
end
S3: begin
if(in==)begin
state <= S0;
out_vld <= ;
end
else if(in==)begin
state <= S0;
out <= ;
out_vld <= ;
end
end
default:state <= S0;
endcase
end
end endmodule

仿真波形如下:

结论:波形和预想一致!

  2. 二段式状态机:

  二段式状态机,第一段用时序逻辑描述state_c(现态)和state_n(次态),第二段用组合逻辑描述状态转移和输出。由于是组合逻辑,为避免产生锁存器,else处一定要写上 if 中说使用了的信号。

 //======================================================================
// --- 名称 : FSM_2
// --- 作者 : xianyu_FPGA
// --- 日期 : 2018-12-15
// --- 描述 : 售货机练习,采用二段式状态机
//====================================================================== module FSM_2
//---------------------<端口声明>---------------------------------------
(
input clk ,
input rst_n ,
input [:] in ,
output reg [:] out ,
output reg out_vld
);
//---------------------<信号定义>---------------------------------------
reg [:] state_c ;
reg [:] state_n ;
//---------------------<状态机参数>-------------------------------------
localparam S0 = 'b0001 ;
localparam S1 = 'b0010 ;
localparam S2 = 'b0100 ;
localparam S3 = 'b1000 ; //----------------------------------------------------------------------
//-- 状态机第1段
//----------------------------------------------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
state_c <= S0;
else
state_c <= state_n;
end //----------------------------------------------------------------------
//-- 状态机第2段
//----------------------------------------------------------------------
always@(*)begin
case(state_c)
S0: begin
if(in==)begin
state_n = S1;
end
else if(in==)begin
state_n = S2;
end
else begin
state_n = state_c;
out = ;
out_vld = ;
end
end
S1: begin
if(in==)begin
state_n = S2;
end
else if(in==)begin
state_n = S3;
end
else begin
state_n = state_c;
end
end
S2: begin
if(in==)begin
state_n = S3;
end
else if(in==)begin
state_n = S0;
out_vld = ;
end
else begin
state_n = state_c;
out_vld = ;
end
end
S3: begin
if(in==)begin
state_n = S0;
out_vld = ;
end
else if(in==)begin
state_n = S0;
out = ;
out_vld = ;
end
else begin
state_n = state_c;
out = ;
out_vld = ;
end
end
default:state_n = S0;
endcase
end endmodule

仿真波形如下所示:

结论:波形和预想一致!但是产生了毛刺,这也是二段式状态机的缺点。

毛刺产生原因:状态机通常包含主控时序进程、主控组合进程和辅助进程三个部分。其中,主控组合进程的任务是根据外部输入的控制信号和当前状态的状态值确定下一 状态的取向,并确定对外输出内容和对内部其他组合或时序进程输出控制信号的内容。一方面,由于有组合逻辑进程的存在,状态机输出信号会出现毛刺——竞争冒险现象;另一方面,如果状态信号是多位值的,则在电路中对应了多条信号线。由于存在传输延迟,各信号线上的值发生改变的时间则存在先后,从而使得状态迁移时在初始状态和目的状态之间出现临时状态——毛刺。

  简单理解为:state_n 会因为组合逻辑原因不断出现临时状态,这些状态是无效的,而输出也因为组合逻辑原因产生这些临时状态,即毛刺。

  3. 三段式状态机

  三段式状态机,第一段用时序逻辑描述state_c(现态)和state_n(次态),第二段用组合逻辑描述状态转移,第三段用时序逻辑描述输出,第三段可以是多个always块。

 //======================================================================
// --- 名称 : FSM_3
// --- 作者 : xianyu_FPGA
// --- 日期 : 2018-12-15
// --- 描述 : 售货机练习,采用三段式状态机
//====================================================================== module FSM_3
//---------------------<端口声明>---------------------------------------
(
input clk ,
input rst_n ,
input [:] in ,
output reg [:] out ,
output reg out_vld
);
//---------------------<信号定义>---------------------------------------
reg [:] state_c ;
reg [:] state_n ;
//---------------------<状态机参数>-------------------------------------
localparam S0 = 'b0001 ;
localparam S1 = 'b0010 ;
localparam S2 = 'b0100 ;
localparam S3 = 'b1000 ; //----------------------------------------------------------------------
//-- 状态机第1段
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
state_c <= S0;
else
state_c <= state_n;
end //----------------------------------------------------------------------
//-- 状态机第2段
//----------------------------------------------------------------------
always @(*)begin
case(state_c)
S0: begin
if(in==)
state_n = S1;
else if(in==)
state_n = S2;
else
state_n = state_c;
end
S1: begin
if(in==)
state_n = S2;
else if(in==)
state_n = S3;
else
state_n = state_c;
end
S2: begin
if(in==)
state_n = S3;
else if(in==)
state_n = S0;
else
state_n = state_c;
end
S3: begin
if(in== || in==) // in != 0也行
state_n = S0;
else
state_n = state_c;
end
default:state_n = S0;
endcase
end //----------------------------------------------------------------------
//-- 状态机第3段
//----------------------------------------------------------------------
//找零钱
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
out <= ;
else if(state_c==S3 && in==)
out <= ;
else
out <= ;
end //输出脉动
always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)
out_vld <= ;
else if((state_c==S2 && in==) || (state_c==S3 && in!=))
out_vld <= ;
else
out_vld <= ;
end endmodule

仿真波形如下所示:

结论:波形和预想一致!这也是较多书籍推荐的写法。

  4. 一段式和三段式结合的状态机(by 威三学院FPGA教程)

  V3学院状态机,只定义一个转移状态:state。第一段用时序逻辑描述state状态转移,第二段用时序逻辑描述输出,第二段可以是多个always块。由于是时序逻辑能够自动保持,所以可以省略else。这种状态机的优点是既消除了组合逻辑可能产生的毛刺,又减少了代码量。

 //======================================================================
// --- 名称 : FSM_V3
// --- 作者 : xianyu_FPGA
// --- 日期 : 2019-06-12
// --- 描述 : 售货机练习,采用V3学院的状态机
//====================================================================== module FSM_V3
//---------------------<端口声明>---------------------------------------
(
input clk ,
input rst_n ,
input [:] in ,
output reg [:] out ,
output reg out_vld
);
//---------------------<信号定义>---------------------------------------
reg [:] state ;
//---------------------<状态机参数>-------------------------------------
localparam S0 = 'b0001 ;
localparam S1 = 'b0010 ;
localparam S2 = 'b0100 ;
localparam S3 = 'b1000 ; //----------------------------------------------------------------------
//-- 状态机
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
state <= S0;
else begin
case(state)
S0: begin
if(in==)
state <= S1;
else if(in==)
state <= S2;
end
S1: begin
if(in==)
state <= S2;
else if(in==)
state <= S3;
end
S2: begin
if(in==)
state <= S3;
else if(in==)
state <= S0;
end
S3: begin
if(in== || in==) // in != 0也行
state <= S0;
end
default:state <= S0;
endcase
end
end //----------------------------------------------------------------------
//-- 输出
//----------------------------------------------------------------------
//找零钱
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
out <= ;
else if(state==S3 && in==)
out <= ;
else
out <= ;
end //输出脉动
always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)
out_vld <= ;
else if((state==S2 && in==) || (state==S3 && in!=))
out_vld <= ;
else
out_vld <= ;
end endmodule

仿真波形如下所示:

结论:波形和预想一致!

四、状态机名称查看器

  可以看到,我的Modelsim波形中出现了一个信号state_name,里面显示了状态机的名称,这是怎么做到的呢?方法有很多种,这里介绍两种。

  1. testbench法

  testbench里增加一段参数转ASCII码的代码,如下所示:

 //----------------------------------------------------------------------
//-- 状态机名称查看器
//----------------------------------------------------------------------
localparam S0 = 'b0001 ;
localparam S1 = 'b0010 ;
localparam S2 = 'b0100 ;
localparam S3 = 'b1000 ;
//2字符16位
reg [:] state_name ; always@(*)begin
case(u_FSM_3.state_c)
S0: state_name = "S0";
S1: state_name = "S1";
S2: state_name = "S2";
S3: state_name = "S3";
default:state_name = "S0";
endcase
end

  在Modelsim中点击信号state_name,右键选择用ASSIC码查看就可以看到状态机的名称,而不再是头疼的的0001、0010等字符。编写时注意一下位宽,一个ASSIC码字符宽度是8位,例如“S0”有2个字符则需要16位宽。

  2. do文件法(tcl文件也是一样的)

  首先你得学会怎么使用Modelsim的自动化脚本仿真,那么我们只要再do文件中加入这段代码即可:

 # ======================================================================
# == 状态机名称查看器
# ====================================================================== # 结构体设置
virtual type {
{'b0001 S0}
{4'b0010 S1}
{'b0100 S2}
{4'b1000 S3}
} fsm_type; # 结构体和信号名关联,命名为state_name
virtual function {(fsm_type)/fsm_tb/u_fsm/state} state_name

参考资料:

[1]小梅哥FPGA教程

[2]威三学院FPGA教程

[3]吴厚航. 深入浅出玩转FPGA[M]. 北京航空航天大学出版社, 2013.

[4]夏宇闻. Verilog数字系统设计教程.第3版[M]. 北京航空航天大学出版社, 2013.

[5]韩彬, 于潇宇, 张雷鸣. FPGA设计技巧与案例开发详解[M]. 电子工业出版社, 2014.

状态机的Verilog写法的更多相关文章

  1. 10010序列检测器的三段式状态机实现(verilog)

    序列检测器是时序数字电路设计中经典的教学范例,夏宇闻的<verilog数字系统设计教程>一书中有这个例子,用verilog设计一个“10010”序列的检测器.看完后我觉得F和G两个状态多余 ...

  2. 计数器的Verilog写法

    计数器是非常基本的使用,没有计数器就无法处理时序.我在学习时发现市面上有几种不同的计数器写法,非常有趣,在此记录下来: 一.时序逻辑和组合逻辑彻底分开(by锆石科技FPGA教程) 1.代码 //=== ...

  3. 基础数字电路的Verilog写法

    Verilog是硬件描述电路,我对此一直稀里糊涂,于是将锆石科技开发板附带的的一些基础数字电路Verilog程序整理记录下来,并且查看他们的RTL视图,总算有点理解了. 1.基本运算符 module ...

  4. Modelsim——显示状态机名称的方法

    方法在本人博客<状态机的Verilog写法>已经写明,为了方便查看,特意拎出来. 方法1: Testbench 设计文件含有状态机时,对应的仿真文件testbench里增加一段参数转ASC ...

  5. 对Verilog 初学者比较有用的整理(转自它处)

    *作者: Ian11122840    时间: 2010-9-27 09:04                                                              ...

  6. Verilog学习笔记设计和验证篇(三)...............同步有限状态机的指导原则

    因为大多数的FPGA内部的触发器数目相当多,又加上独热码状态机(one hot code machine)的译码逻辑最为简单,所以在FPGA实现状态机时,往往采用独热码状态机(即每个状态只有一个寄存器 ...

  7. Verilog学习笔记简单功能实现(三)...............同步有限状态机

    在Verilog中可以采用多种方法来描述有限状态机最常见的方法就是用always和case语句.如下图所示的状态转移图就表示了一个简单的有限状态机: 图中:图表示了一个四状态的状态机,输入为A和Res ...

  8. verilog语法实例学习(11)

    同步时序电路的一般形式 时序电路由组合逻辑以及一个或多个触发器实现.一般的架构如下图所示:W为输入,Z为输出,触发器中存储的状态为Q.在时钟信号的控制下,触发器通过加在其输入端的组合逻辑输入,使得电路 ...

  9. 16c550芯片编写的优化

    参考了 <Altera FPGA/CPLD 设计>高级篇, 关于状态机的推荐写法实现的功能是一样的但是编译使用的逻辑门如下图: 下图是我自己编的状态机需要的逻辑: 下图是使用推荐的有限状态 ...

随机推荐

  1. 用pandas进行数据清洗(二)(Data Analysis Pandas Data Munging/Wrangling)

    在<用pandas进行数据清洗(一)(Data Analysis Pandas Data Munging/Wrangling)>中,我们介绍了数据清洗经常用到的一些pandas命令. 接下 ...

  2. clion下批量删除断点

  3. ICEM-非结构化网格中创建无厚度的面

    原版视频下载地址:https://pan.baidu.com/s/1pLazbOf 密码: 4pii

  4. Linux 权限规划ACL

    什么是ACL ACL是Access Control List的缩写,主要目的是提供传统的owner.group.others的read.write.execute权限之外的具体权限设置 ACL可以针对 ...

  5. 清理系统图标缓存数据库-解决windows图标异常

    1.删除C:\Users\用户名\AppData\Local\IconCache.db文件,重建图标缓存 . 一键脚本 taskkill /f /im explorer.exe echo 清理系统图标 ...

  6. Node Addon

    Node Addon as bridge between javascript and C++ #include <node.h> namespace HelloWorldDemo { u ...

  7. mark_rabbitMQ

    一.1.6 和1.7的区别 二.63跟65好像有点差异 有些jar包问题 三.预取策略 https://blog.csdn.net/hry2015/article/details/79078312 四 ...

  8. 函数式接口, Collection等

    Lambda 函数式接口 lambda 表达式的使用需要借助于 函数式接口, 也就是说只有函数式接口才可以将其用 lambda 表达式进行简化. 函数式接口定义为仅含有一个抽象方法的接口. 按照这个定 ...

  9. PostgreSQL中的一些日志

    1.Write Ahead Log(WAL日志)预写日志(WAL)是保证数据完整性的标准方法.在有关交易处理的大多数(如果不是全部)书籍中都可以找到详细说明. 简而言之,WAL的核心概念是,只有在记录 ...

  10. [转]npm安装教程

    原文地址:https://www.cnblogs.com/lgx5/p/10732016.html 一.使用之前,我们先来掌握3个东西是用来干什么的. npm: Nodejs下的包管理器. webpa ...