本文主要对牛客网Verilog刷题中的进阶习题进行记录。
输入序列连续的序列检测
请编写一个序列检测模块,检测输入信号a是否满足01110001序列,当信号满足该序列,给出指示信号match。
sequence_detect_easy.v
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
94module sequence_detect_easy(
input clk,
input rst_n,
input a,
output reg match
);
//状态定义
parameter IDLE = 4'd0, //初始状态
S1 = 4'd1, //"0"
S2 = 4'd2, //"01"
S3 = 4'd3, //"011"
S4 = 4'd4, //"0111"
S5 = 4'd5, //"01110"
S6 = 4'd6, //"011100"
S7 = 4'd7, //"0111000"
S8 = 4'd8; //"01110001"
reg [3:0] state,next_state;
//状态跳转
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
state <= IDLE;
end
else begin
state <= next_state;
end
end
//次态判断
always @(*) begin
case (state)
IDLE: next_state = a ? IDLE : S1;
S1: next_state = a ? S2 : S1;
S2: next_state = a ? S3 : S1;
S3: next_state = a ? S4 : S1;
S4: next_state = a ? IDLE : S5;
S5: next_state = a ? S2 : S6;
S6: next_state = a ? S2 : S7;
S7: next_state = a ? S8 : S1;
S8: next_state = a ? S3 : S1; //这里实际上重复检测了,重复的是“011”
default: next_state = IDLE; //本来想省略一个状态的,但若省略S8,那么输出会提前一个clk输出,且不能重复检测
endcase
end
//输出时序
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
match <= 1'b0;
end
else begin
match <= (state == S8) ? 1 : 0;
end
end
endmodule
/*使用移位寄存器实现序列检测器会更简单
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
reg [7:0] a_tem;
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
match <= 1'b0;
end
else if (a_tem == 8'b0111_0001)
begin
match <= 1'b1;
end
else
begin
match <= 1'b0;
end
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
a_tem <= 8'b0;
end
else
begin
a_tem <= {a_tem[6:0],a};
end
endmodule
*/
含有无关项的序列检测*
请编写一个序列检测模块,检测输入信号a是否满足011XXX110序列(长度为9位数据,前三位是011,后三位是110,中间三位不做要求),当信号满足该序列,给出指示信号match。
该题使用移位寄存器可以很好的实现
源代码
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
35module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
reg [8 : 0] a_shift_reg;
//移位寄存器
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
a_shift_reg <= 9'd0;
end
else begin
a_shift_reg <= {a_shift_reg[7 : 0], a};
end
end
//match判断
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
match <= 1'b0;
end
else begin
if(a_shift_reg[8 -: 3] == 3'b011 && a_shift_reg[0 +: 3] == 3'b110) begin
match <= 1'b1;
end
else begin
match <= 1'b0;
end
end
end
endmodule
不重复序列检测*
请编写一个序列检测模块,检测输入信号(a)是否满足011100序列, 要求以每六个输入为一组,不检测重复序列,例如第一位数据不符合,则不考虑后五位。一直到第七位数据即下一组信号的第一位开始检测。当信号满足该序列,给出指示信号match。当不满足时给出指示信号not_match。
模块的接口信号图如下:(要求必须用状态机实现)
添加一个ERROR状态,当出现某位出现错误是,不再检测后面的位,只是等待这组数据的结束
源代码:
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
143module Sequence_detect_no_repeat(
input clk,
input rst_n,
input data,
output reg match,
output reg not_match
);
//===============================参数定义=========================
//状态定义
localparam IDLE = 4'd0,
S1 = 4'd1,
S2 = 4'd2,
S3 = 4'd3,
S4 = 4'd4,
S5 = 4'd5,
ERROR = 4'd7;
//现态与次态的定义
reg [3 : 0] cur_state, next_state;
//计数器
reg [2 : 0] counter;
//===============================状态机逻辑=========================
//计数器逻辑
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
counter <= 'd0;
end
else begin
if(counter == 5) begin
counter <= 'd0;
end
else begin
counter <= counter + 1;
end
end
end
//1.状态转移
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
cur_state <= IDLE;
end
else begin
cur_state <= next_state;
end
end
//2.次态判断
always @(*) begin
next_state = IDLE;
case(cur_state)
IDLE: begin
if(data == 1'b0) begin
next_state = S1;
end
else begin
next_state = ERROR;
end
end
S1: begin
if(data == 1'b1) begin
next_state = S2;
end
else begin
next_state = ERROR;
end
end
S2: begin
if(data == 1'b1) begin
next_state = S3;
end
else begin
next_state = ERROR;
end
end
S3: begin
if(data == 1'b1) begin
next_state = S4;
end
else begin
next_state = ERROR;
end
end
S4: begin
if(data == 1'b0) begin
next_state = S5;
end
else begin
next_state = ERROR;
end
end
S5: begin
if(data == 1'b0) begin
next_state = IDLE;
end
else begin
next_state = ERROR;
end
end
ERROR: begin
if(counter == 5) begin
next_state = IDLE;
end
else begin
next_state = ERROR;
end
end
endcase
end
//3.输出逻辑
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
match <= 1'b0;
end
else begin
if(cur_state == S5 && data == 1'b0) begin
match <= 1'b1;
end
else begin
match <= 1'b0;
end
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
not_match <= 1'b0;
end
else begin
if(cur_state == ERROR && counter == 'd5) begin
not_match <= 1'b1;
end
else begin
not_match <= 1'b0;
end
end
end
endmodule
输入序列不连续的序列检测*
请编写一个序列检测模块,输入信号端口为data,表示数据有效的指示信号端口为data_valid。当data_valid信号为高时,表示此刻的输入信号data有效,参与序列检测;当data_valid为低时,data无效,抛弃该时刻的输入。当输入序列的有效信号满足0110时,拉高序列匹配信号match。(要求用状态机实现)
注意判断输出时,由于S3只是011,所以还需对此时输入data(且有效)判断:
cur_state == S3 && data == 1'b0 && data_valid == 1'b1
源代码:
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
84module Sequence_detect_Discontinuous(
input clk,
input rst_n,
input data,
input data_valid,
output reg match
);
//===============================参数定义=========================
//状态定义
localparam IDLE = 2'd0,
S1 = 2'd1,
S2 = 2'd2,
S3 = 2'd3;
//现态与次态定义
reg [1 : 0] cur_state, next_state;
//===============================状态机逻辑=========================
//1.状态转移
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
cur_state <= IDLE;
end
else begin
cur_state <= next_state;
end
end
//2.次态判断
always @(*) begin
next_state = IDLE;
case(cur_state)
IDLE: begin
if(data_valid && data == 1'b0) begin
next_state = S1;
end
else begin
next_state = IDLE;
end
end
S1: begin
if(data_valid && data == 1'b1) begin
next_state = S2;
end
else begin
next_state = S1;
end
end
S2: begin
if(data_valid && data == 1'b1) begin
next_state = S3;
end
else begin
next_state = S2;
end
end
S3: begin
if(data_valid && data == 1'b0) begin
next_state = IDLE;
end
else begin
next_state = S3;
end
end
endcase
end
//3.输出逻辑
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
match <= 1'b0;
end
else begin
if(cur_state == S3 && data == 1'b0 && data_valid == 1'b1) begin
match <= 1'b1;
end
else begin
match <= 1'b0;
end
end
end
endmodule
信号发生器*
请编写一个信号发生器模块,根据波形选择信号wave_choise发出相应的波形:wave_choice=0时,发出方波信号;wave_choice=1时,发出锯齿波信号;wave_choice=2时,发出三角波信号。
注意
square_wave_cnt < 'd10
(1~10、11~19~0
)与square_wave_cnt < 'd9 || square_wave_cnt == 'd19
(0~9、9~19
)的区别。正常来说都是50%占空比的方波,不知为何不加就会牛客网编译器就会报错square_wave_cnt < ‘d10:
square_wave_cnt < ‘d9 || square_wave_cnt == ‘d19
源代码
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
module signal_generator(
input clk,
input rst_n,
input [1:0] wave_choise,
output reg [4:0]wave
);
//方波计数器
reg [4 : 0] square_wave_cnt;
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
square_wave_cnt <= 'd0;
end
else begin
if(wave_choise == 'd0) begin
if(square_wave_cnt == 'd19) begin
square_wave_cnt <= 'd0;
end
else begin
square_wave_cnt <= square_wave_cnt + 1;
end
end
else begin
square_wave_cnt <= 'd0;
end
end
end
//三角波控制器
reg triangle_wave_flag; //其中flag=0代表下降,=1代表上升
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
triangle_wave_flag <= 1'b0;
end
else begin
if(wave_choise == 'd2) begin
if(wave == 'd1) begin
triangle_wave_flag <= 1'b1;
end
else begin
if(wave == 'd19) begin //其实此时是1~20的wave为上升,21~0的wave为下降
triangle_wave_flag <= 1'b0;
end
end
end
else begin
triangle_wave_flag <= 1'b0;
end
end
end
//输出选择控制
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
wave <= 'd0;
end
else begin
case(wave_choise)
2'd0: begin
if(square_wave_cnt < 'd9 || square_wave_cnt == 'd19) begin
wave <= 'd0;
end
else begin
wave <= 'd20;
end
end
2'd1: begin
if(wave == 'd20) begin
wave <= 'd0;
end
else begin
wave <= wave + 1;
end
end
2'd2: begin
if(triangle_wave_flag == 1'b0) begin
wave <= wave - 1;
end
else begin
wave <= wave + 1;
end
end
default: wave <= 'd0;
endcase
end
end
endmodule
数据串转并电路
实现串并转换电路,输入端输入单bit数据,每当本模块接收到6个输入数据后,输出端输出拼接后的6bit数据。本模块输入端与上游的采用valid-ready双向握手机制,输出端与下游采用valid-only握手机制。数据拼接时先接收到的数据放到data_b的低位。电路的接口如下图所示。valid_a用来指示数据输入data_a的有效性,valid_b用来指示数据输出data_b的有效性;ready_a用来指示本模块是否准备好接收上游数据,本模块中一直拉高;clk是时钟信号;rst_n是异步复位信号。
源代码:
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
76module s_to_p(
input clk ,
input rst_n ,
input valid_a ,
input data_a ,
output reg ready_a ,
output reg valid_b ,
output reg [5:0] data_b
);
reg [5 : 0] data_out_r;
reg [2 : 0] cnt;
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
cnt <= 'd0;
end
else begin
if(valid_a && ready_a) begin
if(cnt == 'd5) begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1;
end
end
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_out_r <= 'd0;
end
else begin
if(valid_a) begin
data_out_r <= {data_a, data_out_r[5 : 1]};
end
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_b <= 'd0;
end
else begin
if(cnt == 'd5) begin
data_b <= {data_a, data_out_r[5 : 1]};
end
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
ready_a <= 1'b0;
end
else begin
ready_a <= 1'b1;
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
valid_b <= 1'b0;
end
else begin
if(cnt == 'd5) begin
valid_b <= 1'b1;
end
else begin
valid_b <= 1'b0;
end
end
end
endmodule
数据累加输出***
实现串行输入数据累加输出,输入端输入8bit数据,每当模块接收到4个输入数据后,输出端输出4个接收到数据的累加结果。输入端和输出端与上下游的交互采用valid-ready双向握手机制。要求上下游均能满速传输时,数据传输无气泡,不能由于本模块的设计原因产生额外的性能损失。
电路的接口如下图所示。valid_a用来指示数据输入data_in的有效性,valid_b用来指示数据输出data_out的有效性;ready_a用来指示本模块是否准备好接收上游数据,ready_b表示下游是否准备好接收本模块的输出数据;clk是时钟信号;rst_n是异步复位信号。
好好理解ready_a和ready_b信号对正常只有valid逻辑的影响,重点看代码里面的注释!
源代码:
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
68module valid_ready(
input clk ,
input rst_n ,
input [7:0] data_in ,
input valid_a ,
input ready_b ,
output ready_a ,
output reg valid_b ,
output reg [9:0] data_out
);
reg [1 : 0] cnt;
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
cnt <= 'd0;
end
else begin
if(valid_a && ready_a) begin //与输出相关的信号时,都带上ready_a就行,因为当本级准备好时,才能正常工作
if(cnt == 'd3) begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1;
end
end
end
end
//数据输出
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_out <= 'd0;
end
else begin
if(cnt == 'd0 && valid_a && ready_a && ready_b) begin //这里其实不太好理解,就是当cnt == 0的时候是要开启下一次计算(正常来说),重新加载第一个数据
data_out <= data_in; //如果下一级准备好了,就正常加载,如果没有准备好,数据应该保持,
end
else if(valid_a && ready_a) begin //其实~ready_b成立时,ready_a == 0(根据ready_a的组合逻辑),那么就肯定是data_out保持了
data_out <= data_out + data_in; //所以这里写法可以这样简化,没有单独写出~ready_b的情况
end
end
end
//数据有效信号
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
valid_b <= 1'b0;
end
else begin
if(cnt == 'd3 && valid_a && ready_a) begin //这里很容易出错,注意当cnt=='d3时,可不一定是valid_a==1
valid_b <= 1'b1;
end
else begin
if(~ready_b) begin //可以等效成代码:else if(valid_b && ready_b)
valid_b <= valid_b; // valid_b <= 1'd0;
end //该等效代码的理解是:运算完成后需置高1clk,在下一个时钟周期就要归0了
else begin //如果此时下一级准备好了,说明下一级已经读到数据运算有效信号了,就拉低valid_b(就跟没有这个ready信号的正常操作一样)
valid_b <= 1'b0; //如果此时下一级还没有准备好,那么就要继续保持这个valid_b信号,直到下一级在准备好的时候能读到这个信号,知道数据已经运算有效了
end
end //这个我自己写的分支的理解是:运算有效后如果下一级模块还没有准备好(~ready),那么该valid_b信号就要继续保持(因为如果没有准备好,所有输出数据都是要保持的)
end //如果准备好了,那就正常归0(因为正常情况下valid就只会拉高1clk)
end
//输出给上一级的ready
assign ready_a = (valid_b && ~ready_b) ? 0 : 1; //数据有效且下一级未准备好时 输出给上一级的ready拉高
endmodule
非整数倍数据位宽转换24to128*
实现数据位宽转换电路,实现24bit数据输入转换为128bit数据输出。其中,先到的数据应置于输出的高bit位。电路的接口如下图所示。valid_in用来指示数据输入data_in的有效性,valid_out用来指示数据输出data_out的有效性;clk是时钟信号;rst_n是异步复位信号。
这里的解题思路很巧妙,输入数据是24bit,输出数据是128bit。因为128×3=24×16128×3=24×16,所以每输入16个有效数据,就可以产生三个完整的输出。因此设置一个仅在输入数据有效时工作的计数器
cnt
,计数范围是0-15。设置一个数据暂存器
data_lock
(128bit),每当输入有效时,将数据从低位移入。每当计数器
cnt
计数到5、10、15时,data_out
要进行更新,并拉高valid_out
一个周期。源代码:
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
75module width_24to128(
input clk ,
input rst_n ,
input valid_in ,
input [23:0] data_in ,
output reg valid_out ,
output reg [127:0] data_out
);
reg [3 : 0] cnt;
reg [127 : 0] data_lock;
//计数器逻辑
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
cnt <= 'd0;
end
else begin
if(valid_in) begin
if(cnt == 'd15) begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1;
end
end
end
end
//移位拼接
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_lock <= 'd0;
end
else begin
if(valid_in) begin
data_lock <= {data_lock[103 : 0], data_in};
end
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_out <= 'd0;
end
else begin
if(cnt == 'd5 && valid_in) begin
data_out <= {data_lock[119 : 0], data_in[23 : 16]}; //在输出这组数据最后一个数的同时,这个数将被存到data_lock中,以便下次使用
end
else if(cnt == 'd10 && valid_in) begin
data_out <= {data_lock[111 : 0], data_in[23 : 8]};
end
else if(cnt == 'd15 && valid_in) begin
data_out <= {data_lock[103 : 0], data_in[23 : 0]};
end
end
end
//输出有效信号
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
valid_out <= 1'b0;
end
else begin
if(valid_in && (cnt == 'd5 || cnt == 'd10 || cnt == 'd15)) begin
valid_out <= 1'b1;
end
else begin
valid_out <= 1'b0;
end
end
end
endmodule
非整数倍数据位宽转换8to12
实现数据位宽转换电路,实现8bit数据输入转换为12bit数据输出。其中,先到的数据应置于输出的高bit位。电路的接口如下图所示。valid_in用来指示数据输入data_in的有效性,valid_out用来指示数据输出data_out的有效性;clk是时钟信号;rst_n是异步复位信号。
本题解法与上题一致
源代码:
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
73module width_8to12(
input clk ,
input rst_n ,
input valid_in ,
input [7:0] data_in ,
output reg valid_out,
output reg [11:0] data_out
);
//8 * 3 = 12 * 2
reg [1 : 0] cnt;
reg [11 : 0] data_lock;
//计数器逻辑
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
cnt <= 'd0;
end
else begin
if(valid_in) begin
if(cnt == 'd2) begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
end
end
//移位锁存
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_lock <= 'd0;
end
else begin
if(valid_in) begin
data_lock <= {data_lock[3 : 0], data_in};
end
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_out <= 'd0;
end
else begin
if(valid_in && cnt == 'd1) begin
data_out <= {data_lock[7 : 0], data_in[7 : 4]};
end
else if(valid_in && cnt == 'd2) begin
data_out <= {data_lock[3 : 0], data_in};
end
end
end
//数据有效信号
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
valid_out <= 1'b0;
end
else begin
if(valid_in && (cnt == 'd1 || cnt == 'd2)) begin
valid_out <= 1'b1;
end
else begin
valid_out <= 1'b0;
end
end
end
endmodule
整数倍数据位宽转换8to16
实现数据位宽转换电路,实现8bit数据输入转换为16bit数据输出。其中,先到的8bit数据应置于输出16bit的高8位。电路的接口如下图所示。valid_in用来指示数据输入data_in的有效性,valid_out用来指示数据输出data_out的有效性;clk是时钟信号;rst_n是异步复位信号。
源代码:
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
64module width_8to16_integer(
input clk ,
input rst_n ,
input valid_in ,
input [7:0] data_in ,
output reg valid_out ,
output reg [15:0] data_out
);
reg [15 : 0] data_lock;
//8 * 2 = 16
reg cnt;
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
cnt <= 'd0;
end
else begin
if(valid_in) begin
cnt <= ~cnt;
end
end
end
//移位输出
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_lock <='d0;
end
else begin
if(valid_in) begin
data_lock <= {data_lock[7 : 0], data_in};
end
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_out <= 'd0;
end
else begin
if(valid_in && cnt == 'd1) begin
data_out <= {data_lock[7 : 0], data_in};
end
end
end
//数据输出有效
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
valid_out <= 1'b0;
end
else begin
if(valid_in && cnt == 'd1) begin
valid_out <= 1'b1;
end
else begin
valid_out <= 1'b0;
end
end
end
endmodule
状态机-非重叠的序列检测
设计一个状态机,用来检测序列 10111,要求:
1、进行非重叠检测 即101110111 只会被检测通过一次
2、寄存器输出且同步输出结果(注意rst为低电平复位)
源代码:
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
55module sequence_test1(
input wire clk ,
input wire rst ,
input wire data ,
output reg flag
);
//状态定义
localparam IDLE = 3'd0,
S1 = 3'd1,
S2 = 3'd2,
S3 = 3'd3,
S4 = 3'd4;
//现态与次态定义
reg [2 : 0] cur_state, next_state;
//1.状态跳转
always @(posedge clk or negedge rst) begin
if(~rst) begin
cur_state <= IDLE;
end
else begin
cur_state <= next_state;
end
end
//2.次态判断
always @(*) begin
case(cur_state)
IDLE: next_state = data == 1 ? S1 : IDLE;
S1: next_state = data == 0 ? S2 : S1;
S2: next_state = data == 1 ? S3 : S2;
S3: next_state = data == 1 ? S4 : S3;
S4: next_state = data == 1 ? IDLE : S4;
default: next_state = IDLE;
endcase
end
//3.输出判断
always @(posedge clk or negedge rst) begin
if(~rst) begin
flag <= 1'b0;
end
else begin
if(cur_state == S4 && data == 1) begin
flag <= 1'b1;
end
else begin
flag <= 1'b0;
end
end
end
endmodule
状态机-重叠序列检测
设计一个状态机,用来检测序列 1011,要求:
1、进行重叠检测 即10110111 会被检测通过2次
2、寄存器输出,在序列检测完成下一拍输出检测有效(注意rst为低电平复位)
源文件:
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
56module sequence_test2(
input wire clk ,
input wire rst ,
input wire data ,
output reg flag
);
//*************code***********//
//状态定义
localparam IDLE = 3'd0,
S1 = 3'd1,
S2 = 3'd2,
S3 = 3'd3,
S4 = 3'd4;
//现态与次态定义
reg [2 : 0] cur_state, next_state;
//1.状态跳转
always @(posedge clk or negedge rst) begin
if(~rst) begin
cur_state <= IDLE;
end
else begin
cur_state <= next_state;
end
end
//2.次态判断
always @(*) begin
case(cur_state)
IDLE: next_state = data == 1 ? S1 : IDLE;
S1: next_state = data == 0 ? S2 : S1;
S2: next_state = data == 1 ? S3 : S2;
S3: next_state = data == 1 ? S4 : S3;
S4: next_state = data == 1 ? S1 : S2;
default: next_state = IDLE;
endcase
end
//3.输出判断
always @(posedge clk or negedge rst) begin
if(~rst) begin
flag <= 1'b0;
end
else begin
if(cur_state == S4) begin
flag <= 1'b1;
end
else begin
flag <= 1'b0;
end
end
end
//*************code***********//
endmodule
时钟分频(偶数)*
请使用D触发器设计一个同时输出2/4/8分频的50%占空比的时钟分频。注意rst为低电平复位
其实偶数分频是有些小技巧的,即利用计数器,不过之前已经会啦
源代码:
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
42module even_div
(
input wire rst ,
input wire clk_in,
output wire clk_out2,
output wire clk_out4,
output wire clk_out8
);
//*************code***********//
reg [2 : 0] clk_reg;
reg clk_out2_r;
reg clk_out4_r;
reg clk_out8_r;
always @(posedge clk_in or negedge rst) begin
if(~rst) begin
clk_reg <= 'd0;
end
else begin
clk_reg <= clk_reg + 1;
end
end
always @(posedge clk_in or negedge rst) begin
if(~rst) begin
clk_out2_r <= 1'b1;
clk_out4_r <= 1'b1;
clk_out8_r <= 1'b1;
end
else begin
clk_out2_r <= clk_reg[0];
clk_out4_r <= clk_reg[1];
clk_out8_r <= clk_reg[2];
end
end
assign clk_out2 = ~clk_out2_r;
assign clk_out4 = ~clk_out4_r;
assign clk_out8 = ~clk_out8_r;
//*************code***********//
endmodule
自动贩售机1**
设计一个自动贩售机,输入货币有三种,为0.5/1/2元,饮料价格是1.5元,要求进行找零,找零只会支付0.5元。
ps:投入的货币会自动经过边沿检测并输出一个在时钟上升沿到1,在下降沿到0的脉冲信号
(d1 0.5元;d2 1元;d3 2元;out1 饮料;out2 零钱)
可以借助该题好好理解米粒(输出取决于当前状态+输入)和摩尔型(输出只取决于当前状态)的区别
- 米粒型由于当前状态与输入有关,可以放在输出逻辑里面判断输出,从而减小状态机数量,但增加输出逻辑的复杂度
- 摩尔型由于当前状态与输出无关,那么其实有很多状态只是为了指示输出,这无形中增加了状态机的数量
米粒型版本:(状态仅表示空闲、累计0.5元、累计1元)
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
138module seller1(
input wire clk ,
input wire rst ,
input wire d1 ,
input wire d2 ,
input wire d3 ,
output reg out1,
output reg [1:0]out2
);
//状态定义
parameter IDLE = 'd0,
S1 = 'd1, //0.5
S2 = 'd2; //1
//现态与次态定义
reg [2 : 0] cur_state, next_state;
//1.状态跳转
always @(posedge clk or negedge rst) begin
if(~rst) begin
cur_state <= IDLE;
end
else begin
cur_state <= next_state;
end
end
//2.次态判断
always @(*) begin
//next_state = IDLE; 因为次态判断中存在next_state = next_state;这样的写法,那么就不能把该语句放在前面,不然每次判断后next_state都只会等于IDLE
case(cur_state) //主要原因在于d1,d2,d3都只拉高半个时钟周期
IDLE: begin
case({d1,d2,d3})
3'b100: next_state = S1;
3'b010: next_state = S2;
3'b001: next_state = IDLE;
default: next_state = next_state;
endcase
end
S1: begin
case({d1,d2,d3})
3'b100: next_state = S2;
3'b010: next_state = IDLE;
3'b001: next_state = IDLE;
default: next_state = next_state;
endcase
end
S2: begin
case({d1,d2,d3})
3'b100: next_state = IDLE;
3'b010: next_state = IDLE;
3'b001: next_state = IDLE;
default: next_state = next_state;
endcase
end
default: next_state = IDLE;
endcase
end
//3.输出逻辑
reg out1_r;
reg [1 : 0] out2_r;
always @(posedge clk or negedge rst) begin
if(~rst) begin
out1_r <= 'd0;
out2_r <= 'd0;
end
else begin
case(cur_state)
IDLE: begin
if({d1,d2,d3} == 3'b001) begin //这里{d1,d2,d3}能被捕获到也是挺神奇的
out1_r <= 1'b1;
out2_r <= 'd1;
end
else begin
out1_r <= 1'b0;
out2_r <= 'd0;
end
end
S1: begin
case({d1,d2,d3})
3'b010: begin
out1_r <= 1'b1;
out2_r <= 'd0;
end
3'b001: begin
out1_r <= 1'b1;
out2_r <= 'd2;
end
default: begin
out1_r <= 1'b0;
out2_r <= 'd0;
end
endcase
end
S2: begin
case({d1,d2,d3})
3'b100: begin
out1_r <= 1'b1;
out2_r <= 'd0;
end
3'b010: begin
out1_r <= 1'b1;
out2_r <= 'd1;
end
3'b001: begin
out1_r <= 1'b1;
out2_r <= 'd3;
end
default: begin
out1_r <= 1'b0;
out2_r <= 'd0;
end
endcase
end
default: begin
out1_r <= 1'b0;
out2_r <= 'd0;
end
endcase
end
end
always @(posedge clk or negedge rst) begin
if(~rst) begin
out1 <= 'd0;
out2 <= 'd0;
end
else begin
out1 <= out1_r;
out2 <= out2_r;
end
end
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
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
94module seller1(
input wire clk ,
input wire rst ,
input wire d1 ,
input wire d2 ,
input wire d3 ,
output reg out1,
output reg [1:0]out2
);
//状态定义
parameter IDLE = 'd0,
S1 = 'd1, //0.5
S2 = 'd2, //1
S3 = 'd3, //1.5
S4 = 'd4, //2
S5 = 'd5, //2.5
S6 = 'd6; //3
//现态与次态定义
reg [2 : 0] cur_state, next_state;
//1.状态跳转
always @(posedge clk or negedge rst) begin
if(~rst) begin
cur_state <= IDLE;
end
else begin
cur_state <= next_state;
end
end
//2.次态判断
always @(*) begin
//next_state = IDLE; 因为次态判断中存在next_state = next_state;这样的写法,那么就不能把该语句放在前面,不然每次判断后next_state都只会等于IDLE
case(cur_state) //主要原因在于d1,d2,d3都只拉高半个时钟周期
IDLE: begin
case({d1,d2,d3})
3'b100: next_state = S1;
3'b010: next_state = S2;
3'b001: next_state = S4;
default: next_state = next_state;
endcase
end
S1: begin
case({d1,d2,d3})
3'b100: next_state = S2;
3'b010: next_state = S3;
3'b001: next_state = S5;
default: next_state = next_state;
endcase
end
S2: begin
case({d1,d2,d3})
3'b100: next_state = S3;
3'b010: next_state = S4;
3'b001: next_state = S6;
default: next_state = next_state;
endcase
end
default: next_state = IDLE;
endcase
end
//3.输出逻辑
always @(posedge clk or negedge rst) begin
if(~rst) begin
out1 <= 'd0;
end
else begin
if(next_state == S3 || next_state == S4 || next_state == S5 || next_state == S6) begin
out1 <= 'd1;
end
else begin
out1 <= 'd0;
end
end
end
always @(posedge clk or negedge rst) begin
if(~rst) begin
out2 <= 'd0;
end
else begin
case(next_state)
S4: out2 <= 'd1;
S5: out2 <= 'd2;
S6: out2 <= 'd3;
default: out2 <= 'd0;
endcase
end
end
endmodule
自动贩售机2*
设计一个自动贩售机,输入货币有两种,为0.5/1元,饮料价格是1.5/2.5元,要求进行找零,找零只会支付0.5元。
ps:1、投入的货币会自动经过边沿检测并输出一个在时钟上升沿到1,在下降沿到0的脉冲信号
2、此题忽略出饮料后才能切换饮料的问题
(d1 0.5 d2 1 sel 选择饮料 out1 饮料1 out2 饮料2 out3 零钱)
与上一题类似,次态判断时加上sel判断就行
源代码:
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
187module seller2(
input wire clk ,
input wire rst ,
input wire d1 ,
input wire d2 ,
input wire sel ,
output reg out1,
output reg out2,
output reg out3
);
//状态定义
localparam IDLE = 'd0,
S0_5 = 'd1,
S1 = 'd2,
S1_5 = 'd3,
S2 = 'd4;
//现态与次态定义
reg [2 : 0] cur_state, next_state;
//1.状态跳转
always @(posedge clk or negedge rst) begin
if(~rst) begin
cur_state <= IDLE;
end
else begin
cur_state <= next_state;
end
end
//2.次态判断
always @(*) begin
case(cur_state)
IDLE: begin
case({d1,d2})
2'b10: next_state = S0_5;
2'b01: next_state = S1;
default: next_state = next_state;
endcase
end
S0_5: begin
case({d1,d2})
2'b10: next_state = S1;
2'b01: next_state = ~sel ? IDLE : S1_5;
default: next_state = next_state;
endcase
end
S1: begin
case({d1,d2})
2'b10: next_state = ~sel ? IDLE : S1_5;
2'b01: next_state = ~sel ? IDLE : S2;
default: next_state = next_state;
endcase
end
S1_5: begin
case({d1,d2})
2'b10: next_state = S2;
2'b01: next_state = IDLE;
default: next_state = next_state;
endcase
end
S2: begin
case({d1,d2})
2'b10: next_state = IDLE;
2'b01: next_state = IDLE;
default: next_state = next_state;
endcase
end
default: begin
next_state = IDLE;
end
endcase
end
//3.输出逻辑
reg out1_r;
reg out2_r;
reg out3_r;
always @(posedge clk or negedge rst) begin
if(~rst) begin
out1_r <= 'd0;
out2_r <= 'd0;
out3_r <= 'd0;
end
else begin
case(cur_state)
S0_5: begin
case({d1,d2})
2'b10: begin
out1_r <= 'd0;
out2_r <= 'd0;
out3_r <= 'd0;
end
3'b01: begin
out1_r <= ~sel ? 'd1 : 'd0;
out2_r <= 'd0;
out3_r <= 'd0;
end
default: begin
out1_r <= 'd0;
out2_r <= 'd0;
out3_r <= 'd0;
end
endcase
end
S1: begin
case({d1,d2})
2'b10: begin
out1_r <= ~sel ? 'd1 : 'd0;
out2_r <= 'd0;
out3_r <= 'd0;
end
3'b01: begin
out1_r <= ~sel ? 'd1 : 'd0;
out2_r <= 'd0;
out3_r <= ~sel ? 'd1 : 'd0;
end
default: begin
out1_r <= 'd0;
out2_r <= 'd0;
out3_r <= 'd0;
end
endcase
end
S1_5: begin
case({d1,d2})
2'b10: begin
out1_r <= 'd0;
out2_r <= 'd0;
out3_r <= 'd0;
end
3'b01: begin
out1_r <= 'd0;
out2_r <= sel ? 'd1 : 'd0;
out3_r <= 'd0;
end
default: begin
out1_r <= 'd0;
out2_r <= 'd0;
out3_r <= 'd0;
end
endcase
end
S2: begin
case({d1,d2})
2'b10: begin
out1_r <= 'd0;
out2_r <= sel ? 'd1 : 'd0;
out3_r <= 'd0;
end
3'b01: begin
out1_r <= 'd0;
out2_r <= sel ? 'd1 : 'd0;
out3_r <= 'd1;
end
default: begin
out1_r <= 'd0;
out2_r <= 'd0;
out3_r <= 'd0;
end
endcase
end
default: begin
out1_r <= 'd0;
out2_r <= 'd0;
out3_r <= 'd0;
end
endcase
end
end
always @(posedge clk or negedge rst) begin
if(~rst) begin
out1 <= 'd0;
out2 <= 'd0;
out3 <= 'd0;
end
else begin
out1 <= out1_r;
out2 <= out2_r;
out3 <= out3_r;
end
end
endmodule
占空比50%的奇数分频*
设计一个同时输出7分频的时钟分频器,占空比要求为50%。注意rst为低电平复位
分别利用上升沿和下降沿对计数器进行判断,最后对结果取或
源代码:
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
67module odo_div_or(
input wire rst ,
input wire clk_in,
output wire clk_out7
);
reg [2 : 0] cnt;
always @(posedge clk_in or negedge rst) begin
if(~rst) begin
cnt <= 'd0;
end
else begin
if(cnt == 7 - 1) begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1;
end
end
end
//上升沿判断
reg clk_up;
always @(posedge clk_in or negedge rst) begin
if(~rst) begin
clk_up <= 1'b0;
end
else begin
if(cnt == 7 / 2) begin
clk_up <= 1'b1;
end
else begin
if(cnt == 6) begin
clk_up <= 1'b0;
end
else begin
clk_up <= clk_up;
end
end
end
end
//下升沿判断
reg clk_down;
always @(negedge clk_in or negedge rst) begin
if(~rst) begin
clk_down <= 1'b0;
end
else begin
if(cnt == 7 / 2) begin
clk_down <= 1'b1;
end
else begin
if(cnt == 6) begin
clk_down <= 1'b0;
end
else begin
clk_down <= clk_down;
end
end
end
end
assign clk_out7 = clk_up | clk_down;
endmodule
任意小数分频*
请设计一个可以实现任意小数分频的时钟分频器,比如说8.7分频的时钟信号,注意rst为低电平复位。
假设输出
clk_out
是输入clk_in
的NN分频。首先要将分频系数NN化为分数形式,比如4.75→$\frac{19}{4}$,3.4→$\frac{34}{10}$。本题中,8.7可以化为$\frac{87}{10}$。这意味着在87个clk_in
周期内输出10个clk_out
周期就可以实现分频。然后采用若干种(一般是两种)整数分频在87个原周期
clk_in
内产生10个新时钟周期clk_out
。整数分频的分频系数有很多种选择,但要尽可能接近,提高clk_out
的均匀度。一般推荐在小数分频系数NN的附近选取。因为8<N<9,所以两个整数分频系数是8和9。接着要计算87个clk_out
周期分别有多少个是8分频和9分频的。设每10个clk_out
中有x个8分频输出和y个9分频输出,则可列出如下方程:
$$
x+y=10 \
8x+9y = 87
$$可得x=3,y=7。也就是3个8分频和7个9分频一组,循环输出,就等效于8.7分频。 最后安排组内8分频和9分频的位置。这里的方法也不固定,不过本题要求3个8分频先输出,再输出7个9分频,如下图。
源代码:
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
113module div_M_N(
input wire clk_in,
input wire rst,
output wire clk_out
);
parameter M_N = 8'd87;
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期
//分频器内部计数器
reg [3 : 0] cnt_in;
//分频转换标志
reg div_flag; //0表示偶数分配,1表示奇数分频
//外部总计数器
reg [6 : 0] cnt_out;
//1.分频器内部计数器逻辑
always @(posedge clk_in or negedge rst) begin
if(~rst) begin
cnt_in <= 'd0;
end
else begin
if(~div_flag) begin
if(cnt_in == div_e - 1) begin
cnt_in <= 'd0;
end
else begin
cnt_in <= cnt_in + 1;
end
end
else begin
if(cnt_in == div_o - 1) begin
cnt_in <= 'd0;
end
else begin
cnt_in <= cnt_in + 1;
end
end
end
end
//2.外部计数器逻辑
always @(posedge clk_in or negedge rst) begin
if(~rst) begin
cnt_out <= 'd0;
end
else begin
if(cnt_out == M_N - 1) begin
cnt_out <= 'd0;
end
else begin
cnt_out <= cnt_out + 1;
end
end
end
//3.分频转化器逻辑
always @(posedge clk_in or negedge rst) begin
if(~rst) begin
div_flag <= 'd0;
end
else begin
if(cnt_out == M_N - 1 || cnt_out == c89 - 1) begin
div_flag <= ~div_flag;
end
else begin
div_flag <= div_flag;
end
end
end
//4.时钟输出
reg clk_out_r;
always @(posedge clk_in or negedge rst) begin
if(~rst) begin
clk_out_r <= 1'b0;
end
else begin
// if(~div_flag) begin //这种是在先低后高的情况下
// if(cnt_in == (div_e / 2 - 1 ) || cnt_in == div_e - 1) begin
// clk_out_r <= ~clk_out_r;
// end
// end
// else begin
// if(cnt_in == div_o / 2 || cnt_in == div_o - 1) begin
// clk_out_r <= ~clk_out_r;
// end
// end
if(~div_flag) begin //题目要求是先高后低
if(cnt_in <= (div_e / 2 - 1 )) begin
clk_out_r <= 1'b1;
end
else begin
clk_out_r <= 1'b0;
end
end
else begin
if(cnt_in <= div_o /4 + 1) begin
clk_out_r <= 1'b1;
end
else begin
clk_out_r <= 1'b0;
end
end
end
end
assign clk_out = clk_out_r;
endmodule
无占空比要去的奇数分频
请设计一个同时输出5分频的时钟分频器,本题对占空比没有要求
对占空比没有要求的话,仅在上升沿计数就好
源代码:
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
41module odd_div(
input wire rst ,
input wire clk_in,
output wire clk_out5
);
//计数器
reg [2 : 0] cnt;
always @(posedge clk_in or negedge rst) begin
if(~rst) begin
cnt <= 'd0;
end
else begin
if(cnt == 'd4) begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1;
end
end
end
//上升沿判断
reg clk_up;
always @(posedge clk_in or negedge rst) begin
if(~rst) begin
clk_up <= 1'b0;
end
else begin
if(cnt == 2) begin
clk_up <= 1'b0;
end
else if(cnt == 0) begin
clk_up <= 1'b1;
end
end
end
assign clk_out5 = clk_up;
endmodule
根据状态转移写状态机-三段式
如图所示为两种状态机中的一种,请根据状态转移图写出代码,状态转移线上的0/0等表示的意思是过程中data/flag的值。
要求:
1、 必须使用对应类型的状态机
2、 使用三段式描述方法,输出判断要求要用到对现态的判断
源代码:
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
53module fsm1(
input wire clk ,
input wire rst ,
input wire data ,
output reg flag
);
//状态定义
parameter S0 = 2'd0,
S1 = 2'd1,
S2 = 2'd2,
S3 = 2'd3;
//现态与次态定义
reg [1 : 0] cur_state, next_state;
//1.状态跳转
always @(posedge clk or negedge rst) begin
if(~rst) begin
cur_state <= S0;
end
else begin
cur_state <= next_state;
end
end
//2.次态判断
always @(*) begin
next_state = cur_state;
case(cur_state)
S0: next_state = data ? S1 : S0;
S1: next_state = data ? S2 : S1;
S2: next_state = data ? S3 : S2;
S3: next_state = data ? S0 : S3;
endcase
end
//3.输出判断
always @(posedge clk or negedge rst) begin
if(~rst) begin
flag <= 1'b0;
end
else begin
if(cur_state == S3 && data == 1'b1) begin
flag <= 1'b1;
end
else begin
flag <= 1'b0;
end
end
end
endmodule
根据状态转移写状态机-二段式
如图所示为两种状态机中的一种,请根据状态转移图写出代码,状态转移线上的0/0等表示的意思是过程中data/flag的值。
要求:
1、 必须使用对应类型的状态机
2、 使用二段式描述方法
源代码:
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
56module fsm2(
input wire clk ,
input wire rst ,
input wire data ,
output reg flag
);
//状态定义
parameter S0 = 3'd0,
S1 = 3'd1,
S2 = 3'd2,
S3 = 3'd3,
S4 = 3'd4;
//现态与次态定义
reg [2 : 0] cur_state, next_state;
//1.状态跳转
always @(posedge clk or negedge rst) begin
if(~rst) begin
cur_state <= S0;
end
else begin
cur_state <= next_state;
end
end
//2.次态判断与输出判断
always @(*) begin
next_state = cur_state;
flag = 1'b0;
case(cur_state)
S0: begin
next_state = data ? S1 : S0;
flag = 1'b0;
end
S1: begin
next_state = data ? S2 : S1;
flag = 1'b0;
end
S2: begin
next_state = data ? S3 : S2;
flag = 1'b0;
end
S3: begin
next_state = data ? S4 : S3;
flag = 1'b0;
end
S4: begin
next_state = data ? S1 : S0;
flag = 1'b1;
end
endcase
end
endmodule