【Verilog】编码规范-coding sytle

发布时间 2023-12-19 18:05:31作者: AnchorX

目前所在单位并没有代码规范文档,以致于阅读代码很吃力,并且久而久之自己写的代码可读性也没法保证。在参考了很多资料后,决定按以下规范来写:

一、命名规范

1、文件命名

a、每个文件中只包含一个module、class、package,文件名于文件内容名称应相同。

 

2、module、class、package、function、task命名

a、以 大驼峰 格式命名,package以Pkg结尾。

(*大驼峰:一般驼峰法指第一个单词以小写字母开始,从第二个单词开始以后的每个单词的首字母都采用大写字母,例如:myName、myAge;大驼峰法指第一个单词首字母也大写。)

b、module例化使用前缀:’U_’ 或 ‘Ux_’。

 

3、信号命名

a、所有信号均采用驼峰法命名方式。

b、前缀

前缀用于标志信号的特殊用途或特殊含义,以一个小写字母写在信号名前。常用的前缀有以下:

input端口: i , 例如:iAaaBbb
inout端口: b (bi-directional port), 例如:bAaaBbb
output端口: o , 例如:oAaaBbb
寄存器:r (register) ,例如:rAaaBbb 。
锁存器:l (latch) ,例如:lAaaBbb 。
低有效:n (negative),例如:nAaaBbb 。
异步信号:a (asynchronous),例如:aAaaBbb 。

c、后缀

后缀用于在不改变信号名情况下表示信号属性变化。命名方式:信号名 + ‘_’ + 后缀 。使用 ‘_’ 前的名称应该可以在代码中直接找到该信号。常用的后缀有以下:

寄存器下一个时钟周期的值:f (following),例如:rWrDataEn <= rWrData & ~rWrData_f。
信号取反:i (invert),例如:assign aaaBbbVld_i = ~aaaBbbVld;
同步后信号:s (synchronous),例如:aWrEn_s。

 

4、参数、宏命名

parameter  P_PARAM_A    = 2;
localparam P_PARAM_B    = 3;
localparam P_PARAM_A_LG = $clog2(PARAM_A);
parameter  type type_A  = logic [3:0];

a、由于 参数 和  表示常数,与普通信号不同,因此所有字母全部大写(因为全大写字符串在编辑器中高亮与普通字符串不同),以便于信号进行区分。只有传递数据类型的参数可以包含小写字母。除type参数以外,其他参数定义(parameter和localparam)以 ‘P_’ 开始。

b、在端口中定义顺序为: parameter > parameter type > localparam。

c、针对宏定义,推荐采用M_MCRO的命名方式,大写,M_前缀。

 

 

二、格式规范

1、文件头

每个设计文件都要包含文件头,端口处定义的参数和IO的完整注释要写在文件头中(防止代码中多行注释影响代码可读性),代码中可添加简要注释。文件头中还应包含一些时间、版本、模块作用等信息。文件头格式如下:

////////////////////////////////////////////////////////////////////////////////
// Copyright 2023 AnchorX Open Source. All rights reserved.                //
//                                                                            //
//                   ______________.___.___________                           //
//                   /   _____/\__  |   |\_   _____/                          //
//                   \_____  \  /   |   | |    __)                            //
//                   /        \ \____   | |     \                             //
//                   /_______  / / ______| \___  /                            //
//                           \/  \/            \/                             //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
// Project    :                                                            
// Module     :                                                          
// Author     : Anchor                                                          
// Email      : songyf7410@163.com                                               
// Description:                                                                 
// Modification History:
//   Date   |   Author   |   Version   |   Change Description
//==============================================================================
// 23-12-19 |    Anchor    |     0.1     | Original Version                                                             
////////////////////////////////////////////////////////////////////////////////

这里推荐一个可以生成ASCⅡ签名的网站:“ASCII Generator (network-science.de)

 

2、代码格式

2.1、通用格式

a、由于不同编辑器处理不同,对齐代码使用空格,禁止使用Tab ,一律使用 2 空格
b、begin 在当前行末尾,不重新开启一行, begin前添加一个空格。end 与 else 写在同一行。

always_comb begin
  if(...) begin
    ...
  end else if(...) begin
    ...
  end else begin
    ...
  end
end

