我是靠谱客的博主 故意世界,这篇文章主要介绍《牛客刷verilog》Part II Verilog进阶挑战前言Part I Verilog快速入门Part II Verilog进阶挑战Part III Verilog企业真题后记,现在分享给大家,希望可以做个参考。
前言
- 之前刷过HDLbits上面的题目,点击链接可以查看详细笔记:verilog练习:hdlbits网站系列完结!
- 最近又想刷一下牛客上面的题目,可以点击链接与小编一起刷题:牛客刷题
- 小编不才,文中如有不当之处,可以在评论中互相交流。此处题目推荐看牛客的评论区,再提一嘴,注意积累自己的基本功
算法、设计模式、软件等
Part I Verilog快速入门
- 此处内容,点击链接跳转:《牛客刷verilog》Part I Verilog快速入门
Part II Verilog进阶挑战
01 序列检测
VL25 输入序列连续的序列检测
答案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`timescale 1ns/1ns module sequence_detect( input clk, input rst_n, input a, output reg match ); parameter IDLE = 4'b0000; parameter S0 = 4'b0001; parameter S1 = 4'b0011; parameter S2 = 4'b0010; parameter S3 = 4'b0110; parameter S4 = 4'b0111; parameter S5 = 4'b0101; parameter S6 = 4'b0100; parameter S7 = 4'b1100; reg [3:0] cur_state, next_state; always@(posedge clk or negedge rst_n) if (!rst_n) begin cur_state <= IDLE; next_state <= IDLE; end else begin cur_state <= next_state; end always@(*) case(cur_state) IDLE : next_state = (a==0)?S0:IDLE; S0 : next_state = (a==1)?S1:S0; S1 : next_state = (a==1)?S2:S0; S2 : next_state = (a==1)?S3:S0; S3 : next_state = (a==0)?S4:IDLE; S4 : next_state = (a==0)?S5:IDLE; S5 : next_state = (a==0)?S6:IDLE; S6 : next_state = (a==1)?S7:IDLE; S7 : next_state = (a==0)?S0:IDLE; default: next_state = IDLE; endcase always@(posedge clk or negedge rst_n) if (!rst_n) match <= 1'b0; else if (cur_state == S7) match <= 1'b1; else match <= 1'b0; endmodule
- 再说一个方法,这个方法我会称之为
移位寄存器法
答案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// `define SIZE 8 // `define SEQ 8'b01110001 `timescale 1ns/1ns module sequence_detect( input clk, input rst_n, input a, output reg match ); reg [7:0] reg_8_a; always@(posedge clk or negedge rst_n) if (!rst_n) reg_8_a <= 'b0; else reg_8_a <={reg_8_a[6:0],a}; always@(posedge clk or negedge rst_n) if (!rst_n)begin match <= 1'b0; end else if (reg_8_a == 8'b01110001) match <= 1'b1; else match <= 1'b0; 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`define SIZE 8 `define SEQ 8'b01110001 `timescale 1ns/1ns module sequence_detect( input clk, input rst_n, input a, output reg match ); reg [`SIZE - 1:0] reg_a; always@(posedge clk or negedge rst_n) if (!rst_n) reg_a <= 'b0; else reg_a <={reg_a[`SIZE - 2:0],a}; always@(posedge clk or negedge rst_n) if (!rst_n)begin match <= 1'b0; end else if (reg_a == `SEQ) match <= 1'b1; else match <= 1'b0; endmodule
复盘
-
题目不难,基本思想就是实现如下的状态转移图。(等熟练了,也可以不画)
-
上述的状态编码,使用了
格雷码
,并没有使用8421码
,也没有使用独热码
-
为了可重用设计,推荐使用
移位寄存器法
VL26 含有无关项的序列检测
答案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`timescale 1ns/1ns module sequence_detect( input clk, input rst_n, input a, output reg match ); parameter IDLE = 4'b0000; parameter S0 = 4'b0001; parameter S1 = 4'b0011; parameter S2 = 4'b0010; parameter S3 = 4'b0110; parameter S4 = 4'b0111; parameter S5 = 4'b0101; parameter S6 = 4'b0100; parameter S7 = 4'b1100; parameter S8 = 4'b1101; reg [3:0] cur_state, next_state; always@(posedge clk or negedge rst_n) if (!rst_n) begin cur_state <= IDLE; next_state <= IDLE; end else begin cur_state <= next_state; end always@(*) case(cur_state) IDLE : next_state = (a==0)?S0:IDLE; S0 : next_state = (a==1)?S1:S0; S1 : next_state = (a==1)?S2:S0; S2 : next_state = S3; S3 : next_state = S4; S4 : next_state = S5; S5 : next_state = (a==1)?S6:IDLE; S6 : next_state = (a==1)?S7:IDLE; S7 : next_state = (a==0)?S8:IDLE; S8 : next_state = (a==0)?S0:IDLE; default: next_state = IDLE; endcase always@(posedge clk or negedge rst_n) if (!rst_n) match <= 1'b0; else if (cur_state == S8) match <= 1'b1; else match <= 1'b0; endmodule
答案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`define SIZE 9 `define SEQ 9'011XXX110 `timescale 1ns/1ns module sequence_detect( input clk, input rst_n, input a, output reg match ); reg [`SIZE - 1:0] reg_a; always@(posedge clk or negedge rst_n) if (!rst_n) reg_a <= 'b0; else reg_a <={reg_a[`SIZE - 2:0],a}; always@(posedge clk or negedge rst_n) if (!rst_n)begin match <= 1'b0; end else if (reg_a[8:6] == 3'b011 && reg_a[2:0] == 3'b110) match <= 1'b1; else match <= 1'b0; endmodule
复盘
- 和上题一样
VL27 不重叠序列检测
答案
复制代码
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`define SIZE 6 `define SEQ 6'b011100 `timescale 1ns/1ns module sequence_detect( input clk, input rst_n, input data, output reg match, output reg not_match ); reg [`SIZE - 1:0] reg_a; always@(posedge clk or negedge rst_n) if (!rst_n) reg_a <= 'b0; else reg_a <={reg_a[`SIZE - 2:0],data}; reg [2:0] cnt; always@(posedge clk or negedge rst_n) if (!rst_n)begin cnt <= 1'b0; end else if (cnt == 3'd5) cnt <= 1'b0; else cnt <= cnt + 1'b1; always@(posedge clk or negedge rst_n)begin if (!rst_n)begin match <= 1'b0; not_match <= 1'b0; end else if (cnt == 3'd5)begin if ({reg_a[`SIZE - 2:0],data} == `SEQ)begin match <= 1'b1; end else begin not_match <= 1'b1; end end else begin match <= 1'b0; not_match <= 1'b0; end end endmodule
复盘
- 注意,此题是要求最后一个数,
输入最后1bit立马输出标志位
,不是下一个时钟周期输出! - 前面两题是两个时钟周期才输出。
VL28 输入序列不连续的序列检测
答案
复制代码
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`timescale 1ns/1ns module sequence_detect( input clk, input rst_n, input data, input data_valid, output reg match ); reg [3:0] reg_data; always@(posedge clk or negedge rst_n) if(!rst_n) reg_data <= 'd0; else if(data_valid) reg_data <= {reg_data[2:0],data}; else reg_data <= data; always@(posedge clk or negedge rst_n) if (!rst_n) match <= 'b0; else if({reg_data[2:0],data} == 'b0110) match <= 'b1; else match <= 'b0; endmodule
复盘
- 如果你习惯这种写法,就不用费尽心思去写状态机了。
02 时序逻辑
VL29 信号发生器
答案
复制代码
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`timescale 1ns/1ns module signal_generator( input clk, input rst_n, input [1:0] wave_choise, output reg [4:0]wave ); reg [4:0] cnt; reg flag; // 方波模式下,计数器控制 always@(posedge clk or negedge rst_n) begin if(~rst_n) cnt <= 0; else cnt <= wave_choise!=0 ? 0: cnt ==19? 0: cnt + 1; end // 三角波模式下,标志位控制 always@(posedge clk or negedge rst_n) begin if(~rst_n) flag <= 0; else flag <= wave_choise!=2 ? 0: wave ==1 ? 1: wave ==19? 0: flag; end // 更新wave信号 always@(posedge clk or negedge rst_n) begin if(~rst_n) wave <= 0; else case(wave_choise) 0 : wave <= cnt == 9? 20 : cnt ==19? 0 : wave; 1 : wave <= wave==20? 0 : wave+1; 2 : wave <= flag==0 ? wave-1: wave+1; default: wave <= 0; endcase end endmodule
复盘
- 没看明白题目
- 看题解
VL30 数据串转并电路
答案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`timescale 1ns/1ns module 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 ); always@(posedge clk or negedge rst_n) if(!rst_n)begin ready_a <= 1'b0; end else begin ready_a <= 1'b1; end reg [5:0] data_b_reg; reg [2:0] cnt; always@(posedge clk or negedge rst_n) if(!rst_n)begin data_b_reg <= 'd0; data_b <= 'd0; valid_b <= 1'b0; cnt <= 1'b0; end else if (valid_a)begin data_b_reg <= {data_a, data_b_reg[5:1]}; if(cnt == 3'd5)begin data_b <= {data_a, data_b_reg[5:1]}; valid_b <= 1'b1; cnt <= 'd0; end else begin valid_b <= 1'b0; data_b <= data_b; cnt <= cnt + 1'b1; end end else begin data_b_reg <= data_b_reg; end endmodule
答案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
56
57
58
59
60
61
62
63
64
65
66
67
68`timescale 1ns/1ns module 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 ); always@(posedge clk or negedge rst_n) if(!rst_n)begin ready_a <= 1'b0; end else begin ready_a <= 1'b1; end reg [5:0] data_b_reg; always@(posedge clk or negedge rst_n) if(!rst_n)begin data_b_reg <= 'd0; end else if (valid_a)begin data_b_reg <= {data_a, data_b_reg[5:1]}; end else begin data_b_reg <= data_b_reg; end reg [2:0] cnt; always@(posedge clk or negedge rst_n) if(!rst_n)begin cnt <= 'd0; end else if (valid_a)begin if (cnt == 'd5) cnt <= 'd0; else cnt <= cnt + 1'b1; end always@(posedge clk or negedge rst_n) if(!rst_n)begin valid_b <= 1'b0; end else if(cnt == 3'd5)begin valid_b <= 1'b1; end else begin valid_b <= 1'b0; end always@(posedge clk or negedge rst_n) if(!rst_n)begin data_b <= 'd0; end else if(cnt == 3'd5)begin data_b <= {data_a, data_b_reg[5:1]}; end else begin data_b <= data_b; end endmodule
复盘
- 仔细点就行。
VL31 数据累加输出
答案
复制代码
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`timescale 1ns/1ns module 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) if(!rst_n) cnt <= 'b0; else if(ready_a && valid_a)begin if(cnt == 2'd3) cnt <= 'd0; else cnt <= cnt + 1'b1; end else begin cnt <= cnt; end always@(posedge clk or negedge rst_n) if(!rst_n)begin valid_b <= 'b0; end else if(cnt==2'd3 && valid_a && ready_a)begin valid_b <= 1'b1; end else if(valid_b && ready_b)begin valid_b <= 1'b0; end else begin valid_b <= valid_b ; end always@(posedge clk or negedge rst_n) if(!rst_n)begin data_out <= 'b0; end else if(ready_a && valid_a)begin if(cnt == 2'd0 )begin data_out <= data_in; end else begin data_out <= data_out + data_in ; end end assign ready_a = ~valid_b | ready_b; endmodule
复盘
- 注意观察各个信号间的关系
VL32 非整数倍数据位宽转换24to128
答案
复制代码
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`timescale 1ns/1ns module 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; always@(posedge clk or negedge rst_n) if (!rst_n) cnt <= 'b0; else if (valid_in) begin if(cnt == 'd15) cnt <= 4'd0; else cnt <= cnt + 1'b1; end else cnt <= cnt; always@(posedge clk or negedge rst_n) if (!rst_n) begin valid_out <= 1'b0; end else if (valid_in && (cnt == 'd5 || cnt == 'd10 || cnt == 'd15)) begin valid_out <= 1'b1; end else begin valid_out <= 1'b0; end reg [127:0] data_out_reg; always@(posedge clk or negedge rst_n) if (!rst_n) begin data_out<= 'd0; data_out_reg <= 'd0; end else if (valid_in) begin if (cnt == 'd5) begin data_out <= {data_out_reg,data_in[23:16]}; data_out_reg <= data_in[15:0]; end if (cnt == 'd10)begin data_out <= {data_out_reg,data_in[23:8]}; data_out_reg <= data_in[7:0]; end if (cnt == 'd15)begin data_out <= {data_out_reg,data_in}; data_out_reg <= 'd0; end else begin data_out_reg <= {data_out_reg,data_in}; end end else begin data_out_reg <= data_out_reg; end endmodule
复盘
- 注意理解5、10、15处的逻辑位置
- 我们通常使用计数器来判断输出条件
VL33 非整数倍数据位宽转换8to12
答案
复制代码
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`timescale 1ns/1ns module 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 ); reg [2:0] cnt; always@(posedge clk or negedge rst_n) if (!rst_n) cnt <= 'd0; else if (valid_in) begin if(cnt == 'd2) cnt <= 'd0; else cnt <= cnt + 1'b1; end else cnt <= cnt; always@(posedge clk or negedge rst_n) if (!rst_n) begin valid_out <= 1'b0; end else if (valid_in && (cnt == 'd1 || cnt == 'd2)) begin valid_out <= 1'b1; end else begin valid_out <= 1'b0; end reg [11:0] data_out_reg; always@(posedge clk or negedge rst_n) if (!rst_n) begin data_out<= 'd0; data_out_reg <= 'd0; end else if (valid_in) begin if (cnt == 'd1) begin data_out <= {data_out_reg,data_in[7:4]}; data_out_reg <= data_in[3:0]; end if (cnt == 'd2)begin data_out <= {data_out_reg,data_in}; data_out_reg <= 'd0; end else begin data_out_reg <= {data_out_reg,data_in}; end end else begin data_out_reg <= data_out_reg; end endmodule
复盘
- 和上题思路一致
VL34 整数倍数据位宽转换8to16
答案
复制代码
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`timescale 1ns/1ns module width_8to16( input clk , input rst_n , input valid_in , input [7:0] data_in , output reg valid_out, output reg [15:0] data_out ); reg cnt; always@(posedge clk or negedge rst_n) if (!rst_n) cnt <= 'd0; else if (valid_in) begin if(cnt == 'd1) cnt <= 'd0; else cnt <= cnt + 1'b1; end else cnt <= cnt; always@(posedge clk or negedge rst_n) if (!rst_n) begin valid_out <= 1'b0; end else if (valid_in && cnt == 'd1) begin valid_out <= 1'b1; end else begin valid_out <= 1'b0; end reg [15:0] data_out_reg; always@(posedge clk or negedge rst_n) if (!rst_n) begin data_out<= 'd0; data_out_reg <= 'd0; end else if (valid_in) begin if (cnt == 'd1) begin data_out <= {data_out_reg,data_in}; data_out_reg <= data_in; end else begin data_out_reg <= {data_out_reg,data_in}; end end else begin data_out_reg <= data_out_reg; end endmodule
复盘
- 和上题思路一致
VL35 状态机-非重叠的序列检测
答案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`define SEQR 5'b10111 `timescale 1ns/1ns module sequence_test1( input wire clk , input wire rst , input wire data , output reg flag ); //*************code***********// reg [4:0] data_reg; always@(posedge clk or negedge rst) if(!rst) data_reg <= 'd0; else data_reg <= {data_reg,data}; reg flag_bit = 1'b1; always@(posedge clk or negedge rst) if(!rst) flag <= 1'b0; else if(flag_bit && {data_reg,data} == `SEQR)begin flag <= 1'b1; flag_bit <= 1'b0; end else flag <= 1'b0; //*************code***********// endmodule
答案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`define SEQR 5'b10111 `timescale 1ns/1ns module sequence_test1( input wire clk , input wire rst , input wire data , output reg flag ); //*************code***********// parameter IDLE = 4'b0000; parameter S0 = 4'b0001; parameter S1 = 4'b0011; parameter S2 = 4'b0010; parameter S3 = 4'b0110; parameter S4 = 4'b0111; parameter S5 = 4'b0101; parameter S6 = 4'b0100; parameter S7 = 4'b1100; reg [3:0] cur_state, next_state; always@(posedge clk or negedge rst) if (!rst) begin cur_state <= IDLE; next_state <= IDLE; end else begin cur_state <= next_state; end always@(*) case(cur_state) IDLE : next_state = (data==1)?S0:IDLE; S0 : next_state = (data==0)?S1:IDLE; S1 : next_state = (data==1)?S2:IDLE; S2 : next_state = (data==1)?S3:S1; S3 : next_state = (data==1)?S4:S1; S4 : next_state = S5; default: next_state = IDLE; endcase always@(*) if (cur_state == S4) flag <= 1'b1; else flag <= 1'b0; //*************code***********// endmodule
复盘
- 这和1、2、3、4题一致
VL36 状态机-重叠序列检测
答案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`timescale 1ns/1ns `define SEQR 4'b1011 module sequence_test2( input wire clk , input wire rst , input wire data , output reg flag ); //*************code***********// reg [3:0] data_reg; always@(posedge clk or negedge rst) if(!rst) data_reg <= 'd0; else data_reg <= {data_reg,data}; reg cnt = 1'b1; always@(posedge clk or negedge rst) if(!rst) flag <= 1'b0; else if(data_reg == `SEQR)begin flag <= 1'b1; end else flag <= 1'b0; //*************code***********// endmodule
方法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`timescale 1ns/1ns `define SEQR 4'b1011 module sequence_test2( input wire clk , input wire rst , input wire data , output reg flag ); //*************code***********// parameter IDLE = 4'b0000; parameter S0 = 4'b0001; parameter S1 = 4'b0011; parameter S2 = 4'b0010; parameter S3 = 4'b0110; parameter S4 = 4'b0111; parameter S5 = 4'b0101; parameter S6 = 4'b0100; parameter S7 = 4'b1100; reg [3:0] cur_state, next_state; always@(posedge clk or negedge rst) if (!rst) begin cur_state <= IDLE; next_state <= IDLE; end else begin cur_state <= next_state; end always@(*) case(cur_state) IDLE : next_state = (data==1)?S0:IDLE; S0 : next_state = (data==0)?S1:IDLE; S1 : next_state = (data==1)?S2:IDLE; S2 : next_state = (data==1)?S3:S0; S3 : next_state = (data==1)?S0:S1; default: next_state = IDLE; endcase always@(posedge clk or negedge rst) if (!rst) flag <= 1'b0; else if (cur_state == S3) flag <= 1'b1; else flag <= 1'b0; //*************code***********// endmodule
VL37 时钟分频(偶数)
答案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`timescale 1ns/1ns module even_div ( input wire rst , input wire clk_in, output wire clk_out2, output wire clk_out4, output wire clk_out8 ); //*************code***********// /*------------------------------------------------------------- 二分频 -----------------------------------------------------------*/ reg clk_out2_reg; always@(posedge clk_in or negedge rst) if(!rst) clk_out2_reg <= 1'b0; else clk_out2_reg <= ~clk_out2_reg; assign clk_out2 = clk_out2_reg; /*------------------------------------------------------------- 四分频 -----------------------------------------------------------*/ reg [1:0] cnt_4; always@(posedge clk_in or negedge rst) if(!rst) cnt_4 <= 'd0; // else if(cnt_4 == 'd3) // cnt_4 <= 1'b0; else cnt_4 <= cnt_4 + 1'b1; reg clk_out4_reg; always@(posedge clk_in or negedge rst) if(!rst) clk_out4_reg <= 1'b0; else if(cnt_4 < 2'd2) clk_out4_reg <= 1'b1; else clk_out4_reg <= 1'b0; assign clk_out4 = clk_out4_reg; /*------------------------------------------------------------- 八分频 -----------------------------------------------------------*/ reg [2:0] cnt_8; always@(posedge clk_in or negedge rst) if(!rst) cnt_8 <= 'b0; // else if(cnt_8 == 'd7) // cnt_8 <= 1'b0; else cnt_8 <= cnt_8 + 1'b1; reg clk_out8_reg; always@(posedge clk_in or negedge rst) if(!rst) clk_out8_reg <= 1'b0; else if(cnt_8 < 3'd4) clk_out8_reg <= 1'b1; else clk_out8_reg <= 1'b0; assign clk_out8 = clk_out8_reg; //*************code***********// endmodule
答案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`timescale 1ns/1ns module even_div ( input wire rst , input wire clk_in, output wire clk_out2, output wire clk_out4, output wire clk_out8 ); //*************code***********// reg clk_out2_reg, clk_out4_reg, clk_out8_reg; always@(posedge clk_in or negedge rst) begin if(~rst) clk_out2_reg <= 0; else clk_out2_reg <= ~clk_out2_reg; end always@(posedge clk_out2 or negedge rst) begin if(~rst) clk_out4_reg <= 0; else clk_out4_reg <= ~clk_out4_reg; end always@(posedge clk_out4 or negedge rst) begin if(~rst) clk_out8_reg <= 0; else clk_out8_reg <= ~clk_out8_reg; end assign clk_out2 = clk_out2_reg; assign clk_out4 = clk_out4_reg; assign clk_out8 = clk_out8_reg; //*************code***********// endmodule
答案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`timescale 1ns/1ns module 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] cnt8; always @ (posedge clk_in or negedge rst) begin if(~rst) begin cnt8 <= 3'b0; end else begin cnt8 <= cnt8 - 3'd1; end end assign clk_out2 = cnt8[0]; assign clk_out4 = cnt8[1]; assign clk_out8 = cnt8[2]; endmodule
复盘
- 推荐一篇文章【数字IC手撕代码】Verilog偶数分频|题目|原理|设计|仿真(二分频,四分频,六分频,八分频,偶数分频及特殊占空比)
VL38 自动贩售机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`timescale 1ns/1ns module seller1( input wire clk , input wire rst , input wire d1 , input wire d2 , input wire d3 , output reg out1, output reg [1:0]out2 ); //*************code***********// parameter IDLE = 4'b0000; parameter S0 = 4'b0001; parameter S1 = 4'b0011; parameter S2 = 4'b0010; parameter S3 = 4'b0110; parameter S4 = 4'b0111; parameter S5 = 4'b0101; // parameter S6 = 4'b0100; // parameter S7 = 4'b1100; reg [3:0] cur_state, next_state; always@(posedge clk or negedge rst) if (!rst) begin cur_state <= IDLE; end else begin cur_state <= next_state; end always@(*) case(cur_state) IDLE : begin if(d1) next_state = S0; else if(d2) next_state = S1; else if(d3) next_state = S3; else next_state = next_state; end S0 : begin if(d1) next_state = S1; else if(d2) next_state = S2; else if(d3) next_state = S4; else next_state = next_state; end S1 : begin if(d1) next_state = S2; else if(d2) next_state = S3; else if(d3) next_state = S5; else next_state = next_state; end default: next_state = IDLE; endcase always@(posedge clk or negedge rst) if (!rst)begin out1 <= 'd0; out2 <= 'd0; end else begin case(next_state) S2:begin out1 <= 1'b1;out2 <= 2'b00;end S3:begin out1 <= 1'b1;out2 <= 2'b01;end S4:begin out1 <= 1'b1;out2 <= 2'b10;end S5:begin out1 <= 1'b1;out2 <= 2'b11;end default:begin out1 <= 1'b0;out2 <= 2'b00;end endcase end //*************code***********// endmodule
复盘
本题可以抽象为三输入二输出
,存在状态0 0.5 1 1.5 2 2.5 3
七个状态,
这样输出信号的特性应该为: out1饮料为一位,out2找零为2位(0 1 2 3个0.5元)
因此先做状态转移图:
- 注意,0.5/1/2是不会同时给信号的;在钱数大于2.5时就不能再继续投币了,在下一拍就要回到IDLE。
VL39 自动贩售机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
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`timescale 1ns/1ns module 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 ); //*************code***********// parameter IDLE = 4'b0000; parameter S0 = 4'b0001; parameter S1 = 4'b0011; parameter S2 = 4'b0010; parameter S3 = 4'b0110; parameter S4 = 4'b0111; parameter S5 = 4'b0101; // parameter S6 = 4'b0100; // parameter S7 = 4'b1100; reg [3:0] cur_state, next_state; always@(posedge clk or negedge rst) if (!rst) begin cur_state <= IDLE; end else begin cur_state <= next_state; end always@(*) case(cur_state) IDLE : begin if(d1) next_state = S0; else if(d2) next_state = S1; else next_state = next_state; end S0 : begin if(d1) next_state = S1; else if(d2) next_state = S2; else next_state = next_state; end S1 : begin if(d1) next_state = S2; else if(d2) next_state = S3; else next_state = next_state; end S2 : begin if(sel == 0)begin next_state = IDLE; end else begin if(d1) next_state = S3; else if(d2) next_state = S4; else next_state = next_state; end end S3 : begin if(sel == 0)begin next_state = IDLE; end else begin if(d1) next_state = S4; else if(d2) next_state = S5; else next_state = next_state; end end default: next_state = IDLE; endcase always@(posedge clk or negedge rst) if (!rst)begin out1 <= 'd0; out2 <= 'd0; out3 <= 'd0; end else begin case(next_state) S2: begin if(sel == 0) begin out1 <= 1'b1; out2 <= 1'b0; out3 <= 1'b0; end else begin out1 <= 1'b0; out2 <= 1'b0; out3 <= 1'b0; end end S3: begin if(sel == 0) begin out1 <= 1'b1; out2 <= 1'b0; out3 <= 1'b1; end else begin out1 <= 1'b0; out2 <= 1'b0; out3 <= 1'b0; end end S4: begin out1 <= 1'b0; out2 <= 1'b1; out3 <= 1'b0; end S5: begin out1 <= 1'b0; out2 <= 1'b1; out3 <= 1'b1; end default: begin out1 <= 1'b0; out2 <= 1'b0; out3 <= 1'b0; end endcase end //*************code***********// endmodule
复盘
- 经典的三段式,状态转移图如下:
- 第一段,时序逻辑寄存下一个状态
- 第二段,组合逻辑计算下一个状态
- 第三段,输出当前状态需要输出的条件
- 如果觉得代码不好看,可以继续更改格式
VL40 占空比50%的奇数分频
答案
复制代码
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`define CNT 3'd7 `timescale 1ns/1ns module odo_div_or ( input wire rst , input wire clk_in, output wire clk_out7 ); //*************code***********// reg [2:0] count_p; //上升沿计数 reg [2:0] count_n; //下降沿计数 reg clk_p; //上升沿分频 reg clk_n; //下降沿分频 //上升沿计数 always @ ( posedge clk_in or negedge rst ) begin if( !rst ) count_p <= 3'b0; else if( count_p == `CNT - 1 ) count_p <= 3'b0; else count_p <= count_p + 1'b1; end //上升沿分频 always @ ( posedge clk_in or negedge rst ) begin if( !rst ) begin clk_p <= 1'b0; end else begin if( count_p == `CNT / 2 || count_p == `CNT - 1 ) begin clk_p <= ~clk_p; end end end //下降沿计数 always @ ( negedge clk_in or negedge rst ) begin if( !rst ) count_n <= 3'b0; else if( count_n == `CNT - 1) count_n <= 3'b0; else count_n <= count_n + 1'b1; end //下降沿分频 always @ ( negedge clk_in or negedge rst ) begin if( !rst ) begin clk_n <= 1'b0; end else begin if( count_n == `CNT / 2 || count_n == `CNT - 1 ) begin clk_n <= ~clk_n; end end end assign clk_out7 = clk_p | clk_n; endmodule
复盘
-
前面的答案使用了
上升沿计数器
和下降沿计数器
分别分别生成了两个时钟
,然后将其或
即可 -
参考 Verilog笔记——奇数分频和小数分频
-
其实,我们也可以不使用两个计数器
复制代码
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`define CNT 3'd7 `timescale 1ns/1ns module odo_div_or ( input wire rst , input wire clk_in, output wire clk_out7 ); //*************code***********// reg [2:0] count; //计数 reg clk_p; //上升沿分频 reg clk_n; //下降沿分频 //沿计数 always @ ( posedge clk_in or negedge rst ) begin if( !rst ) count <= 3'b0; else if( count == `CNT - 1 ) count <= 3'b0; else count <= count + 1'b1; end //上升沿分频 always @ ( posedge clk_in or negedge rst ) begin if( !rst ) begin clk_p <= 1'b0; end else begin if( count == `CNT / 2 || count == `CNT - 1 ) begin clk_p <= ~clk_p; end end end //下降沿分频 always @ ( negedge clk_in or negedge rst ) begin if( !rst ) begin clk_n <= 1'b0; end else begin if( count == `CNT / 2 || count == `CNT - 1 ) begin clk_n <= ~clk_n; end end end assign clk_out7 = clk_p | clk_n; endmodule
- 推荐文章 【数字IC手撕代码】Verilog奇数分频|题目|原理|设计|仿真(三分频,五分频,奇数分频及特殊占空比)
VL41 任意小数分频
答案
复盘
VL42 无占空比要去的奇数分频
答案
复盘
VL43 根据状态转移写状态机-三段式
答案
复盘
VL44 根据状态转移写状态机-二段式
03 跨时钟域传输
VL45 异步FIFO
答案
复制代码
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`timescale 1ns/1ns /***************************************RAM*****************************************/ module dual_port_RAM #(parameter DEPTH = 16, parameter WIDTH = 8)( input wclk ,input wenc ,input [$clog2(DEPTH)-1:0] waddr //深度对2取对数,得到地址的位宽。 ,input [WIDTH-1:0] wdata //数据写入 ,input rclk ,input renc ,input [$clog2(DEPTH)-1:0] raddr //深度对2取对数,得到地址的位宽。 ,output reg [WIDTH-1:0] rdata //数据输出 ); reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1]; always @(posedge wclk) begin if(wenc) RAM_MEM[waddr] <= wdata; end always @(posedge rclk) begin if(renc) rdata <= RAM_MEM[raddr]; end endmodule /***************************************AFIFO*****************************************/ module asyn_fifo#( parameter WIDTH = 8, parameter DEPTH = 16 )( input wclk , input rclk , input wrstn , input rrstn , input winc , input rinc , input [WIDTH-1:0] wdata , output wire wfull , output wire rempty , output wire [WIDTH-1:0] rdata ); localparam ADDR_WIDTH = $clog2(DEPTH) - 1 + 1; //通过深度换算出需要多少位宽,因为fifo在最高位需要一个标志位产生满信号,上述再+1 //第二步:给RAM申明缺少的端口信号 /// reg [ADDR_WIDTH:0] waddr,raddr;//写地址,读地址 wire wenc,renc;//写使能信号,读使能信号 //第三步:写地址操作,什么时候写 /// always@(posedge wclk or negedge wrstn) begin if(!wrstn) begin waddr <= 'd0; end else begin if(wenc) begin waddr <= waddr + 1'b1; end else begin waddr <= waddr; end end end assign wenc = winc && ~wfull;//写使能信号 //第四步:读地址操作,什么时候读 /// always@(posedge rclk or negedge rrstn) begin if(!rrstn) begin raddr <= 'd0; end else begin if(renc) begin raddr <= raddr + 1'b1; end else begin raddr <= raddr; end end end assign renc = rinc && ~rempty;//读使能信号 //第五步:产生空满信号---->异步 涉及到 跨时钟域同步 //空信号是在读时钟域产生,所以需要将写时钟域的写地址同步到读时钟域 //满信号是在写时钟域产生,所以需要将读时钟域的读地址同步到写时钟域 //---->推荐将跨时钟域的地址转换为格雷码再同步 /// /// //5.1 生成格雷码地址 /// reg [ADDR_WIDTH:0] waddr_gray,raddr_gray;//写地址格雷码,读地址格雷码 always@(posedge wclk or negedge wrstn) begin if(!wrstn) begin waddr_gray <= 'd0; end else begin waddr_gray <= waddr ^ (waddr >> 1); end end always@(posedge rclk or negedge rrstn) begin if(!rrstn) begin raddr_gray<= 'd0; end else begin raddr_gray <= raddr ^ (raddr >> 1); end end /// //5.2 将格雷码地址同步到相应的时钟域 /// reg [ADDR_WIDTH:0] addr_w2r_reg,addr_w2r; always@(posedge rclk or negedge rrstn) begin if(!rrstn) begin addr_w2r_reg <= 'd0; addr_w2r <= 'd0; end else begin addr_w2r_reg <= waddr_gray; addr_w2r <= addr_w2r_reg; end end reg [ADDR_WIDTH:0] addr_r2w_reg,addr_r2w; always@(posedge wclk or negedge wrstn) begin if(!wrstn) begin addr_r2w_reg <= 'd0; addr_r2w <= 'd0; end else begin addr_r2w_reg <= raddr_gray; addr_r2w <= addr_r2w_reg; end end /// //5.3 产生空满信号 /// assign wfull = (waddr_gray == {~addr_r2w[ADDR_WIDTH:ADDR_WIDTH-1],addr_r2w[ADDR_WIDTH-2:0]}); assign rempty = (raddr_gray == addr_w2r); //第一步:例化双口RAM /// dual_port_RAM #( .DEPTH(DEPTH) ,.WIDTH(WIDTH) ) dual_port_RAM_inst ( .wclk (wclk ) ,.wenc (wenc ) ,.waddr (waddr[ADDR_WIDTH - 1 : 0] ) //深度对2取对数,得到地址的位宽。 ,.wdata (wdata ) //数据写入 ,.rclk (rclk ) ,.renc (renc ) ,.raddr (raddr[ADDR_WIDTH - 1 : 0] ) //深度对2取对数,得到地址的位宽。 ,.rdata (rdata ) //数据输出 ); endmodule
复盘
- 异步fifo,而且还是通过双口RAM充当存储结构
- 常规的思路就是,
先进行例化,缺什么信号,补什么信号
。具体的步骤已经在答案中做了详细的注释 - 推荐查看【FPGA——基础篇】同步FIFO与异步FIFO——Verilog实现了解不使用双口RAM如何手撕。
VL46 同步FIFO
答案
复制代码
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`timescale 1ns/1ns /**********************************RAM************************************/ module dual_port_RAM #(parameter DEPTH = 16, parameter WIDTH = 8)( input wclk ,input wenc ,input [$clog2(DEPTH)-1:0] waddr //深度对2取对数,得到地址的位宽。 ,input [WIDTH-1:0] wdata //数据写入 ,input rclk ,input renc ,input [$clog2(DEPTH)-1:0] raddr //深度对2取对数,得到地址的位宽。 ,output reg [WIDTH-1:0] rdata //数据输出 ); reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1]; always @(posedge wclk) begin if(wenc) RAM_MEM[waddr] <= wdata; end always @(posedge rclk) begin if(renc) rdata <= RAM_MEM[raddr]; end endmodule /**********************************SFIFO************************************/ module sfifo#( parameter WIDTH = 8, parameter DEPTH = 16 )( input clk , input rst_n , input winc , input rinc , input [WIDTH-1:0] wdata , output reg wfull , output reg rempty , output wire [WIDTH-1:0] rdata ); localparam ADDR_WIDTH = $clog2(DEPTH) - 1 + 1; //通过深度换算出需要多少位宽,因为fifo在最高位需要一个标志位产生满信号,上述再+1 //第二步:给RAM申明缺少的端口信号 /// reg [ADDR_WIDTH:0] waddr,raddr;//写地址,读地址 wire wenc,renc;//写使能信号,读使能信号 //第三步:写地址操作,什么时候写 /// always@(posedge clk or negedge rst_n) begin if(!rst_n) begin waddr <= 'd0; end else begin if(wenc) begin waddr <= waddr + 1'b1; end else begin waddr <= waddr; end end end assign wenc = winc && ~wfull;//写使能信号 //第四步:读地址操作,什么时候读 /// always@(posedge clk or negedge rst_n) begin if(!rst_n) begin raddr <= 'd0; end else begin if(renc) begin raddr <= raddr + 1'b1; end else begin raddr <= raddr; end end end assign renc = rinc && ~rempty;//读使能信号 //第五步:产生空满信号 /// always@(posedge clk or negedge rst_n) begin if(!rst_n) begin wfull <= 'd0; rempty <= 'd0; end else begin wfull <= ((waddr[ADDR_WIDTH] != raddr[ADDR_WIDTH]) && (waddr[ADDR_WIDTH-1:0] == raddr[ADDR_WIDTH-1:0])); rempty <= raddr == waddr; end end //第一步:例化双口RAM /// dual_port_RAM #( .DEPTH(DEPTH) ,.WIDTH(WIDTH) ) dual_port_RAM_inst ( .wclk (clk ) ,.wenc (wenc ) ,.waddr (waddr ) //深度对2取对数,得到地址的位宽。 ,.wdata (wdata ) //数据写入 ,.rclk (clk ) ,.renc (renc ) ,.raddr (raddr ) //深度对2取对数,得到地址的位宽。 ,.rdata (rdata ) //数据输出 ); endmodule
复盘
- 同步fifo,而且还是通过双口RAM充当存储结构
- 常规的思路就是,
先进行例化,缺什么信号,补什么信号
。具体的步骤已经在答案中做了详细的注释 - 推荐查看【FPGA——基础篇】同步FIFO与异步FIFO——Verilog实现了解不使用双口RAM如何手撕。
VL47 格雷码计数器
答案
- 什么逆天计数器,两个时钟周期
复制代码
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`timescale 1ns/1ns module gray_counter( input clk, input rst_n, output reg [3:0] gray_out ); reg [4:0] bin_cnt = 'd0; always @ (posedge clk or negedge rst_n) begin if( ~rst_n ) begin bin_cnt <= 'd0; end else begin bin_cnt <= bin_cnt + 'd1; end end wire [3:0] bin; assign bin = bin_cnt[4:1]; always @ (*) begin gray_out = bin ^ (bin>>1); // gray_out[3] = bin[3]; // gray_out[2] = bin[3] ^ bin[2]; // gray_out[1] = bin[2] ^ bin[1]; // gray_out[0] = bin[1] ^ bin[0]; end endmodule
进行修改
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21`timescale 1ns/1ns module gray_counter( input clk, input rst_n, output reg [3:0] gray_out ); reg [4:0] bin_cnt = 'd0; always @ (posedge clk or rst_n) begin if( ~rst_n ) begin bin_cnt <= 'd0; gray_out <= 'd0; end else begin bin_cnt <= bin_cnt + 'd1; gray_out <= bin_cnt[4:1] ^ (bin_cnt[4:1]>>1); 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`timescale 1ns/1ns module gray_counter( input clk, input rst_n, output reg [3:0] gray_out ); reg clk2; always @ (posedge clk or negedge rst_n) if(!rst_n) clk2 <= 'b0; else clk2 <= ~clk2; reg [3:0] bin_cnt = 'd0; always @ (posedge clk2 or negedge rst_n) begin if( !rst_n ) begin bin_cnt <= 'd0; end else begin bin_cnt <= bin_cnt + 'd1; end end always@(posedge clk or negedge rst_n) begin if(!rst_n) begin gray_out <= 0; end else begin gray_out = (bin_cnt >> 1) ^ bin_cnt; end end endmodule
复盘
- 格雷码与二进制的转换
VL48 多bit MUX同步器
答案
复制代码
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`timescale 1ns/1ns module mux( input clk_a , input clk_b , input arstn , input brstn , input [3:0] data_in , input data_en , output reg [3:0] dataout ); reg data_en_b,data_en_b_reg; reg [3:0] data_in_b,data_in_b_reg; always@(posedge clk_b or negedge brstn) begin if(!brstn)begin {data_en_b,data_en_b_reg} <= 'd0; {data_in_b,data_in_b_reg} <= 'd0; end else begin {data_en_b,data_en_b_reg} <= {data_en_b_reg,data_en}; {data_in_b,data_in_b_reg} <= {data_in_b_reg,data_in}; end end always@(posedge clk_b or negedge brstn) begin if(!brstn)begin dataout <= 'd0; end else if (data_en_b)begin dataout <= data_in_b; end else begin dataout <= dataout; end end endmodule
复盘
- 注意对比异步FIFO的格雷码跨时钟域同步
VL49 脉冲同步电路
答案
复制代码
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`timescale 1ns/1ns module pulse_detect( input clk_fast , input clk_slow , input rst_n , input data_in , output dataout ); reg data_in_fast; always @ (posedge clk_fast or negedge rst_n) begin if(~rst_n) begin data_in_fast <= 1'b0; end else begin if(data_in == 1'b1) data_in_fast <= ~data_in_fast; else data_in_fast <= data_in_fast; end end reg data_in_slow_t1; reg data_in_slow_t2; reg data_in_slow_t3; always @ (posedge clk_slow or negedge rst_n) begin if(~rst_n) begin data_in_slow_t1 <= 1'b0; data_in_slow_t2 <= 1'b0; data_in_slow_t3 <= 1'b0; end else begin data_in_slow_t1 <= data_in_fast; data_in_slow_t2 <= data_in_slow_t1; data_in_slow_t3 <= data_in_slow_t2; end end assign dataout = data_in_slow_t2 ^ data_in_slow_t3; endmodule
复盘
04 计数器
VL50 简易秒表
答案
复制代码
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`timescale 1ns/1ns module count_module( input clk, input rst_n, output reg [5:0]second, output reg [5:0]minute ); always@(posedge clk or negedge rst_n) begin if(!rst_n) begin second <= 'd0; end else if (minute == 'd60)begin second <= 'd0; end else if (second == 'd60)begin second <= 'd1; end else begin second <= second + 'd1; end end always@(posedge clk or negedge rst_n) begin if(!rst_n) begin minute <= 'd0; end // else if (minute == 'd60)begin // minute <= minute; // end else if(second == 'd60)begin minute <= minute + 'd1; end else begin minute <= minute; end end endmodule
复盘
- 就是简单的计数器
- 注意思考,被注释掉的三行代码,
VL51 可置位计数器
答案
复制代码
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`timescale 1ns/1ns module count_module( input clk, input rst_n, input set, input [3:0] set_num, output reg [3:0]number, output reg zero ); reg [3:0] cnt; always@(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt <= 'd0; end else if(set) begin cnt <= set_num; end else begin cnt <= cnt + 1'b1; end end always@(posedge clk or negedge rst_n) begin if(!rst_n) begin zero <= 'd0; end else if(cnt == 'd0)begin zero <= 1'b1; end else begin zero <= 1'b0; end end always@(posedge clk or negedge rst_n) begin if(!rst_n) begin number <= 'd0; end else begin number <= cnt; end end endmodule
复盘
- 计数器的基础上,增加了设置计数器数值的功能
VL52 加减计数器
答案
复制代码
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`timescale 1ns/1ns module count_module( input clk, input rst_n, input mode, output reg [3:0]number, output reg zero ); reg [3:0] cnt; always@(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt <= 'd0; end else if(mode) begin if(cnt == 'd9) begin cnt <= 'd0; end else begin cnt <= cnt + 1'b1; end end else begin if(cnt == 'd0) begin cnt <= 'd9; end else begin cnt <= cnt - 1'b1; end end end always@(posedge clk or negedge rst_n) begin if(!rst_n) begin zero <= 'd0; end else if(cnt == 'd0)begin zero <= 1'b1; end else begin zero <= 1'b0; end end always@(posedge clk or negedge rst_n) begin if(!rst_n) begin number <= 'd0; end else begin number <= cnt; end end endmodule
复盘
- 就是在计数器的逻辑上增加了一些基本条件
05 存储器
VL53 单端口RAM
答案
复制代码
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`timescale 1ns/1ns module RAM_1port( input clk, input rst, input enb, input [6:0]addr, input [3:0]w_data, output wire [3:0]r_data ); //*************code***********// reg[3:0] mem [127:0]; reg[3:0] data; integer i; always@(posedge clk or negedge rst) begin if(!rst)begin for(i = 0; i < 128; i = i + 1)begin mem[i] = 'd0; end end else if(enb) mem[addr] <= w_data; end reg [3:0] r_data_reg; always@(*) begin r_data_reg = 'd0; if(~enb) r_data_reg = mem[addr]; end // always@(negedge clk or negedge rst) begin // if(!rst)begin // r_data_reg <= 'd0; // end // else if(~enb) // r_data_reg <= mem[addr]; // end assign r_data = r_data_reg; //*************code***********// endmodule
复盘
- enb=1的时候是写,addr就是写地址,enb=0的时候读,addr就是读地址。
- 深度是128bit, 宽度是4bit. 使用reg [3:0] ram_reg[127:0]表示。这个初始化不能用 ram_reg <= 'd0; 需要用一个for循环来实现初始化。
- 注意思考被注释的逻辑与上述组合逻辑的区别
VL54 RAM的简单实现
答案
复制代码
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`timescale 1ns/1ns module ram_mod( input clk, input rst_n, input write_en, input [7:0]write_addr, input [3:0]write_data, input read_en, input [7:0]read_addr, output reg [3:0]read_data ); reg [3:0] RAM [255:0]; integer i; // 不可综合 // reg [8:0] i; always@(posedge clk or negedge rst_n) begin if(~rst_n) for(i=0;i<256;i=i+1) RAM[i] <= 0; else if(write_en) RAM[write_addr] <= write_data; else RAM[write_addr] <= RAM[write_addr]; end always@(posedge clk or negedge rst_n) begin if(~rst_n) read_data <= 0; else if(read_en) read_data <= RAM[read_addr]; else read_data <= read_data; end endmodule
复盘
- 最基本的双口RAM
06 综合
VL55 Johnson Counter
答案
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16`timescale 1ns/1ns module JC_counter( input clk , input rst_n, output reg [3:0] Q ); always@(posedge clk or negedge rst_n) if(!rst_n) Q <= 'd0; else Q <= {~Q[0],Q[3:1]}; endmodule
复盘
- 实现思路:将最后一位
取反
,挪到最高位
VL56 流水线乘法器
答案
复制代码
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`timescale 1ns/1ns module multi_pipe#( parameter size = 4 )( input clk , input rst_n , input [size-1:0] mul_a , input [size-1:0] mul_b , output reg [size*2-1:0] mul_out ); reg [7:0] addr01; reg [7:0] addr23; wire [7:0] temp0 ; wire [7:0] temp1 ; wire [7:0] temp2 ; wire [7:0] temp3 ; assign temp0 = mul_b[0]? {4'b0, mul_a} : 'd0; assign temp1 = mul_b[1]? {3'b0, mul_a, 1'b0} : 'd0; assign temp2 = mul_b[2]? {2'b0, mul_a, 2'b0} : 'd0; assign temp3 = mul_b[3]? {1'b0, mul_a, 3'b0} : 'd0; always @(posedge clk or negedge rst_n) begin if(~rst_n) begin addr01 <= 'd0; addr23 <= 'd0; mul_out <= 'd0; end else begin addr01 <= temp0 + temp1; addr23 <= temp2 + temp3; mul_out <= addr01 + addr23; end end endmodule
复盘
- 略
VL57 交通灯
答案
- 略
复盘
VL58 游戏机计费程序
答案
- 略
复盘
Part III Verilog企业真题
- 如果你想练习真题,可以点击链接:《牛客刷verilog》Part III Verilog企业真题
后记
推荐相关文章
- 校招Verilog篇刷题
- B站-FPGA数字IC牛客Verilog刷题
- FPGA数字IC刷题58道Verilog题解代码及视频讲解【FPGA探索者】【同步/异步FIFO】【跨时钟】
最后
以上就是故意世界最近收集整理的关于《牛客刷verilog》Part II Verilog进阶挑战前言Part I Verilog快速入门Part II Verilog进阶挑战Part III Verilog企业真题后记的全部内容,更多相关《牛客刷verilog》Part内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复