對Verilog 初學者比較有用的整理(轉自它處)

********************************************************************************************************************前端

*做者: Ian11122840    時間: 2010-9-27 09:04                                                                                                                                                                *
*標題: 菜鳥作設計必看!有關如何作設計的總體思路,以及可否綜合的筆記                                                                                                                                       *
*所謂綜合,就是把描述語言轉化成能硬件實現的電路,學verilog的時候,沒有人給我說要不要考慮可否綜合的問題~~~                                                                            *
*看了5本書,竟然沒有一本書講到可否綜合,因此設計出來的程序徹底不能用~~~                                                                                                                             *
*並且,書中都是講語句的具體使用辦法,例如always @(),可是何時用always,幾個always之間、時序電路、邏輯電路、任務與函數何時用,卻沒有一本書能講清楚。*
*這個筆記詳細寫了這些思路的問題,分享給新手看看,學習一種思路~~
*點擊此處下載 ourdev_585849OJ54KV.doc(文件大小:720K) (原文件名:verilog_經驗(適合初學者).doc)                                                                                   *程序員

********************************************************************************************************************shell

先記下來:
一、不使用初始化語句;
二、不使用延時語句;
三、不使用循環次數不肯定的語句,如:forever,while等;
四、儘可能採用同步方式設計電路;
五、儘可能採用行爲語句完成設計;
六、always過程塊描述組合邏輯,應在敏感信號表中列出全部的輸入信號;
七、全部的內部寄存器都應該能夠被複位;
八、用戶自定義原件(UDP元件)是不能被綜合的。
一:基本
Verilog中的變量有線網類型和寄存器類型。線網型變量綜合成wire,而寄存器可能綜合成WIRE,鎖存器和觸發器,還有可能被優化掉。
二:verilog語句結構到門級的映射
一、連續性賦值:assign
連續性賦值語句邏輯結構上就是將等式右邊的驅動左邊的結點。所以連續性賦值的目標結點老是綜合成由組合邏輯驅動的結點。Assign語句中的延時綜合時都將忽視。
二、過程性賦值:
過程性賦值只出如今always語句中。
阻塞賦值和非阻塞賦值就該賦值自己是沒有區別的,只是對後面的語句有不一樣的影響。
建議設計組合邏輯電路時用阻塞賦值,設計時序電路時用非阻塞賦值。
過程性賦值的賦值對象有可能綜合成wire, latch,和flip-flop,取決於具體情況。如,時鐘控制下的非阻塞賦值綜合成flip-flop。
過程性賦值語句中的任何延時在綜合時都將忽略。
建議同一個變量單一地使用阻塞或者非阻塞賦值。
三、邏輯操做符:
邏輯操做符對應於硬件中已有的邏輯門,一些操做符不能被綜合:===、!==。
四、算術操做符:
Verilog中將reg視爲無符號數,而integer視爲有符號數。所以,進行有符號操做時使用integer,使用無符號操做時使用reg。
五、進位:
一般會將進行運算操做的結果比原操做數擴展一位,用來存放進位或者借位。如:
Wire [3:0] A,B;
Wire [4:0] C;
Assign C=A+B;
C的最高位用來存放進位。
六、關係運算符:
關係運算符:<,>,<=,>=
和算術操做符同樣,能夠進行有符號和無符號運算,取決於數據類型是reg,net仍是integer。
七、相等運算符:==,!=
注意:===和!==是不可綜合的。
能夠進行有符號或無符號操做,取決於數據類型
八、移位運算符:
左移,右移,右邊操做數能夠是常數或者是變量,兩者綜合出來的結果不一樣。
九、部分選擇:
部分選擇索引必須是常量。
十、BIT選擇:
BIT選擇中的索引能夠用變量,這樣將綜合成多路(複用)器。
十一、敏感表:Always過程當中,全部被讀取的數據,即等號右邊的變量都要應放在敏感表中,否則,綜合時不能正確地映射到所用的門。
十二、IF:
若是變量沒有在IF語句的每一個分支中進行賦值,將會產生latch。若是IF語句中產生了latch,則IF的條件中最好不要用到算術操做。Case語句相似。Case的條款能夠是變量。
若是一個變量在同一個IF條件分支中先贖值而後讀取,則不會產生latch。若是先讀取,後贖值,則會產生latch。
1三、循環:
只有for-loop語句是能夠綜合的。
1四、設計時序電路時,建議變量在always語句中賦值,而在該always語句外使用,使綜合時能準確地匹配。建議不要使用局部變量。
1五、不能在多個always塊中對同一個變量贖值
1六、函數
函數表明一個組合邏輯,全部內部定義的變量都是臨時的,這些變量綜合後爲wire。
1七、任務:
任務多是組合邏輯或者時序邏輯,取決於何種狀況下調用任務。
1八、Z:
Z會綜合成一個三態門,必須在條件語句中賦值
1九、參數化設計:
優勢:參數可重載,不須要屢次定義模塊
四:模塊優化
一、資源共享:
當進程涉及到共用ALU時,要考慮資源分配問題。能夠共享的操做符主要有:關係操做符、加減乘除操做符。一般乘和加不共用ALU,乘除一般在其內部共用。
二、共用表達式:
如:C=A+B;
    D=G+(A+B);
二者雖然有共用的A+B,可是有些綜合工具不能識別.能夠將第二句改成:D=G+C;這樣只需兩個加法器.
三、轉移代碼:
如循環語句中沒有發生變化的語句移出循環.
四、避免latch:
兩種方法:一、在每個IF分支中對變量賦值。二、在每個IF語句中都對變量賦初值。
5:模塊:
綜合生成的存儲器如ROM或RAM不是一種好方法,只是成堆的寄存器,很費資源。最好用庫自帶的存儲器模塊。
5、驗證:
1、敏感表:
在always語句中,若是敏感表不含時鐘,最好將全部的被讀取的信號都放在敏感表中。
2、異步復位:
建議不要在異步時對變量讀取,即異步復位時,對信號賦以常數值。

Averilog的流行,有兩方面的緣由;
B verilog與VHDL相比的優勢
C典型的verilog模塊
D verilog語法要點

A) verilog的流行,有兩方面的緣由:
1它是cadence的模擬器verilog-XL的基礎,cadence的普遍流行使得verilog在90年代深刻人心;
2它在硅谷得到普遍使用;
B) verilog與VHDL相比的優勢兩者的關係彷彿C與FORTRAN,具體而言:
1 verilog的代碼效率更高:
比較明顯的對比:
VHDL在描述一個實體時採用entity/architecture模式,
verilog在描述一個實體時只需用一個"module/edumodule"語句塊.
此外verilog的高效性還在不少地方體現出來;
2 verilog支持二進制的加減運算:
VHDL在進行二進制的加減運算時使用conv_***函數或者進行其餘的定義,總之必須通知編譯器;verilog直接用形如"c=a+b"的表示二進制的加減運算;
3綜合時可控制性好:
VHDL對信號不加區分地定義爲"signal",
而verilog區分爲register類型的和wire類型的;
可是也有人支持VHDL,認爲verilog和VHDL的關係彷彿C和C++.C)典型的verilog模塊
討論如下典型電路的verilog描述:
*與非門;
*加法器;  //即全加器
* D觸發器;
*計數器; //**分頻的counter
* latch;
*時序機;
*RAM;   //用synopsys的
*模塊引用;
*預編譯;
*與非門的verilog描述以下:
//verilog使用和C語言相同的註釋方法
module nd02(a1,a2,zn);//一個verilog模塊老是以module開始,以endmodule   結束,nd02是模塊名,a1,a2,zn是模塊的3個輸入輸出信號
input a1,a2;  //告訴編譯器a1,a2對此模塊而言是輸入,而且數據類型是"bit"
output zn;  //告訴編譯器zn對此模塊而言是輸出,數據類型也是"bit"
nand  (zn,a1,a2); //我理解nand是運算符,咱們沒必要深究verilog中的正式術語是什
麼了吧,總之這種形式表示zn=~(a1 && a2);你必定已經想到相似的運算符還有"not","and","or","nor","xor"了吧;除了"not",括號裏的信號數能夠任意,例如or (z,f,g,h)表示z=f || g || h,而且延時是3個單位時間,#x表示延時x個單位時間;
endmodule

*加法器的verilog描述以下:
module ad03d1(A,B,CI,S,CO) ;
input [2:0] A,B;  //表示A,B是輸入信號,而且是3位矢量,上界是2,下界是0
input CI;
output [2:0] S;
output CO;
assign {CO,S}=A+B+CI;//一對"{"和"}"表示連接,即將CO和S合併成4位矢量
endmodule

*帶異步清零端的D觸發器的verilog描述以下:
module dfctnb (d,cp,cdn,q,qn);
input d,cp,cdn;
output q,qn;
reg q,qn;    //關鍵字"reg"表示q和qn是"register"類型的信號;verilog中有兩種類型的信號:"register"類型和"wire"類型.你能夠簡單地把register類型的信號想象爲某個D觸發器的輸出,而wire類型的的信號是組合邏輯的輸出.兩者的最大區別在於:你能夠對register類型的信號進行定時賦值(用wait語句在特定時刻的賦值,詳見下面always語句),而對於wire類型的信號則不可.
always wait (cdn==0) //表示每當cdn=0時,將要對D觸發器清零,"always"和"wait"嵌套,"wait"和"@"是verilog的兩個關鍵字,表示一旦有某事發生;則執行下面的語句塊,"always"有點象C語言中的"if ... then...","wait"和"@"的區別:請參考本模塊.wait表示本語句塊的進程中止,直到"cdn=0"的條件出現才繼續;我理解在verilog中,每一個最外層語句塊都是一個***的進程;"@"(請看下個always語句)也表示本語句塊的進程中止,直到後面定義"posedge cp"(即出現cp的上升沿)的事件出現才繼續;也許wait和@能夠合二爲一吧,但至少到目前verilog中wait表示"條件",@表示"事件";具體運用中,wait老是用於相似"wait(xxx=1)"之類的場合,@老是用於相似"@(xxx)"或"@(posedge/negedge xxx)"之類的場合整句話的意思是"每當cdn等於0時,則做如下事情"
begin     //begin...end結構的用法相似於pascal語言
       q=0;
         qn=1;
        wait (cdn==1);
end
always @ (posedge cp)//"@(posedge cp)"中有兩個關鍵字:"@ (x)"表示"每當事件x發
生","posedge x"表示"x的上升沿,"negedge x"表示"x的降低沿",整句話的意思是"每當cp的上升沿,則做如下事情"
        if (cdn)  //若是cdn=1(意味着清零端無效)
        begin
                 q=d;
                 qn=~q;//"~"表示反相
        end
endmodule
*計數器的verilog描述以下:
module count(in,set,cp,out) ;//此計數器,在cp的上升沿將輸入賦給輸出,在cp的上升沿使輸出加一
input [15:0] in;
input set,cp;
output [15:0] out;
reg [15:0] out;
always @ (posedge set)
out = in;
always @(posedge cp)
out = out+1;  //verilog允許一個信號同時出如今等號兩端,只要它是reg類型的
endmodule

*latch的描述以下:
always @(clk or d)
    if (clk) q = d;

*時序機的verilog描述以下:
always @(posedge CLK)  //D是下一個狀態,Q是當前狀態,e1,e2是輸入,a,b是輸出
Q=D;
always @(Q or othercase) begin //當Q變化或輸入e1,e2變化時D要相應變化
D = Q; //note1
a = 0;
b = 0;
......
case(Q)
  q1:begin
   q1 action;
   if(e1)D=d1;
   if(e2)D=d2;
   else D=d3;
   a = 1; //note 2
   end
  q2:begin
   b = 1;
   ......
   end
  default:begin
   a = 0;
   b = 0;
   ......
end
end
---annotations---
note 1:
  This is a custom expression,after reset,D should be equal to Q;
note 2:
  In this state machine,a is only equal to 1 at state q1,in
  other state,a is equal to 0;
* RAM的verilog描述以下:
module ram(din,ain,dout,aout,rd,wr);//這是一個雙口RAM,分別有:輸入端:輸入地址ain;輸入數據din;上升沿有效的寫信號wr;/輸出端:輸出地址aout;輸出數據dout;高電平有效的讀信號rd;
  inout [7:0] din;
  input [7:0] ain,aout;
  input rd,wr;
  output [7:0] dout;
  reg [7:0] memory [0:255];   //請注意這是存儲陣列的描述方法,描述了一個共有2
56個字的存儲陣列,每一個字是8位
  assign dout = rd ? memory[aout] : 8'bz; //"assign"關鍵字表示並行賦值語句的
