Verilog編寫規(guī)范實例
掃描二維碼
隨時隨地手機看文章
1.命名規(guī)則
-
用有意義而有效的名字
有效的命名有時并不是要求將功能描述出來,如:
for(i = 0;i < 1024;i = i + 1) Mem[i] <= #132'b0;
-
用連貫的縮寫
采用縮寫時應該主義同一信號在模塊中的一致性.如:
Addr address Pntr pointer Clk clock Rst reset
- 用最右邊的字符下劃線代表低電平有效,高電平有效的信號不得以下劃線表示,短暫的有效信號建議采用高電平有效.如:
Rst_ Trdy_ Irdy_
-
大小寫原則
名字一般首字符大寫,其余小寫(但是parameter/integer定義的數(shù)值名可以全部大寫),兩個單詞之間用下劃線連接.如:
Data_in Mem_wr Rd_req Sensor_ctrl
-
全局信號名中應包含信號來源的信息.
如:D_addr[7:2],這里的"D"指明了地址是解碼模塊(Decoder module)的地址.
-
同一信號在不同層次應保持一致性
-
自己定義的常數(shù)和類型等用大寫表示.如:
parameter CYCLE =100;
-
避免使用保留字
如:in,out,x,z等不能夠作為變量,端口或模塊名
-
添加有意義的后綴,使信號名更加明確,常用的后綴有:
_Clk 時鐘信號 _next 寄存前的信號 _z 連到三態(tài)輸出的信號 _f 下降沿有效的寄存器 _xi 芯片原始輸入信號 _xo 芯片原始輸出信號 _xod 芯片的漏極開路輸出 _xz 芯片的三態(tài)輸出 -xbio 芯片的雙向信號
2.Modules
-
頂層模塊應知識內部模塊間的互聯(lián)
Verilog設計一般都是層次性的設計,也就是在設計中會出現(xiàn)一個或多個模塊,模塊間的調用在所難免.可把設計比喻成樹,被調用的模塊就是輸液,沒被調用的模塊就是樹根,那么在這個樹根模塊中,除了內部的互聯(lián)和模塊的調用外,盡量避免再做邏輯,如不能再出現(xiàn)對reg變量賦值等.這樣做的目的是為了更有效的綜合,因為在頂層模塊VS出現(xiàn)中間邏輯,Synopsys的Design Compiler就不能把子模塊中的邏輯綜合到最優(yōu).
-
每個模塊應該在開始處注明文件名,功能描述,引用模塊,設計者,設計時間,以及版權信息等.如:
/*==============================================================================================*/ Filename : RX_MUX.v Author : Dongyi Lin Description : Called by : Top module Revision History : 22-05-27 Revision : 1.0 Email : lindongyi@163.com Company : Hunan Institute of Advanced Sensing and Information Technology, Xiangtan Univeristy Copyright : 2022, Xiangtan University, All right reserved /*==============================================================================================*/
-
不要對Input進行驅動,在module內不要存在沒有驅動的信號,更不能在端口模塊出現(xiàn)沒有驅動的輸出信號,避免再仿真或綜合時產生warning,干擾錯誤定位.
-
每行應限制在80個字符以內,以保持代碼的清晰,沒關和層次感.
一條語句占用一行,如果超過80個字符則要換行.
-
電路中調用的module名用Uxx表示.向量大小要表示清晰,采用基于名字的(name_based)調用,而非基于順序的(order_based).
Instance Uinstance2( .DataOut (DOUT ), .DataIn (DIN ), .Cs_ (Cs_ ), );
-
時鐘的上升沿或下降沿采樣信號,不能一會上升沿,一會用下降沿.如果既要用上升沿又要用下降沿,應該分成兩個模塊設計.建議在頂層模塊中對Clock做一個not門,
在層次模塊中如果要用時鐘下降沿就可以用not門產生的Posedge Clk_,這樣的好處是在整個設計中采用同一種時鐘出發(fā),有利于綜合. -
在模塊中增加注釋.
對信號,參量,引腳,模塊,函數(shù)及進程等加以說明,便于閱讀與維護.
-
Module名稱要用大寫表示,且應該與文件名保持一致.如:
module DFF_ADSYNC_RST( Reset, Clk, Data, Qout );
-
要嚴格芯片級模塊的劃分
只有頂層包括IO引腳(pads),中間層是時鐘產生模塊,JTAG,芯片的內核(CORE),這樣便于對每個模塊加以約束仿真,對時鐘也可以仔細仿真.
-
模塊輸出寄存器化
對所有的模塊的數(shù)據(jù)加以寄存,使得輸出的驅動強度和輸入的延遲可以預測,從而使得模塊的綜合過程更簡單.
3.Net and Register
-
一個reg變量只能在一個always語句中賦值
-
向量有效位順序一邊為從大到小
推薦Data[4:0]這種格式的定義. -
對net和register類型的數(shù)據(jù)要做聲明(在PORT中).
4.Expressions
- 用括號表示執(zhí)行的優(yōu)先級,對讀者更情緒,更有意義,如:
if(alpha < beta && gamma >= delta)...就不如下面的更好 if((alpha < beta) && (gamma >= delta))
-
用函數(shù)(function)來代替表達式的多次重復
這樣在以后的版本升級時更便利,而且經常使用的一組描述可以寫到一個任務(task)中
5.IF語句
-
向量比較時,比較的向量要相等
在向量比較時,verilog將位數(shù)小的向量做0擴展以使得他們的長度相匹配,它的自動擴展是隱式的.建議采用顯式擴展.如:
reg Abc [7:0]; reg Bca [3:0]; ...... if(Abc == {4'b0,Bca})begin ...... if(Abc == 8'b0)begin
-
每一個if都應該有一個else與之對應
沒有else可能會使得綜合出的邏輯和RTL級的邏輯不同,如果條件為假時不盡興任何操作,則使用一條空語句,如:always@(Cond)begin if(Cond) DataOut <= DataIn; end //以上語句DataOut會綜合出鎖存器.
-
如果變量在if-else語句或case語句中沒有被完全賦值,則應該提前給變量一個缺省值,即:
V1 = 2'b00; V2 = 2'b00; V3 = 2'b00; //給V1,V2,V3缺省值,在后面賦值變量時有一個默認值在 if(a == b)begin V1 = 2'b01; V2 = 2'b10; //V3 isnot assigned, so the default value of V3 is2'b00; end elseif(a == c)begin V2 = 2'b10; V3 = 2'b11; //V1 isnot assigned, so the default value of V1 is2'b00; end ...
6.case語句
-
case語句通常被綜合為一級多路復用器,而if-then-else語句則綜合為優(yōu)先編碼的串接的多個多路復用器.通常case語句比if語句快,有限編碼結果在信號到達時有先后.case語句仿真比條件語句快.
-
所有的case語句都應該有一個default case,且允許空語句出現(xiàn)如:
default: ;
7.Function
- 在function的最后給function賦值,如:
function CompareVectors; input [199:0] Vector1; input [199:0] Vector2; input [31:0] Length; //local variables integer i; reg Equal; begin i = 0; Equal = 1; //給Equal賦初值 while((i < Length)&& Equal)begin if(Vector2[i] !== 1'bx)begin if(Vector1[i] !== Vector2[i]) Equal = 0; else; end i = i + 1; end CompareVectors = Equal; //賦值放在function的最后 endendfunction
-
在function中避免使用全局變量
否則容易引起HDL行為及仿真和門級仿真的差異.如:
function ByteCompare; input [15:0] Vector1; input [15:0] Vector2; input [7:0] Length; begin if(ByteSel) //ByteSel是全局變量,如果在其他位置無意修改了,可能導致函數(shù)結果錯誤, //所以最好在端口加以定義 ... else ... end endfunction
注意,函數(shù)與任務的調用均為靜態(tài)調用.
8.Assignment
-
Verilog有兩種賦值方式:過程賦值(procedural)和連續(xù)賦值(continuous).過程復制用于過程代碼(initial,always,task,function)中給reg和integer變量,time\realtime\real復制,而連續(xù)賦值一般給wire變量賦值.
-
always@(敏感表),敏感表要完整,如果不完整,將會引起仿真和綜合結果不一致,如:
always@(d or Clr) if(Clr) q = 1'b0; elseif(e) q = d; //以上語句在行為及仿真時e的變化不會使仿真器進入該always塊,導致仿真結果錯誤.
-
assign/deassign僅用于仿真加速,僅對寄存器有用.
-
force/release僅用于debug,對寄存器和線網型都有用.
-
避免使用disable
-
對任何reg賦值,都用非阻塞賦值(<=)代替阻塞賦值(=),reg的非阻塞賦值要加單位延遲,但異步復位可加可不加.如:
always@(posedge Clk or negedge Rst_)begin if(!Rst_)begin Rega <= 0; //non_blocking assignment Regb <= 0; end elseif(Soft_rst_all)begin Rega <= #u_dly 0; //add unit delay Regb <= #u_dly 0; end elseif(Load_init)begin Rega <= #u_dly init_rega; Regb <= #u_dly init_rega; end elsebegin Rega <= #u_dly Rega << 1; Rega <= #u_dly St_1; end end //end Rega,Regb assignment
9.Combinatorial vs Sequential Logic
- 如果一個事件持續(xù)幾個時鐘周期,設計時就用時序邏輯代替組合邏輯.如:
wire Ct_24_e4; //Ct_24_e4 last over several clock cycles assign Ct_24_e4 = (count8bit[7:0] >= 8'h24) & (count8bit[7:0] <= 8'he4);
這種設計將綜合處兩個8bit加法器,而且會產生毛刺,對于這種電路,要采用時序設計,代碼如下:
reg Ct_24_e4; always@(posedge Clk ornegedge Rst_)begin if(!Rst_) Ct_24_e4 <= 1'b0; elseif(count8bit[7:0] > 8'he4) Ct_24_e4 <= #u_dly 1'b0; elseif(count8bit[7:0] > 8'h23) Ct_24_e4 <= #u_dly 1'b1; else ; end
- 內部總線不要懸空.在default狀態(tài),要把他上拉或下拉.
wire OE_default; assign OE_default = !(oe1 | oe2 | oe3); assign bus[31:0] = oe1 ? Data1[31:0]: oe2 ? Data2[31:0]: oe3 ? Data3[31:0]: OE_default ? 32'h0000_0000: //如果bus不等于oe1,oe2,oe3中的任何一個, //若等于OE_default,則bus為32'h0即拉低,否則拉高為高阻態(tài). 32'hzzzz_zzzz;
10.Macros 宏指令
-
為了保持代碼的可讀性常用`define做常數(shù)聲明
-
把`define放在一個獨立的文件中
參數(shù)(parameter)必須在一個模塊中定義,不要傳遞參數(shù)到模塊(仿真測試向量例外);
define可以在任何地方定義,要把所有的define定義在一個文件中(極少的一個兩個define就不用了吧),在編譯源代碼時首先把這個文件讀入.
如果希望宏的作用于僅在一個模塊中,就用參數(shù)來代替.
11.Comments
-
對更新的內容要做注釋
-
在語法塊的結尾做標記
-
每一個模塊都應該在模塊開始處做模塊級的注釋(參考前面的標準模塊頭)
-
在端口列表中出現(xiàn)的端口信號,都應該做簡要的功能描述.
12.FSM 狀態(tài)機
-
狀態(tài)機的狀態(tài)分配
Verilog描述狀態(tài)機時必須有parameter分配好狀態(tài).
-
組合邏輯和時序邏輯分開用不同的進程
組合邏輯包括狀態(tài)譯碼和輸出,時序邏輯則是狀態(tài)寄存器的切換
-
必須對所有狀態(tài)都處理,不能出現(xiàn)無法處理的狀態(tài),使狀態(tài)機時空
-
Mealy狀態(tài)機輸出不僅取決于當前狀態(tài),還與輸入有關;Moore狀態(tài)機輸出僅與當前狀態(tài)有關.
Mealy狀態(tài)機的例子如下:
... reg CurrentState,NextState,Out1; parameter S0 = 0, S1 = 1; always@(posedge Clk ornegedge Rst_) if(!Rst_) CurrentState <= S0; else CurrentState <= #u_dly NextState; always@(In1 or In2 or CurrentState) case(CurrentState) S0:begin NextState <= #u_dly S1; Out1 <= #u_dly 1'b0; end S1:begin if(In1)begin NextState <= #u_dly S0; Out1 <= #u_dly !In2; end end endcase
13.Module 編寫示例
/*==============================================================================================*/ Filename : module_name.v Author : Dongyi Lin Description : Called by : Top module Revision History : 22-05-27 Revision : 1.0 Email : lindongyi@163.com Company : Hunan Institute of Advanced Sensing and Information Technology, Xiangtan Univeristy Copyright : 2022, Xiangtan University, All right reserved/*==============================================================================================*/module module_name( Output_ports, //comment:port description Input_ports, //comment:port description Io_ports, //comment:port description Clk_port, //comment:port description Rst_port //comment:port description);//port declarations output [31:0] Dataout; input [31:0] Datain; inout Bi_dir_siginal; input input1; input2; //interrnal wire/reg declarations wire [31:0] internal_data; reg output_enable; //module instantiations, Self-build module module_name1 Uinstance_name1( .port1(...); .port2(...); ); module_name2 Uinstance_name2( .port1(...); .port2(...); ); //TSC4000 cell DTC12V1( .Clk(Clk), .CLRZ(Clr), .D(Data), .Q(Qout) );//always block always@(input2)begin ... end//function and task definitions function [function_type] function_name; declarations_of_inputs; [declarations_of_local_variables]; begin behavirol_statement; function_name = function_express; end endfunction endmodule
14.Testbench 編寫示例
下面是一個格雷碼的測試模塊:
module TB_GRAY; reg Clock; reg Reset; wire [7:0] Qout; integer fout; //輸出文件指針 parameter CYC = 20; GRAY DUT( .Clock(Clock), .Reset(Reset), .Qout(Qout) ); initial begin Clock = 1'b0; Reset = 1'b1; #(5 *CYC) Reset = 1'b0; #(5 *CYC) Reset = 1'b1; #(5000 *CYC); $fclose(fout); $finish; end initial begin $shm_open("GRAY.shm"); $shm_probe("AS"); fout = $fopen("gray.dat"); end always #(CYC) Clock = ~Clock; //輸出數(shù)據(jù)到文件gray.dat always@(posedge Clock)begin $fwrite(fout,"%d %b\n", Qout, Qout); end endmodule
-
在testbench中避免使用絕對的時間,如#20,#15或#(CYC + 15)等,應該在文件前端使用parameter定義一些常量,使得時間的定義像#(CYC + OFF0)這樣的形式,便于修改;
-
觀測結果可以輸出到波形文件GRAY.shm,或數(shù)據(jù)文件gray.dat.生成波形文件可以使用simwave或gtkwave觀測結果,比較直觀;而生成數(shù)據(jù)文件則既可以快速定位,也可以通過編寫的小程序工具對它進行進一步的處理;
-
對大的設計的頂層方針,一般不要對所有信號進行跟蹤,大的設計波形文件會很大,仿真時間也會延長,可以有選擇的觀測一些信號.





