2012年7月8日 星期日

數位電路之後,verilog系列文(4)

寫testbench

testbench是verilog另一個很好用的功能,一般來說,如果設計的電路是要完成某個特定的演算法,比如我們在實驗中要實作256bits的montgomery algorithm,把電路透過quartus合成、燒進FPGA執行,透過Logic analyser分析行為實在太曠日費時(那時寫的不好,合成一次就要30分鐘= =)。
這時候testbench出現了,testbench提供了一個方式,讓我們能利用軟體模擬電路的行為,看看電路的反應,每次模擬只需要幾秒鐘,就可以得到電路的行為。

這篇文章就是要來談testbench的譔寫,會分成以前幾個部分:
1. 軟體安裝:
2. testbench譔寫:
3. 看模擬結果:
4. 常見問題:
5. 相關資源:

1. 軟體安裝:
為了要進行verilog code的模擬,我們需要安裝verilog的模擬軟體,有不少公司都有相關的軟體,如學校工作站安裝Cadence公司的NCverilog,但這是需要付費的,也要連結到工作站才能使用;這裡我推薦使用另一套免費提供的開源模擬軟體:iverilog,可以裝在Linux或Windows上使用,如果是在Ubuntu或Debian系列可以使用apt來安裝:
$apt-get install iverilog
個人使用Archlinux的話,要找AUR,請見相關資源。

另外還要安裝gtkwave,才能看到電路輸出訊號的波型檔。
$apt-get install gtkwave
$pacman –S gtkwave

2. testbench譔寫
其實,testbench也就是一個verilog module,用來產生輸入電路的信號,如果把電路燒在FPGA裡面,輸入的信號可能是來自晶體振盪器的時脈信號,按鈕輸入、信號產生器的輸入……,但寫testbench時,就要自己寫信號的輸入,以及時脈信號的模擬。
這個testbench的module會將待測的電路整個包進去,然後把信號輸給它。
注意在譔寫時,給input 的連接需要用reg,輸出則使用wire來連接。

下面是我在測試Montgomery algorithm module的testbench,目標測試的module的輸入是3個256bits的數A,B,N,n_start降低就會開始運算;輸出則是告知計算完成的n_ready與輸出結果的S:


`timescale 1ns/100ps
`define CYCLE 10

module Montgomery_tb ();

//**************************** wire & reg**********************//
reg clk;
reg [255:0] A;
reg [255:0] B;
reg [255:0] N;
reg n_rst;
reg n_start;

wire [255:0] S;
wire n_ready;
//**************************** module **********************//

Montgomery lalala(.clk(clk),.A(A),.B(B),.N(N),.S(S),.n_rst(n_rst),.n_start(n_start),.n_ready(n_ready));

//**************************** clock gen **********************//
always begin #(`CYCLE/2) clk = ~clk; end

//**************************** initial and wavegen **********************//
initial begin
 $dumpfile("montgomery.vcd");
 $dumpvars;
end

//**************************** testdata **********************//
initial begin
 clk = 1'b0;
 A = 256'd0;
 B = 256'd0;
 N = 256'd0;
 n_rst = 1'b0;
 n_start = 1'b1;
 #1 n_rst = 1'b1;
 A = 256'h4;
 B = 256'h8;
 N = 256'd13;
 #100 n_start = 1'b0;
 #10 n_start = 1'b1;
 #100000 $finish;
end

endmodule

第一行的
`timescale 1ns/10ps
告訴iverilog等模擬軟體,以前者(1ns)為單位,以後者(10ps)的時間,查看一次電路的行為。
首先要先連接測試信號,在Module的地方將待測的電路連起來,再次提醒輸入要接reg,這是我們可以指定其值的地方,輸出接wire,只能觀看。
以下這幾行也是很重要的,用來產生給該module的時脈信號:
`define CYCLE 10
reg clk;
always begin #(`CYCLE/2) clk <= ~clk; end
initial begin clk = 1'b0; end
在testbench裡面,#(數字)代表經過多少delay,initial則是在電路開始時賦值,否則會如下圖一樣,輸出預設為X的信號:


這樣就能產生一個週期為CYCLE* (1ns)長的信號。

initial begin
 $dumpfile("montgomery.vcd");
 $dumpvars;
end
則是讓iverilog把記錄的波型寫入montgomery.vcd中,dumpvars則可以指定要記錄哪些信號的輸出,一般,我都不寫直接記下全部的信號。
以上都算是前置作業,最後,就可以對輸入的信號進行給值:
#1 n_rst = 1'b1;
A = 256'h4;
B = 256'h8;
N = 256'd13;
#100 n_start = 1'b0;
#10 n_start = 1'b1;
#100000 $finish;
A給4, B給8,N給13,嗯…非常白爛的測資,哎呀舉例啦
在100ns時把n_start降下來,10ns後升回去,100000ns讓它慢慢算。

