前言 在Verilog等HDL中,有限状态机(Finite-State Machine)是一个比较重要的知识点,而且在软件工程、编译器设计等其他领域也是一个值得注意的方面。本文讨论的是有限状态机在Logisim、Verilog以及MIPS汇编中的具体实现。在本文的后面也会讨论同步复位以及异步复位的问题。
关于FSM 有限状态机的构成包括输入、状态转移与输出。本文主要讨论的有限状态机有两种——Moore型状态机以及Mealy型状态机。
关于状态转移逻辑 状态转移是状态机中最主要的部分,这一部分代表着状态机的主要运作机理。状态转移逻辑的构成主要包括当前状态 、输入 以及下一状态 ,也就是 。创建状态转移关系时尤其要注意次态的指向问题,比如字符串匹配电路,当字符串成功匹配时如果后半部分可以作为下一次匹配的前缀,则次态的指向不应该为初始状态,而是前缀匹配的状态(具体题目的要求可能不同,需要具体分析)。通常情况下,Mealy机的状态可能比Moore机状态要少,这是因为Mealy机的输出还与输入有关系,可以在相同状态下根据不同的输入得到不同的输出。
关于输出内容 输出的内容与输入和状态的关系是两种状态机的主要区别。假设当前的状态为 ,输入为 ,输出为 ,在Moore型状态机中,输出的内容仅与当前的状态有关,即 ;而在Mealy型状态机中,输出的内容与当前的状态和输入有关,即 。输出的内容也有时间上的区别,形象地说Mealy机的输出可以随时改变(这也导致其对于毛刺敏感)而Moore机只能在完整时钟周期改变;Mealy机的输出时长是状态与输入的AND,而Moore机是完整的时钟周期;Mealy机的输出比Moore机的输出要“早”。原因很好解释,Mealy机的输出与当前输入有关,并且此输出对应着Moore机下一个状态的输出。
输出关于时间的区别
Logisim实现有限状态机 搭建Moore型状态机 在Logisim中,寄存器(Register)对于状态机等时序逻辑电路的搭建是十分重要的。它的端口主要有输入、输出、使能信号、时钟信号以及复位信号,其中复位信号为异步复位,即只要信号为1则立即复位(输出端变0)。对于有限状态机,我们用寄存器控制电路的时序逻辑。其主要电路如下:
Moore型状态机电路
对于其中的转移电路与输出电路,偷懒的话我们只需用Logisim中的Analyze Circuit或者Combinational Analysis功能即可,如下图
转移电路
输出电路
搭建Mealy型状态机 Mealy型状态机与Moore型状态机总体上是同的,只是输出部分需要稍作改变(也可以有输出部分外其他部分的考虑,不过出于简便本文只考虑输出部分的不同)。
Mealy型状态机电路
Verilog实现有限状态机 步骤说明 在Verilog HDL中,搭建有限状态机电路流程是:理清状态转移逻辑,针对逻辑及需求写出电路,最后对电路进行调试。Verilog中的状态转移逻辑与上文Logisim的逻辑类似。我们需要对各状态进行编码,方便在代码实现时更好的组织电路。编码可以用普通的序号表示,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 model example( input clk, input in, output out ); reg [2 :0 ] state, next_state;parameter A = 0 , B = 1 , C = 2 ; assign out = state == A; assign out = (state == A) && in; endmodule
这样编码的好处是简洁方便,代码更加容易理解。除了上述编码,我们还可以使用独热码进行表示,独热码表示的好处是校验处于某种状态或者转移到某种状态时只需要考虑某一位即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 model example( input clk, input in, input [3 :0 ] state, output [3 :0 ] next_state, output out ); parameter A = 0 , B = 1 , C = 2 , D = 3 ; next_state[A] = (state[A] & ~in) | (state[B] & in); assign out = state[D]; assign out = state[D] & in; endmodule
状态机的写法 通常来说Verilog实现有限状态机有三种写法——一段式、两段式、三段式。三种不同类型的写法以代码中状态转移、状态更新、输出逻辑的相对位置区分。一段式写法中三者在同一个always块内,这是极其令人唾弃的写法,因为难以调试(虽然看起来比较简短)。常用的写法是后两者,即将组合逻辑与时序逻辑分开的写法。代码中的always块可以有很多个,要根据需求确定。下面给出三段式写法的例子(Moore型状态机,异步复位):1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 model example( input clk, input in1, input in2, input areset, output out1, output out2 ); parameter A = 0 , B = 1 , C = 2 , D = 3 ; reg [1 :0 ] state, next_state;always @(*) begin case (state) A: next_state = in1 ? in2 ? B : C : D; endcase end always @(posedge clk, posedge areset) begin if (areset) state <= 0 ; else state <= next_state; end always @(posedge clk, posedge areset) begin if (areset) {out1, out2} = 2'b0 ; else begin case (state) A: {out1, out2} = 2'b10 ; endcase end end endmodule
两种状态机的区别 两种状态机在Verilog中的区别与Logisim一样,主要在于输出逻辑,下面举例:
Moore型状态机 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 module MooreStateMachine( input clk, input areset, input x, output reg y ); reg [1 :0 ] current_state, next_state;initial current_state = S0;always @(posedge clk or posedge areset) begin if (areset) begin current_state <= S0; end else begin current_state <= next_state; end end always @(posedge clk or posedge areset) begin if (areset) begin next_state <= S0; end else begin case (current_state) S0: next_state <= (x == 1'b1 ) ? S1 : S0; S1: next_state <= (x == 1'b0 ) ? S2 : S0; S2: next_state <= (x == 1'b1 ) ? S3 : S0; S3: next_state <= S0; default : next_state <= S0; endcase end end always @(current_state) begin case (current_state) S0: y = 1'b0 ; S1: y = 1'b0 ; S2: y = 1'b1 ; S3: y = 1'b0 ; default : y = 1'b0 ; endcase end endmodule
Mealy型状态机 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 module MealyStateMachine( input clk, input areset, input x, output reg y ); reg [1 :0 ] current_state, next_state;initial current_state = S0;always @(posedge clk or posedge areset) begin if (areset) begin current_state <= S0; end else begin current_state <= next_state; end end always @(posedge clk or posedge areset) begin if (areset) begin next_state <= S0; end else begin case (current_state) S0: next_state <= (x == 1'b1 ) ? S1 : S0; S1: next_state <= (x == 1'b0 ) ? S2 : S0; S2: next_state <= (x == 1'b1 ) ? S2 : S0; default : next_state <= S0; endcase end end always @(current_state or x) begin case (current_state) S0: y = (x == 1'b1 ) ? 1'b0 : 1'b0 ; S1: y = (x == 1'b0 ) ? 1'b0 : 1'b0 ; S2: y = (x == 1'b1 ) ? 1'b1 : 1'b0 ; default : y = 1'b0 ; endcase end endmodule
MIPS汇编实现有限状态机 MIPS实现状态机的应用不多,但是作为汇编语言也是可以实现有限状态机的,下面仅举一个小例子:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 .data state: .word 0 input: .word 0 .text main: lw $t0 , input lw $t1 , state beq $t1 , $zero , state0 beq $t1 , $t0 , state1 state0: li $t1 , 0 j done state1: li $t1 , 1 j done done: sw $t1 , state li $v0 , 10 syscall
同步复位与异步复位 同步复位与异步复位是电路中不可忽视的一部分,此类功能用于在异常情况下重启电路。同步复位是指复位信后到来后,在下一个时钟信号上升沿将电路复位,而异步复位是指不考虑时钟信号的影响,复位信号到来即复位。在构造电路时要注意二者的区别。本节只讨论Logisim和Verilog有限状态机中的同步/异步复位。
Logisim实现同步/异步复位 在Logisim中,实现异步复位非常简单,由于寄存器的复位端口默认是异步复位的,所以只需要将复位信号连接至寄存器的复位端口即可。
异步复位
而同步复位需要考虑时钟信号,所以需要将复位信号与时钟信号结合以对寄存器进行复位,最简单的方法是多路选择器(Mutiplexer)的运用。
同步复位
除了上述方法,我们还可以将复位信号与寄存器的输入信号与与门(AND Gate)相连,再接到寄存器输入端口,其原理与多路选择器相似。
与门同步复位
Verilog实现同步/异步复位 Verilog有限状态机中同步复位与异步复位的区别主要在于时序逻辑中敏感列表的不同,同步复位电路的时序逻辑模块敏感列表只有时钟沿:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 module SynchronousReset( input clk, input reset, input in, output out ); always @(posedge clk) begin if (reset) else end endmodule
而异步复位电路的时序逻辑模块敏感列表需要加上复位信号的上升/下降沿:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 module AsynchronousReset( input clk, input reset, input in, output out ); always @(posedge clk) begin if (reset) else end endmodule
总结 本文主要讨论了计算机组成原理中的有限状态机以及同步/异步复位问题,希望对读者关于此方面的理解有所帮助。