当前位置:   article > 正文

FPGA状态机详解

fpga状态机

在进行FPGA设计时,状态机是一个经常需要用到的设计手段,究竟什么是状态机?一段式和三段式状态机又有何区别,分别如何实现?本篇文章会进行详细的介绍,帮助大家迅速掌握一段式与三段式状态机的设计


状态机介绍

状态机可归纳为4个要素,即现态、条件、动作、次态。

现态:是指当前所处的状态

条件:又称为"事件",当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。

动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

次态:条件满足后要迁往的新状态。"次态"是相对于"现态"而言的,"次态"一旦被激活,就转变成新的"现态"了。

状态机分类

状态机一般分为三种类型:
  1、Moore型状态机:下一状态只由当前状态决定
  2、Mealy 型状态机:下一状态不但与当前状态有关,还与当前输入值有关
  3、混合型状态机

A.Mealy状态机:时序逻辑的输出不但取决于状态还取决于输入,如图所示。

B.Moore状态机:时序逻辑的输出仅仅取决于上一时刻的状态,而与当前时刻的输入无关,如图所示。

在实际设计中,Mealy类型的状态机是用的最多的,Moore类型虽然清晰明了,但是状态太多,不适合大型状态机的设计。

之所以Moore状态多,我们可以举个例子:比如要判断一个数是不是4'b1100

用Moore状态机:

状态内容输出
S110
S2110
S31100
S411001

输出只和状态有关,而和输入无关(因为根本就没有给出输入情况),这种情况下我们需要4个状态

用Mealy状态机:

状态内容输入输出
S110/110/11
S2110/1110/111
S31100/11100/1101

输出不仅和状态有关,也和输入的数有关,在这种情况下我们需要3个状态

可见 用Mealy状态机可以减少状态的个数

状态机设计步骤

状态机的设计步骤主要分为以下4个步骤:

A.逻辑抽象:得出状态转换图:就是把给出的一个实际问题表示为时序逻辑函数。可以用状态转换表来描述,也可以用状态转换图来描述。

B.状态化简:如果在状态转换图中出现这样两个状态:它们在相同的输入下转换到同一个状态中,并得到相同的输出,或称它们为等价状态。显然等价状态是重复的,可以合并为一个(电路的状态越少,存储电路也就越简单)。

C.状态分配:状态分配又称状态编码。通常有很多编码方法,编码方案选择得当,设计的电路简单,反之,选得不好,则设计的电路就会复杂许多

D.用逻辑描述语言进行描述

  Ps编码方式常见的有

二进制码binary_code、格雷码gray_code 、独热码one_hot_code

1.binary_code

二进制码,编码按照二进制数进行递加,容易产生亚稳态,代码不稳定

2. gray_code

在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code),另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码或反射码。Gray码不容易产生亚稳态。

3. one_hot_code

独热编码即 One-Hot 编码,又称一位有效编码,其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候,其中只有一位有效。虽然使用较多的触发器,但由于状态译码简单,可减少组合逻辑且速度较快, 这种编码方式还易于修改,增加状态或改变状态转换条件都可以在不影响状态机的其它部分的情况下很方便地实现。另外,它的速度独立于状态数量。与之相比,压缩状态编码在状态增加时速度会明显下降。独热码值每个码元值只有一位是'1',其他位都是'0'

eg:分别用3种编码方式实现4个编码

        独热         格雷        二进制

       4'b0001       2'b00      2’b00

       4'b0010       2'b01      2’b01

       4'b0100       2'b11      2’b10

       4'b1000       2'b10      2’b11

状态机的描述方式

状态机的描述方式有很多种,但是最常用的主要有3中,分别是一段式(always)、两段式(2 always)、三段式(3 always)

A.单always块把组合逻辑和时序逻辑用同一个时序always块描述,其输出是寄存器输出,无毛刺;但是这种方式可能会产生多余的触发器,代码难于修改和调试,应该尽量避免使用。

B.双always块大多用于描述Mealy状态机和组合输出的Moore状态机。

时序always块描述当前状态逻辑,组合逻辑always块描述次态逻辑并给输出赋值。

这种方式结构清晰,综合后的面积和时间性能好。但组合逻辑输出往往会有毛刺,当输出向量作为时钟信号时,这些毛刺会对电路产生致命的影响。

C.三always块大多用于同步Mealy状态机。

