Verilog有限状态机(6)

HDLBits链接


前言

今天继续更新状态机小节的习题。


题库

Fsm hdlc

同步帧检测涉及对数据的连续位流进行解码,以寻找指示帧(数据包)开始和结束的位模式。 6个连续的1(即01111110)是表示帧边界的“标志”。 为了避免数据流意外包含“标志”,发送方必须在接收方必须检测并丢弃的每5个连续的1秒后插入一个零。 如果连续7个或更多1,我们还需要发出错误信号。

可以通过状态机来识别下面三种序列:

0111110:表示5个1后面的0bit需被忽略;

01111110:表示一帧的开始或结束;

01111111…:错误

当状态机被复位时,它应当表现为之前的输入为0;

下面是三种波形示例:

1

2

3

官方提供的状态机设计提示:

4

Solution:

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//----------------way1----------------------
module top_module(
input clk,
input reset, // Synchronous reset
input in,
output disc,
output flag,
output err);

parameter NONE = 4'd0,ONE = 4'd1,TWO = 4'd2;
parameter THREE = 4'd3,FOUR = 4'd4,FIVE = 4'd5;
parameter SIX = 4'd6,ERROR = 4'd7;
parameter DISC = 4'd8,FLAG = 4'd9;

reg [3:0] current_state,next_state;

always @(*) begin
case(current_state)
NONE:begin
next_state = in ? ONE : NONE;
end
ONE:begin
next_state = in ? TWO : NONE;
end
TWO:begin
next_state = in ? THREE : NONE;
end
THREE:begin
next_state = in ? FOUR : NONE;
end
FOUR:begin
next_state = in ? FIVE : NONE;
end
FIVE:begin
next_state = in ? SIX : DISC;
end
SIX:begin
next_state = in ? ERROR : FLAG;
end
DISC:begin
next_state = in ? ONE : NONE;
end
FLAG:begin
next_state = in ? ONE : NONE;
end
ERROR:begin
next_state = in ? ERROR : NONE;
end
endcase
end

always @(posedge clk) begin
if(reset)begin
current_state <= NONE;
end
else begin
current_state <= next_state;
end
end

always @(posedge clk) begin
if(reset)begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd0;
end
else begin
case(next_state)
DISC:begin
disc <= 1'd1;
flag <= 1'd0;
err <= 1'd0;
end
FLAG:begin
disc <= 1'd0;
flag <= 1'd1;
err <= 1'd0;
end
ERROR:begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd1;
end
default:begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd0;
end
endcase
end
end

endmodule
//----------------way2----------------------
module top_module(
input clk,
input reset, // Synchronous reset
input in,
output disc,
output flag,
output err);

parameter NONE = 3'd0,DATA = 3'd1;
parameter DISC = 3'd2,FLAG = 3'd3,ERROR = 3'd4;

reg [2:0] current_state,next_state;
reg [2:0] counter;

always @(*) begin
case(current_state)
NONE:begin
next_state = in ? DATA : NONE;
end
DATA:begin
case(counter)
3'd5: next_state = in ? DATA : DISC;
3'd6: next_state = in ? ERROR : FLAG;
default:next_state = in ? DATA : NONE;
endcase
end
DISC:begin
next_state = in ? DATA : NONE;
end
FLAG:begin
next_state = in ? DATA : NONE;
end
ERROR:begin
next_state = in ? ERROR : NONE;
end
endcase
end

always @(posedge clk) begin
if(reset)begin
current_state <= NONE;
end
else begin
current_state <= next_state;
end
end

always @(posedge clk) begin
if(reset)begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd0;
counter <= 3'd0;
end
else begin
case(next_state)
DATA:begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd0;
counter <= counter + 1'd1;
end
DISC:begin
disc <= 1'd1;
flag <= 1'd0;
err <= 1'd0;
counter <= 3'd0;
end
FLAG:begin
disc <= 1'd0;
flag <= 1'd1;
err <= 1'd0;
counter <= 3'd0;
end
ERROR:begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd1;
counter <= 3'd0;
end
default:begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd0;
counter <= 3'd0;
end
endcase
end
end

endmodule

其中way1与题目提示的思路相同,其中第三段状态机一开始忘记加reset的那种情况,果然还是长时间没接触verilog手生了,以后继续练习;

way2使用counter去掉中间状态,靠输入和counter的值来决定状态转移,是一种米利状态机的思想;状态转移图如下(手绘有点丑,见谅):

5

米利型状态机

检测输入的X中”101“是否出现,出现的话输出Z为1,否则为0。复位为异步低电平复位;只允许出现3种状态;允许交叠检测:即输入若为10101时,Z应该在时刻3和时刻5各输出一次1;

代码中主要妙在两处:一是如何用3种状态表示,需在第三种状态中将输出与输入关联起来;二是如何进行交叠检测,状态的转移有妙处;

Solution

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
module top_module (
input clk,
input aresetn, // Asynchronous active-low reset
input x,
output z );

parameter S0 = 2'd0,S1 = 2'd1,S2 = 2'd2;
reg [1:0] current_state,next_state;

always @(*) begin
case(current_state)
S0: next_state = x ? S1 : S0;
S1: next_state = x ? S1 : S2;
S2: next_state = x ? S1 : S0;
endcase
end

always @(posedge clk or negedge aresetn) begin
if(~aresetn)begin
current_state <= S0;
end
else begin
current_state <= next_state;
end
end

always @(*) begin
z = (current_state == S2) ? x : 1'b0;
end

endmodule

Q5a:Serial two’s complementer(Moore FSM)

6

作者这里假设我们这边输入的都是负数,不用管符号位的问题;即补码全部都是取反加1求得。

以上图为例,输入的左边为低位数据,右边为高位数据;即输入为00110100,则取反加1后得输出为11001100;

取反操作好进行,主要麻烦在加一的操作上,不知道进位到哪一位为止,此时我们用状态机来解决;若最前面的输入都是0的话,取反均为1,低位加1的时候一直进位,则输出都是0,直到输入有个1为止(取反加1不进位),这一阶段我们用一个状态S1来表示;后面阶段就将输入取反进行输出即可,因为进位链在S1状态已结束;

因为是摩尔型状态机,输出的结果与输入无关,仅与状态有关。所以我们这里用到3个状态,代码如下所示:

Solution

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
module top_module (
input clk,
input areset,
input x,
output z
);

parameter S0 = 2'd0, S1 = 2'd1, S2 = 2'd2;
reg [1:0] current_state, next_state;

always @(*) begin
case(current_state)
S0: next_state = x ? S1 : S0;
S1: next_state = x ? S2 : S1;
S2: next_state = x ? S2 : S1;
endcase
end

always @(posedge clk or posedge areset) begin
if(areset)begin
current_state <= S0;
end
else begin
current_state <= next_state;
end
end

assign z = (current_state == S1);

endmodule

Q5b:Serial two’s complementer(Mealy FSM)

题意与上题相同,此时用米利型状态机实现。下面是官方提供的状态转移提示:

7

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
module top_module (
input clk,
input areset,
input x,
output z
);

parameter S0 = 1'b0, S1 = 1'b1;
reg current_state,next_state;

always @(*) begin
case(current_state)
S0: next_state = x ? S1 : S0;
S1: next_state = S1;
endcase
end

always @(posedge clk or posedge areset) begin
if(areset)begin
current_state <= S0;
end
else begin
current_state <= next_state;
end
end

assign z = ((current_state == S0) && x) || ((current_state == S1) && ~x);

endmodule

结语

今天先更新这几题吧,大家如果对转补码的题目还有什么疑问欢迎评论交流,代码有不足之处还望指正。