0%

关于异步复位的错误写法导致的问题

这是一个异步复位的错误写法引发的血案

问题

2020.10.20晚:

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 freq_div(
input wire clk,
input wire en,
input wire rst_n,

output reg clk_o
);

reg [31:0] clk_cnt = 32'b0; //clk的计数器

always @(posedge clk or negedge rst_n) begin
if (rst_n) begin

if (en) begin
if (clk_cnt == (25000000 - 1)) begin
clk_cnt <= 32'b0;
clk_o <= ~clk_o;
end else begin
clk_cnt <= clk_cnt+1;
clk_o <= clk_o;
end
end else begin
clk_cnt <= clk_cnt;
clk_o <= clk_o;
end

end else begin
clk_cnt <= 0;
clk_o <= 0;
end
end

endmodule

综合后有如下警告:

image-20201020232826101

其中37行就是if (clk_cnt == (25000000 - 1)) begin这一行(因为前面有一部分文件头没有放上来)。

一个比较坑的地方就是,行为仿真的时候并没有什么问题,但是下载后,在板子上的现象出错。

如果去掉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
module freq_div(
input wire clk,
input wire en,

output reg clk_o
);

reg [31:0] clk_cnt = 32'b0; //clk的计数器

always @(posedge clk) begin

if (en) begin
if (clk_cnt == (25000000 - 1)) begin
clk_cnt <= 32'b0;
clk_o <= ~clk_o;
end else begin
clk_cnt <= clk_cnt+1;
clk_o <= clk_o;
end
end else begin
clk_cnt <= clk_cnt;
clk_o <= clk_o;
end

end

endmodule

综合就没有问题,板子上的效果也正常。

有点迷惑,先记下。

解决

2020.10.21上午:

这是一个很傻x的问题,本质是我把软件的编程思路也带进来了:在含有rst_n的代码中,由于always模块的敏感信号是rst_n的下降沿,当rst_n产生下降沿的时候,立即进入always块,此时rst_n肯定是低电平,因为是下降沿触发的,所以电路肯定执行的是else部分的逻辑,这就相当于rst_n信号每次下降沿时,总是复位状态,并不能进入到前半部分的if中,所以clk_cnt在综合时被vivado给优化掉了(至于有人想问:clk触发时如果rst_n为高电平时不是可以进入if吗?并且仿真的时候也没有问题呀?为什么还会被优化?——emmm这个问题我也挺想问的……具体应该与vivado的仿真及综合算法有关)。

解决问题的方法就是把if的条件换成!rst_n,然后把if和else中的内容交换一下位置就行了(具体如下)。

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
module freq_div(
input wire clk,
input wire en,
input wire rst_n,

output reg clk_o
);

reg [31:0] clk_cnt = 32'b0; //clk的计数器

always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin

clk_cnt <= 0;
clk_o <= 0;

end else begin

if (en) begin
if (clk_cnt == (25000000 - 1)) begin
clk_cnt <= 32'b0;
clk_o <= ~clk_o;
end else begin
clk_cnt <= clk_cnt+1;
clk_o <= clk_o;
end
end else begin
clk_cnt <= clk_cnt;
clk_o <= clk_o;
end
end

end

endmodule

所以,以后一定要注意,always块在涉及到边沿触发的信号时,块中的判断条件一定要写成满足触发沿后的状态的样子。