Verilog有限状态机(5)

HDLBits链接


前言

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


题库

题目描述1:

第一道题目比较容易,题目中的in信号包含了一个起始位(0),8个数据位和一个停止位(1),开始in为1,也就是IDLE状态,当in为0时,进入START状态,然后经过8个周期,如果in为1,则进入STOP状态,接着如果in为0,进入第二轮START状态,否则进入IDLE状态。

这里我用了很多个状态,实际上可以用计数器来代替中间的8个状态,这里是8个周期,如果是100个、200个周期,那么需要100个、200个状态,显然不现实。这道题目是三道题目中的基础题,大家不用考虑那么多,直接完成就好了。

1

Solution1:

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
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output done
);
parameter [3:0] START = 4'd0;
parameter [3:0] ONE = 4'd1;
parameter [3:0] TWO = 4'd2;
parameter [3:0] THREE = 4'd3;
parameter [3:0] FOUR = 4'd4;
parameter [3:0] FIVE = 4'd5;
parameter [3:0] SIX = 4'd6;
parameter [3:0] SEVEN = 4'd7;
parameter [3:0] EIGHT = 4'd8;
parameter [3:0] STOP = 4'd9;
parameter [3:0] IDLE = 4'd10;
parameter [3:0] WAIT = 4'd11;

reg [3:0] state,next_state;

always @(*)begin
case(state)
START:begin
next_state = ONE;
end
ONE:begin
next_state = TWO;
end
TWO:begin
next_state = THREE;
end
THREE:begin
next_state = FOUR;
end
FOUR:begin
next_state = FIVE;
end
FIVE:begin
next_state = SIX;
end
SIX:begin
next_state = SEVEN;
end
SEVEN:begin
next_state = EIGHT;
end
EIGHT:begin
if(in)begin
next_state = STOP;
end
else begin
next_state = WAIT;
end
end
STOP:begin
if(in)begin
next_state = IDLE;
end
else begin
next_state = START;
end
end
WAIT:begin
if(in)begin
next_state = IDLE;
end
else begin
next_state = WAIT;
end
end
IDLE:begin
if(~in)begin
next_state = START;
end
else begin
next_state = IDLE;
end
end
endcase
end

always @(posedge clk)begin
if(reset)begin
state <= IDLE;
end
else begin
state <= next_state;
end
end

assign done = (state == STOP);

endmodule

题目描述2:

这道题目是上一道题目的扩展,不仅需要输出done信号,还需要输出数据。

2

Solution2:

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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/* way 1*/
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output [7:0] out_byte,
output done
); //

parameter [3:0] START = 4'd0;
parameter [3:0] ONE = 4'd1;
parameter [3:0] TWO = 4'd2;
parameter [3:0] THREE = 4'd3;
parameter [3:0] FOUR = 4'd4;
parameter [3:0] FIVE = 4'd5;
parameter [3:0] SIX = 4'd6;
parameter [3:0] SEVEN = 4'd7;
parameter [3:0] EIGHT = 4'd8;
parameter [3:0] STOP = 4'd9;
parameter [3:0] IDLE = 4'd10;
parameter [3:0] WAIT = 4'd11;

reg [3:0] state,next_state;

reg [7:0] par_in;

always @(*)begin
case(state)
START:begin
next_state = ONE;
par_in[0] = in;
end
ONE:begin
next_state = TWO;
par_in[1] = in;
end
TWO:begin
next_state = THREE;
par_in[2] = in;
end
THREE:begin
next_state = FOUR;
par_in[3] = in;
end
FOUR:begin
next_state = FIVE;
par_in[4] = in;
end
FIVE:begin
next_state = SIX;
par_in[5] = in;
end
SIX:begin
next_state = SEVEN;
par_in[6] = in;
end
SEVEN:begin
next_state = EIGHT;
par_in[7] = in;
end
EIGHT:begin
if(in)begin
next_state = STOP;
end
else begin
next_state = WAIT;
end
end
STOP:begin
if(in)begin
next_state = IDLE;
end
else begin
next_state = START;
end
end
WAIT:begin
if(in)begin
next_state = IDLE;
end
else begin
next_state = WAIT;
end
end
IDLE:begin
if(~in)begin
next_state = START;
end
else begin
next_state = IDLE;
end
end
endcase
end

always @(posedge clk)begin
if(reset)begin
state <= IDLE;
end
else begin
state <= next_state;
end
end

assign done = (state == STOP);
assign out_byte = (state == STOP) ? par_in : 8'd0;

endmodule