3. 看模擬結果
寫好testbench後,就可以用iverilog編譯,記得要把testbench跟所有相關的v檔都包進去。
$iverilog 所有的.v檔 -o 執行檔名稱
這樣iverilog會產生syntax error的訊息,喔不是,是一個執行檔,再執行這個執行檔。
如果有寫dumpfile的話,就會產生相對應的波型檔囉,這一切都是在轉瞬間完成。
用gtkwave打開該波型檔,如下圖所示,這時候可以從左邊把想要看的信號加到右邊去,階層下的module都可以打開,看裡面的信號波型。
像現在我把A,B,N拉出來,看到他們的確變成4,8,13這三個白爛的測資,在信號上點右鍵可以改變表示的方式,我現在開的是16進位,所以13是d。


這樣是不是比直接燒電路快多了呢?
祝大家testbench愉快!

4. 常見問題:
    1. 為什麼執行檔一執行下去就停不下來了?
如果testbench是你從頭到尾寫完的話,有可能是忘了加$finish,個人因為都改用模板去改,這個問題很少出現。
另一個可能就是你的combinational circuit中出現了,條件中修改了條件;這時候在該時間點上,會讓模擬軟體陷到模擬的迴圈中,也就停不下來了。
    2. 其實我也不知道有什麼常見問題…….

5. 相關資源:
    1. iverilog官網:http://iverilog.icarus.com/
    2. Archlinux iverilog AUR:https://aur.archlinux.org/packages.php?ID=3552

2012年7月6日 星期五

數位電路之後,verilog系列文(3)


寫一個module

在上一篇裡面,我們談過了verilog 三大塊的寫法,以及常見的verilog錯誤,那現在就來看看,一個verilog module的構成,
其實一個module,就好像在寫一個完整的電路,有哪些input, output,要有多少個register,之間的接線,甚至要包住其他的module,是一塊很完整的block。
下面是我在vim裡預設的module格式,大家可以參考,不過我覺得其實這部分每個人都有每個人的風格,有人覺得parameter的部分應該提到module之外,因為那些其實不是電路本體的部分,而是在編譯器編譯時就把code裡的變數代換掉了,所以最重要的還是自己習慣就好:)

module /*module name*/(
/*parameter*/
);

/*================================================================*
 * PARAMETER declarations
 *================================================================*/
/*================================================================*
 * REG/WIRE declarations
 *================================================================*/
/*================================================================* 
 * Module 
 *================================================================*/          
/*================================================================* 
 * Combinational circuit 
 *================================================================*/          
always @(*)begin
end  
/*================================================================*
 * Sequential circuit
 *================================================================*/ 
always @(posedge clk or negedge n_rst) begin
end 
/*================================================================* 
 * Output circuit
 *================================================================*/ 
always @(*)begin
end  
    
endmodule

1. inout宣告,行1~3:
第一行宣告module 名字就不用說了。
從第二行開始是input與output的宣告,其實這好像也沒什麼好說的,就直接宣告各個input 與output:
Ex: input clk_50M,
有兩點可以注意的是:
第一在top module,input與output的名字,最好能和原廠提供的pin assignment的qsf檔或是xls檔相對應,才能大量節省pin assignment的時間,記得剛開始實驗時,都會把input, output名字取得比較好記,像送給七段顯示器的秒數資料,我就叫second1, second2這樣,當然pin assignment 的時候就要一個一個去對照pdf 檔,把名字對應起來,這樣當然很智障= =。
好的做法是,取名就跟pin assignment一樣,如果覺得名字實在太鳥了,下面再用assign的方式,承接input的值或是對output給值。
當然也可以直接去改pin assignment的檔案,不過何苦呢XD?

第二點是註記:
如果是top module,因為input output是來自於開發板上的各元件,通常就依照元件分群;如果是其他的module,則依信號給的對象分群,例如現在寫了一個「輸入為4bits的數,輸出為七段顯示器的信號」。則我會這樣註記,把對象分開:
//main
input clk,
input [3:0] number,
//seven segment
output [6:0] segment_signal

2. Parameter 和reg, wire,行5~10:
這個好像真的沒什麼好講的…
大概只有wire,如果是一個行為很簡單的wire,例如要連接到inout的輸出:
wire sram_data = (state == STATE_WRITE)? data_in_buf : 16'bz;
就可以直接補完。