開始"?"運算符的做用和在C語言中同樣"8'bz"是一個常量,表示一個字節的高阻態,其中8表示長度是8bit,"'"是固定分割符,"b"表示後面的數據是以比特形式給出的,"z"表示高阻;舉例:4'ha表示長4bit的數"1010"。相似的還可舉出5'b10111,6'o33等等
  always @(posedge wr)
memory[ain] = din;
endmodule
*模塊引用
假設在前面(能夠是別的模塊)定義了module ram(din,ain,dout,aout,rd,wr),則引用此
模塊時只需寫
ram myram(din_in_map,ain_in_map,dout_in_map,aout_in_map,rd_in_map,wr_in_map)
;
//其中"ram"是所引用的module名,"myram"是你起的instance名,"din_in_map"等等是圖中的節點名,和器件(module)中的"din..."進行"虛實結合";
*預編譯
相似C語言,只需寫
`include "<pathname:filename>",反上撇號"`"是verilog的預編譯符,相似C中的"#".
D) verilog語法要點
*基本原則
設計時應該把你的系統劃分爲計數器,觸發器,時序機,組合邏輯等等可綜合的單元,對此不一樣的IC公司和EDA開發商可能根據本身的看法和經驗提出不一樣的要求,而且對verilog程序的細節進行本身的規定,但有一點是對的:即寫硬件描述語言不象寫C語言那樣符合語法就行.單單符合verilog語法的程序可能被拒絕綜合,甚至被拒絕模擬;
*最外層能夠寫什麼?
這裏所說的最外層是指module語句後的第一層,在這一層能夠寫這些可執行語句:
assign和nand等定義組合邏輯的語句,
always語句,
模塊引用語句,
一些以"$"開頭的系統定義語句.
特別注意不能夠寫if語句.if語句只能放在always內部.
不推薦寫wait語句,由於不能綜合.
*不能夠在多個always語句中對一個信號賦值.


1.        強烈建議用同步設計
2.在設計時老是記住時序問題
3.在一個設計開始就要考慮到地電平或高電平復位、同步或異步復位、上升沿或降低沿觸發等問題,在全部模塊中都要遵照它
4.在不一樣的狀況下用if和case,最好少用if的多層嵌套(1層或2層比較合適,當在3層以上時,最好修改寫法,由於這樣不只能夠reduce area,並且能夠得到好的timing)
5.在鎖存一個信號或總線時要當心,對於整個design,儘可能避免使用latch,由於在DFT時很難test。
6.確信全部的信號被複位,在DFT時,全部的FlipFlop都是controllable,
7.永遠不要再寫入以前讀取任何內部存儲器(如SRAM)
8.從一個時鐘到另外一個不一樣的時鐘傳輸數據時用數據緩衝,他工做像一個雙時鐘FIFO(是異步的),能夠用Async SRAM搭建Async FIFO。
9.在VHDL中二維數組能夠使用,它是很是有用的。在VERILOG中他僅僅能夠使用在測試模塊中,不能被綜合
10.遵照register-in register-out規則
11.像synopsys的DC的綜合工具是很是穩定的,任何bugs都不會從綜合工具中產生
12.確保FPGA版本與ASIC的版本儘量的類似,特別是SRAM類型,若版本一致是最理想的,可是在工做中FPGA版本通常用FPGA自帶的SRAM,ASIC版本通常用廠商提供的SRAM。
13.在嵌入式存儲器中使用BIST
14.虛單元和一些修正電路是必需的
15.一些簡單的測試電路也是須要的,常常在一個芯片中有許多測試模塊
16.除非低功耗不要用門控時鐘,強烈建議不要在design中使用gate clock
17.不要依靠腳原本保證設計。可是在腳本中的一些好的約束可以起到更好的性能(例如前向加法器)
18.若是時間充裕,經過時鐘作一個多鎖存器來取代用MUX
19.不要用內部tri-state, ASIC須要總線保持器來處理內部tri-state,如IO cell。
20.在top level中做pad insertion
21.選擇pad時要當心(如上拉能力,施密特觸發器,5伏耐壓等),選擇合適的IO cell
22.當心由時鐘誤差引發的問題
23.不要試着產生半週期信號
24.若是有不少函數要修正,請一個一個地做,修正一個函數檢查一個函數
25.在一個計算等式中排列每一個信號的位數是一個好習慣,即便綜合工具能作
26.不要使用HDL提供的除法器
27.削減沒必要要的時鐘。它會在設計和佈局中引發不少麻煩,大多數FPGA有1-4個專門的時鐘通道

良好代碼編寫風格能夠知足信、達、雅的要求。在知足功能和性能目標的前提下,加強代碼的可讀性、可移植性,首要的工做是在項目開發以前爲整個設計團隊創建一個命名約定和縮略語清單,以文檔的形式記錄下來,並要求每位設計人員在代碼編寫過程當中都要嚴格遵照。良好代碼編寫風格的通則歸納以下:  
(1) 對全部的信號名、變量名和端口名都用小寫,這樣作是爲了和業界的習慣保持一致;對常量名和用戶定義的類型用大寫;  
(2) 使用有意義的信號名、端口名、函數名和參數名;  
(3) 信號名長度不要太長;  
(4) 對於時鐘信號使用clk 做爲信號名,若是設計中存在多個時鐘,使用clk 做爲時鐘信號的前綴;  
(5) 對來自同一驅動源的信號在不一樣的子模塊中採用相同的名字,這要求在芯片整體設計時就定義好頂層子模塊間連線的名字,端口和鏈接端口的信號儘量採用相同的名字;  
(6) 對於低電平有效的信號,應該以一個下劃線跟一個小寫字母b 或n 表示。注意在同一個設計中要使用同一個小寫字母表示低電平有效;  
(7) 對於復位信號使用rst 做爲信號名,若是復位信號是低電平有效,建議使用rst_n;  
(8) 當描述多比特總線時,使用一致的定義順序,對於verilog 建議採用bus_signal[x:0]的表示;  
(9) 儘可能遵循業界已經習慣的一些約定。如*_r 表示寄存器輸出,*_a 表示異步信號,*_pn 表示多週期路徑第n 個週期使用的信號,*_nxt 表示鎖存前的信號,*_z 表示三態信號等;  
(10)在源文件、批處理文件的開始應該包含一個文件頭、文件頭通常包含的內容以下例所示:文件名,做者,模塊的實現功能概述和關鍵特性描述,文件建立和修改的記錄,包括修改時間,修改的內容等;  
(11)使用適當的註釋來解釋全部的always 進程、函數、端口定義、信號含義、變量含義或信號組、變量組的意義等。註釋應該放在它所註釋的代碼附近,要求簡明扼要,只要足夠說明設計意圖便可,避免過於複雜;  
(12)每一行語句獨立成行。儘管VHDL 和Verilog 都容許一行能夠寫多個語句,當時每一個語句獨立成行能夠增長可讀性和可維護性。同時保持每行小於或等於72 個字符,這樣作都是爲了提升代碼得可讀性;  
(13)建議採用縮進提升續行和嵌套語句得可讀性。縮進通常採用兩個空格,如西安交通大學SOC 設計中心2 若是空格太多則在深層嵌套時限制行長。同時縮進避免使用TAB 鍵,這樣能夠避免不一樣機器TAB 鍵得設置不一樣限制代碼得可移植能力;  
(14)在RTL 源碼的設計中任何元素包括端口、信號、變量、函數、任務、模塊等的命名都不能取Verilog 和VHDL 語言的關鍵字;  
(15)在進行模塊的端口申明時,每行只申明一個端口,並建議採用如下順序:  
輸入信號的clk、rst、enables other control signals、data and address signals。而後再申明輸出信號的clk、rst、enalbes other control signals、data signals;  
(16)在例化模塊時,使用名字相關的顯式映射而不要採用位置相關的映射,這樣能夠提升代碼的可讀性和方便debug 連線錯誤;  
(17)若是同一段代碼須要重複屢次,儘量使用函數,若是有可能,能夠將函數通用化,以使得它能夠複用。注意,內部函數的定義通常要添加註釋,這樣能夠提升代碼的可讀性;  
(18)儘量使用循環語句和寄存器組來提升源代碼的可讀性,這樣能夠有效地減小代碼行數;  
(19)對一些重要的always 語句塊定義一個有意義的標號,這樣有助於調試。注意標號名不要與信號名、變量名重複;  
(20)代碼編寫時的數據類型只使用IEEE 定義的標準類型,在VHDL 語言中,設計者能夠定義新的類型和子類型,可是全部這些都必須基於IEEE 的標準;  
(21)在設計中不要直接使用數字,做爲例外,能夠使用0 和1。建議採用參數定義代替直接的數字。同時,在定義常量時,若是一個常量依賴於另外一個常量,建議在定義該常量時用表達式表示出這種關係;  
(22)不要在源代碼中使用嵌入式的dc_shell 綜合命令。這是由於其餘的綜合工具並不認得這些隱含命令,從而致使錯誤的或較差的綜合結果。即便使用Design Compiler,當綜合策略改變時,嵌入式的綜合命令也不如放到批處理綜合文件中易於維護。這個規則有一個例外的綜合命令,即編譯開關的打開和關閉能夠嵌入到代碼中;  
(23)在設計中避免實例化具體的門級電路。門級電路可讀性差,且難於理解和維護,若是使用特定工藝的門電路,設計將變得不可移植。若是必須實例化門電路,咱們建議採用獨立於工藝庫的門電路,如SYNOPSYS 公司提供的GTECH 庫包含了高質量的經常使用的門級電路;  
(24)避免冗長的邏輯和子表達式;  
(25)避免採用內部三態電路,建議用多路選擇電路代替內部三態電路。

規則 #1: 創建時序邏輯模型時,採用非阻塞賦值語句。
規則 #2: 創建latch模型時,採用非阻塞賦值語句。
規則 #3: 在always塊中創建組合邏輯模型時,採用阻塞賦值語句。
規則 #4: 在一個always塊中同時有組合和時序邏輯時時,採用非阻塞賦值語句。
規則 #5: 不要在一個always塊中同時採用阻塞和非阻塞賦值語句。
規則 #6: 同一個變量不要在多個always塊中賦值。
規則 #7: 調用$strobe系統函數顯示用非阻塞賦值語句賦的值。
規則 #8: 不要使用#0延時賦值。
組合邏輯
1,敏感變量的描述完備性
Verilog中,用always塊設計組合邏輯電路時,在賦值表達式右端參與賦值的全部信號都必須在always @(敏感電平列表)中列出,always中if語句的判斷表達式必須在敏感電平列表中列出。若是在賦值表達式右端引用了敏感電平列表中沒有列出的信號,在綜合時將會爲沒有列出的信號隱含地產生一個透明鎖存器。這是由於該信號的變化不會馬上引發所賦值的變化,而必須等到敏感電平列表中的某一個信號變化時,它的做用才表現出來,即至關於存在一個透明鎖存器,把該信號的變化暫存起來,待敏感電平列表中的某一個
信號變化時再起做用,純組合邏輯電路不可能做到這一點。綜合器會發出警告。
Example1:
input a,b,c;
reg e,d;
always @(a or b or c)
    begin
    e=d&a&b; /*d沒有在敏感電平列表中,d變化時e不會馬上變化,直到a,b,c中某一個變化*/
    d=e |c;
    end
Example2:
input a,b,c;
reg e,d;
always @(a or b or c or d)
    begin
    e=d&a&b; /*d在敏感電平列表中,d變化時e馬上變化*/
    d=e |c;
    end
