FSM有限状态机序列检测,涉及到:;
(1)摩尔型与米利型状态机;
(2)一段式、两段式、三段式状态机;
(3)状态编码(二进制、格雷码、独热码);
摩尔型和米利型状态机
摩尔型:输出只与当前状态有关;
米利型:输出不仅与当前状态有关,还与当前输入有关
- // one step 第一段
- always@(posedge clk or negedge rst_n)begin
- if(!rst_n) 时序逻辑
- curr_state <=2'b00;
- else 次态赋给现态
- curr_state <= next_state; cs <= ns
- end
- //two step
- always@(*)begin 第二段
- case(curr_state)
- 2'b00: next_state = A?2'b11:2'b01; 组合逻辑
- 2'b01: next_state = A?2'b00:2'b10;
- 2'b10: next_state = A?2'b01:2'b11; 现态和输入决定次态
- 2'b11: next_state = A?2'b10:2'b00;
- default : next_state = 2'b00;
- endcase case(cs)
- end ....
- endcase
三段式寄存器输出,不产生毛刺,有利于时序约束
(1)一段式:一个always块,既描述状态转移,又描述状态的输入输出,当前状态用寄存器输出;
(2)二段式:两个always块,时序逻辑与组合逻辑分开,一个always块采用同步时序描述状态转移;另一个always块采用组合逻辑判断状态转移条件,描述状态转移规律以及输出,当前状态用组合逻辑输出,可能出现竞争冒险,产生毛刺,而且不利于约束,不利于综合器和布局布线器实现高性能的设计;
(当然,第二个always里的状态跳转和输出可以拆分用组合逻辑描述,也可能有三个always块,但是这并不是三段式,和三段式的区别在于输出到底是组合逻辑还是时序逻辑)
(3)三段式:三个always块,一个always模块采用同步时序描述状态转移;一个always采用组合逻辑判断状态转移条件,描述状态转移规律;第三个always块使用同步时序描述状态输出,寄存器输出。
三段式与二段式相比,关键在于根据状态转移规律,在上一状态根据输入条件判断出当前状态的输出,从而在不插入额外时钟节拍的前提下,实现了寄存器输出。
二进制码Binary Code和格雷码Gray Code占用的位宽少,相应的使用的触发器资源少,但是状态对比时需要比较多个bit,消耗的组合逻辑比较多,适用于组合电路资源丰富的情况(CPLD);
独热码One-Hot Code的状态比较时只比较1 bit,节省逻辑资源,使用的触发器资源比较多,适用于触发器资源丰富的情况(FPGA);
总体来讲,状态较少时(4-24个状态)用独热码效果好,状态多时格雷码(状态数大于24)效果好。
对四个状态编码:
二进制码:
S0 = 2’b00;
S1 = 2’b01;
S2 = 2’b10;
S3 = 2’b11;
格雷码相邻码元之间有且只有一位不同:
S0 = 2’b00;
S1 = 2’b01;
S2 = 2’b11;
S3 = 2’b10;
独热码只有一位是“1”:
S0 = 4’b0001;
S1 = 4’b0010;
S2 = 4’b0100;
S3 = 4’b1000;
有时候也是用连续编码,状态值连续:
S0 = 2’d0;
S1 = 2’d1;
S2 = 2’d2;
S3 = 2’d3;
解法一
- `timescale 1ns/1ns
- module seq_circuit(
- input A,
- input clk,
- input rst_n,
-
- output wire Y
- );
- reg [1:0] curr_state;
- reg [1:0] next_state;
- // one step
- always@(posedge clk or negedge rst_n)begin
- if(!rst_n)
- curr_state <=2'b00;
- else
- curr_state <= next_state;
- end
- //two step
- always@(*)begin
- case(curr_state)
- 2'b00: next_state = A?2'b11:2'b01;
- 2'b01: next_state = A?2'b00:2'b10;
- 2'b10: next_state = A?2'b01:2'b11;
- 2'b11: next_state = A?2'b10:2'b00;
- endcase
- end
-
- assign Y = (curr_state==2'b11)?1:0;
- endmodule
解法二
基础的时序电路设计,可采用列激励方程、输出方程,进而用D触发器和组合逻辑电路实现的方案。
由状态表可得出,电路共4个状态,所以使用2个寄存器来实现状态的寄存。两个寄存器的输出为Q1和Q0。
由状态转换表可列出激励方程如下:
- `timescale 1ns/1ns
-
-
-
- module seq_circuit(
- input A ,
- input clk ,
- input rst_n,
-
- output wire Y
- );
-
- reg Q1 ;
- reg Q0 ;
-
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)
- Q1 <= 1'b0;
- else
- Q1 <= Q1 ^ Q0 ^A;
- end
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)
- Q0 <= 1'b0;
- else
- Q0 <= ~Q0;
- end
-
- assign Y = Q1 & Q0;
-
- endmodule
解法三:三段式状态机
- `timescale 1ns/1ns
- module seq_circuit(
- input A,
- input clk,
- input rst_n,
- output wire Y
- );
- localparam ZERO = 2'b00;
- localparam ONE = 2'b01;
- localparam TWO = 2'b10;
- localparam THREE = 2'b11;
- reg Y_temp;
- assign Y = Y_temp;
- reg [1:0] curr_state;
- reg [1:0] next_state;
- //状态转移
- always@(posedge clk or negedge rst_n)begin
- if(!rst_n)
- curr_state <= ZERO;
- else
- curr_state <= next_state;
- end
- //状态判断逻辑
- always@(*)begin
- case(curr_state)
- ZERO : next_state = A ? THREE : ONE;
- ONE : next_state = A ? ZERO : TWO;
- TWO : next_state = A ? ONE : THREE;
- THREE : next_state = A ? TWO : ZERO;
- default: next_state = ONE;
- endcase
- end
- //输出
- always@(posedge clk or negedge rst_n)begin
- if(!rst_n)
- Y_temp <= 1'b0;
- else begin
- case(next_state)
- ZERO : Y_temp <= 1'b0;
- ONE : Y_temp <= 1'b0;
- TWO : Y_temp <= 1'b0;
- THREE : Y_temp <= 1'b1;
- endcase
- end
- end
- endmodule
解法四:容易理解 都是curr_state 没有next_state
- `timescale 1ns/1ns
- module seq_circuit(
- input A,
- input clk,
- input rst_n,
- output wire Y
- );
- reg [1:0] curr_state;
- parameter IDLE = 2'b00;
- parameter ONE = 2'b01;
- parameter TWO = 2'b10;
- parameter THREE = 2'b11;
- always@(posedge clk or negedge rst_n)begin
- if(!rst_n)
- curr_state <= IDLE;
- else
- case(curr_state)
- IDLE:
- if(A== 1'b0)
- curr_state <= ONE;
- else if(A == 1'b1)
- curr_state <= THREE;
- else
- curr_state <= curr_state;
- ONE:
- if(A== 1'b0)
- curr_state <= TWO;
- else if(A == 1'b1)
- curr_state <= IDLE;
- else
- curr_state <= curr_state;
- TWO:
- if(A== 1'b0)
- curr_state <= THREE;
- else if(A == 1'b1)
- curr_state <= ONE;
- else
- curr_state <= curr_state;
- THREE:
- if(A== 1'b0)
- curr_state <= IDLE;
- else if(A == 1'b1)
- curr_state <= TWO;
- else
- curr_state <= curr_state;
- endcase
- end
- assign Y = (curr_state==THREE) ? 1 : 0;
- endmodule
解法五:不太容易想到
- `timescale 1ns/1ns
- module seq_circuit(
- input A,
- input clk,
- input rst_n,
- output wire Y
- );
- reg [1:0] q = 2'b00;
- always@(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- q <= 2'b00;
- end
- else begin
- q <= A ? (q-1'b1):(q+1'b1);
- end
- end
- assign Y = q[0] & q[1];
- endmodule