3. Module,行11~13:
就連接module嘛 = =,好像也沒什麼好講的。

4. 電路設計,行14~28:
這裡包括了next state logic, sequential block, output logic,其實不一定每一塊都要有,看電路的設計。
據我強者同學phoning表示,DSD的建議是,next state logic跟output logic寫在一起,不過我習慣還是會分開寫;但我的確有在想,把sequential block的部分移到電路最後,因為這個部分通常是最不常改到的地方,我想這也是一樣,大家習慣就好。

我倒覺得這裡正好講一下reset的觀念,也就是code裡的n_rst:
在verilog裡面,因為寫的是電路,當電路通電的那一瞬間,所有register裡面的值都是亂七八糟的,而不是像c code,可以在宣告時寫int a=1,它就是1;偏偏這時候,verilog又用一個很容易誤會的關鍵字:initial。
所以就很常有人以為,只要寫:
initial begin
    State <= 0;
end
就可以讓reg回到原來值…(其實這部分個人不確定,因為這樣寫過,而且好像還真的能用)

比較正確的作法是:用一個開關之類的為reset信號
然後在sequential電路的部分都加上
if(~n_rst)begin     //要negedge reset或posedge reset隨你高興啦……
    state<=0;
end
else …………其他的內容
這樣一但發出reset 信號,所有的register都會清成起始值了。

5. 註解:
這個在prototype上面沒有,我也是最近才學到的,在寫完module,別忘了加上instance,把這個module該如何引用寫在最前面,方便其他人的引用啊,如下所示:
/* instance
module_name(
// front comment
.port1(), // back comment
.port2(),
.port3()
);
*/

2012年7月4日 星期三

數位電路之後,verilog系列文(2)


常見的verilog 譔寫錯誤:
感謝鄭為中大神的提醒,要寫這篇verilog常見錯誤文,也感謝鄭為中大神對我verilog觀念的澄清:)

譔寫verilog最常見的錯誤,當然就是syntax error……= =
當然這裡不討論這些,雖然他們很常出現,像忘了加分號、拼錯字之類的,我們延續上一篇對verilog結構的討論,再來看看,寫得不好的verilog code會造成怎麼樣硬體上的後果,與上一篇結構問題相同,需要轉成硬體的結果造成這個verilog獨有的錯誤。

這篇討論使用Altera Quartus II編譯時,兩種常見的錯誤:
1.產生latch
2.產生combinational loop

1.產生latch:
產生Latch最主要的原因是沒有把所有條件寫乾淨。
我們考慮電路合成的情形,當我們寫一個if,或者case,這些東西在電路內都會轉成mux,例如以下的code:
if(in=1) begin counter_next = counter +1; end
else begin counter_next = counter; end
這會變成下面的電路(好醜的multiplexer…..):

如果是case,就是多對一的mux。
例如常見的七段顯示器的code,輸入一個數,轉成相對應7個光點的輸出,這個大概就會合成類似,16對1的mux:
case(digit)
   4'd0: segment_out = 7'b1000000; //1 -&gt; dark 0-&gt; light
    4'd1: segment_out = 7'b1111001;//      ___a___
    4'd2: segment_out = 7'b0100100;//    |       |
    4'd3: segment_out = 7'b0110000;//   f|       |b
    4'd4: segment_out = 7'b0011001;//    |       |
    4'd5: segment_out = 7'b0010010;//    |_______|
    4'd6: segment_out = 7'b0000010;//    |   g   |
    4'd7: segment_out = 7'b1111000;//   e|       |c
    4'd8: segment_out = 7'b0000000;//    |       |
    4'd9: segment_out = 7'b0010000;//    |_______|
    default: segment_out = 7'b1111111;//      d
endcase

注意到,上面無論是if還是case,最後都有一個else/default,再次重申,verilog和一般的C是不一樣的,在C裡面的if 就是判斷條件,符合就跳到指定的程式碼,不符合就繼續執行;但verilog是要轉成電路的,你不指定else,mux的輸入要接給誰?
一般verilog的編譯器會指給輸出,也就是如下的電路:

如此有個訊號被鎖在這個mux裡面,就是基本的latch,雖然一般來說,這樣的latch對電路的行為影響不算太大,有時候充滿latch的code還是可以正常運作;但個人習慣上還是強烈建議把if, case全都加上else,消掉所有的latch;因為有latch 的code的行為較難預料,輸出變得依據”上一個狀態”來決定,而不是單純的combinational circuilt。