2,條件的描述完備性
若是if語句和case語句的條件描述不完備,也會形成沒必要要的鎖存器。
Example1:
if (a==1'b1) q=1'b1;//若是a==1'b0,q=? q將保持原值不變,生成鎖存器!
Example2:
if (a==1'b1) q=1'b1;
else         q=1'b0;//q有明確的值。不會生成鎖存器!
Example3:
   reg[1:0] a,q;
   ....
   case (a)
      2'b00 : q=2'b00;
      2'b01 : q=2'b11;//若是a==2'b10或a==2'b11,q=? q將保持原值不變,鎖存器!
   endcase
Example4:
   reg[1:0] a,q;
   ....
   case (a)
      2'b00 : q=2'b00;
      2'b01 : q=2'b11;
      default: q=2'b00;//q有明確的值。不會生成鎖存器!
   endcase
     Verilog中端口的描述
1,端口的位寬最好定義在I/O說明中,不要放在數據類型定義中;
Example1:
module test(addr,read,write,datain,dataout)
input[7:0]  datain;
input[15:0] addr;
input       read,write;
output[7:0] dataout;  //要這樣定義端口的位寬!
wire addr,read,write,datain;
reg  dataout;
Example2:
module test(addr,read,write,datain,dataout)
input  datain,addr,read,write;
output dataout;
wire[15:0] addr;
wire[7:0]  datain;
wire       read,write;
reg[7:0]   dataout;   //不要這樣定義端口的位寬!!
2,端口的I/O與數據類型的關係:
    端口的I/O           端口的數據類型
                       module內部     module外部
      input              wire          wire或reg
      output         wire或reg           wire
      inout            wire              wire
3,assign語句的左端變量必須是wire;直接用"="給變量賦值時左端變量必須是reg!
Example:
assign a=b; //a必須被定義爲wire!!
********
begin
   a=b; //a必須被定義爲reg!
end
  VHDL中STD_LOGIC_VECTOR和INTEGER的區別
例如A是INTEGER型,範圍從0到255;B是STD_LOGIC_VECTOR,定義爲8位。A累加到255時,再加1就一直保持255不變,不會自動反轉到0,除非令其爲0;而B累加到255時,再加1就會自動反轉到0。因此在使用時要特別注意!
以觸發器爲例說明描述的規範性
1,無置位/清零的時序邏輯
    always @( posedge CLK)
       begin
       Q<=D;
       end
2,有異步置位/清零的時序邏輯
  異步置位/清零是與時鐘無關的,當異步置位/清零信號到來時,觸發器的輸出當即  被置爲1或0,不須要等到時鐘沿到來才置位/清零。因此,必需要把置位/清零信號  列入always塊的事件控制表達式。
    always @( posedge CLK or negedge RESET)
       begin
       if (!RESET)
          Q=0;
       else
          Q<=D;
       end
3,有同步置位/清零的時序邏輯
   同步置位/清零是指只有在時鐘的有效跳變時刻置位/清零,才能使觸發器的輸出分   別轉換爲1或0。因此,不要把置位/清零信號列入always塊的事件控制表達式。可是   必須在always塊中首先檢查置位/清零信號的電平。
    always @( posedge CLK )
    begin
       if (!RESET)
          Q=0;
       else
          Q<=D;
       end
結構規範性
在整個芯片設計項目中,行爲設計和結構設計的編碼是最重要的一個步驟。 它對邏輯綜合和佈線結果、時序測定、校驗能力、測試能力甚至產品支持 都有重要的影響。考慮到仿真器和真實的邏輯電路之間的差別,爲了有效的
進行仿真測試:
  1,避免使用內部生成的時鐘
    內部生成的時鐘稱爲門生時鐘(gated clock)。若是外部輸入時鐘和門生時鐘同時驅動,    則不可避免的二者的步調不一致,形成邏輯混亂。並且,門生時鐘將會增長測試的難度    和時間。
  2,絕對避免使用內部生成的異步置位/清零信號
    內部生成的置位/清零信號會引發測試問題。使某些輸出信號被置位或清零,沒法正常    測試。
3,避免使用鎖存器
    鎖存器可能引發測試問題。對於測試向量自動生成(ATPG),    爲了使掃描進行,鎖存器須要置爲透明模式(transparent mode),    反過來,測試鎖存器須要構造特定的向量,這可非同通常。
  4,時序過程要有明確的復位值
    使觸發器帶有復位端,在製造測試、ATPG以及模擬初始化時,能夠對整個電路進行    快速復位。
  5,避免模塊內的三態/雙向
    內部三態信號在製造測試和邏輯綜合過程當中難於處理.

近日讀 J.Bhasker 的<verilog synthesis practical primer> , 受益不淺,理清了很多基礎電路知識 , 記下一些 tips :
1. 過程賦值(always 中觸發賦值)的變量,可能會被綜合成連線 或觸發器 或鎖存器.
2.綜合成鎖存器的規則:
a. 變量在條件語句(if 或case)中,被賦值.
b. 變量未在條件語句的全部分支中被賦值.
c. 在always語句屢次調用之間須要保持變量值 .
以上三個條件必須同時知足.
3.綜合成觸發器的規則:
變量在時鐘沿的控制下被賦值。
例外狀況:變量的賦值和引用都僅出如今一條always語句中,則該變量被視爲中
間變量而不是觸發器。
4. 對於無時鐘事情的always語句(即組合邏輯建模),其時間表應包括該alwa語
句引用的全部變量,不然會出現RTL與Netlist的不一致
芯片外部引腳不少都使用inout類型的,爲的是節省管腿。通常信號線用作總線等雙向數據傳輸的時候就要用到INOUT類型了。就是一個端口同時作輸入和輸出。 inout在具體實現上通常用三態門來實現。三態門的第三個狀態就是高阻'Z'。 當inout端口不輸出時,將三態門置高阻。這樣信號就不會由於兩端同時輸出而出錯了,更詳細的內容能夠搜索一下三態門tri-state的資料.
1 使用inout類型數據,能夠用以下寫法:
inout data_inout;
input data_in;
reg data_reg;//data_inout的映象寄存器
reg link_data;
assign data_inout=link_data?data_reg:1’bz;//link_data控制三態門
//對於data_reg,能夠經過組合邏輯或者時序邏輯根據data_in對其賦值.經過控制link_data的高低電平,從而設置data_inout是輸出數據仍是處於高阻態,若是處於高阻態,則此時看成輸入端口使用.link_data能夠經過相關電路來控制.
2 編寫測試模塊時,對於inout類型的端口,須要定義成wire類型變量,而其它輸入端口都定義成reg類型,這二者是有區別的.
當上面例子中的data_inout用做輸入時,須要賦值給data_inout,其他狀況能夠斷開.此時能夠用assign語句實現:assign data_inout=link?data_in_t:1’bz;其中的link ,data_in_t是reg類型變量,在測試模塊中賦值.
另外,能夠設置一個輸出端口觀察data_inout用做輸出的狀況:
Wire data_out;
Assign data_out_t=(!link)?data_inout:1’bz;
else,in RTL
inout use in top module(PAD)
dont use inout(tri) in sub module
也就是說,在內部模塊最好不要出現inout,若是確實須要,那麼用兩個port實現,到頂層的時候再用三態實現。理由是:在非頂層模塊用雙向口的話,該雙向口必然有它的上層跟它相連。既然是雙向口,則上層至少有一個輸入口和一個輸出口聯到該雙向口上,則發生兩個內部輸出單元鏈接到一塊兒的狀況出現,這樣在綜合時每每會出錯。
對雙向口,咱們能夠將其理解爲2個份量:一個輸入份量,一個輸出份量。另外還須要一個控制信號控制輸出份量什麼時候輸出。此時,咱們就能夠很容易地對雙向端口建模。
例子:
CODE:
module dual_port (
....
inout_pin,
....
);
inout inout_pin;
wire inout_pin;
wire input_of_inout;
wire output_of_inout;
wire out_en;
assign input_of_inout = inout_pin;
assign inout_pin = out_en ? output_of_inout : 高阻;
endmodule
可見,此時input_of_inout和output_of_inout就能夠看成普通訊號使用了。
在仿真的時候,須要注意雙向口的處理。若是是直接與另一個模塊的雙向口鏈接,那麼只要保證一個模塊在輸出的時候,另一個模塊沒有輸出(處於高阻態)就能夠了。
若是是在ModelSim中做爲單獨的模塊仿真,那麼在模塊輸出的時候,不能使用force命令將其設爲高阻態,而是使用release命令將總線釋放掉
不少初學者在寫testbench進行仿真和驗證的時候,被inout雙向口難住了。仿真器總是提示錯誤不能進行。下面是我我的對inout端口寫testbench仿真的一些總結,並舉例進行說明。在這裏先要說明一下inout口在testbench中要定義爲wire型變量。
先假設有一源代碼爲:
module xx(data_inout , ........);
inout data_inout;
........................
assign data_inout=(! link)?datareg:1'bz;
endmodule
方法一:使用相反控制信號inout口,等於兩個模塊之間用inout雙向口互連。這種方法要注意assign 語句只能放在initial和always塊內。
module test();
wire data_inout;
reg data_reg;
reg link;
initial begin
..........
end
assign data_inout=link?data_reg:1'bz;
endmodule
方法二:使用force和release語句,但這種方法不能準確反映雙向端口的信號變化,但這種方法能夠反在塊內。
module test();
wire data_inout;
reg data_reg;
reg link;
#xx;        //延時
force data_inout=1'bx;           //強制做爲輸入端口
...............
#xx;
release data_inout;          //釋放輸入端口
endmodule
不少讀者反映仿真雙向端口的時候遇到困難,這裏介紹一下雙向端口的仿真方法。一個典型的雙向端口如圖1所示。

其中inner_port與芯片內部其餘邏輯相連,outer_port爲芯片外部管腳,out_en用於控制雙向端口的方向,out_en爲1時,端口爲輸出方向,out_en爲0時,端口爲輸入方向。

用Verilog語言描述以下:
module bidirection_io(inner_port,out_en,outer_port);
input out_en;
inout[7:0] inner_port;
inout[7:0] outer_port;
assign outer_port=(out_en==1)?inner_port:8'hzz;
assign inner_port=(out_en==0)?outer_port:8'hzz;
endmodule

用VHDL語言描述雙向端口以下:
library ieee;
use IEEE.STD_LOGIC_1164.ALL;
entity bidirection_io is
port ( inner_port : inout std_logic_vector(7 downto 0);
out_en : in std_logic;
outer_port : inout std_logic_vector(7 downto 0) );
end bidirection_io;
architecture behavioral of bidirection_io is
begin
outer_port<=inner_port when out_en='1' else (OTHERS=>'Z');
inner_port<=outer_port when out_en='0' else (OTHERS=>'Z');
end behavioral;

仿真時須要驗證雙向端口能正確輸出數據,以及正確讀入數據,所以須要驅動out_en端口,當out_en端口爲1時,testbench驅動inner_port端口,而後檢查outer_port端口輸出的數據是否正確;當out_en端口爲0時,testbench驅動outer_port端口,而後檢查inner_port端口讀入的數據是否正確。因爲inner_port和outer_port端口都是雙向端口(在VHDL和Verilog語言中都用inout定義),所以驅動方法與單向端口有所不一樣。
驗證該雙向端口的testbench結構如圖2所示。

這是一個self-checking testbench,能夠自動檢查仿真結果是否正確,並在Modelsim控制檯上打印出提示信息。圖中Monitor完成信號採樣、結果自動比較的功能。
testbench的工做過程爲
1)out_en=1時,雙向端口處於輸出狀態,testbench給inner_port_tb_reg信號賦值,而後讀取outer_port_tb_wire的值,若是二者一致,雙向端口工做正常。
2)out_en=0時,雙向端口處於輸如狀態,testbench給outer_port_tb_reg信號賦值,而後讀取inner_port_tb_wire的值,若是二者一致,雙向端口工做正常。

用Verilog代碼編寫的testbench以下,其中使用了自動結果比較,隨機化激勵產生等技術。

