Verilog笔面试常考代码精选10题

Verilog笔面试常考代码精选10题

1. 用Verilog实现1bit信号边沿检测功能,输出一个周期宽度的脉冲信号。

  • 上升沿
  • 下降沿
  • 上升沿或下降沿
1
2
input clk, rst_n, data; 
output data_edge;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module Edge_Detect(
input clk,
input rst_n,
input data,
output pos_edge,
output neg_edge,
output data_edge
);

reg [1:0] data_r;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_r <= 2'b00;
end
else begin
data_r <= {data_r[0], data};
end
end

assign pos_edge = ~data_r[1] & data_r[0];
assign neg_edge = data_r[1] & ~data_r[0];
assign data_edge = pos_edge | neg_edge;

endmodule

2. 用Verilog实现串并转换

  • lsb优先
  • msb优先
1
2
input clk, rst_n, data_i; 
output [7:0] data_o;
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
module Deserialize(
input clk,
input rst_n,
input data_i,
output reg [7:0] data_o
);

//lsb first
/*
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_o <= 8'b0;
end
else begin
data_o <= {data_o[6:0], data_i};
end
end
*/

//msb first
reg [2:0] cnt;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_o <= 8'b0;
cnt <= 3'd0;
end
else begin
data_o[7 - cnt] <= data_i;
cnt <= cnt + 1'b1;
end
end

endmodule

3. 序列检测器:有“101”序列输入时输出为1,其他输入情况下,输出为0。画出状态转移图,并用Verilog描述。

1
2
input clk, rst_n, data; 
output flag_101;

image

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
module Detect_101(
input clk,
input rst_n,
input data,
output flag_101
);

parameter S0 = 0,
S1 = 1,
S2 = 2,
S3 = 3;

reg [1:0] state;

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
state <= S0;
end
else begin
case(state)
S0:
if(data == 1)
state <= S1;
else
state <= S0;
S1:
if(data == 0)
state <= S2;
else
state <= S1;
S2:
if(data == 1)
state <= S3;
else
state <= S0;
S3:
if(data == 1)
state <= S1;
else
state <= S2;
endcase
end
end

assign flag_101 = (state == S3)? 1'b1: 1'b0;

endmodule

4. 用Verilog实现一个异步双端口ram,深度16,位宽8bit。A口读出,B口写入。支持片选,读写请求,要求代码可综合。

1
2
3
4
5
6
7
8
9
10
11
12
module dpram_16x8 ( 
input clk_a,
input [3:0] addr_a,
output [7:0] dout_a,
...
input clk_b,
input [7:0] din_b,
input [3:0] addr_b,
...
);
...
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
module Dual_Port_Sram
#(
parameter ADDR_WIDTH = 4,
parameter DATA_WIDTH = 8,
parameter DATA_DEPTH = 1 << ADDR_WIDTH
)
(
input clka,
input clkb,
input rst_n,
input csen_n,
//Port A Signal
input [ADDR_WIDTH-1:0] addra,
output reg [DATA_WIDTH-1:0] data_a,
input rdena_n,
//Port B Signal
input [ADDR_WIDTH-1:0] addrb,
input wrenb_n,
input [DATA_WIDTH-1:0] data_b
);

integer i;
reg [DATA_WIDTH-1:0] register[DATA_DEPTH-1:0];