2.Combinational loop:
另一個情況是產生combinational loop。
理論上,用我上一篇所說的FSM和三大塊架構下去寫,是不會出現combinational loop的,三大塊的架構最主要的核心,就是把”變數” (state)和”變數的下一個狀態”(stata_next)的運算分開,在combinational 的部分就只靠state(和其他變數)去運算state_next。
所以說,在verilog 裡面,絕對絕對絕對沒有assign的左右兩邊是相同的狀況,這也是verilog最難讓人習慣的地方,因為一般程式寫a=a+1實在太常見了。
比如說
always @(*) begin
    state = (counter=2’d2)?state+1’b1:state;
end
這樣的code是絕對不可以出現的狀況,會產生另一個常見的combinational loop的警告。有時候這樣的code也可以存活,但出現combinational loop一般都會大大加重quartus編譯時的負擔,他會針對電路去做很多的模擬,確認電路工作沒有異常,造成編譯效率的下降,曾經遇過充滿combinational loop的code,編一次耗時40分鐘,修掉之後卻只需要10分鐘,影響之大由此可見;寫verilog時,養成良好的習慣,把”會受到自己狀態,而影響到下個狀態的變數”,都產生兩組,X,跟X_next,才不會把自己搞得暈頭轉向。

2012年6月30日 星期六

數位電路之後,Verilog 系列文(1)

Verilog coding style:談談verilog三大塊的架構

常說「寫verilog一定要有硬體的思維」,這是因為verilog,亦或VHDL的用意,最終都是要轉成硬體上的register和combination circuit,我自己的寫verilog比較不像在寫一般的程式語言,可以宣告各個變數、作運算,然後輸出結果;verilog會不斷宣告module、register跟combinational circuit與其中的接線,結合成一個完整的電路。

總的來說,我認為verilog對語法結構的要求更為嚴格,每寫一行code都會對應生成一塊硬體,如果code結構不好,生成的硬體架構也會很糟,我認為大多數的書跟講義,都沒有強調這點,大多花了很多篇幅先介紹各種combinational circuit的寫法,把語法結構的內容放在FSM的範例裡,很像在教物件導向的語言,卻不先教物件的概念一樣,套句黃猿的口頭禪:「真奇怪呢~」。
依照個人寫verilog的經驗,統整成個人所謂的「三大塊的架構」,在這第一篇中分享給大家,希望大家都能有效掌握三大塊架構的威力,輕鬆寫出「行為和預期中的相同的電路」。
從下面我會先介紹一些電路設計時的基本硬體,verilog如何轉換成這些硬體,最後再來講,FSM的概念和三大塊架構的語法。
以下是本文的架構:
1. 基本硬體
2. verilog的轉換
3. FSM與三大塊的架構介紹
4. 相關內容

1. 基本硬體:
上過基本電路設計的都會知道,電路元件分成兩大類:combinational circuit和sequential circuit。簡單來說,combinational circuit就是不斷的進行運算,輸出只和輸入有關;sequential circuit則會輸出則會看輸入以及當前電路的狀態。
但以當前數位電路的情況,其實可以說,sequential circuit大多都是由D-flip flop作為register所組成,特色在於,D flip-flop每遇到clock跳動就會把輸入點的資料記錄下來,待下一次clock跳動。
拿一個城市的交通作比喻:
Combinational circuit就像馬路,負責讓車(信號)流動,改變車道(運算)
Sequential circuit則像紅綠燈,負責管理信號什麼時候可以通行,到下一個combinational circuit。

就如下最經典的電路架構圖所示:

上方的D-flip flop在clock來時記錄combinational 的輸出,combinational circuit則靠register的記錄值與輸入信號,計算輸出與下個記錄值。

為了要對應combinational 和sequential 兩種電路,verilog裡面兩種不同的宣告方式:
Always @( posedge/negedge clock) – edge trigger
Always @( state, counter)               – level trigger(感謝為中大神指正)

前者會合成出:由D flip flop的register架構,由括號中的接線作為register的觸發信號,把資料寫入某個register當中,如:state <= state_next。
就會在clock跳動時,將state_next的資料,寫進state的register裡。記得在實驗中討論時,我都管這個叫「敲clock」,因為感覺很像在clock來的時候,就把資料敲進register裡面XD。
後者則會合成出:純粹的combinational circuit,以括號中的訊號為輸入訊號,執行其中的邏輯設計;所以說,這樣的寫法中,所有有用來assign值的信號(RHS),都要加到括號裡面,也就是sensitivity list,如下面的counter:
Always @(state, counter) begin counter_next = counter; end
因為總要有這個信號當輸入,才能用它來給其他信號值,現在也可以用always @(*)來代替,讓compiler自動宣告為輸入。