`timescale 1ns/10ps
module tb();
reg[7:0] inner_port_tb_reg;
wire[7:0] inner_port_tb_wire;
reg[7:0] outer_port_tb_reg;
wire[7:0] outer_port_tb_wire;
reg out_en_tb;
integer i;

initial
begin
out_en_tb=0;
inner_port_tb_reg=0;
outer_port_tb_reg=0;
i=0;
repeat(20)
begin
#50
i=$random;
out_en_tb=i[0]; //randomize out_en_tb
inner_port_tb_reg=$random; //randomize data
outer_port_tb_reg=$random;
end
end

//**** drive the ports connecting to bidirction_io
assign inner_port_tb_wire=(out_en_tb==1)?inner_port_tb_reg:8'hzz;
assign outer_port_tb_wire=(out_en_tb==0)?outer_port_tb_reg:8'hzz;

//instatiate the bidirction_io module
bidirection_io bidirection_io_inst(.inner_port(inner_port_tb_wire),
.out_en(out_en_tb),
.outer_port(outer_port_tb_wire));

//***** monitor ******
always@(out_en_tb,inner_port_tb_wire,outer_port_tb_wire)
begin
#1;
if(outer_port_tb_wire===inner_port_tb_wire)
begin
$display("\n **** time=%t ****",$time);
$display("OK! out_en=%d",out_en_tb);
$display("OK! outer_port_tb_wire=%d,inner_port_tb_wire=%d",
outer_port_tb_wire,inner_port_tb_wire);
end
else
begin
$display("\n **** time=%t ****",$time);
$display("ERROR! out_en=%d",out_en_tb);
$display("ERROR! outer_port_tb_wire != inner_port_tb_wire" );
$display("ERROR! outer_port_tb_wire=%d, inner_port_tb_wire=%d",
outer_port_tb_wire,inner_port_tb_wire);
end
end
endmodule

今天從新回顧了一下阻塞賦值和非阻塞賦值的概念,感受又有所收穫。 
1、特色:
    阻塞賦值:一、RHS的表達式計算和LHS的賦值更新,這兩個動做之間不能插入其餘動做,即所謂計算完畢,當即更新。
             二、所謂阻塞就是指在一個「begin...end」塊中的多個阻塞賦值語句內,只有上一句徹底執行完畢後,纔會執行下一語句,不然阻塞程序的執行。
    非阻塞賦值:RHS的表達式計算和LHS的賦值更新分兩個節拍執行,首先,應該是RHS的表達式計算,獲得新值後並不當即賦值,而是放在事件隊列中等待,直到
              當前仿真時刻的後期才執行(緣由下文會提到)。
2、Verilog的分層事件隊列:
    在Verilog中,事件隊列能夠劃分爲5個不一樣的區域,不一樣的事件根據規定放在不一樣的區域內,按照優先級的高低決定執行的前後順序,下表就列出了部分Verilog分層事件隊列。其中,活躍事件的優先級最高(最早執行),而監控事件的優先級最低,並且在活躍事件中的各事件的執行順序是隨機的(注:爲方便起見,在通常的仿真器中,對同一區域的不一樣事件是按照調度的前後關係執行的)。
   

當前仿真
時間事件

活躍事件         阻塞賦值,非阻塞賦值的RHS計算……

非活躍事件

顯式0延時的阻塞賦值……




非阻塞賦值更新事件

由非阻塞語句產生的一個非阻塞賦值更新事件,並被調入當前仿真時刻。



監控事件

$monitor和$strobe等系統任務


未來仿真
時間事件                  被調度到未來仿真時間的事件

3、結論:
    由上表就能夠知道,阻塞賦值屬於活躍事件,會馬上執行,這就是阻塞賦值「計算完畢,馬上更新」的緣由。此外,因爲在分層事件隊列中,只有將活躍事件中排在前面的事件調出,並執行完畢後,纔可以執行下面的事件,這就能夠解釋阻塞賦值的第二個特色。
    一樣是由上表知,非阻塞賦值的RHS計算屬於活躍事件,而非阻塞賦值更新事件排在非活躍事件以後,所以只有仿真隊列中全部的活躍事件和非活躍事件都執行完畢後,才輪到非阻塞賦值更新事件,這就是非阻塞賦值必須分兩拍完成的緣由。

以上就是我今天的讀書筆記,寫得倉促,若有不對,敬請指出 。


一. 強調Verilog代碼編寫風格的必要性。
強調Verilog代碼編寫規範,常常是一個不太受歡迎的話題,但倒是很是有必要的。
每一個代碼編寫者都有本身的編寫習慣,並且都喜歡按照本身的習慣去編寫代碼。與本身編寫風格相近的代碼,閱讀起來容易接受和理解。相反和本身編寫風格差異較大的代碼,閱讀和接受起來就困難一些。
曾有編程大師總結說,一個優秀的程序員,能維護的代碼長度大約在1萬行數量級。代碼的整潔程度,很大程度上影響着代碼的維護難度。
遵循代碼編寫規範書寫的代碼,很容易閱讀、理解、維護、修改、跟蹤調試、整理文檔。相反代碼編寫風格隨意的代碼,一般晦澀、凌亂,會給開發者本人的調試、修改工做帶來困難,也會給合做者帶來很大麻煩。
(實際上英文Coding Style有另外一層涵義,更偏重的是,某一個電路,用那一種形式的語言描述,才能將電路描述得更準確,綜合之後產生的電路更合理。本文更偏重的是,編寫Verilog代碼時的書寫習慣。)

二. 強調編寫規範的宗旨。
縮小篇幅
提升整潔度
便於跟蹤、分析、調試
加強可讀性,幫助閱讀者理解
便於整理文檔
便於交流合做
三. 變量及信號命名規範。
1. 系統級信號的命名。
系統級信號指復位信號,置位信號,時鐘信號等須要輸送到各個模塊的全局信號;系統信號以字符串Sys開頭。
2. 低電平有效的信號後一概加下劃線和字母n。如:SysRst_n;FifoFull_n;
3. 通過鎖存器鎖存後的信號,後加下劃線和字母r,與鎖存前的信號區別。如CpuRamRd信號,經鎖存後應命名爲CpuRamRd_r。
低電平有效的信號通過鎖存器鎖存後,其命名應在_n後加r。如CpuRamRd_n信號,經鎖存後應命名爲CpuRamRd_nr
多級鎖存的信號,可多加r以標明。如CpuRamRd信號,經兩級觸發器鎖存後,應命名爲CpuRamRd_rr。
4. 模塊的命名。
在系統設計階段應該爲每一個模塊進行命名。命名的方法是,將模塊英文名稱的各個單詞首字母組合起來,造成3到5個字符的縮寫。若模塊的英文名只有一個單詞,可取該單詞的前3個字母。各模塊的命名以3個字母爲宜。例如:
Arithmatic Logical Unit模塊,命名爲ALU。
Data Memory Interface模塊,命名爲DMI。
Decoder模塊,命名爲DEC。

5. 模塊之間的接口信號的命名。
全部變量命名分爲兩個部分,第一部分代表數據方向,其中數據發出方在前,數據接收方在後,第二部分爲數據名稱。
兩部分之間用下劃線隔離開。
第一部分所有大寫,第二部分全部具備明確意義的英文名所有拼寫或縮寫的第一個字母大寫,其他部分小寫。
舉例:CPUMMU_WrReq,下劃線左邊是第一部分,表明數據方向是從CPU模塊發向存儲器管理單元模塊(MMU)。下劃線右邊Wr爲Write的縮寫,Req是Request的縮寫。兩個縮寫的第一個字母都大寫,便於理解。整個變量連起來的意思就是CPU發送給MMU的寫請求信號。
模塊上下層次間信號的命名也遵循本規定。
若某個信號從一個模塊傳遞到多個模塊,其命名應視信號的主要路徑而定。
6. 模塊內部信號:
模塊內部的信號由幾個單詞鏈接而成,縮寫要求能基本代表本單詞的含義;
單詞除經常使用的縮寫方法外(如:Clock->Clk, Write->Wr, Read->Rd等),一概取該單詞的前幾個字母( 如:Frequency->Freq, Variable->Var 等);
每一個縮寫單詞的第一個字母大寫;
若遇兩個大寫字母相鄰,中間添加一個下劃線(如DivN_Cntr);
舉例:SdramWrEn_n;FlashAddrLatchEn;
四. 編碼格式規範。
1. 分節書寫,各節之間加1到多行空格。如每一個always,initial語句都是一節。每節基本上完成一個特定的功能,即用於描述某幾個信號的產生。在每節以前有幾行註釋對該節代碼加以描述,至少列出本節中描述的信號的含義。
2. 行首不要使用空格來對齊,而是用Tab鍵,Tab鍵的寬度設爲4個字符寬度。行尾不要有多餘的空格。
3. 註釋。
使用//進行的註釋行以分號結束;
使用/* */進行的註釋,/*和*/各佔用一行,而且頂頭;
例:
// Edge detector used to synchronize the input signal;
4. 空格的使用:
不一樣變量,以及變量與符號、變量與括號之間都應當保留一個空格。
Verilog關鍵字與其它任何字符串之間都應當保留一個空格。如:
Always @ (……)
使用大括號和小括號時,前括號的後邊和後括號的前邊應當留有一個空格。
邏輯運算符、算術運算符、比較運算符等運算符的兩側各留一個空格,與變量分隔開來;單操做數運算符例外,直接位於操做數前,不使用空格。
使用//進行的註釋,在//後應當有一個空格;註釋行的末尾不要有多餘的空格。
例:
assign SramAddrBus = { AddrBus[31:24], AddrBus[7:0] };
assign DivCntr[3:0] = DivCntr[3:0] + 4’b0001;
assign Result = ~Operand;

5. 同一個層次的全部語句左端對齊;Initial、always等語句塊的begin關鍵詞跟在本行的末尾,相應的end關鍵詞與Initial、always對齊;這樣作的好處是避免因begin獨佔一行而形成行數太多;
例:
always @ ( posedge SysClk or negedge SysRst ) begin
if( !SysRst ) DataOut <= 4'b0000;
else if( LdEn ) begin
DataOut <= DataIn;
End
else DataOut <= DataOut + 4'b0001;
end
6. 不一樣層次之間的語句使用Tab鍵進行縮進,每加深一層縮進一個Tab;
8. 在endmodule,endtask,endcase等標記一個代碼塊結束的關鍵詞後面要加上一行註釋說明這個代碼塊的名稱;
9. 在task名稱前加tsk以示標記。在function的名稱前加func以示標記。例如:
task tskResetSystem;
……
endtask //of tskResetSystem
五.小結:
以上列出的代碼編寫規範沒法覆蓋代碼編寫的方方面面,還有不少細節問題,須要在實際編寫過程當中加以考慮。而且有些規定也不是絕對的,須要靈活處理。並非律條,可是在一個項目組內部、一個項目的進程中,應該有一套相似的代碼編寫規範來做爲約束。
總的方向是,努力寫整潔、可讀性好的代碼


二.reg型
在「always」塊內被賦值的每個信號都必須定義成reg型。
reg型數據的缺省初始值是不定值。
reg型只表示被定義的信號將用在「always」塊內,理解這一點很重要。並非說reg型信號必定是寄存器或觸發器的輸出。雖然reg型信號經常是寄存器或觸發器的輸出,但並不必定老是這樣。

三.memory型
memory型數據是經過擴展reg型數據的地址範圍來生成的。其格式以下:

reg [n-1:0] 存儲器名[m-1:0];
或        reg [n-1:0] 存儲器名[m:1];

在這裏,reg[n-1:0]定義了存儲器中每個存儲單元的大小,即該存儲單元是一個n位的寄存器。存儲器名後的[m-1:0]或[m:1]則定義了該存儲器中有多少個這樣的寄存器。
reg [7:0] mema[255:0];

這個例子定義了一個名爲mema的存儲器,該存儲器有256個8位的存儲器。該存儲器的地址範圍是0到255。注意:對存儲器進行地址索引的表達式必須是常數表達式。
儘管memory型數據和reg型數據的定義格式很類似,但要注意其不一樣之處。如一個由n個1位寄存器構成的存儲器組是不一樣於一個n位的寄存器的。見下例:

reg [n-1:0] rega;    //一個n位的寄存器
reg mema [n-1:0];    //一個由n個1位寄存器構成的存儲器組

一個n位的寄存器能夠在一條賦值語句裏進行賦值,而一個完整的存儲器則不行。見下例:

rega =0;        //合法賦值語句
mema =0;        //非法賦值語句

若是想對memory中的存儲單元進行讀寫操做,必須指定該單元在存儲器中的地址。下面的寫法是正確的。
mema[3]=0;        //給memory中的第3個存儲單元賦值爲0。
3.3.1.基本的算術運算符

在Verilog HDL語言中,算術運算符又稱爲二進制運算符,共有下面幾種:
1)      + (加法運算符,或正值運算符,如 rega+regb,+3)
2)      -(減法運算符,或負值運算符,如 rega-3,-3)
3)      ×(乘法運算符,如rega*3)
4)      / (除法運算符,如5/3)
5)      % (模運算符,或稱爲求餘運算符,要求%兩側均爲整型數據。如7%3的值爲1)

注意:        在進行算術運算操做時,若是某一個操做數有不肯定的值x,則整個結果也爲不定值x。
1)      ~          //取反
2)      &          //按位與
3)      |          //按位或
4)      ^          //按位異或
5)      ^~         //按位同或(異或非)
在Verilog HDL語言中存在三種邏輯運算符:
1)      && 邏輯與
2)      || 邏輯或
3)      ! 邏輯非

關係運算符共有如下四種:

a < b       a小於b
a > b       a大於b
a <= b      a小於或等於b
a >= b      a大於或等於b

3.3.5.等式運算符
3.3.6.移位運算符
3.3.7.位拼接運算符(Concatation)
3.3.10.關鍵詞
在Verilog HDL中,全部的關鍵詞是事先定義好的確認符,用來組織語言結構。關鍵詞是用小寫字母定義的,所以在編寫原程序時要注意關鍵詞的書寫,以免出錯。下面是Verilog HDL中使用的關鍵詞(請參閱附錄:Verilog語言參考手冊):

always, and, assign,begin,buf,bufif0,bufif1,case,casex,casez,cmos,deassign,default,defparam,disable,edge,else,end,endcase,endmodule,endfunction,endprimitive, endspecify, endtable, endtask, event, for, force, forever, fork, function,highz0, highz1, if,initial, inout, input,integer,join,large,macromodule,medium,module,nand,negedge,nmos,nor,not,notif0,notifl, or, output, parameter, pmos, posedge, primitive, pull0, pull1, pullup, pulldown, rcmos, reg, releses, repeat, mmos, rpmos, rtran, rtranif0,rtranif1,scalared,small,specify,specparam,strength,strong0, strong1, supply0, supply1, table, task, time, tran, tranif0, tranif1, tri, tri0, tri1, triand, trior, trireg,vectored,wait,wand,weak0,weak1,while, wire,wor, xnor, xor



(1).非阻塞(Non_Blocking)賦值方式( 如 b <= a; )
1)      塊結束後才完成賦值操做。
2)      b的值並非馬上就改變的。
3)      這是一種比較經常使用的賦值方法。(特別在編寫可綜合模塊時)

(2).阻塞(Blocking)賦值方式( 如 b = a; )
1)      賦值語句執行完後,塊才結束。
2)      b的值在賦值語句執行完後馬上就改變的。
3)      可能會產生意想不到的結果。

一.順序塊
順序塊有如下特色:
1)      塊內的語句是按順序執行的,即只有上面一條語句執行完後下面的語句才能執行。
2)      每條語句的延遲時間是相對於前一條語句的仿真時間而言的。
3)      直到最後一條語句執行完,程序流程控制才跳出該語句塊。
順序塊的格式以下:
begin
語句1;
語句2;
......
語句n;
end
其中:
        塊名即該塊的名字,一個標識名。其做用後面再詳細介紹。
        塊內聲明語句能夠是參數聲明語句、reg型變量聲明語句、integer型變量聲明語句、real型變量聲明語句。

二. 並行塊
並行塊有如下四個特色:
1)      塊內語句是同時執行的,即程序流程控制一進入到該並行塊,塊內語句則開始同時並行地執行。
2)      塊內每條語句的延遲時間是相對於程序流程控制進入到塊內時的仿真時間的。
3)      延遲時間是用來給賦值語句提供執行時序的。
4)      當按時間時序排序在最後的語句執行完後或一個disable語句執行時,程序流程控制跳出該程序塊。

並行塊的格式以下:
fork
語句1;
語句2;
.......
語句n;
join
其中:
•        塊名即標識該塊的一個名字,至關於一個標識符。
•        塊內說明語句能夠是參數說明語句、reg型變量聲明語句、integer型變量聲明語句、real型變量聲明語句、time型變量聲明語句、事件(event)說明語句。

在fork_join塊內,各條語句沒必要按順序給出,所以在並行塊裏,各條語句在前仍是在後是可有可無的。見下例:

三. 塊名
在VerilgHDL語言中,能夠給每一個塊取一個名字,只需將名字加在關鍵詞begin或fork後面便可。這樣作的緣由有如下幾點。
1)      這樣能夠在塊內定義局部變量,即只在塊內使用的變量。
2)      這樣能夠容許塊被其它語句調用,如被disable語句。
3)      在Verilog語言裏,全部的變量都是靜態的,即全部的變量都只有一個惟一的存儲地址,所以進入或跳出塊並不影響存儲在變量內的值。
基於以上緣由,塊名就提供了一個在任何仿真時刻確認變量值的方法。





casez語句用來處理不考慮高阻值z的比較過程,casex語句則將高阻值z和不定值都視爲沒必要關心的狀況。

若是用到if語句,最好寫上else項。若是用case語句,最好寫上default項。遵循上面兩條原則,就能夠避免發生這種錯誤,使設計者更加明確設計目標,同時也加強了Verilog程序的可讀性。
3.6.循環語句

在Verilog HDL中存在着四種類型的循環語句,用來控制執行語句的執行次數。

1)    forever        連續的執行語句。
2)    repeat        連續執行一條語句 n 次。
3)    while        執行一條語句直到某個條件不知足。若是一開始條件即不知足(爲假),
             則語句一次也不能被執行。
4)    for經過如下三個步驟來決定語句的循環執行。
a)      先給控制循環次數的變量賦初值。
b)      斷定控制循環的表達式的值,如爲假則跳出循環語句,如爲真則執行指定的語句後,轉到第三步。
c) 


#1:當爲時序邏輯建模,使用「非阻塞賦值」。
#2:當爲鎖存器(latch)建模,使用「非阻塞賦值」。
#3:當用always塊爲組合邏輯建模,使用「阻塞賦值」
#4:當在同一個always塊裏面既爲組合邏輯又爲時序邏輯建模,使用「非阻塞賦值」。
#5:不要在同一個always塊裏面混合使用「阻塞賦值」和「非阻塞賦值」。
#6:不要在兩個或兩個以上always塊裏面對同一個變量進行賦值。
#7:使用$strobe以顯示已被「非阻塞賦值」的值。
#8:不要使用#0延遲的賦值。
#9:在VERILOG語法中, if...else if ... else 語句是有優先級的,通常說來第一個IF的優先級最高,最後一個ELSE的優先級最低。若是描述一個編碼器,在XILINX的XST綜合參數就有一個關於優先級編碼器硬件原語句的選項Priority Encoder Extraction. 而CASE語句是"平行"的結構,全部的CASE的條件和執行都沒有「優先級」。而創建優先級結構會消耗大量的組合邏輯,因此若是可以使用CASE語句的地方,儘可能使用CASE替換IF...ELSE結構。
#10:XILINX的底層可編程硬件資源叫SLICE,由2個FF和2個LUT組成。 FF觸發器  LUT查找表
ALTERA的底層可編程硬件資源叫LE,  由1個FF和1個LUT組成。
#11:慎用鎖存器(latch),同步時序設計要儘可能避免使用鎖存器,綜合出非目的性latch的主要緣由在於不徹底的條件判斷句。另一種狀況是設計中有組合邏輯的反饋環路(combinatorial feedback loops)。
#12:狀態機的通常設計原則,Biary, gray-code 編碼使用最少的觸發器,較多的組合邏輯。而one-hot編碼反之。因此CPLD多使用GRAY-CODE, 而FPGA多使用ONE-HOT編碼。另外一方面,小型設計使用GRAY-CODE和BINARY編碼更有效,而大型狀態機使用ONE-HOT更有效。
#13:業界主流CPLD產品是lattice的LC4000系列和ALTERA的MAX3000系列。
#14:復位使初始狀態可預測,防止出現禁用狀態。FPGA 和CPLD 的復位信號採用異步低電平有效信號,鏈接到其全局復位輸入端,使用專用路徑通道,復位信號必須鏈接到FPGA 和CPLD 的全局復位管腳。。
#15:不要用時鐘或復位信號做數據或使能信號,也不能用數據信號做爲時鐘或復位信號,不然HDL 綜合時會出現時序驗證問題。信號穿過期鐘的兩半個週期時,要在先後分別取樣;防止出現半穩定狀態。
#16:fpga設計中 不要使用門時鐘(don't use gated clock)。時鐘信號必須鏈接到全局時鐘管腳上。
#17:不要使用內部三態信號,不然增長功耗。
#18:只使用同步設計,不要使用延時單元。
#19:避免使用負延觸發的雙穩態多諧振盪器(flip flop)。
#20:不要使用信號和變量的默認值(或初始值),用復位脈衝初始化
信號和變量。
#21:不要在代碼中使用buffer 類型的端口讀取輸出數據;要使用out 類型,再增長另外變量或信號,以獲取輸出值。
這是由於buffer 類型的端口不能鏈接到其餘類型的端口上,所以buffer 類型就會在整個設計的端口中傳播下去。
#22:對變量要先讀後寫;若是先寫後讀,就會產生長的組合邏輯和鎖存器(或寄存器)。這是由於變量值是當即獲取的。
#23:在組合邏輯進程中,其敏感向量標中要包含全部要讀取得信號;這是爲了防止出現沒必要要的鎖存器。

近期,在stephen Brown的一本書數字邏輯基礎與verilog設計一書中看到關於觸發器電路的時序分析。之前一直沒有搞明白這個問題,如今以爲豁然開朗。怕忘記了,特意摘抄與此與edacn網友分享。
觸發器電路的時序分析:
圖7-84給出了一個使用D觸發器的簡單電路。咱們想要計算該電路能正常工做的最大的時鐘頻率Fmax,而且想肯定該電路的保持時間是否不夠長。在技術文獻中,這種類型的電路分析一般叫作時序分析。假設該觸發器的時序參數爲:tsu=0.6ns,th=0.4ns,0.8ns<=tcQ<=1.0ns。給tcq參數規定一個範圍是由於延遲參數分佈在必定範圍內,這樣處理是現成集成電路芯片經常使用的方法。爲了計算最小的時鐘信號週期Tmin=1/Fmax,咱們必須考慮在觸發器中從開始到結束的全部路徑。在這個簡單的電路中,只有一條這樣的路徑,這條路徑開始於數據被時鐘信號的正跳變沿加載進入觸發器,通過tcQ的延遲後傳播到Q的輸出端,再傳播經過非門,同時必須知足D輸入端的創建時間要求。所以:
Tmin=tcQ+tNOT+tsu
因爲咱們關注的只是計算出最長的延遲時間,因此應該用tcQ的最大值。爲了計算出tNOT,咱們將假設經過任何邏輯門的延遲均可以用1+0.1k進行計算,其中k是該門的輸入信號的個數。對非門而言,k=1,所以獲得以下Tmin和Fmax的值:Tmin=1.0+1.1+0.6=2.7ns
Fmax=1/2.7ns=370.37MHz
固然,有必要檢查電路中的保持時間是否違反規定。在這種場合,咱們必須覈查從時鐘信號的正跳變沿到D輸入值改變的最短延遲。該延遲由tcQ+tNOT=0.8+1.1=1.9ns給定。由於1.9ns>0.4ns,因此保持時間夠長,沒有違反規定。再舉一個觸發器電路時序分析的例子,請考慮圖7-85所示的計數器電路。假設所用的觸發器的時序參數與圖7-84中用過的觸發器相同,請計算該電路能正常運行的最高頻率。再次假設經過邏輯門的傳播延遲能夠用1+0.1k來計算。
在這個電路中,存在着四個觸發器從開始到結束的許多路徑。最長的路徑從觸發器Q0起到觸發器Q3結束。在某個電路中最長的路徑成爲關鍵路徑。關鍵路徑的延遲包括觸發器Q0的時鐘信號到Q的延遲、經過三個與門的傳播延遲和一個異或門的延遲。咱們還必須考慮觸發器Q3的創建時間。所以,獲得
Tmin=tcQ+3(tAND)+tXOR+tsu
用tcQ的最大值,獲得
Tmin=1.0+3(1.2)+1.2+0.6=6.4ns
Fmax=1/6.4ns=156.25MHz
該電路的最短路徑是從每一個觸發器經過異或門反饋到該觸發器自己的輸入端。沿每一個這樣路徑的最小延遲爲tcQ+tXOR=0.8+1.2=2.0ns。由於2.0ns>th=0.4ns,所以保持時間足夠長,沒有違反規定。
       在上面的分析中,假設時鐘信號同時到達全部四個觸發器。咱們如今將重複這個分析,假設時鐘信號同時到達觸發器Q0,Q1,Q2,但到達觸發器Q3有一些延遲。始終到達不一樣的觸發器之間的時間差稱爲時鐘誤差(clock skew),記做tskew,時鐘誤差能夠由許多緣由引發。
       在圖7-85中,電路的關鍵路徑是從觸發器Q0起到觸發器Q3。然而,Q3的時鐘誤差使得這個延遲減小,由於時鐘誤差在數據被加載進該觸發器前提供了附加的時間。若是考慮增長1.5ns的時鐘誤差,則從觸發器Q0到觸發器Q3的路徑延遲由tcQ+3(tAND)+tXOR+tsu-tskew=6.4-1.5ns=4.9ns給定。該電路如今還存在一個不一樣的關鍵路徑,該路徑從觸發器Q0起到觸發器Q2結束。這條路徑的延遲爲
Tmin=tcQ+2(tAND)+tXOR+tsu=1.0+2(1.2)+1.2+0.6ns=5.2ns
Fmax=1/5.2ns=192.31MHz
       在這種場合,時鐘誤差致使電路的最高時鐘頻率提升。可是,若是時鐘誤差是負的,即觸發器Q3的時鐘到達時間比其餘觸發器更早一些,則會形成該電路的最高時鐘頻率Fmax下降。
       由於數據加載到觸發器Q3被時鐘誤差延遲了,因此對全部起始於Q0,Q1,Q2而以Q3爲結束點的路徑,都會產生使觸發器Q3的保持時間須要增長到th+tskew的影響。在該電路中,這種最短的路徑是從觸發器Q2到Q3的路徑,其延遲時間爲TcQ+tAND+tXOR=0.8+1.2+1.2=3.2ns。由於3.2ns>th+tskew=1.9ns,因此保持時間足夠長,沒有違反規定。
若是對時鐘誤差值tskew>=3.2-th=2.8ns,重複以上保持時間的分析,則會出現保持時間不夠的狀況。當tskew>=2.8ns時,該電路將不可能在任何頻率下可靠地運行。因爲時鐘誤差的存在會引發電路時序問題,因此好的電路設計方法必須保證時鐘信號到達全部觸發器的誤差儘量小。
最後是個人總結:肯定最小週期是找關鍵路徑即最長路徑。肯定Th是否違例是找最短路徑。最短路徑要大於Th。若是有Tskew的狀況則要大於Th+Tskew(有skew的寄存器爲最短路徑的終點的時候)
還有就是我對有Tskew的狀況的時候爲何防止違例要最短路徑>Th+Tskew。由於Q0,Q1和Q2時鐘比Q3早,以他們爲起點的路徑已經開始走了一段時間後Q3的時鐘纔到纔開始打入數據。因此保持時間上要加上這段skew

ISE 約束文件的基本操做
1.約束文件的概念

FPGA設計中的約束文件有3類:用戶設計文件(.UCF文件)、網表約束文件(.NCF文件)以及物理約束文件(.PCF文件),能夠完成時序約束、管腳約束以及區域約束。3類約束文件的關係爲:用戶在設計輸入階段編寫UCF文件,而後UCF文件和設計綜合後生成NCF文件,最後再通過實現後生成PCF 文件。本節主要介紹UCF文件的使用方法。

UCF文件是ASC 2碼文件,描述了邏輯設計的約束,能夠用文本編輯器和Xilinx約束文件編輯器進行編輯。NCF約束文件的語法和UCF文件相同,兩者的區別在於: UCF文件由用戶輸入,NCF文件由綜合工具自動生成,當兩者發生衝突時,以UCF文件爲準,這是由於UCF的優先級最高。PCF文件能夠分爲兩個部分:一部分是映射產生的物理約束,另外一部分是用戶輸入的約束,一樣用戶約束輸入的優先級最高。通常狀況下,用戶約束都應在UCF文件中完成,不建議直接修改 NCF文件和PCF文件。

2.建立約束文件

約束文件的後綴是.ucf,因此通常也被稱爲UCF文件。建立約束文件有兩種方法,一種是經過新建方式,另外一種則是利用過程管理器來完成。

第一種方法:新建一個源文件,在代碼類型中選取「Implementation Constrains File」,在「File Name」中輸入「one2two_ucf」。單擊「Next」按鍵進入模塊選擇對話框,選擇模塊「one2two」,而後單擊「Next」進入下一頁,再單擊「Finish」按鍵完成約束文件的建立。

第二種方法:在工程管理區中,將「Source for」設置爲「Synthesis/Implementation」。「Constrains Editor」是一個專用的約束文件編輯器,雙擊過程管理區中「User Constrains」下的「Create Timing Constrains」就能夠打開「Constrains Editor」,其界面如圖所示:


圖 啓動Constrains Editor引腳約束編輯

在「Ports」選項卡中能夠看到,全部的端口都已經羅列出來了,若是要修改端口和FPGA管腳的對應關係,只須要在每一個端口的「Location」列中填入管腳的編號便可。例如在UCF文件中描述管腳分配的語法爲:

        NET 「端口名稱」 LOC = 引腳編號;

須要注意的是,UCF文件是大小敏感的,端口名稱必須和源代碼中的名字一致,且端口名字不能和關鍵字同樣。可是關鍵字NET是不區分大小寫的。

3.編輯約束文件

在工程管理區中,將「Source for」設置爲「Synthesis/Implementation」,而後雙擊過程管理區中「User Constrains」下的「Edit Constraints (Text)」就能夠打開約束文件編輯器,以下圖所示,就會新建當前工程的約束文件。
  

圖 用戶約束管理窗口

UCF文件的語法說明
1.語法 
        UCF文件的語法爲:
{NET|INST|PIN} "signal_name" Attribute;
其中,「signal_name」是指所約束對象的名字,包含了對象所在層次的描述;「Attribute」爲約束的具體描述;語句必須以分號「;」結束。能夠用「#」或「/* */」添加註釋。須要注意的是:UCF文件是大小寫敏感的,信號名必須和設計中保持大小寫一致,但約束的關鍵字能夠是大寫、小寫甚至大小寫混合。例如:
NET "CLK" LOC = P30;
「CLK」就是所約束信號名,LOC = P30;是約束具體的含義,將CLK信號分配到FPGA的P30管腳上。

對於全部的約束文件,使用與約束關鍵字或設計環境保留字相同的信號名會產生錯誤信息,除非將其用" "括起來,所以在輸入約束文件時,最好用" "將全部的信號名括起來。

2.通配符
在UCF文件中,通配符指的是「*」和「?」。「*」能夠表明任何字符串以及空,「?」則表明一個字符。在編輯約束文件時,使用通配符能夠快速選擇一組信號,固然這些信號都要包含部分共有的字符串。例如:
NET "*CLK?" FAST;
將包含「CLK」字符並以一個字符結尾的全部信號,並提升了其速率。
在位置約束中,能夠在行號和列號中使用通配符。例如:
INST "/CLK_logic/*" LOC = CLB_r*c7;
把CLK_logic層次中全部的實例放在第7列的CLB中。

3.定義設計層次
       在UCF文件中,經過通配符*能夠指定信號的設計層次。其語法規則爲:
* 遍歷全部層次
Level1/* 遍歷level1及如下層次中的模塊
Level1/*/ 遍歷level1種的模塊,但不遍歷更低層的模塊

例4-5 根據圖4-75所示的結構,使用通配符遍歷表4-3所要求的各個模塊。


圖 層次模塊示意圖
表 要求遍歷的符號列表

管腳和區域約束語法

LOC約束是FPGA設計中最基本的佈局約束和綜合約束,可以定義基本設計單元在FPGA芯片中的位置,可實現絕對定位、範圍定位以及區域定位。此外, LOC還能將一組基本單元約束在特定區域之中。LOC語句既能夠書寫在約束文件中,也能夠直接添加到設計文件中。換句話說,ISE中的FPGA底層工具編輯器(FPGA Editor)、佈局規劃器(Floorplanner)和引腳和區域約束編輯器的主要功能均可以經過LOC語句完成。 
•         LOC語句語法
INST "instance_name " LOC = location;

其中「location」能夠是FPGA芯片中任一或多個合法位置。若是爲多個定位,須要用逗號「,」隔開,以下所示:
LOC = location1,location2,...,locationx;
目前,還不支持將多個邏輯置於同一位置以及將多個邏輯至於多個位置上。須要說明的是,多位置約束並非將設計定位到全部的位置上,而是在佈局佈線過程當中,佈局器任意挑選其中的一個做爲最終的佈局位置。

範圍定位的語法爲:
INST 「instance_name」 LOC=location:location [SOFT];

經常使用的LOC定位語句如表4-4所列。
表 經常使用的LOC定位語句

使用LOC完成端口定義時,其語法以下:
NET "Top_Module_PORT" LOC = "Chip_Port";

其中,「Top_Module_PORT」爲用戶設計中頂層模塊的信號端口,「Chip_Port」爲FPGA芯片的管腳名。

LOC語句中是存在優先級的,當同時指定LOC端口和其端口連線時,對其連線約束的優先級是最高的。例如,在圖4-76中,LOC=11的優先級高於LOC=38。


圖 LOC優先級示意圖
2.LOC屬性說明

LOC語句經過加載不一樣的屬性能夠約束管腳位置、CLB、Slice、TBUF、塊RAM、硬核乘法器、全局時鐘、數字鎖相環(DLL)以及DCM模塊等資源,基本涵蓋了FPGA芯片中全部類型的資源。因而可知,LOC語句功能十分強大,表4-5列出了LOC的經常使用屬性。
表 LOC語句經常使用屬性列表



Verilog HDL代碼描述對狀態機綜合的研究
2007-11-25 16:59
1 引言
    Verilog HDL做爲當今國際主流的HDL語言,在芯片的前端設計中有着普遍的應用。它的語法豐富,成功地應用於設計的各個階段:建模、仿真、驗證和綜合等。可綜合是指綜合工具能將Verilog HDL代碼轉換成標準的門級結構網表,所以代碼的描述必須符合必定的規則。大部分數字系統均可以分爲控制單元和數據單元兩個部分,控制單元的主體是一個狀態機,它接收外部信號以及數據單元產生的狀態信息,產生控制信號,於是狀態機性能的好壞對系統性能有很大的影響。
    有許多可綜合狀態機的Verilog代碼描述風格,不一樣代碼描述風格經綜合後獲得電路的物理實如今速度和麪積上有很大差異。優秀的代碼描述應當易於修改、易於編寫和理解,有助於仿真和調試,並能生成高效的綜合結果。
2 有限狀態機
    有限狀態機(Finite State Machine,FSM)在數字系統設計中應用十分普遍。根據狀態機的輸出是否與輸入有關,可將狀態機分爲兩大類:摩爾(Moore)型狀態機和米莉(Mealy)型狀態機。Moore型狀態機的輸出僅與現態有關;Mealy型狀態機的輸出不只與現態有關,並且和輸入也有關。圖1是有限狀態機的通常結構圖,它主要包括三個部分,其中組合邏輯部分包括狀態譯碼器和輸出譯碼器,狀態譯碼器肯定狀態機的下一個狀態,輸出譯碼器肯定狀態機的輸出,狀態寄存器屬於時序邏輯部分,用來存儲狀態機的內部狀態。

圖1 狀態機的結構框圖
2.1 好的狀態機標準
    好的狀態機的標準不少,最重要的幾個方面以下:
第一,狀態機要安全,是指FSM不會進入死循環,特別是不會進入非預知的狀態,並且因爲某些擾動進入非設計狀態,也能很快的恢復到正常的狀態循環中來。這裏面有兩層含義。其一要求該FSM的綜合實現結果無_毛刺等異常擾動,其二要求FSM要完備,即便受到異常擾動進入非設計狀態,也能很快恢復到正常狀態。
第二,狀態機的設計要知足設計的面積和速度的要求。
第三,狀態機的設計要清晰易懂、易維護。
    須要說明的是,以上各項標準,不是割裂的,它們有着直接緊密的內在聯繫。在芯片設計中,對綜合結果評判的兩個基本標準爲:面積和速度。「面積」是指設計所佔用的邏輯資源數量;「速度」指設計在芯片上穩定運行所可以達到的最高頻率。二者是對立統一的矛盾體,要求一個設計同時具有設計面積最小,運行頻率最高,這是不現實的。科學的設計目標應該是:在知足設計時序要求(包含對設計最高頻率的要求)的前提下,佔用最小的芯片面積,或者在所規定的面積下,使設計的時序餘量更大,頻率更高。另外,若是要求FSM安全,則不少時候須要使用「full case」的編碼方式,即將狀態轉移變量的全部向量組合狀況都在FSM 中有相應的處理,這常常勢必意味着要多花更多的設計資源,有時也會影響FSM的頻率因此,上述的標準要綜合考慮,根據設計的要求進行權衡。

2.2 狀態機描述方法
    狀態機描述時關鍵是要描述清楚幾個狀態機的要素,即如何進行狀態轉移,每一個狀態的輸出是什麼,狀態轉移的條件等。具體描述時方法各類各樣,最多見的有三種描述方式:
第一,整個狀態機寫到一個always模塊裏面,在該模塊中既描述狀態轉移,又描述狀態的輸入和輸出;
第二,用兩個always模塊來描述狀態機,其中一個always模塊採用同步時序描述狀態轉移;另外一個模塊採用組合邏輯判斷狀態轉移條件,描述狀態轉移規律以及輸出;
第三,在兩個always模塊描述方法基礎上,使用三個always模塊,一個always模塊採用同步時序描述狀態轉移,一個採用組合邏輯判斷狀態轉移條件,描述狀態轉移規律,另外一個always模塊描述狀態的輸出(能夠用組合電路輸出,也能夠時序電路輸出)。
    通常而言,推薦的FSM 描述方法是後兩種。這是由於:FSM和其餘設計同樣,最好使用同步時序方式設計,以提升設計的穩定性,消除毛刺。狀態機實現後,通常來講,狀態轉移部分是同步時序電路而狀態的轉移條件的判斷是組合邏輯。
    第二種描述方法同第一種描述方法相比,將同步時序和組合邏輯分別放到不一樣的always模塊中實現,這樣作的好處不只僅是便於閱讀、理解、維護,更重要的是利於綜合器優化代碼,利於用戶添加合適的時序約束條件,利於佈局佈線器實現設計。在第二種方式的描述中,描述當前狀態的輸出用組合邏輯實現,組合邏輯很容易產生毛刺,並且不利於約束,不利於綜合器和佈局佈線器實現高性能的設計。第三種描述方式與第二種相比,關鍵在於根據狀態轉移規律,在上一狀態根據輸入條件判斷出當前狀態的輸出,從而在不插入額外時鐘節拍的前提下,實現了寄存器輸出。

2.3 狀態機的編碼
    二進制編碼(Binary)、格雷碼(Gray-code)編碼使用最少的觸發器,較多的組合邏輯,而獨熱碼(One-hot)編碼反之。獨熱碼編碼的最大優點在於狀態比較時僅僅須要比較一個位,從而必定程度上簡化了比較邏輯,減小了毛刺產生的機率。因爲CPLD更多地提供組合邏輯資源,而FPGA更多地提供觸發器資源,因此CPLD多使用二進制編碼或格雷碼,而FPGA多使用獨熱碼編碼。另外一方面,對於小型設計使用二進制和格雷碼編碼更有效,而大型狀態機使用獨熱碼更高效。
3 實例說明
    下面經過實例來講明Verilog HDL代碼描述對狀態機綜合結果的影響。
    設計一個序列檢測器,用於檢測串行的二進制序列,每當連續輸入三個或三個以上的1時,序列檢測器的輸出爲1,其它狀況下輸出爲0。
    假設初始的狀態爲s0,輸入一個1的狀態記爲s1,連續輸入二個1後的狀態記爲s2,輸入三個或以上1的狀態記爲s3,不論現態是何種狀態一旦輸入0的話,就返回到初始狀態。根據題意,可畫出狀態圖如圖2所示。

圖2 狀態圖
根據狀態圖以及前面狀態機的介紹,能夠採用一個always模塊來描述,狀態編碼採用二進制編碼,程序以下:
module fsm(clk,ina,out);
input clk,ina;
output out;
reg out;
parameter s0 = 3'bOO,s1 =3'b01,s2 =3'b10,s3=3'b11;
reg[0:1]state;
always @ (posedge clk)
begin
state<=s0;
out =0;
case(state)
s0:begin
state<=(ina)?s1:s0;out=0;
end
s1:begin
state<=(ina)?s2:s0;out=0;
end
s2:begin
state<=(ina)?s3:s0;out=0;
end
s3:begin
state<=(ina)?s3:s0;out=1;
end
endcase
end
endmodule
    採用Synplify Pro工具在Altera EPF10K10系列器件上進行綜合,其綜合的結果如圖3所示。

    若是採用兩個always來描述,程序的模塊聲明、端口定義和信號類型部分不變,只是改動邏輯功能描述部分,改動部分的程序以下:
alwys @ (posedge dk)
state fsm <=next_state;
always @ (state_fsm or ina)
begin
state<=s0;out =0;
case(state_fsm )
s0:begin
next_state=(ina)?s1:s0;out=0;
end
s1:begin
next state=(ina)?s2:s0;out=0:
end
s2:begin
next_state=(ina)?s3:s0;out=0;
end
s3:begin
next_state=(ina)?s3:s0;out=1;
end
endcase
end
endmodule
    在相同的器件上其綜合的結果如圖4所示,比較圖3與圖4的綜合結果,能夠看出。兩種綜合結果都是採用了兩個觸發器來存儲狀態。其不一樣的地方是輸出部分,採用一個always模塊的輸出結果是寄存器輸出。採用兩個always模塊描述的是組合邏輯直接輸出,這是由於代碼中的輸出賦值也放在了時鐘的上升沿(always @ (posedge clk))。其綜合的結果是寄存器,所以它比直接組合邏輯輸出延遲一個時鐘週期。

圖4
    若是採用一位hot編碼,僅改動參數設置的兩行程序。採用一個always模塊描述,改動部分的程序以下:
parameter s0 = 3'b0001,s1 =3'b0010,s2 =3'b0100,s3=3'b1000;
reg[0:3] state;

圖5
    綜合的結果如圖5所示。將圖5與圖3相比,能夠看出:
    圖5中狀態寄存器採用了4個觸發器來存儲狀態,而圖3採用了兩個觸發器來存儲狀態,這是因爲它們的狀態編碼的不一樣而獲得的不一樣的綜合結果,採用二進制編碼綜合獲得的觸發器要比採用獨熱碼綜合獲得的觸發器少。它們的共同之處都是採用了寄存器來輸出的。
3 結束語
    有多種可綜合狀態機的Verilog HDL代碼描述風格。其綜合的結果是不一樣的。其中普遍採用的是兩個或三個always模塊描述。組合邏輯輸出型狀態機不適合應用在高速複雜系統設計中,在高速系統中應當採用寄存器輸出型狀態機。寄存器類型信號不會產生毛刺,而且輸出不含組合邏輯。會減小組合邏輯門延時。容易知足高速系統設計要求。總之,狀態機的設計是數字系統設計中的關鍵部分,設計時作到心中有電路。充分考慮其綜合的結果,才能編寫出高質量的綜合代碼。進而提升設計水平。


模塊劃分很是重要,除了關係到是否最大程度上發揮項目成員的協同設計能力,並且直接決定着設計的綜合、實現時間。下面是一些模塊劃分的原則。
     a.對每一個同步設計的子模塊的輸出使用寄存器(registering)。也即用寄存器分割同步時序模塊的原則。) @( F3 f+ D" j
     使用寄存器的好處有:綜合工具在編譯綜合時會將所分割的子模塊中的組合電路和同步時序電路總體考慮。並且這種模塊結構符合時序約束的習慣,便於使用時序約束熟悉進行約束。) q9 t/ |# a  \7 p0 C
     b.將相關的邏輯或者能夠複用的邏輯劃分在同一模塊內。
     這樣作的好處有,一方面將相關的邏輯和能夠複用的邏輯劃分在同一模塊,能夠最大程度的複用資源,減小設計消耗的面積。同時也更利於綜合工具優化一個具體功能(操做)在時序上的關鍵路徑。其緣由是,綜合工具只能同時考慮一部分邏輯,而所同時優化的邏輯單元就是模塊,因此將相關功能劃分在同一模塊更有利於綜合器的優化。; l/ w" k5 r9 G4 X4 x
     c.將不一樣優化目標的邏輯分開。
     好的設計,在規劃階段,設計者就已經思考了設計的大概規模和關鍵路徑,並對設計的優化目標有一個總體上的把握。對於時序緊張的部分,應該獨立劃分爲一個模塊,其優化目標爲「speed」,這種劃分方法便於設計者進行時序約束,也便於綜合和實現工具進行優化。好比時序優化的利器Amplify,使用模塊進行區域優化更方便一些。另外一類矛盾集中在面積的設計,也應該劃分紅獨立的模塊,這類模塊的優化目標是「Area」,一樣將他們規劃到一塊兒,更有利於區域佈局與約束。這種根據優化目標進行優化的方法的最大好處是,對於某個模塊綜合器僅僅須要考慮一種優化目標和策略,從而比較容易達到較好的優化效果。相反的若是同時考慮兩種優化目標,會使綜合器陷入互相制約的困境。
     d.將鬆約束的邏輯歸到同一模塊。
     有些邏輯的時序很是寬鬆,不須要較高的時序約束,能夠將這類邏輯納入同一模塊,如多週期路徑「multi-cycle」等。將這些模塊歸類,並指定鬆約束,則可讓綜合器儘可能的節省面積資源。
     e.將RAM/ROM/FIFO等邏輯獨立劃分紅模塊。
     這樣作的好處是便於綜合器將這類資源類推爲器件的硬件原語,同時仿真時消耗的內存也會少些,便於提升仿真速度。(大多數仿真器對大面積的RAM都有獨特的內存管理方式)0 o4 B! p5 Q- D) O) Y7 M/ ]
     f.合適的模塊規模。
     規模大,利於「Resource Sharing」。可是對綜合器同時處理的邏輯量太大,不利於多模塊和增量編譯模式。


關於約束,時序分析的問題彙總
不少人發貼,來信詢問關於約束、時序分析的問題,好比:如何設置setup,hold時間?如何使用全局時鐘和第二全局時鐘(長線資源)?如何進行分組約束?如何約束某部分組合邏輯?如何經過約束保證異步時鐘域之間的數據交換可靠?如何使用I/O邏輯單元內部的寄存器資源?如何進行物理區域約束,完成物理綜合和物理實現?等等。。。
爲了解決你們的疑難,咱們將逐一討論這些問題。

今天先討論一下約束的做用?
有些人不知道什麼時候該添加約束,什麼時候不須要添加?有些人認爲低速設計不須要時序約束?關於這些問題,但願下面關於約束做用的論述可以有所幫助!
附加約束的基本做用有3:
(1)提升設計的工做頻率
對不少數字電路設計來講,提升工做頻率很是重要,由於高工做頻率意味着高處理能力。經過附加約束能夠控制邏輯的綜合、映射、佈局和佈線,以減少邏輯和佈線延時,從而提升工做頻率。
(2)得到正確的時序分析報告
幾乎全部的FPGA設計平臺都包含靜態時序分析工具,利用這類工具能夠得到映射或佈局佈線後的時序分析報告,從而對設計的性能作出評估。靜態時序分析工具以約束做爲判斷時序是否知足設計要求的標準,所以要求設計者正確輸入約束,以便靜態時序分析工具輸出正確的時序分析報告。
(3)指定FPGA/CPLD引腳位置與電氣標準
FPGA/CPLD的可編程特性使電路板設計加工和FPGA/CPLD設計能夠同時進行,而沒必要等FPGA/CPLD引腳位置徹底肯定,從而節省了系統開發時間。這樣,電路板加工完成後,設計者要根據電路板的走線對FPGA/CPLD加上引腳位置約束,使FPGA/CPLD與電路板正確鏈接。另外經過約束還能夠指定IO引腳所支持的接口標準和其餘電氣特性。爲了知足突飛猛進的通訊發展,Xilinx新型FPGA/CPLD能夠經過IO引腳約束設置支持諸如AGP、BLVDS、CTT、GTL、GTLP、HSTL、LDT、LVCMOS、LVDCI、LVDS、LVPECL、LVDSEXT、LVTTL、PCI、PCIX、SSTL、ULVDS等豐富的IO接口標準。

另外經過區域約束還能在FPGA上規劃各個模塊的實現區域,經過物理佈局佈線約束,完成模塊化設計等。

貼2:時序約束的概念和基本策略!
時序約束主要包括週期約束(FFS到FFS,即觸發器到觸發器)和偏移約束(IPAD到FFS、FFS到OPAD)以及靜態路徑約束(IPAD到OPAD)等3種。經過附加約束條件能夠使綜合佈線工具調整映射和佈局佈線過程,使設計達到時序要求。例如用OFFSET_IN_BEFORE約束能夠告訴綜合佈線工具輸入信號在時鐘以前何時準備好,綜合佈線工具就能夠根據這個約束調整與IPAD相連的Logic Circuitry的綜合實現過程,使結果知足FFS的創建時間要求。
附加時序約束的通常策略是先附加全局約束,而後對快速和慢速例外路徑附加專門約束。附加全局約束時,首先定義設計的全部時鐘,對各時鐘域內的同步元件進行分組,對分組附加週期約束,而後對FPGA/CPLD輸入輸出PAD附加偏移約束、對全組合邏輯的PAD TO PAD路徑附加約束。附加專門約束時,首先約束分組之間的路徑,而後約束快、慢速例外路徑和多週期路徑,以及其餘特殊路徑。

貼3:週期(PERIOD)的含義
週期的含義是時序中最簡單也是最重要的含義,其它不少時序概念會由於軟件商不一樣略有差別,而週期的概念確是最通用的,週期的概念是FPGA/ASIC時序定義的基礎概念。後面要講到的其它時序約束都是創建在週期約束的基礎上的,不少其它時序公式,能夠用週期公式推導。
週期約束是一個基本時序和綜合約束,它附加在時鐘網線上,時序分析工具根據PERIOD約束檢查時鐘域內全部同步元件的時序是否知足要求。PERIOD約束會自動處理寄存器時鐘端的反相問題,若是相鄰同步元件時鐘相位相反,那麼它們之間的延遲將被默認限制爲PERIOD約束值的一半。
以下圖所示,時鐘的最小週期爲:
TCLK = TCKO +TLOGIC +TNET +TSETUP -TCLK_SKEW
TCLK_SKEW =TCD2 -TCD1
其中TCKO爲時鐘輸出時間,TLOGIC爲同步元件之間的組合邏輯延遲,TNET爲網線延遲,TSETUP爲同步元件的創建時間,TCLK_SKEW爲時鐘信號延遲的差異。

這個帖子打算先澄清一些時序約束的基本概念,而後將在綜合工具(Synplify Pro爲例),設計平臺(ISE5.x 和Quartus 2.2爲例)的具體約束方法和技巧,而後將如何利用時序分析工具分析關鍵路徑。若是沒有意外,應該30多個帖子吧。
仿真時序原本是Deve的老本行,隨時須要Deve加入一塊兒把這個帖子辦好。歡迎你們暢談觀點,本站的版主,衝鋒啊,嘻嘻。

貼4:數據和時鐘之間的約束:OFFSET和SETUP、HOLD時間。
爲了確保芯片數據採樣可靠和下級芯片之間正確的交換數據,須要約束外部時鐘和數據輸入輸出引腳之間的時序關係(或者內部時鐘和外部輸入/輸出數據之間的關係,這僅僅是從採用了不一樣的參照系罷了)。約束的內容爲告訴綜合器、佈線器輸入數據到達的時刻,或者輸出數據穩定的時刻,從而保證與下一級電路的時序關係。
這種時序約束在Xilinx中用Setup to Clock(edge),Clock(edge) to hold等表示。在Altera裏經常使用tsu (Input Setup Times)、th (Input Hold Times)、tco (Clock to Out Delays)來表示。不少其它時序工具直接用setup和hold表示。其實他們所要描述的是同一個問題,僅僅是時間節點的定義上略有不一樣。下面依次介紹。

貼5:關於輸入到達時間,這一貼估計問題比較多,看起來也比較累,可是沒有辦法,這些都是時序的基本概念啊。搞不清楚,永遠痛苦,長痛不如短痛了,呵呵。

Xilinx的"輸入到達時間的計算"時序描述如圖所示:

定義的含義是輸入數據在有效時鐘沿以後的TARRIVAL時刻到達。則,
TARRIVAL=TCKO+TOUTPUT+TLOGIC     公式1
根據」貼3「介紹的週期(Period)公式,咱們能夠獲得:
Tcko+Toutput+Tlogic+Tinput+Tsetup-Tclk_skew=Tclk; 公式2
將公式1代入公式2:
Tarrival+Tinput+Tsetup-Tclk_skew=Tclk, 而Tclk_skew知足時序關係後爲負,因此
TARRIVAL +TINPUT+TSETUP <TCLK  公式3,
這就是Tarrival應該知足的時序關係。其中TINPUT爲輸入端的組合邏輯、網線和PAD的延遲之和,TSETUP爲輸入同步元件的創建時間。

貼6 數據延時和數據到達時間的關係:
TDELAY爲要求的芯片內部輸入延遲,其最大值TDELAY_MAX與輸入數據到達時間TARRIVAL的關係如圖2所示。也就是說:
TDELAY_MAX+TARRIVAL=TPERIOD 公式4
因此:
TDELAY<TDELAY_MAX=TPERIOD-TARRIVAL
帖7 要求輸出的穩定時間
從下一級輸入端的延遲能夠計算出當前設計輸出的數據必須在什麼時候穩定下來,根據這個數據對設計輸出端的邏輯佈線進行約束,以知足下一級的創建時間要求,保證下一級採樣的數據是穩定的。
計算要求的輸出穩定時間如圖所示。
公式的推導以下:
定義:TSTABLE = TLOGIC +TINPUT +TSETUP
從前面帖子介紹的週期(Period)公式,能夠獲得(其中TCLK_SKEW=TCLK1-TCLK2):
TCLK=TCKO+TOUTPUT+TLOGIC+TINPUT+TSETUP+TCLK_SKEW
將TSTABLE的定義代入到週期公式,能夠獲得:
TCLK=TCKO+TOUTPUT+TSTABLE+TCLK_SKEW
因此,
TCKO +TOUTPUT+TSTABLE<TCLK
這個公式就是TSTABLE必需要知足的基本時序關係,即本級的輸出應該保持怎麼樣的穩定狀態,才能保證下級芯片的採樣穩定。有時咱們也稱這個約束關係是輸出數據的保持時間的時序約束關係。只要知足上述關係,當前芯片輸出端的數據比時鐘上升沿提前TSTABLE 時間穩定下來,下一級就能夠正確地採樣數據。
其中TOUTPUT爲設計中鏈接同步元件輸出端的組合邏輯、網線和PAD的延遲之和,TCKO爲同步元件時鐘輸出時間。

/*******************************************************************/
這裏的概念介紹比較繁複,可是若是想掌握數據與時鐘關係的基本約束,就必須搞清楚這些概念,下一帖介紹這些概念的具體應用,實施上述約束的方法和具體命令。

轉貼lipple的問題:
請問斑竹上面幾貼那些延時屬於setup,哪些屬於hold啊
週期=Tsetup+Tlogic+Thold這個公式對比斑竹的公式,區別在因而不是劃分的不夠細啊?
westor的答覆:
基本是哪一個意思。這些公式描述的對象是意義的,只是每一個變量的定義略有區別罷了,換句話說,變量定義的節點不一樣。
這個公式是altera等採用的描述方法,一些工具爲了便於理解用
週期=Tsetup+Tlogic+Thold約束時序。
和我前面介紹的公式:
TCLK = TCKO +TLOGIC +TNET +TSETUP -TCLK_SKEW
相比,他把到寄存器前的全部組合邏輯logic和線延時都歸在Tsetup裏面了,並且上面公式忽略了Tclk_skew。

帖8 實施上述約束的方法和命令。
實施上述約束的基本方法是,根據已知時序信息,推算須要約束的時間值,實施約約束。具體的說是這樣的,首先對於通常設計,首先掌握的是TCLK,這個對於設計者來講是個已知量。前面介紹公式和圖中的TCKO和TSETUP(注:有的工具軟件對TCKO和TSETUP的定義與前面圖形不一樣,還包含了到達同步器件的一段logic的時延)是器件內部固有的一個時間量,通常咱們選取典型值,對於FPGA,這個量值比較小,通常不大於1~2ns。比較難以肯定的是TINPUT和TOUTPUT兩個時間量。
約束輸入時間偏移,須要知道TINPUT,TINPUT爲輸入端的組合邏輯、網線和PAD的延遲之和(詳細定義見帖5),PAD的延時也根據器件型號也有典型值可選,可是到達輸入端的組合邏輯電路和網線的延時就比較難以肯定了,只能經過靜態時序分析工具分析,或者經過底層佈局佈線工具量取,有很大的經驗和試探的成分在裏面。
約束輸出時間偏移,須要知道TOUTPUT,TOUTPUT爲設計中鏈接同步元件輸出端的組合邏輯、網線和PAD的延遲之和(見帖7),仍然是到達輸出端的組合邏輯電路和網線的延時就比較難以肯定,須要經過靜態時序分析工具分析,或者經過底層佈局佈線工具量取,有很大的經驗和試探的成分在裏面。
約束的具體命令根據約束工具不一樣而異,首先說使用Xilinx器件的狀況下,實施上述約束的命令和方法。Xilinx把上述約束統稱爲:OFFSET約束(偏移約束),一共有4個相關約束屬性:OFFSET_IN_BEFORE、OFFSET_IN_AFTER、OFFSET_OUT_BEFORE和OFFSET_OUT_AFTER。
其中前兩個屬性叫作輸入偏移(OFFSET_IN)約束,基本功能類似,僅僅是約束取的參考對象不一樣而已。後兩個屬性叫作輸出偏移(OFFSET_OUT)約束,基本功能類似,也是約束取的參考對象不一樣而已。
爲了便於理解,舉例說明。
輸入偏移約束例:時鐘週期爲20ns,前級寄存器的TCKO選則1ns,前級輸出邏輯延時TOUTPUT爲3ns,中間邏輯TLOGIC的延時爲10ns,那麼TARRIVAL=14ns,因而能夠在數據輸入引腳附加
NET DATA_IN FFET=IN  14ns  AFTER CLK
約束,也能夠使用OFFSET_IN_BEFORE對芯片內部的輸入邏輯進行約束,其語法以下:
NET DATA_IN FFET=IN  TDELAY   BEFORE CLK
其中TDELAY爲要求的芯片內部輸入延遲,其最大值與輸入數據到達時間TARRIVAL的關係如帖6所述:TDELAY_MAX + TARRIVAL = TPERIOD,因此
TDELAY < TPERIOD - TARRIVAL = 20 - 14 =6 ns.
輸出偏移約束例:設時鐘週期爲20ns,後級輸入邏輯延時TINPUT爲4ns、創建時間TSETUP爲1ns,中間邏輯TLOGIC的延時爲10ns,那麼TSTABLE=15ns,因而能夠在數據輸入引腳附加NET DATA_OUT FFET=OUT  15ns  BEFORE CLK
約束,也能夠直接對芯片內部的輸出邏輯直接進行約束,
NET DATA_OUT FFET=OUT  TOUTPUT_DELAY  AFTER CLK,
其中TOUTPUT_DELAY爲要求的芯片內部輸出延遲,其最大值與要求的輸出數據穩定時間TSTABLE的關係爲:TOUTPUT_DELAY_MAX+TSTABLE= TPERIOD.
TOUT_DELAY< TPERIOD - TSTABLE = 20 - 15 = 5ns
/*******************************************************************/
這些概念和推導有些枯燥和乏味,可是若是要掌握好數據與時鐘之間的約束,就要耐心看下去,明天介紹一下Altera的相關約束方法。

帖9 Altera對應的時序概念
這兩天太忙了,帖子上的有些慢,請朋友們原諒,我會盡可能按照計劃寫完這個主題的。
前面8個帖子介紹了一些時序概念,有的是FPGA/ASIC設計的通常性時序概念,有的爲了方便敘述,主要介紹了Xilinx對應的這些時序概念,和具體的約束熟悉。下面幾個帖子主要介紹Altera對應的這些時序概念和約束方法。
前面首先介紹的第一個時序概念是週期,Period,這個概念是FPGA/ASIC通用的一個概念,各方的定義至關統一,至可能是描述方式不一樣罷了,全部的FPGA設計都首先要進行週期約束,這樣作的好處除了在綜合與佈局佈線時給出規定目標外,還能讓時序分析工具考察整個設計的Fmax等。
Altera的週期定義如圖所示,公式描述以下:
Clock Period = Clk-to-out + Data Delay + Setup Time - Clk Skew
即,
Tclk         = Tco        + B          + Tsu        -(E-C)
Fmax         = 1/Tclk
對比一下前面的介紹,只要理解了B包含了兩級寄存器之間的全部logic和net的延時就會發現與前面公式徹底一致。
一個設計的Fmax在時序報告,或者在圖形界面觀察。以Quartus2爲例,在圖形界面的觀察方法是,編譯實現完成後,展開Compilation Report下面的Timing Analyses,單擊Fmax(not include delays to / from pins)便可。在詳細報告窗口能夠觀察到影響週期惡化的10條最差時序路徑,根據這些信息能夠找出關鍵路徑,進行時序分析。
關於時序分析和關鍵路徑改進等內容在後面的帖子會有專門的討論,暫時不作進一步介紹。
貼10
Clock Setup Time (tsu)
要想正確採樣數據,就必須使數據和使能信號在有效時鐘沿到達前就準備好,所謂時鐘創建時間就是指時鐘到達前,數據和使能已經準備好的最小時間間隔。如圖1所示:
注:這裏定義Setup時間是站在同步時序整個路徑上的,須要區別的是另外一個概念Micro tsu。Micro tsu指的是一個觸發器內部的創建時間,它是觸發器的固有屬性,通常典型值小於1~2ns。在Xilinx等的時序概念中,稱Altera的Micro tsu爲setup時間,用Tsetup表示,請你們區分一下。
回到Altera的時序概念,Altera的tsu定義以下:
tsu = Data Delay – Clock Delay + Micro tsu

貼11
Clock Hold Time tH
時鐘保持時間是隻能保證有效時鐘沿正確採用的數據和使能信號的最小穩定時間。其定義如圖2所示。定義的公式爲:
tH= Clock Delay – Data Delay + Micro tH
注:其中Micro tH是指寄存器內部的固有保持時間,一樣是寄存器的一個固有參數,典型值小於1~2ns。

貼12
Clock-to-Output Delay(tco)
這個時間指的是當時鐘有效沿變化後,將數據推倒同步時序路徑的輸出端的最小時間間隔。如圖3所示。
tco = Clock Delay + Micro tco + Data Delay
注:其中 Micor tco也是一個寄存器的固有屬性,指的是寄存器相應時鐘有效沿,將數據送到輸出端口的內部時間參數。它與Xilinx的時序定義中,有一個概念叫Tcko是同一個概念。

Pin to Pin Delay (tpd)
tpd指輸入管腳經過純組合邏輯到達輸出管腳這段路徑的延時,特別須要說明的是,要求輸入到輸出之間只有組合邏輯,纔是tpd延時。
7.Slack
Slack是表示設計是否知足時序的一個稱謂,正的slack表示知足時序(時序的餘量),負的slack表示不知足時序(時序的欠缺量)。slack的定義和圖形如圖4所示。
Slack = Required clock period – Actual clock period
Slack = Slack clock period – (Micro tCO+ Data Delay + Micro tSU)
8.Clock Skew
Clock Skew指一個同源時鐘到達兩個不一樣的寄存器時鐘端的時間偏移。express

相關文章
相關標籤/搜索