/* way 2 */
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output [7:0] out_byte,
output done
);
parameter IDLE = 3'd0, START = 3'd1, DATA = 3'd2;
parameter STOP = 3'd3, WAIT = 3'd4;
reg [3:0] current_state;
reg [3:0] next_state;
reg [3:0] counter;
reg [7:0] par_in;
// Use FSM from Fsm_serial
always @(*)begin
case(current_state)
IDLE:begin
if(~in)begin
next_state = START;
end
else begin
next_state = IDLE;
end
end
START:begin
next_state = DATA;
end
DATA:begin
if(counter == 4'd8)begin
next_state = in? STOP:WAIT;
end
else begin
next_state = DATA;
end
end
STOP:begin
next_state = in? IDLE:START;
end
WAIT:begin
next_state = in? IDLE:WAIT;
end
default:begin
next_state = IDLE;
end
endcase
end

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

always @(posedge clk)begin
if(reset)begin
done <= 1'd0;
out_byte <= 8'd0;
counter <= 4'd0;
end
else begin
case(next_state)
IDLE:begin
done <= 1'd0;
out_byte <= 8'd0;
counter <= 4'd0;
end
START:begin
done <= 1'd0;
out_byte <= 8'd0;
counter <= 4'd0;
end
DATA:begin
done <= 1'd0;
out_byte <= 8'd0;
par_in[counter] <= in;
counter <= counter + 1'd1;
end
STOP:begin
done <= 1'd1;
out_byte <= par_in;
counter <= 4'd0;
end
WAIT:begin
done <= 1'd0;
out_byte <= 8'd0;
counter <= 4'd0;
end
endcase
end
end
// New: Datapath to latch input bits.

endmodule

上述第一种解法虽然正确,但是会生成锁存器,并且这里只有1个字节数据还好,用8个状态是可以的,如果换成了10个字节、100个字节,难道要80、800个状态吗?这是不可能的,于是便诞生了方法二博主最想让大家使用的方法。大家可以好好看一下,这里将8个数据状态定义成了一个DATA状态,不用定义S1、S2、S3什么的了,在状态机的第三段,将除了DATA状态的其他状态时的counter都清零,只有到了DATA状态时开始计数,顺便将par_in中的位数用counter代替,这样只需要增加counter和out_byte的位宽,就可以实现任意宽度数据的输出,实用性强。

题目描述3:

较上题添加了奇偶校验位。

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
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output [7:0] out_byte,
output done
); //

parameter IDLE = 3'd0,START = 3'd1,DATA = 3'd2;
parameter STOP = 3'd3,WAIT = 3'd4;

reg [2:0] current_state,next_state;
reg [3:0] counter;
reg [8:0] data_in;
reg odd_temp;
wire Isdone;

// Modify FSM and datapath from Fsm_serialdata
always @(*) begin
case(current_state)
IDLE:begin
if(~in)begin
next_state = START;
end
else begin
next_state = IDLE;
end
end
START:begin
next_state = DATA;
end
DATA:begin
if(counter == 4'd9)begin
next_state = in ? STOP : WAIT;
end
else begin
next_state = DATA;
end
end
STOP:begin
next_state = in ? IDLE : START;
end
WAIT:begin
next_state = in ? IDLE : WAIT;
end
default:begin
next_state = IDLE;
end
endcase
end

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

always @(posedge clk) begin
if(reset)begin
done <= 1'd0;
out_byte <= 8'd0;
counter <= 4'd0;
end
else begin
case(next_state)
IDLE:begin
done <= 1'd0;
out_byte <= 8'd0;
counter <= 4'd0;
end
START:begin
done <= 1'd0;
out_byte <= 8'd0;
counter <= 4'd0;
end
DATA:begin
done <= 1'd0;
out_byte <= 8'd0;
counter <= counter + 1'd1;
data_in[counter] <= in;
end
STOP:begin
done <= odd_temp ? 1'd1 : 1'd0;
out_byte <= odd_temp ? data_in[7:0] : 8'd0;
counter <= 4'd0;
end
WAIT:begin
done <= 1'd0;
out_byte <= 8'd0;
counter <= 4'd0;
end
endcase
end
end
// New: Add parity checking.
assign Isdone = (next_state == START);
parity u0(clk,Isdone,in,odd_temp);

endmodule

该题答案与上题类似,这里我将奇偶校验位数据也计算在DATA状态内,判断起来相对较容易,大家可以借鉴一下。

注意作者在题目中给了奇偶校验的模块,大家直接调用即可。

结语

好久没更新HDLBits了,寒假在家闲来无事,希望勉励自己把HDLBits刷完吧,加油!有问题的地方欢迎大家与我讨论交流。