Verilog 編碼風(fēng)格

2023-02-28 10:08 更新

良好的編碼風(fēng)格,有助于代碼的閱讀、調(diào)試和修改。雖然 Verilog 代碼可以在保證語法正確的前提下任意編寫,但是潦草的編碼風(fēng)格往往是一錘子買賣。有時回看自己編寫的代碼,既看不出信號的意義,也不了解模塊的功能,還得從邏輯上一步步分析,就會消耗大量的時間和精力去消化,嚴(yán)重影響設(shè)計進(jìn)度。

為了不讓別人或自己由衷的感嘆出:這特喵的是哪個"小傻寶"寫的代碼!下面對編碼風(fēng)格進(jìn)行一定意義上的建議。

關(guān)于命名

信號變量、模塊等一定要使用有意義的名字,且信號名稱在模塊間穿梭時也應(yīng)該保持不變,以便代碼自身就具有清晰的說明信息,增強(qiáng)可讀性。

當(dāng)名字單詞數(shù)量過多時,可以使用首字母大寫或下劃線"?_?"進(jìn)行拼接。個人喜歡后者,比較清晰。

  reg     DataToDestinationClock ;
  reg     data_to_destination_clock ; //推薦

建議使用單詞縮寫的方式對信號進(jìn)行命名,并懂得取舍,避免過長的信號命名。例如 clock 縮寫為 clk, destination 縮寫為 dest,source 縮寫為 src 等。

reg data_to_destination_clock ; reg des_data ; //推薦

巧用數(shù)字代表英文字母,例如 2 代表 to, 4 代表 for, 可以省略一丟丟代碼空間。

  reg     clk_for_test, sig_uart_to_spi ;
  reg     clk4test, sig_uart2spi ; //推薦

雖然 Verilog 區(qū)分大小寫,但是建議一般功能模塊的名稱、端口、信號變量等全部使用小寫,parameter 使用大寫,一些電源、pad 等特殊端口使用大寫。只為編寫代碼方便,容易區(qū)分常量變量,不用考慮大小寫不一樣但名字相同的信號變量的差異。

  parameter         DW = 8 ; //常量
  reg [DW-1 : 0]    wdata ;  //變量

寄存器變量一般加后綴 ?_r?, 延遲打拍的變量加后綴 ?_r1?、?_r2?等。主要有兩大好處。一是 RTL 設(shè)計時容易根據(jù)變量類型對數(shù)據(jù)進(jìn)行操作。二是綜合后網(wǎng)表的信號名字經(jīng)常會改變,加入后綴容易在綜合后網(wǎng)表中找到與 RTL 中對應(yīng)的信號變量。

  wire      dout_en ;
  reg       dout_en_r ;
  ...  //dout_en_r 的邏輯
  assign    dout_en = dout_en_r ;

其他尾綴:?_d? 可以表示延遲后的信號,?_t? 可以表示暫時存儲的信號,?_n? 可以表示低有效的信號,?_s? 可以表示 slave 信號,?_m? 可以表示 master 信號等。

避免使用關(guān)鍵字對信號進(jìn)行命名,例如 in, out, x, z 均不建議作為變量。

文件名字保持與設(shè)計的 module 名字一致,文件內(nèi)盡量只包含一個設(shè)計模塊。

關(guān)于注釋

每一個設(shè)計模塊開頭,都應(yīng)該包含文件說明信息,包括版權(quán)、模塊名字、作者、日期、梗概、修改記錄等信息。例如:

/**********************************************************
// Copyright 1891.06.02-2017.07.14
// Contact with willrious@sina.com
================ w3cschool.v ======================
>> Author       : willrious
>> Date         : 1995.09.07
>> Description  : Welcome
>> note         : (1)To 
>>              : (2)My
>> V180121      : World.
************************************************************/

注釋應(yīng)該精煉的表達(dá)出代碼所描述的意義,簡短的注釋在一行語句代碼之后添加,過長的注釋提前一行書寫。

   //輸出位寬小于輸入位寬,求取縮小的倍數(shù)及對應(yīng)的位數(shù)
   parameter       SHRINK       = DWI/DWO ;
   reg [AWI-1:0]         ADDR_WR ; //寫地址

