如何寫出易於維護的Verilog程式碼?

眾所周知,用於FPGA開發的硬體描述語言(HDL)主要有兩種:

Verilog和VHDL

。其中,VHDL的出現時間要比Verilog早,而Verilog由於其簡單的語法,跟C語言的相似性,目前被各大公司廣泛使用。

其實在上大學時,我學習的就是VHDL語言。後來由於公司使用的都是Verilog,於是又重新學習了Verilog。好在有C語言基礎,Verilog很快就上手了。

如何寫出易於維護的Verilog程式碼?

Verilog標準文件主要有3個版本:

Verilog-1995

Verilog-2001

Verilog-2005

都是由IEEE頒佈。目前最新的Verilog標準是2005版,相比於前兩個版本,2005更簡潔,更靈活。

雖然一些官方的程式碼,如Xilinx一些IP核程式碼,為了相容以前的綜合工具,還是基於Verilog-2001標準,但我還是強烈建議你使用最新的Verilog-2005標準。

如何寫出易於維護的Verilog程式碼?

IEEE-2005

所以,本文是基於IEEE-2005語法標準,即《IEEE P1364-2005/D3:Draft Standard for Verilog ® Hardware Description Language》官方標準文件。

良好的程式碼規範可以提高程式碼的可讀性、可複用性、簡潔清晰,這也是一種職業素質的體現。

我們的目標是:

Write Nowhere,Read Everywhere

如何寫出易於維護的Verilog程式碼?

都有哪些內容?

命名

檔案命名

埠命名

變數命名

引數命名

結構

整體結構

埠宣告

空格和縮排讓程式碼更清晰

小括號增加可讀性

例化

註釋

其他

IEEE-2005標準下載

嵌入式物聯網需要學的東西真的非常多,千萬不要學錯了路線和內容,導致工資要不上去!

無償分享大家一個資料包,差不多150多G。裡面學習內容、面經、專案都比較新也比較全!某魚上買估計至少要好幾十。

點選這裡找小助理0元領取:

嵌入式物聯網學習資料(頭條)

如何寫出易於維護的Verilog程式碼?

如何寫出易於維護的Verilog程式碼?

命名

命名主要包括檔案和模組命名,埠命名,變數命名,引數命名等,概括來說就是:

有意義,簡短易讀

,重要的是

不要使用拼音

檔案命名

檔名和模組名保持一致,一個檔案只寫一個模組。

如何寫出易於維護的Verilog程式碼?

檔案命名

檔案命名要有含義,且簡短易讀,檔名統一使用小寫字母,並使用下劃線分割檔名。

底層驅動類模組命名,使用

drv_xxx

型別,如:

drv_led。v

drv_i2c。v

drv_ad7606。v

串列埠傳送一位元組:

uart_tx_byte。v

非同步和同步FIFO,並指示深度和寬度:

fifo_async_512_16。v

fifo_sync_256_64。v

TestBench檔名問原始檔名後加_tb,如原始檔

drv_led。v

,則對應的testbench檔案命名為

drv_led_tb。v

頂層模組統一命名為

top。v

,或

top_project_name。v

,如EEPROM讀寫示例工程頂層命名為

top_eeprom_demo。v

埠命名

埠命名和檔案命名一樣,統一使用小寫字母,並使用下劃線進行分割。

如果是頂層模組,而且是連線到實際的FPGA管腳,後加

_pad_i

_pad_o

_pad_io

來命名,表示連線到實際的FPGA硬體管腳上。

如何寫出易於維護的Verilog程式碼?

頂層埠命名

變數命名

時鐘訊號統一使用

clk

命名,如果是特定時鐘頻率,可以在後面新增時鐘頻率,如

clk_50m

復位訊號統一使用rst命名,如果是低電平有效,後加_n表示,如

rst_n

標誌位命名:

flag_rise/flag_fall/flag_clr

暫存器打拍訊號命名新增

_reg

reg rxd_reg

移位暫存器命名新增字尾

_sreg

reg [3:0] busy_sreg

部分通用的縮寫:

縮寫

全拼

含義

rst

reset

復位

clk

clock

時鐘

rd

read

讀取

wr

write

寫入

addr

address

地址

ack

acknowledge

響應

引數命名

Verilog中的引數類似於C語言中的define,主要有以下兩類

localparam

parameter

,兩者的區別是前者不可以在例化時進行引數傳遞,而後者可以在例化時進行引數傳遞。

其他的變數,檔名都是統一小寫,只有引數定義有

全部大寫的待遇

,當需要定義一些常量時,可以透過引數宣告指定一個有意義的名稱。如:

parameter LED_ON = 1‘b0;parameter LED_OFF = !LED_ON;parameter BAUD_RATE = 115200;parameter TIME_100MS = 25_000_000;

狀態機的狀態通常使用localparam來定義,並指定有意義的名稱,統一使用Sn_NAME的格式,並指定位寬。如:

localparam S0_IDLE = 4’d0;localparam S1_START = 4‘d1;localparam S2_DOING = 4’d2;localparam S3_END = 4‘d3;localparam S_FINISH = 4’d4;localparam S_ERROR = ‘d5;//auto adaptive

結構

整體結構

建議Verilog模組檔案按照如下結構進行書寫。

/* 1。檔案頭: 說明版權資訊,檔案功能,作者,版本歷史等 *//* 2。埠宣告: input/output/inout *//* 3。宏定義: `define *//* 4。引數定義: localparam/parameter *//* 5。暫存器定義: reg *//* 6。線網型別定義: wire *//* 7。互聯定義: assign *//* 8。時序邏輯描述: always */