always @(posedge clkb or negedge rst_n)begin
if(rst_n == 1'b0)begin
for(i = 0; i < DATA_DEPTH; i = i + 1)
register[i] <= 'b0000_1111;
end
else if(wrenb_n == 1'b0 && csen_n == 1'b0)
register[addrb] <= data_b;
end

always @(posedge clka or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_a <= 0;
end
else if(rdena_n == 1'b0 && csen_n == 1'b0)
data_a <= register[addra];
else
data_a <= data_a;
end

endmodule

5. 用Verilog实现三分频电路,要求输出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
module Div_three(
input clk,
input rst_n,
output div_three
);

reg [1:0] cnt;
reg div_clk1;
reg div_clk2;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
cnt <= 0;
end
else if(cnt == 2)
cnt <= 0;
else begin
cnt <= cnt + 1;
end
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
div_clk1 <= 0;
end
else if(cnt == 0)begin
div_clk1 <= ~div_clk1;
end
else
div_clk1 <= div_clk1;
end

always @(negedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
div_clk2 <= 0;
end
else if(cnt == 2)begin
div_clk2 <= ~div_clk2;
end
else
div_clk2 <= div_clk2;
end

assign div_three = div_clk2 ^ div_clk1;

endmodule

6. 用Verilog实现glitch free时钟切换电路。输入sel,clka,clkb,sel为1输出clka,sel为0输出clkb。

part1是比较垃圾的写法

part2 是两个时钟源是倍数的关系

part3是两个时钟源为异步时钟的关系

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
module Change_Clk_Source(
input clk1,
input clk0,
input select,
input rst_n,
output outclk
);

//-------------------------------------------------------
//part 1
//assign outclk = (clk1 & select) | (~select & clk0);

//-------------------------------------------------------
//part 2
reg out1;
reg out0;
always @(negedge clk1 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out1 <= 0;
end
else begin
out1 <= ~out0 & select;
end
end


always @(negedge clk0 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out0 <= 0;
end
else begin
out0 <= ~select & ~out1;
end
end

assign outclk = (out1 & clk1) | (out0 & clk0);
/*
//-------------------------------------------------------
//part 3
reg out_r1;
reg out1;
reg out_r0;
reg out0;

always @(posedge clk1 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out_r1 <= 0;
end
else begin
out_r1 <= ~out0 & select;
end
end

always @(negedge clk1 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out1 <= 0;
end
else begin
out1 <= out_r1;
end
end

always @(posedge clk0 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out_r0 <= 0;
end
else begin
out_r0 <= ~select & ~out1;
end
end

always @(negedge clk0 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out0 <= 0;
end
else begin
out0 <= out_r0;
end
end

assign outclk = (out1 & clk1) | (out0 & clk0);
*/
endmodule

两个时钟源频率呈倍数关系

image

两个时钟源是异步时钟。

image

7. 用Verilog实现异步复位同步释放电路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module Sys_Rst(
input clk,
input rst,
output sys_rst
);

reg rst_r0;
reg rst_r1;

always @(posedge clk or posedge rst)begin
if(rst)begin
rst_r0 <= 1'b1;
rst_r1 <= 1'b1;
end
else begin
rst_r0 <= 1'b0;
rst_r1 <= rst_r0;
end
end

assign sys_rst = rst_r1;

endmodule

8. 用Verilog实现按键抖动消除电路,抖动小于15ms,输入时钟12MHz。

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
module debounce(
input clk,//12Mhz
input rst_n,
input key_in,
output key_flag
);

parameter JITTER = 240;//12Mhz / (1/20ms)

reg [1:0] key_r;
wire change;
reg [15:0] delay_cnt;

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
key_r <= 0;
end
else begin
key_r <= {key_r[0],key_in};
end
end

assign change = (~key_r[1] & key_r[0]) | (key_r[1] & ~key_r[0]);

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
delay_cnt <= 0;
end
else if(change == 1'b1)
delay_cnt <= 0;
else if(delay_cnt == JITTER)
delay_cnt <= delay_cnt;
else
delay_cnt <= delay_cnt + 1;
end

assign key_flag = ((delay_cnt == JITTER - 1) && (key_in == 1'b1))? 1'b1: 1'b0;

endmodule

9. 用Verilog实现一个同步FIFO,深度16,数据位宽8bit。

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
module Syn_fifo
#(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 4,
parameter RAM_DEPTH = (1 << ADDR_WIDTH)
)
(
input clk,
input rst_n,
input [DATA_WIDTH-1:0] data_in,
input wr_en,
input rd_en,
output reg [DATA_WIDTH-1:0] data_out,
output empty, //fifo empty
output full //fifo full
);

reg [ADDR_WIDTH-1:0] wr_cnt;
reg [ADDR_WIDTH-1:0] rd_cnt;
reg [ADDR_WIDTH-1:0] status_cnt;
reg [DATA_WIDTH-1:0] data_ram;

//-------------------------------------------------------
assign full = (status_cnt == (RAM_DEPTH-1))? 1'b1: 1'b0;
assign empty = (status_cnt == 0)? 1'b1: 1'b0;


//Syn
reg rd_en_r;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
rd_en_r <= 0;
end
else begin
rd_en_r <= rd_en;
end
end


//-------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
wr_cnt <= 0;
end
else if(wr_cnt == RAM_DEPTH-1)
wr_cnt <= 0;
else if(wr_en)begin
wr_cnt <= wr_cnt + 1'b1;
end
else
wr_cnt <= wr_cnt;
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
rd_cnt <= 0;
end
else if(rd_cnt == RAM_DEPTH-1)
rd_cnt <= 0;
else if(rd_en)begin
rd_cnt <= rd_cnt + 1'b1;
end
else
rd_cnt <= rd_cnt;
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_out <= 0;
end
else if(rd_en_r)begin
data_out <= data_ram;
end
end


always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
status_cnt <= 0;
end
else if(rd_en && !wr_en && (status_cnt != 0))begin
status_cnt <= status_cnt - 1;
end
else if(wr_en && !rd_en && (status_cnt != RAM_DEPTH-1))
status_cnt <= status_cnt + 1;
else
status_cnt <= status_cnt;
end

//-------------------------------------------------------
//Syn_Dual_Port_RAM
integer i;
reg [DATA_WIDTH-1:0] register[RAM_DEPTH-1:0];

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
for(i = 0; i < RAM_DEPTH; i = i + 1)
register[i] <= 0;
end
else if(wr_en == 1'b1)
register[wr_cnt] <= data_in;
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_ram <= 0;
end
else if(rd_en == 1'b1)
data_ram <= register[rd_cnt];
else
data_ram <= data_ram;
end

endmodule

Reference

http://www.asic-world.com/examples/verilog/syn_fifo.html

10. 在clk a时钟域的一个单周期脉冲信号,如何正确的传递到clk b时钟域? 要考虑clk a和b的各种不同频率/相位的场景。

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
//慢时钟域到快时钟域,两级寄存器同步
reg [1:0] signal_r;
//-------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_r <= 2'b00;
end
else begin
signal_r <= {signal_r[0], signal_in};
end
end

assign signal_out = signal_r[1];

//快时钟域到慢时钟域
//Synchronous
module Sync_Pulse(
input clka,
input clkb,
input rst_n,
input pulse_ina,
output pulse_outb,
output signal_outb
);

//-------------------------------------------------------
reg signal_a;
reg signal_b;
reg [1:0] signal_b_r;
reg [1:0] signal_a_r;

//-------------------------------------------------------
//在clka下,生成展宽信号signal_a
always @(posedge clka or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_a <= 1'b0;
end
else if(pulse_ina == 1'b1)begin
signal_a <= 1'b1;
end
else if(signal_a_r[1] == 1'b1)
signal_a <= 1'b0;
else
signal_a <= signal_a;
end

//-------------------------------------------------------
//在clkb下同步signal_a
always @(posedge clkb or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_b <= 1'b0;
end
else begin
signal_b <= signal_a;
end
end

//-------------------------------------------------------
//在clkb下生成脉冲信号和输出信号
always @(posedge clkb or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_b_r <= 2'b00;
end
else begin
signal_b_r <= {signal_b_r[0], signal_b};
end
end

assign pulse_outb = ~signal_b_r[1] & signal_b_r[0];
assign signal_outb = signal_b_r[1];

//-------------------------------------------------------
//在clka下采集signal_b[1],生成signal_a_r[1]用于反馈拉低signal_a
always @(posedge clka or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_a_r <= 2'b00;
end
else begin
signal_a_r <= {signal_a_r[0], signal_b_r[1]};
end
end

endmodule

image

image

NingHeChuan wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!