(原創) 如何實現簡易的數位濾波器? (SOC) (Verilog)
Abstract
本文使用D-FF製作一個簡單的濾波器,方便在FPGA使用,可以避掉因PCB板設計不良而產生的glitch。
Introduction
使用環境:Quartus II 10.1 + Synplify Pro 2009.06-SP1
有很多論文專門在討論數位濾波器,本文提出的方法,只是剛好適用在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的原理圖為例:
digital_filter_1.v / Verilog
1 /*
2 (C) OOMusou 2011 http://oomusou.cnblogs.com
3
4 Filename : digital_filter_1
5 Synthesizer : Quartus II 10.1
6 Description : degital filter 1
7 Release : Feb.03,2011 1.0
8 */
9
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 /*
2 (C) OOMusou 2011 http://oomusou.cnblogs.com
3
4 Filename : digital_filter_2
5 Synthesizer : Quartus II 10.1
6 Description : degital filter 2
7 Release : Feb.03,2011 1.0
8 */
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相當常見,在(筆記) 如何將值delay n個clock? (SOC) (Verilog) 已經出現過這種寫法, 算是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種寫法所描述的結果。
完整程式碼下載
digital_filter_1.7z
digital_filter_2.7z
Conclusion
本博文提出一個簡單的方法,可以在使用FPGA做RTL驗證時,暫時將因PCB版設計不良所產生的glitch做濾波,但這種方式所帶來的副作用必須特別小心,是否因此而導致不符合datasheet規定,也示範了其Verilog的實現方式,其中包含了一些Verilog的小技巧。
此外,我們可以發現整篇博文的思路為:波形圖推導 –> 原理圖 –> Verilog,所以Verilog code絕對不是憑空生出來的,若初學者直接看到第二種寫法的Verilog code,一定會看不懂它的意義是什麼,此時我們可以逆推回去,首先將其所代表的硬體原理圖畫出來,運氣好在這個階段就可以了解,若還是不懂,就實際搭配波形圖,應該就更可以了解code所要表達的意義。這點與C語言有很大的差別,C語言基本上只要語法能懂,再將其所要表達的流程圖畫出來,就能了解其所要表達的演算法,至於他所代表的組合語言為何我們不知道也沒關係。但是Verilog是硬體描述語言,其所描述的是一個實體硬體,Verilog或VHDL只是試著用code的方式表示那個硬體而已,所以在寫code之前要以經先有原理圖,而不是直接去寫連自己都不知道結果為什麼硬體的code,這是寫Verilog與寫C最大的差異。
See Also
(筆記) 如何將值delay n個clock? (SOC) (Verilog)
全文完。