示例:

/* 1。file head *//***********************************************************Copyright 2021 ’wechat:mcu149‘。 All rights reserved。FileName: drv_led。vFunction: led driverAuthor : mcu149SVN : SVN Revision: 1490 SVN Date: 2021-06-05 19:49:49Revision: 2020-09-09: Rev 1。0 2020-10-01: Rev 1。1************************************************************//* 2。input/output/inout */module drv_led( //Inputs input rst_n, input clk_50m, input en, //Outputs output led1, output reg led2 = 0 //define initial value is 0);/* 3。define *//* 4。parameter/localparam */parameter TIME_500MS = 32’d25_000_000;/* 5。reg */reg [31:0] cnt = 0;/* 6。wire */wire flag_toggle = (cnt == TIME_500MS);/* 7。assign */assign led1 = (en == 0) ? 0 : cnt[20];/* 8。always block */always @ (posedge clk_50m) begin if(!rst_n) cnt <= 0; else if(flag_toggle) cnt <= 0; else cnt <= cnt + 1;endalways @ (posedge clk_50m) begin if(!rst_n) led2 <= 0; else if(!en) led2 <= 0; else if(en) begin if(flag_toggle) led2 <= !led2; endendendmodule

埠宣告

輸入埠放在一起,輸出埠放在一起,雙向埠放在一起。如果某個輸出訊號需要確定初始值,可以在埠定義時直接進行指定,這也是Verilog-2005新新增的功能。

如何寫出易於維護的Verilog程式碼?

埠命名

這一點有些朋友可能是按照功能進行劃分,如連線同一晶片的放在一起。

輸入輸出分開放的好處是,在例化時可以很方便的區分哪些是輸入哪些是輸出。

空格和縮排讓程式碼更清晰

運算子兩端增加一個空格,可以讓程式結構更清晰,可讀性更高

縮排風格採用KR風格,即begin寫在行尾,不佔用單獨一行,end單獨佔用一行

縮排統一使用4個空格來代替TAB鍵

if/else等語句只有一行時,可以省略begin-end

合理新增空行進行塊區分,不同的always塊進行換行隔開

以下是兩種程式碼的書寫規範,合理縮排,合理增加空格大大增加了可讀性。

如何寫出易於維護的Verilog程式碼?

合理縮排

小括號增加可讀性

在學校裡有些考試題,為了考察學生對各種運算子優先順序的掌握程度,出一些反人類的題目。

而做實際專案不像考試,追求的是可讀性和易用性,所以當使用多個運算子時,為了增強可讀性,避免歧義,不要吝嗇使用小括號來表示運算的優先順序。

如何寫出易於維護的Verilog程式碼?

運算子優先順序

例化

例化可以認為是FPGA開發的靈魂所在了,例化的過程其實就是硬體模組的呼叫過程,比如我們用Verilog描述了一個3-8譯碼器的模組,可以在不同的地方去使用(例化)它,並分別命名為ut0/ut1/ut2等等,而且還可以在例化時指定引數,如串列埠傳送和接收模組,透過指定不同的引數來實現不同波特率的相容。

例化和埠宣告順序保持一致,輸入埠放在一起,輸出埠放在一起;

多位元訊號,在例化時需要指定位寬,以增加可讀性;

頂層模組只進行模組例化,不寫任何控制語句。

示例:

wire [7:0] rx_data;wire rx_done;wire rx_err;/* 串列埠接收1位元組 */uart_rx_byte #( 。BAUD_RATE(32‘d115200), 。EN_PARITY(2’d0), //0:無校驗位, 1:奇校驗, 2:偶校驗 。EN_STOP_2(1‘b0) //1:使能2位停止位)uart_rx_byte_0( // Inputs 。clk(clk_32m), 。rst_n(rst_n), 。rxd(uart_rxd), // Outputs 。data_rx(rx_data[7:0]), //指定位寬 。done(rx_done), 。err(rx_err));

註釋

不要相信什麼程式碼就是最好的註釋

!我不否認有些人的程式碼寫的就是很規範,命名合理,格式清晰。

但是我覺得你還沒有達到那種程度,不能保證每一個人都能讀懂沒有註釋的程式碼。註釋不僅是為了給別人看,更多的也是為了給自己看,

好記性不如爛筆頭

註釋統一使用

/**/

註釋的方式,或者使用與

//

混合使用,看個人習慣!

每個變數定義後需要註釋變數的功能;

每個always塊功能需要註釋;

狀態機狀態含義需要註釋;

條件語句的後面需要添加註釋;

程式碼修改,註釋也要隨之修改。

其他

合理使用generate for可以批次化定義和例化模組,減少程式碼量,提高可讀性;

testbench中使用task和function可以提高效率;

移位操作替換為拼接補0操作,更易讀;

時序邏輯統一使用非阻塞賦值,即

<=

符號;

一行字元不要超過80個,過長透過換行來處理;

先有頂層設計,然後劃分子模組功能,或者自底向上方法;

模組化設計,提高模組可複用程度;

能用case的儘量不要用if/else;

reg型別變數,根據需要看是否鎖存。

原文連結:https://mp。weixin。qq。com/s/Gx-iFGETTf8EBWgn8dgO8A

轉載自:嵌入式微處理器

原文連結:如何寫出易於維護的Verilog程式碼?

本文來源網路,免費傳達知識,版權歸原作者所有。如涉及作品版權問題,請聯絡我進行刪除