两个时序always块分别用来描述现态逻辑和对输出赋值,组合always块用于产生下一状态。

时序电路的状态是一个状态变量集合,这些状态变量在任意时刻的值都包含了为确定电路的未来行为而必需考虑的所有历史信息。

这种方式的状态机也是寄存器输出,输出无毛刺,并且代码比单always块清晰易读,但是面积大于双always块。随着芯片资源和速度的提高,目前这种方式得到了广泛应用。

一段式状态机

在一个always块下完成:条件判断+本状态动作+跳转下个状态

eg 用状态机实现流水灯: 

  1. always @(posedge clk_25m)begin
  2. 11. if(reset)begin
  3. 12. led_out<=4'b0000; //初始状态led灯全亮,检验灯是否完好
  4. 13. state<=3'd0; //初始状态state=0
  5. 14. end
  6. 15. else begin
  7. 16. case(state) //state作为敏感信号
  8. 17. 3'd0:begin //state=0状态
  9. 18. if(time_en)begin //条件T满足时
  10. 19. led_out<=4'b0111; //1点亮
  11. 20. state<=3'd1; //state进入1状态
  12. 21. end
  13. 22. else begin //否则state还在0状态
  14. 23. led_out<=4'b0000;
  15. 24. state<=3'd0;
  16. 25. end
  17. 26. end
  18. 27. 3'd1:begin //state=1状态
  19. 28. if(time_en)begin //若条件T满足
  20. 29. led_out<=4'b1011; //灯2点亮
  21. 30. state<=3'd2; //state进入2状态
  22. 31. end
  23. 32. else begin
  24. 33. led_out<=4'b0111; //否则state还在1状态
  25. 34. state<=3'd1;
  26. 35. end
  27. 36. end
  28. 37. 3'd2:begin
  29. 38. if(time_en)begin
  30. 39. led_out<=4'b1101;
  31. 40. state<=3'd3;
  32. 41. end
  33. 42. else begin
  34. 43. led_out<=4'b1011;
  35. 44. state<=3'd2;
  36. 45. end
  37. 46. end
  38. 47. 3'd3:begin
  39. 48. if(time_en)begin
  40. 49. led_out<=4'b1110;
  41. 50. state<=3'd4;
  42. 51. end
  43. 52. else begin
  44. 53. led_out<=4'b1101;
  45. 54. state<=3'd3;
  46. 55. end
  47. 56. end
  48. 57. 3'd4:begin
  49. 58. if(time_en)begin
  50. 59. led_out<=4'b0111;
  51. 60. state<=3'd1;
  52. 61. end
  53. 62. else begin
  54. 63. led_out<=4'b1110;
  55. 64. state<=3'd4;
  56. 65. end
  57. 66. end
  58. 67. default:begin //2^3=8,state有没有用到的数,需要写default
  59. 68. state<=3'd0; //让state回到初始值
  60. 69. led_out<=4'b0000;
  61. 70. end
  62. 71. endcase
  63. 72. end
  64. 73. end

三段式状态机

第一段:时序逻辑

将下个将发生的状态赋值给当前状态,将next_state赋值给current_state

第二段:组合逻辑

      根据current_state对next_state进行操作, 第二段只根据判断条件对 next_state 赋值

注意:

1组合逻辑的赋值号是=而不是<=

2

3是根据当前状态进行判断:

4只对下一个状态赋值:

5若出错则回到初始状态:

第三段:时序逻辑

对输出结果进行操作,根据current_state 对被操作的变量进行赋值

