1.命名規(guī)則
用有意義而有效的名字
有效的命名有時并不是要求將功能描述出來,如:
for(i =0;i
用連貫的縮寫
采用縮寫時應該主義同一信號在模塊中的一致性.如:
Addraddress Pntrpointer Clkclock Rstreset
用最右邊的字符下劃線代表低電平有效,高電平有效的信號不得以下劃線表示,短暫的有效信號建議采用高電平有效.如:
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 SensingandInformation 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 =delta)...就不如下面的更好 if((alpha =delta))
用函數(shù)(function)來代替表達式的多次重復
這樣在以后的版本升級時更便利,而且經(jīng)常使用的一組描述可以寫到一個任務(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; //V3isnotassigned, so thedefaultvalueofV3is2'b00; end elseif(a == c)begin V2 =2'b10; V3 =2'b11; //V1isnotassigned, so thedefaultvalueofV1is2'b00; end ...
6.case語句
case語句通常被綜合為一級多路復用器,而if-then-else語句則綜合為優(yōu)先編碼的串接的多個多路復用器.通常case語句比if語句快,有限編碼結果在信號到達時有先后.case語句仿真比條件語句快.
所有的case語句都應該有一個default case,且允許空語句出現(xiàn)如:
default:;
7.Function
在function的最后給function賦值,如:
functionCompareVectors; input[199:0]Vector1; input[199:0]Vector2; input[31:0]Length; //local variables integeri; regEqual; 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行為及仿真和門級仿真的差異.如:
functionByteCompare; 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 ealtime eal復制,而連續(xù)賦值一般給wire變量賦值.
always@(敏感表),敏感表要完整,如果不完整,將會引起仿真和綜合結果不一致,如:
always@(dorClr) if(Clr) q =1'b0; elseif(e) q = d; //以上語句在行為及仿真時e的變化不會使仿真器進入該always塊,導致仿真結果錯誤.
assign/deassign僅用于仿真加速,僅對寄存器有用.
force/release僅用于debug,對寄存器和線網(wǎng)型都有用.
避免使用disable
對任何reg賦值,都用非阻塞賦值(<=)代替阻塞賦值(=),reg的非阻塞賦值要加單位延遲,但異步復位可加可不加.如:
always@(posedge Clk or negedge Rst_)begin if(!Rst_)begin Rega <=?0; ?//non_blocking assignment ? ? ? ? ? ? Regb <=?0; ? ? ? ?end? ? ? ? ?else?if(Soft_rst_all)begin? ? ? ? ? ? ?Rega <=?#u_dly ?0; ?//add unit delay? ? ? ? ? ? ?Regb <=?#u_dly ?0;? ? ? ? ?end? ? ? ? ?else?if(Load_init)begin? ? ? ? ? ? ?Rega <=?#u_dly ?init_rega;? ? ? ? ? ? ?Regb <=?#u_dly ?init_rega;? ? ? ? ?end? ? ? ? ?else?begin? ? ? ? ? ? ?Rega <=?#u_dly ?Rega << 1; ? ?? ? ? ? ? ? ?Rega <=?#u_dly ?St_1; ? ?? ? ? ? ?end? ? ?end?//end?Rega,Regb assignment
9.Combinatorial vs Sequential Logic
如果一個事件持續(xù)幾個時鐘周期,設計時就用時序邏輯代替組合邏輯. 如:
wireCt_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加法器,而且會產生毛刺,對于這種電路,要采用時序設計,代碼如下:
regCt_24_e4; always@(posedgeClkornegedgeRst_)begin if(!Rst_) Ct_24_e4 <=?1'b0; ? ? ? ?else?if(count8bit[7:0] >8'he4) Ct_24_e4 <= #u_dly ?1'b0; ? ? ? ?else?if(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)機的例子如下:
... regCurrentState,NextState,Out1; parameterS0 =0, S1 =1; always@(posedgeClkornegedgeRst_) 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 : Topmodule Revision History : 22-05-27 Revision : 1.0 Email : lindongyi@163.com Company : Hunan Institute of Advanced SensingandInformation Technology, Xiangtan Univeristy Copyright : 2022, Xiangtan University, All right reserved/*==============================================================================================*/modulemodule_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 ", Qout, Qout); end endmodule
在testbench中避免使用絕對的時間,如#20,#15或#(CYC + 15)等,應該在文件前端使用parameter定義一些常量,使得時間的定義像#(CYC + OFF0)這樣的形式,便于修改;
觀測結果可以輸出到波形文件GRAY.shm,或數(shù)據(jù)文件gray.dat.生成波形文件可以使用simwave或gtkwave觀測結果,比較直觀;而生成數(shù)據(jù)文件則既可以快速定位,也可以通過編寫的小程序工具對它進行進一步的處理;
對大的設計的頂層方針,一般不要對所有信號進行跟蹤,大的設計波形文件會很大,仿真時間也會延長,可以有選擇的觀測一些信號.
-
模塊
+關注
關注
7文章
2777瀏覽量
49192 -
Verilog
+關注
關注
28文章
1364瀏覽量
111595 -
波形
+關注
關注
3文章
386瀏覽量
32043
原文標題:Verilog編寫規(guī)范
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設計論壇】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
評論