FPGA--关于状态机

​ 状态机,是我大二在实验室学习时,学长给我们安排的任务之一。那个时候,我还没有学过《数电》,一直搞不明白状态机的意思,做了一周,都没有结果。
任务是检测序列,检测到1011时输出一个高电平。

我们都知道verilog语言依靠不同的always语句块实现了硬件电路的并行执行,但是在工程中,我们不仅要处理并行执行电路,偶尔也会遇到需要串行执行的电路要求。刚开始学习FPGA的话,可能会想到我们可以利用很多很多的使能信号实现,但是这样维护的成本大大增加。状态机就可以完美的实现这一功能。先简单介绍一下状态机的基本概念。

状态机的基本要素是输入、输出和状态。输入是引起状态变化的条件,输出是状态变化引起的变化。状态就是字面理解的意思了,状态机,通俗的说就是因为输入导致状态在不断的变化的硬件电路。

Moore型状态机的输出仅与状态有关,与输入无关。
Mealy型状态机的输入不仅与状态有关,还与输入条件有关。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oq0qQTjK-1571226216451)(http://img.blog.csdn.net/20180119154529552?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hlcmlzaF94/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)]
一段式写法,输入、输出和状态在一个always语句块中,后期不容易维护。

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
48
49
50
51
52
53
54
55
module pro(
input clk,
input rst_n,

input data,
output cntout
);
parameter INITIALSTATE = 5'b00_001,
STATE_0 = 5'b00_010,
STATE_1 = 5'b00_100,
STATE_2 = 5'b01_000,
STATE_3 = 5'b10_000;



//————————————————————————————————————————————
//一段式写法, 既把输入、输出和状态写在一个always语句块

reg[4:0] state_ce; //状态寄存器
reg cnt;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 1'b0;
state_ce <= INITIALSTATE;
end
else
case(state_ce)
INITIALSTATE: begin
cnt <= 1'b0;
if(data == 1'b1)
state_ce <= STATE_0;
else
state_ce <= INITIALSTATE;
end
STATE_0: if(data == 1'b0)
state_ce <= STATE_1;
else
state_ce <= STATE_0;
STATE_1: if(data == 1'b1)
state_ce <= STATE_2;
else
state_ce <= INITIALSTATE;
STATE_2: if(data == 1'b1)
state_ce <= STATE_3;
else
state_ce <= STATE_1;
STATE_3: begin
state_ce <= INITIALSTATE;
cnt <= 1'b1;
end
endcase
end

assign cntout = cnt;
endmodule

[外链图片转存中…(img-CLvmuOJa-1571226216454)]

二段式写法,分成了组合逻辑和时序逻辑。时序逻辑里作状态的转移,组合逻辑里作输入条件判断和输出。但是组合逻辑输出容易出现毛刺问题。

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
module pro(
input clk,
input rst_n,

input data,
output cntout
);
parameter INITIALSTATE = 5'b00_001,
STATE_0 = 5'b00_010,
STATE_1 = 5'b00_100,
STATE_2 = 5'b01_000,
STATE_3 = 5'b10_000;

reg[4:0] state_ce;
reg[4:0] state_nt;
reg cnt;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
state_ce <= INITIALSTATE;
else
state_ce <= state_nt;
end

always @ (state_ce or data) begin
case(state_ce)
INITIALSTATE: begin
cnt = 1'b0;
if(data == 1'b1)
state_nt = STATE_0;
else
state_nt = INITIALSTATE;
end
STATE_0: begin
cnt = 1'b0;
if(data == 1'b0)
state_nt = STATE_1;
else
state_nt = STATE_0;
end
STATE_1: begin
cnt = 1'b0;
if(data == 1'b1)
state_nt = STATE_2;
else
state_nt = INITIALSTATE;
end
STATE_2: begin
if(data == 1'b1)
state_nt= STATE_3;
else
state_nt =STATE_1;
end
STATE_3: begin
cnt = 1'b1;
state_nt = INITIALSTATE;
end
endcase
end


assign cntout = cnt;

endmodule

三段式写法,又将输出写作时序逻辑输出,解决了二段式中毛刺的问题。

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
48
49
50
51
52
53
54
55
56
57
module pro(
input clk,
input rst_n,

input data,
output cntout
);
parameter INITIALSTATE = 5'b00_001,
STATE_0 = 5'b00_010,
STATE_1 = 5'b00_100,
STATE_2 = 5'b01_000,
STATE_3 = 5'b10_000;

reg[4:0] state_ce;
reg[4:0] state_nt;
reg cnt;

always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
state_ce <= INITIALSTATE;
else
state_ce <= state_nt;
end

always @ (state_ce or data) begin
case(state_ce)
INITIALSTATE: if(data == 1'b1)
state_nt = STATE_0;
else
state_nt = INITIALSTATE;
STATE_0: if(data == 1'b0)
state_nt = STATE_1;
else
state_nt = STATE_0;
STATE_1: if(data == 1'b1)
state_nt = STATE_2;
else
state_nt = INITIALSTATE;
STATE_2: if(data == 1'b1)
state_nt = STATE_3;
else
state_nt = STATE_2;
STATE_3: state_nt = INITIALSTATE;
endcase
end

always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
cnt <= 'd0;
else if(state_nt == STATE_3)
cnt <= 1'b1;
else
cnt <= 1'b0;
end

assign cntout = cnt;
endmodule
------ 本文结束 ------
0%