注釋盡量用英文書寫,以保證不同操作系統(tǒng)、不同編輯器下能夠正常顯示。

端口信號中,除一般的時鐘和復(fù)位信號,其他信號最好也進(jìn)行注釋。

注釋功能非常強(qiáng)大,可以使用注釋信息畫出時序圖,甚至可以使用注釋畫出數(shù)字電路結(jié)構(gòu)圖。



關(guān)于優(yōu)化

使用圓括號確定程序的優(yōu)先級或邏輯結(jié)構(gòu)。為避免操作符優(yōu)先級問題導(dǎo)致設(shè)計錯誤,建議多多使用圓括號。同時,圓括號的巧妙使用有時候也會優(yōu)化邏輯綜合后的結(jié)構(gòu)。例如:

    //往往被綜合成串行的 3 個加法器
    assign F = A + B + C + D ;
    //往往被綜合成并行的的 2 個加法器和 1 個級聯(lián)的加法器,時序更加寬松
    assign F = (A + B) + (C + D) ;
 
    //不推薦
    assign flag = cnt == 4'd2 && mode == 2'b01;
    //推薦
    assign flag = (cnt == 4'd2) && (mode == 2'b01);

條件語句盡量使用 case 語句代替 if 語句。當(dāng)同級別的條件判斷語句過多時,使用 case 語句綜合后的硬件結(jié)構(gòu),往往比 if 語句消耗更少的資源,擁有更好的時序。

狀態(tài)機(jī)編寫時,盡量使用 3 段式,以保證代碼具有良好的整潔性和安全性。

系統(tǒng)設(shè)計時,盡量采用模塊按功能分割、然后進(jìn)行模塊例化的方法。相比成千上萬行代碼都集成在一個文件中,模塊分割有利于團(tuán)隊(duì)設(shè)計,便于更新維護(hù)。

關(guān)于美觀

端口信號保證每行一個信號,逗號緊跟在端口聲明之后,強(qiáng)迫癥患者請保持逗號也對齊。

//不推薦
module even_divisor (input rstn, clk, output clk_div2, clk_div4, clk_div10) ;

//推薦
module even_divisor    (
    input               rstn     ,
    input               clk      ,
    output              clk_div2 ,
    output              clk_div4 ,
    output              clk_div10
    );

一行代碼內(nèi)容過長時,盡量換行編寫,無需使用換行符,例如:

      assign rempty    = (rover_flag == rq2_wptr_decode[AWI]) &&
                         (raddr_ex >= rq2_wptr_decode[AWI-1:0]);

盡量使用 begin + end 的方式保證執(zhí)行語句間的層疊關(guān)系。begin 與關(guān)鍵字同行,end 另起一行。例如,always 語句塊使用時,或條件語句只有一條執(zhí)行語句時,都可以省略 begin + end 關(guān)鍵字。但為保證結(jié)構(gòu)的完整性,以及后續(xù)代碼的調(diào)試與修改,還是建議加入此類關(guān)鍵字。

   always @(posedge dout_clk or negedge rstn) begin
      if (!rstn) begin
         dout_en_r       <= 1'b0 ;
      end
      else begin
         dout_en_r       <= rd_en_wir ;
      end
   end

盡量使用 tab 鍵和空格,保證語句按照層級結(jié)構(gòu)對齊,變量、關(guān)鍵字、操作符之間也應(yīng)該留有空隙,便于邏輯判斷。

   generate
      if (DWO >= DWI) begin
         reg [DWI-1:0]         mem [(1<<AWI)-1 : 0] ;
         always @(posedge CLK_WR) begin
            if (WR_EN) begin
               mem[ADDR_WR]  <= D ;
            end
         end
      end
   endgenerate

模塊例化時,端口信號盡量與連接信號隔開,并各自對齊。連接信號為向量時指明其位寬,方便閱讀、調(diào)試。

    ram   u_ram(
        .CLK_WR          (clk),
        .WR_EN           (wren), //寫滿時禁止寫
        .ADDR_WR         (addr),
        .D               (wdata[9:0]),
        .Q               (rdata[31:0])
        );

例化多個相同的模塊時,盡量使用 generate 語句,避免過長的例化代碼描述。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號