"); //-->
本文使用D-FF制作一个简单的滤波器,方便在FPGA使用,可以避掉因PCB板设计不良而产生的glitch。
Introduction
有很多论文专门在讨论数字滤波器,本文提出的方法,只是刚好适用在FPGA上,而且颇为简单,可以作为一般用途的滤波用。
背景是在最近工作时,需要将RTL在FPGA上验证,可能是因为PCB版的设计不良,所以导致有一根reset信号会在某种状况下产生glitch,通常reset信号都是异步设计,所以只要reset有一点点的low信号,就可能造成整个系统reset,long term solution当然是去改版子,但因为急着要验证RTL,所以short term solution只好先从FPGA下手,想办法去滤掉这个glitch。
在LA与SignalTap观察下,这根reset信号的glitch都很短,远小于50Mhz的周期20 ns,首先假设我们想滤掉小于1个周期的glitch,共有以下几种可能:
case 1:glitch小于1个周期,且在clk正缘时有敲到
host_rst_n原本是根reset信号,low active,因为PCB版问题产生极短glitch,导致整个系统reset,假设刚好被clk的正缘敲到,当使host_rst_n信号delay过 两级后,再将第一级与第二级作OR(稍后会有完整的原理图),可以发现成功的滤掉了host_rst_n原本的glitch。
case 2:glitch小于1个周期,但clk正缘没有敲到
host_rst_n在delay第一级就已经滤掉了,所以之后都没问题,目前看来这个solution能滤掉小于1个周期的glitch。
目前为止,这个能滤掉小于1个周期glitch的原理图如下:
case 3:glitch大于1个周期且小于2个周期,但clk正缘时有敲到
由以上波形图推导可以发现,光使用host_rst_n_d1 | host_rst_n_d2是不够的,仍然会产生glitch,必须多delay一级到host_rst_n_d3,并且将host_rst_n_d1 | host_rst_n_d2 | host_rst_n_d3才能完全消除glitch。
case 4:glitch大于1个周期且小于2个周期,但clk正缘没有敲到
由以上波形图推导可以发现,其实host_rst_n_d1 | host_rst_n_d2就足够消除glitch,并不一定得delay到第3级,当然多了第3级去OR也不会有错。
目前为止,这个能滤掉大于1个周期且小于2个周期glitch的原理图如下:
Summary
若要滤掉小于n个周期的glitch,需要delay (n+1)级,并将delay 1、delay 2....delay (n+1)级的register一起做OR,即可消除此glitch。
这样就结束了吗?还没这么单纯,因为这种解法,会有其『副作用』 ,接下来就要谈谈其副作用部份。
case 5: glitch小于1个周期,但连续2个周期出现,且在clk正缘时有敲到
由以上波形图推导可以发现,连续2个小于1个周期的glitch,很意外的也在这个delay 3级作OR的解法中也被滤掉了,同理可推得连续3个周期都出现glitch,要delay 4级作OR才可滤掉,推导的方式相同,在此省略。
Summary
若要滤掉n个小于1个周期的glitch,需要delay (n+1)级,并将delay 1、delay 2....delay (n+1)级的register一起做OR,即可消除此glitch。
所以若你想滤掉小于2个周期的glitch,而采用了delay 3级作OR的方式,就会一同将连续2个小于1个周期的glitch一起滤掉,这没有好或不好,就端看你datasheet对reset信号的规定,通常reset low信号都会很长,所以应该不至于将正常的reset信号滤掉,重点是你要知道这种solution的一些限制,进而自己决定是否该使用。
使用Verilog表示
原理图已经确定后,接着要讨论如何使用Verilog表示,我们以delay 3级作OR的原理图为例:
10 module digital_filter_1
(
11 clk_50M,
12 rst_n,
13 host_rst_n,
14 host_rst_n_filter
15 );
16
17 input clk_50M;
18 input rst_n;
19 input host_rst_n;
20 output host_rst_n_filter;
21
22 reg host_rst_n_d1;
23 reg host_rst_n_d2;
24 reg host_rst_n_d3;
25
26 always@(posedge clk_50M or negedge rst_n)
begin
27 if (~rst_n) begin
28
host_rst_n_d1
<= 1'b1;
29
host_rst_n_d2 <= 1'b1;
30
host_rst_n_d3 <= 1'b1;
31 end
32 else begin
33
host_rst_n_d1
<= host_rst_n;
34
host_rst_n_d2
<= host_rst_n_d1;
35
host_rst_n_d3
<= host_rst_n_d2;
36 end
37 end
38
39 assign host_rst_n_filter = host_rst_n_d1 |
host_rst_n_d2 | host_rst_n_d3;
40
41 endmodule
这是一般初学者的写法,就乖乖的将原理图用Verilog去表示,无论是Quartus II、Synplify Pro或者Design Compiler都可以看的懂,并且合成出正确的电路。
digital_filter_2.v / Verilog
1
9
10 module digital_filter_2 (
11 clk_50M,
12 rst_n,
13 host_rst_n,
14 host_rst_n_filter
15 );
16
17 input clk_50M;
18 input rst_n;
19 input host_rst_n;
20 output host_rst_n_filter;
21
22 reg [2:0] host_rst_n_dly;
23
24 always@(posedge clk_50M or negedge rst_n)
begin
25 if (~rst_n)
26
host_rst_n_dly
<= 3'b111;
27 else
28
host_rst_n_dly
<= {host_rst_n_dly[1:0], host_rst_n};
29 end
30
31 assign host_rst_n_filter =
|host_rst_n_dly;
32
33 endmodule
这是较有经验的写法,初学者可能会看不懂,这种写法在Quartus II、Synplify Pro与Design Compiler都可以看的懂,也可以合成出正确的电路。
22行
reg [2:0] host_rst_n_dly;
因为要delay 3级,所以宣告3 bit的host_rst_n_dly,事实上,host_rst_n_dly[0]就相当于host_rst_n_d1,host_rst_n_dly[1]就相当于host_rst_n_d2,host_rst_n_dly[2]就相当于host_rst_n_d3。
28行
host_rst_n_dly <= {host_rst_n_dly[1:0], host_rst_n};
事实上这一行可以拆成以下3行
host_rst_n_dly[0] <=
host_rst_n;
host_rst_n_dly[1] <= host_rst_n_dly[0];
host_rst_n_dly[2] <= host_rst_n_dly[1];
因为host_rst_n_dly[0]相当于host_rst_n_d1,host_rst_n_dly[1]相当于host_rst_n_d2,host_rst_n_dly[2]相当于host_rst_n_d3,所以也相当于第一种写法的以下3行。
host_rst_n_d1 <= host_rst_n;
host_rst_n_d2 <= host_rst_n_d1;
host_rst_n_d3 <= host_rst_n_d2;
事实上这个技巧在RTL相当常见, 算是Verilog的惯用写法,要让自己习惯并熟析这种写法。
31行
assign host_rst_n_filter = |host_rst_n_dly;
这里用到了Verilog的一个独门语法:reduction operator,这在VHDL与C/C++都没有,这一行相当于以下的写法。
assign host_rst_n_filter = host_rst_n_dly[2] | host_rst_n_dly[1] | host_rst_n_dly[0];
也就是一个vector内,每个bit彼此去做OR。
以前在学Verilog时,一直不懂为什么Verilog要特别提供reduction写法,今天总算在这里看到reduction写法的威力了。
Quartus II合成结果
以上两种写法,经过Quartus II合成后的结果都一样,事实上,Quartus II合成的结果就是第2种写法所描述的结果。
Conclusion
本博文提出一个简单的方法,可以在使用FPGA做RTL验证时,暂时将因PCB版设计不良所产生的
glitch做滤波,但这种方式所带来的副作用必须特别小心,是否因此而导致不符合datasheet规定,也示范了其Verilog的实现方式,其中包
含了一些Verilog的小技巧。
此外,我们可以发现整篇博文的思路为:波形图推导 –> 原理图 –> Verilog,所以Verilog code绝对不是凭空生出来的,若初学者直接看到第二种写法的Verilog code,一定会看不懂它的意义是什么,此时我们可以逆推回去,首先将其所代表的硬件原理图画出来,运气好在这个阶段就可以了解,若还是不懂,就实际搭配 波形图,应该就更可以了解code所要表达的意义。这点与C语言有很大的差别,C语言基本上只要语法能懂,再将其所要表达的流程图画出来,就能了解其所要 表达的算法,至于他所代表的汇编语言为何我们不知道也没关系。但是Verilog是硬件描述语言,其所描述的是一个实体硬件,Verilog或VHDL只 是试着用code的方式表示那个硬件而已,所以在写code之前要以经先有原理图,而不是直接去写连自己都不知道结果为什么硬件的code,这是写 Verilog与写C最大的差异。
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。