3. FSM與三大塊的架構介紹
現在我們明白了combinational circuit和sequential circuit的關係後,就可以看一下有限狀態機FSM的概念。
有限狀態機其實是一個很常用的model,用來描述整個電路面對不同輸入(需求)時,該有什麼樣的變化和行為,無論要寫什麼樣的電路,個人還是會先在紙上構思完FSM的架構,再由FSM下去寫,架構就會很清楚。
下圖是一個很簡單的FSM的模型,用來描述:

呃…一個2 bits的counter,數到三的時候輸出是1,這是很簡單的FSM。
FSM最重要的有幾個元素:狀態、輸入、輸出。
隨不同的輸入,會跳到不同的狀態;輸出可以只和狀態有關(Moore machine如上圖),也可以和狀態、輸入都有關(Mealy machine)。

其實,從FSM上面,已經能看出所謂的三大塊架構,那就是:
1. State register: 把狀態的寫入,透過clock控制的register,把next  state隨clock寫入state的register中。
2. Next state logic:計算下一個狀態,根據目前的input和state,運算下一個next state。
3. Output logic:輸出,根據目前的狀態(或輸入),運算當前輸出。
相對應的,也就是三大塊的架構:
1. 含posedge的always block,將變數的next state寫入register。
2. 不含posedge的always block,當中運算各個next state。
3. 不含posedge的always block,當中只負責其他數值運算。
實際對應的code就是:

//state register
Always @(posedge clock) begin  State <= state_next;   end
注意,只有這裡可以用posedge clock,因為只有這裡宣告所有register。

//next state logic
always @(*) begin
    if (input  == 1’b1) State_next = state+1’b1;
    else state_next = state;
end
這裡寫下一個要敲進register的值的運算

//output logic
Always @(*) begin  output = (state == 2’d3)1’b1:1’b0;   end
這裡寫輸出的function。

通常我會每個不同的變數,都有一個獨立的always block,如下面的state跟counter:
//state register
//state
always @(posedge clock) begin  state <= state_next;   end
//counter
always @(posedge clock) begin  counter <= counter_next;   end

//next state logic
//state
always @(*) begin
    if (counter  == 2’d10) state_next = state+1’b1;
    else state_next = state;
end
//counter
always @(*) begin
    if (input  == 1’b1) counter_next = counter+1’b1;
    else counter_next = counter;
end
如此一來,每個變數的logic都很獨立,各自受誰影響也可以一目了然,要改變數的行為時,只要搜尋該變數名,就可以快速跳到該修改的地方;這在變數多的時候,修改起來會變得很輕鬆。

4. 其他相關的內容整理:
1..看看各種verilog語法是如何合成基層的電路。
http://eesun.free.fr/DOC/VERILOG/synvlg.html
2..OO無雙部落格,對FSM寫法的描述,相當完整,推薦花時間讀完。
http://www.cnblogs.com/oomusou/archive/2011/06/05/fsm_coding_style.html

2012年6月11日 星期一

bypass與測試線小技巧

無論是放大器、主動混頻器還是振盪器,都需要外加電線才能運作
這時候我們就面臨到一個問題
當你接一個DC的電線到電源,這段線的長度非常長
即便DC電源是個理想的ground,經過這麼長的線
各頻率的阻抗會散開到smith chart的整個圓周上

因此需要有方法在DC的位置創造一個理想的ground
方法很簡單,用並聯的電容即可
並聯的電容對RF信號來說,就像直接short到ground一樣
也可以說,電容會隨信號充放電,達到穩定的效果
對於愈高頻的電路,電容就要愈小

畫起來大概像這樣
當然這只是示意圖,電容值是預設值沒有改
也不能直接接ground,而是接back via的元件
通常會用一路只有電容的把主要in-band 的頻率cover住
另外一路會用大一點的電容跟串聯電阻,來cover較低的頻率
如此就能做到一個不錯的RF ground.

為了看看這個bypass好不好
通常會加上測試線來試試,其實測試線說穿了就是
用一段長線去模擬真實狀況的DC長線
模擬上長這個樣子
很簡單吧

但是,平時模擬時不會把測試線打開,它會讓模擬跑得久一些些
等到大概模擬完,要開測試線,要一個一個打開又很麻煩
最近發現ADS裡面也支援簡單的script這個小技巧
先拉出一個控制的var,例如叫:testline_valid好了
把上面測試線的Z改成
if (testline_valid == "open") then 20 Ohm else 1e10 Ohm endif
你不想叫"open",要用什麼東西隨你便

總之只要把testline_valid設成你要的值,就可以方便的加入測試線的模擬。