注意:

  1. 是根据当前状态判断:
  2. 只对输出结果赋值:

  1. 记得写default:

  1. 1. module water_led(
  2. 2. input clk,
  3. 3. input reset,
  4. 4. input time_en,
  5. 5. output reg [3:0]led_out
  6. 6. );
  7. 7.
  8. 8. reg [2:0]current_state;//定义当前状态
  9. 9. reg [2:0]next_state; //定义下一个状态
  10. 10.
  11. 11. parameter IDLE=3'd0; //定义初始状态IDLE
  12. 12. parameter LED1=3'd1; //定义第一盏灯
  13. 13. parameter LED2=3'd2;
  14. 14. parameter LED3=3'd3;
  15. 15. parameter LED4=3'd4;
  16. 16. //第一段:将下个将发生的状态赋值给当前状态(时序逻辑)
  17. 17. always@(posedge clk)begin
  18. 18. current_state<=next_state;
  19. 19. end
  20. 20. //第二段:根据current_state对next_state进行操作(组合逻辑)
  21. 21. always @(*)begin // (*)在组合逻辑中代表任何一个变量
  22. 22. if(reset)begin
  23. 23. next_state=IDLE; //组合逻辑变量的赋值号用=
  24. 24. end
  25. 25. else begin
  26. 26. case (current_state) //当前状态
  27. 27. IDLE:begin
  28. 28. if(time_en)begin
  29. 29. next_state=LED1; //只对state进行操作
  30. 30. end
  31. 31. else begin
  32. 32. next_state=IDLE;
  33. 33. end
  34. 34. end
  35. 35. LED1:begin
  36. 36. if(time_en)begin
  37. 37. next_state=LED2;
  38. 38. end
  39. 39. else begin
  40. 40. next_state=LED1;
  41. 41. end
  42. 42. end
  43. 43. LED2:begin
  44. 44. if(time_en)begin
  45. 45. next_state=LED3;
  46. 46. end
  47. 47. else begin
  48. 48. next_state=LED2;
  49. 49. end
  50. 50. end
  51. 51. LED3:begin
  52. 52. if(time_en)begin
  53. 53. next_state=LED4;
  54. 54. end
  55. 55. else begin
  56. 56. next_state=LED3;
  57. 57. end
  58. 58. end
  59. 59. LED4:begin
  60. 60. if(time_en)begin
  61. 61. next_state=LED1;
  62. 62. end
  63. 63. else begin
  64. 64. next_state=LED4;
  65. 65. end
  66. 66. end
  67. 67. default:begin
  68. 68. next_state=IDLE;
  69. 69. end
  70. 70. endcase
  71. 71. end
  72. 72. end
  73. 73. //第三段:对输出结果进行操作,根据current_state 对被操作的变量进行赋值(时序逻辑)
  74. 74. always @(posedge clk)begin
  75. 75. if(reset)begin
  76. 76. led_out<=4'b0000;
  77. 77. end
  78. 78. else begin
  79. 79. case (current_state) //当前状态
  80. 80. IDLE:begin
  81. 81. if(time_en)begin
  82. 82. led_out<=4'b0111; //只对输出变量操作
  83. 83. end
  84. 84. else begin
  85. 85. led_out<=4'b0000;
  86. 86. end
  87. 87. end
  88. 88. LED1:begin
  89. 89. if(time_en)begin
  90. 90. led_out<=4'b1011;
  91. 91. end
  92. 92. else begin
  93. 93. led_out<=4'b0111;
  94. 94. end
  95. 95. end
  96. 96. LED2:begin
  97. 97. if(time_en)begin
  98. 98. led_out<=4'b1101;
  99. 99. end
  100. 100. else begin
  101. 101. led_out<=4'b1011;
  102. 102. end
  103. 103. end
  104. 104. LED3:begin
  105. 105. if(time_en)begin
  106. 106. led_out<=4'b1110;
  107. 107. end
  108. 108. else begin
  109. 109. led_out<=4'b1101;
  110. 110. end
  111. 111. end
  112. 112. LED4:begin
  113. 113. if(time_en)begin
  114. 114. led_out<=4'b0111;
  115. 115. end
  116. 116. else begin
  117. 117. led_out<=4'b1110;
  118. 118. end
  119. 119. end
  120. 120. default:begin
  121. 121. led_out<=4'b0000;
  122. 122. end
  123. 123. endcase
  124. 124. end
  125. 125. end
  126. 126. endmodule

三段式状态机与一段式区别与联系

1三段式是一段式的三个部分,就是把一段式的三个关系分别独立出来

2.三段式要定义两个 state 变量;一个是 current_state(当前状态);一个是 next_state(下一个状态)

3.三段式中稳定性强,适用于时钟频率很高的程序中,并且一个时钟下只对一个变量进行操作。一段式中整个程序都在一个时钟下操作所以容易出错。

4. 三段式可以把稳定区域的current_state赋值给next_state


本篇文章介绍了状态机原理,Mealy和Moore状态机分类,一段式状态机与三段式状态机写法,并用流水灯举例分析了一段式状态机和三段式状态机的异同

创作不易,点赞支持!thank

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/915487
推荐阅读
相关标签
  

闽ICP备14008679号