c、语句间可以有1个或多个空格。多余一个空格可以方便对齐和查看(便于使用对齐插件查看代码)。
d、重要的 block,及包含信号定义的 block,需要添加 block name 。所有 module、 interface、package 和有名字的 block 主要添加对应的 ending name。block name 和 ending name 之前的 ‘:’ 前后都需要添加空格。

module DemoModule();
  always_comb begin : DemoBlock
    ...
  end : DemoBlock
endmodule : DemoModule

 

2.2、module端口格式

端口格式定义如下:

module DemoLib_ModuleXxxYyy // 单独一行,前后无空格。
import DemoAaaPkg::*;       // 引用package,单独一行,前后无空格。
import DemoBbbPkg::*;       // 多个package写在不同的行中。
#(P_A = "_",                // 第一个参数以 '#(' 开头,定义在新行中,前后无空格,省略parameter标识符。
  P_B = "_",                // 其他parameter在新的行中定义,定义前需要 2个空格 进行缩进。
localparam                  // 若存在local parameter,localparam在新的一行中定义,前后无空格。
  P_B_LG = $clog2(P_B),     // local parameter定义格式与parameter相同。
  P_C = P_A - 1
)(                          // 在新的行中写 '参数定义右括号' 和 '端口定义左括号'。
  input        clk,rst ,    // 端口在新行中定义,2个空格缩进。'clk,rst' 可以写在同一行。
  input        iEn     ,    // 端口定义顺序:input, inout, output。
  input        iDat    ,    // 同方向端口定义顺序:clock, reset, enable, data。
  inout        bVld    ,    // 端口和参数定义结尾的逗号分隔符可以对齐也可以不对齐。
  output logic oDat         // 代码中端口部分参数和信号后可添加简要注释,完整注释在文件头中添加。
);                          // 端口定义 右括号 及 分号 单独一行,前后无空格。
  ...
endmodule : DemoLib_ModuleXxxYyy //单独一行,前后无空格。添加 ending name。':' 前后各有一个空格。


module DemoLib_Aaa
(                           // 如果没有模块中没有参数,直接在新行中写接口定义左括号。
  input  clk,rst,
  input  iDat,
  output oDat
);
  ...
endmodule : DemoLib_Aaa

 

2.3、module例化格式

 模块例化时,参数在例化时通过 ‘#()’ 直接传递,尽量避免使用 defparam,因为在最新的标准中,已经不推荐使用defparam定义参数。模块例化可以在同一行完成,也可以分多行完成。示例代码:

Adder
  U_Adder( // 2个空格
    .a(a), // 4个空格
    .b(b),
    .o(o)
  );       // 与例化U_对齐

Sub #(
    .type_A(logic [3:0]),
    .type_B(logic [3:0]))
  U_Sub(
    .a(a),
    .b(b),
    .o(o)
  );

And #(.width(8)) U_And(.a(a),.b(b),.o(o));
And #(8) U_And(a,b,o);

 

 

3、设计规范

 3.1、信号定义

a、寄存器类型的信号要初始化。

 

3.2、位宽定义及固定值赋值

a、MSB写在左侧,LSB写在右侧。
b、LSB最好从0开始,如果有特殊需求,LSB可以从非零值开始,比如总线对齐:logic [31:2] BusAddr;
c、固定值赋值使用以下方式:

  • 0赋值使用:‘0,例如:assign dat = ‘0;
  • 全1赋值值使用:‘1,例如:assign dat = ‘1;
  • 某确定值使用:位宽 + ‘b/d/h/o + 数值,例如:assign dat = 8’d1;

d、尽量使用 [位置+:位宽] 或 [位置-:位宽] 方式赋值。

// Use like this.
assign xx = dat[16+:8];
assign yy = dat[16-:8];

// Dont use like this!!
assign xx = dat[23:16];
assign yy = dat[15:8];

 

3.3、逻辑电路设计规范(组合/时序)

a、用“//”做小于1行的注释,用“/* */”做多于1行的注释。

b、除移位寄存器外,每个always语句只对一个变量赋值,尽量避免在一个always语句出现多个变量进行运算或赋值。

c、在表达式中插入空格,避免代码拥挤,包括:

  • 赋值符号两边要有空格;
  • 双目运算符两边要有空格;
  • 单目运算符和操作数之间可没有空格;

示例如下: