Verilog 高级知识点---状态机

这篇具有很好参考价值的文章主要介绍了Verilog 高级知识点---状态机。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

状态机

1、Mealy 状态机

2、Moore 状态机

3、三段式状态机


状态机

       Verilog 是硬件描述语言,硬件电路是并行执行的,当需要按照流程或者步骤来完成某个功能时,代码中通常会使用很多个 if 嵌套语句来实现,这样就增加了代码的复杂度,以及降低了代码的可读性,这个时候就可以使用状态机来编写代码。状态机相当于一个控制器,它将一项功能的完成分解为若干步,每一步对应于二进制的一个状态,通过预先设计的顺序在各状态之间进行转换,状态转换的过程就是实现逻辑功能的过程。

       状态机,全称是有限状态机(Finite State Machine,缩写为 FSM),是一种在有限个状态之间按一定规律转换的时序电路,可以认为是组合逻辑和时序逻辑的一种组合。状态机通过控制各个状态的跳转来控制流程,使得整个代码看上去更加清晰易懂,在控制复杂流程的时候,状态机优势明显,因此基本上都会用到状态机,如 SDRAM 控制器等。在本手册提供的例程中,会有多个用到状态机设计的例子,希望大家能够慢慢体会和理解,并且能够熟练掌握。

       根据状态机的输出是否与输入条件相关,可将状态机分为两大类,即摩尔(Moore)型状态机和米勒(Mealy) 型状态机。

  • ➢ Mealy 状态机:组合逻辑的输出不仅取决于当前状态,还取决于输入状态。
  • ➢ Moore 状态机:组合逻辑的输出只取决于当前状态。

1、Mealy 状态机

       米勒状态机的模型如下图所示,模型中第一个方框是指产生下一状态的组合逻辑 FF 是当前状态和输入信号的函数,状态是否改变、如何改变,取决于组合逻辑 F 的输出;第二框图是指状态寄存器,其由一组触发器组成,用来记忆状态机当前所处的状态,状态的改变只发生在时钟的跳边沿;第三个框图是指产生输出的组合逻辑 G,状态机的输出是由输出组合逻辑 G 提供的,G 也是当前状态和输入信号的函数。

Verilog 高级知识点---状态机

2、Moore 状态机

       摩尔状态机的模型如下图所示,对比米勒状态机的模型可以发现,其区别在于米勒状态机的输出由当前状态和输入条件决定的,而摩尔状态机的输出只取决于当前状态。

Verilog 高级知识点---状态机

3、三段式状态机

      根据状态机的实际写法,状态机还可以分为一段式、二段式和三段式状态机。

      一段式:整个状态机写到一个 always 模块里面,在该模块中既描述状态转移,又描述状态的输入和输出。不推荐采用这种状态机,因为从代码风格方面来讲,一般都会要求把组合逻辑和时序逻辑分开;从代码维护和升级来说,组合逻辑和时序逻辑混合在一起不利于代码维护和修改,也不利于约束。

        二段式:用两个 always 模块来描述状态机,其中一个 always 模块采用同步时序描述状态转移;另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律以及输出。不同于一段式状态机的是,它需要定义两个状态,现态和次态,然后通过现态和次态的转换来实现时序逻辑。

        三段式:在两个 always 模块描述方法基础上,使用三个 always 模块,一个 always 模块采用同步时序描述状态转移,一个 always 采用组合逻辑判断状态转移条件,描述状态转移规律,另一个 always 模块描述状态输出(可以用组合电路输出,也可以时序电路输出)

        实际应用中三段式状态机使用最多,因为三段式状态机将组合逻辑和时序分开,有利于综合器分析优化以及程序的维护;并且三段式状态机将状态转移与状态输出分开,使代码看上去更加清晰易懂,提高了代码的可读性,推荐大家使用三段式状态机,本文也着重讲解三段式。

       三段式状态机的基本格式是:

  •       第一个 always 语句实现同步状态跳转;
  •       第二个 always 语句采用组合逻辑判断状态转移条件;
  •       第三个 always 语句描述状态输出(可以用组合电路输出,也可以时序电路输出)

        在开始编写状态机代码之前,一般先画出状态跳转图,这样在编写代码时思路会比较清晰,下面以一个 7 分频为例(对于分频等较简单的功能,可以不使用状态机,这里只是演示状态机编写的方法),状态跳转图如下图所示:

Verilog 高级知识点---状态机

状态跳转图画完之后,接下来通过 parameter 来定义各个不同状态的参数,如下代码所示:

parameter S0 = 7'b0000001; //独热码定义方式
parameter S1 = 7'b0000010;
parameter S2 = 7'b0000100;
parameter S3 = 7'b0001000;
parameter S4 = 7'b0010000;
parameter S5 = 7'b0100000;
parameter S6 = 7'b1000000;

这里是使用独热码的方式来定义状态机,每个状态只有一位为 1,当然也可以直接定义成十进制的 0,1,2……7

       因为我们定义成独热码的方式,每一个状态的位宽为 7 位,接下来还需要定义两个 7 位的寄存器,一 个用来表示当前状态,另一个用来表示下一个状态,如下所示:

reg [6:0] curr_st ; //当前状态
reg [6:0] next_st ; //下一个状态

       接下来就可以使用三个 always 语句来开始编写状态机的代码,第一个 always 采用同步时序描述状态转移,第二个 always 采用组合逻辑判断状态转移条件,第三个 always 是描述状态输出,一个完整的三段式状态机的例子如下代码所示:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/01 14:27:15
// Design Name: 
// Module Name: divider7_fsm
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

//三段式状态机的基本格式是:
//第一个 always 语句实现同步状态跳转;
//第二个 always 语句采用组合逻辑判断状态转移条件; 
//第三个 always 语句描述状态输出(可以用组合电路输出,也可以时序电路输出)。
//在开始编写状态机代码之前,一般先画出状态跳转图,这样在编写代码时思路会比较清晰.
//以一个 7 分频为例
module divider7_fsm(
     //系统时钟与复位
     input              sys_clk,
     input              sys_rst_n,
     
     //输出时钟
     output  reg       clk_divide_7
    );
//在编写状态机代码时首先要定义状态变量(代码中的参数 S0~S6)与状态寄存器(curr_st、next_st)
//parameter define
parameter S0 = 7'b0000001;    //独热码定义方式
parameter S1 = 7'b0000010;
parameter S2 = 7'b0000100;
parameter S3 = 7'b0001000;
parameter S4 = 7'b0010000;
parameter S5 = 7'b0100000;
parameter S6 = 7'b1000000;

//reg define
reg  [6:0]     curr_st;     //当前状态
reg  [6:0]     next_st;     //下一个状态

//*********************************************************
//**           main  code
//**********************************************************

//状态机的第一段采用同步时序描述状态转移
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
         curr_st <= S0;
    else
         curr_st <= next_st;
end

//状态机的第二段采用逻辑组合判断状态转移条件
always @(*) begin
    case (curr_st)
        S0:next_st = S1;
        S1:next_st = S2;
        S2:next_st = S3;
        S3:next_st = S4;
        S4:next_st = S5;
        S5:next_st = S6;
        S6:next_st = S0;
        default:next_st = S0;
    endcase
end

//状态机的第三段描述状态输出(这里采用时序电路输出)
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        clk_divide_7 <= 1'b0;
    else if ((curr_st == S0) | (curr_st == S1) | (curr_st == S2) | (curr_st == S3))
        clk_divide_7 <= 1'b0;
    else if ((curr_st == S4) | (curr_st == S5) | (curr_st == S6))
        clk_divide_7 <= 1'b1;
    else
        ;
end
  
endmodule

//采用这种描述方法虽然代码结构复杂了一些,
//但是这样做的好处是可以有效地滤去组合逻辑输出的毛刺,
//同时也可以更好的进行时序计算与约束,另外对于总线形式的输出信号来说,
//容易使总线数据对齐,减小总线数据间的偏移,从而降低接收端数据采样出错的频率。

        在编写状态机代码时首先要定义状态变量(代码中的参数 S0~S6)与状态寄存器curr_stnext_st),如代码中第 10 行至第 21 行所示;接下来使用三个 always 语句来实现三段状态机,第一个 always 语句实现同步状态跳转(如代码的第 27 至第 33 行所示),在复位的时候,当前状态处在 S0 状态,否则将下一个状态赋值给当前状态;第二个 always 采用组合逻辑判断状态转移条件(如代码的第 35 行至第 47 行代码所示),这里每一个状态只保持一个时钟周期,也就是直接跳转到下一个状态,在实际应用中,一般根据输入的条件来判断是否跳转到其它状态或者停留在当前转态,最后在 case 语句后面增加一个 default 语句,来防止状态机处在异常的状态;第三个 always 输出分频后的时钟(如代码的第 49 至第 59 行代码所示),状态机的第三段可以使用组合逻辑电路输出,也可以使用时序逻辑电路输出,一般推荐使用时序电路输出,因为状态机的设计和其它设计一样,最好使用同步时序方式设计,以提高设计的稳定性,消除毛刺。

       从代码中可以看出,输出的分频时钟 clk_divide_7 只与当前状态(curr_st)有关,而与输入状态无关,所以属于摩尔型状态机。状态机的第一段对应摩尔状态机模型的状态寄存器,用来记忆状态机当前所处的状态;状态机的第二段对应摩尔状态机模型产生下一状态的组合逻辑 F;状态机的第三段对应摩尔状态机产生输出的组合逻辑 G,因为采用时序电路输出有很大的优势,所以这里第三段状态机是由时序电路输出的。

       状态机采用时序逻辑输出的状态机模型如下图所示:

Verilog 高级知识点---状态机

       采用这种描述方法虽然代码结构复杂了一些,但是这样做的好处是可以有效地滤去组合逻辑输出的毛刺,同时也可以更好的进行时序计算与约束,另外对于总线形式的输出信号来说,容易使总线数据对齐,减小总线数据间的偏移,从而降低接收端数据采样出错的频率。 文章来源地址https://www.toymoban.com/news/detail-489597.html

到了这里,关于Verilog 高级知识点---状态机的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA学习笔记-知识点3-Verilog语法1

    按其功能可分为以下几类: 1) 算术运算符(+,-,×,/,%) 2) 赋值运算符(=,=) 3) 关系运算符(,,=,=) 4) 逻辑运算符(,||,!) 5) 条件运算符( ? :) 6) 位运算符(,|,^,,^) 7) 移位运算符(,) 8) 拼接运算符({ }) 9) 其它 按其所带操作数的个数运算符可分为三种: 1) 单目运算符(unary operator):可以带一个

    2024年02月06日
    浏览(46)
  • 数据库相关理论知识(有目录便于直接锁定相关知识点+期末复习)

    一,数据模型,关系型数据模型,网状模型,层次模型 1. 数据库模型 是用来描述和表示现实世界中的事物、概念以及它们之间的关系的工具, 但是并不是越专业越好,还要平衡它的模型的复杂性、通用性和成本效益等因素 。数据模型按不同的应用层次可以分为 三个层次 ,

    2024年03月16日
    浏览(42)
  • [SVA知识点一]: System verilog 断言(assert)的基本介绍

    断言(System Verilog Assertion 简称SVA) 可以被放在RTL设计或验证平台中,方便在仿真时查看异常情况。一般在数字电路设计中都要加入断言,断言占整个设计的比例应不少于30%。断言通常被称为序列监视器或者序列检验器,是对设计应当如何执行特定行为的描述,是一种嵌入设

    2024年02月10日
    浏览(32)
  • [SVA知识点二]: System verilog 断言(assert)的基本介绍

    举例介绍序列: 例1 序列seq1检查信号 “b” 在每个时钟上升沿都为高电平。如果信号 “b” 在任何一个时钟上升沿不为高电平,断言将失败。这相当于 “b == 1’b1”。 例2 例3:带参数的sequence 例4:带时序关系的sequence,在SVA 中时钟延时用符号 “##” 来表示,如 “##2” 表示

    2024年02月02日
    浏览(30)
  • Flutter 空安全的糖果罐,带你全面掌握高级知识点

    -h, --[no-]help 显示帮助信息 -p, --path Flutter 项目的根路径 (默认 “.”) -f, --folder assets 文件夹的名字 (默认 “assets”) -w, --[no-]watch 是否继续监听 assets 的变化 (默认 开启) -t, --type pubsepec.yaml 生成配置的类型 “d” 代表以文件夹方式生成 “- assets/images/” “f” 代表以文件方式生成

    2024年04月15日
    浏览(33)
  • 深度解析Redisson框架的分布式锁运行原理与高级知识点

    分布式系统中的锁管理一直是一个复杂而关键的问 题。在这个领域,Redisson框架凭借其出色的性能和功能成为了开发者的首选之一。本篇博客将深入探讨Redisson框架的分布式锁运行原理以及涉及的高级知识点。通过详细的解释和示例代码,您将更好地理解如何在分布式环境中

    2024年02月09日
    浏览(34)
  • c#多线程—基础概念到“双色球”项目实现(附知识点目录、代码、视频)

    总结:视频中对于多线程讲的非常透彻,从线程基础概念—.net不同版本出现的线程方法—多线程常出现问题—双色球项目实践,每个知识点都有代码实操,受益匪浅。附上学习笔记和实操代码。 视频 线程:程序执行的最小单位,任何操作都是由线程完成的,使用同步时,资

    2024年02月11日
    浏览(39)
  • 【PPT】《使用ChatGPT一键制作带自动配图的PPT演讲稿》- 知识点目录

    为了制作《汽车诞生史》PPT,您可以考虑以下提示: 开场:引用一句关于汽车的名言或警句,如“汽车是现代生活的象征”,“车祸并非事故,而是可以避免的错误”。 起源:介绍汽车的起源和早期发展历史,包括发明汽车的重要人物和他们的贡献。 技术进步:介绍汽车技

    2023年04月09日
    浏览(66)
  • 轻松掌握K8S目录持久卷PV/PVC的kubectl操作知识点04

    1、介绍 在docker中可以将容器中的目录挂载出来,在k8s中pod可以部署在不同节点,假如该节点的机器宕机了,k8s可能就会将此Pod转移到其他机器,就不是原先的机器了。k8s有自己的一套挂载方案,如下图所示, 原理为将所有节点的挂载的目录统一抽象管理为叫做 存储层的概念

    2024年02月12日
    浏览(35)
  • 【SciSpace】人工智能太强大了!文献阅读版ChatGPT,一站式科研文献阅读工具 - 知识点目录

    首先需要上传PDF 网站支持中文问答 Explain math table - 可以询问表格或者公式信息

    2024年02月16日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包