欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Verilog coding style

程序员文章站 2022-07-04 15:00:23
...

Coding Style

1. 文件书写规范

1.1 文件头格式

文件头包含三部分内容:

  1. 版权信息声明,该部分保持不变;

  2. 文件描述:包括文件名、版本信息、日期、作者,特别关注Description项,可以添加使用限制、模块功能说明等内容。

  3. 日志信息:该日志信息仅用于记录较大改动(如需求变更),对于仿真BUG修正记录则不用放在这里。

    //-------------------------------------
    //COPYRIGHT(c) 2021, huawei
    //ALL right are reserved.
    //-------------------------------------
    //              Design information
    //-------------------------------------
    //File name           :iic_top.v
    //Version             :v0.1
    //Date                :2021/04/10
    //Author              :WA
    //Decription          :
    //-------------------------------------
    //Log:
    //       v0.1     :initial
    //
    //-------------------------------------
    

1.2 文件命名规则

  1. 文件名 必须与 模块名相同(模块命名遵照命名规则)。
  2. 单个文件只能定义一个模块,无论该模块代码行数多少,禁止在单个文件内定义多个模块。

1.3 有效注释

  1. 单个文件中代码注释(不包含文件头 和 日志信息)不低于30%
  2. 注释信息采用英文
  3. 注释说明添加在目标代码的前面,力求准确

2. 变量命名规范

2.1 参数、宏命名规范

参数、宏命名采用 大写字母+下划线+数字 的方式,禁止使用其他类别的字符

//-----------Parameter Define-----------------
parameter DEC_IDLE             = 3'h0;
parameter SYS2DEC              = 3'h1;
parameter DEC                  = 3'h2;
parameter DEC2SYS              = 3'h3;
parameter DEC_REAL_DONE        = 3'h4;

2.2 普通变量的命名

变量命名遵循简洁、清晰、有意义的原则,禁止私有&&随意的命名,好的变量命名能够提高程序的健壮性和可维护性。

  1. 变量命名采用 小写字母+下划线+数字 的方式,禁止使用其他类别的字符

  2. 除IP Core外,独立开发模块命名 同 变量命名规则

  3. 变量命名应表征该变量的具体含义,禁止随意使用a,b,x,temp等没有含义的单个字符串来命名

  4. 对于低电平有效变量,在名称后面加“_n”或者“_b”,例如rst_n

  5. 读?写是能信号命名为xxx_we/xxx_re,高电平有效;低电平有效的读写使能信号命名为xxx_wen/xxx_ren,其他高有效使能信号采用后缀“_ena”标记,低有效使能信号采用后缀“、_ena_n”标记

  6. 边沿变量后缀为“_pos”和“_neg”, 例如ack_pos表示总线响应变量ack的上升沿

    always @ (posedge clk or negedge rst_n)begin
        if(rst_n==1'b1)
            ncu_ack_1z <= `TD 1'b0;
        else begin
            ncu_ack_1z <= `TD i_ncu_ack;
        end
    end
    assign ncu_ack_pos = i_ncu_ack & (~ncu_ack_1z)
    
  7. 打拍变量采用“xz”后缀表示,x表示数字;禁止组合逻辑采用“_z“命名

    [email protected](posedge clk)begin
    	drx_data_1z <= i_drx_data;
        drx_data_2z <= drx_data_1z;
        drx_data_3z <= drx_data_2z;
        drx_data_4z <= drx_data_3z;
    end
    
  8. 【推荐】对于流水线处理业务推荐采用”xstg“命名方式,x表示数字,stg取stage缩写。优点:流水线划分明确,便于调试。下图为 UART接口接收端到APB-S端转换的设计方案:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9zwxEHv1-1618033851392)(coding style.assets/image-20210410100413267.png)]

    如图采用_xstg命名方式,所有变量对应的层次明确,例如rx_data_2stg只在rx_vld_2stg而非rx_vld_1stg有效时有效。该命名凡是可以有效避免”_xd“类变量作为判断条件时产生混乱

2.3 端口变量命名

由于顶层连线工作量庞大且容易出错,端口命名规范区分以及模块和低级子模块。

  1. 对于以及模块,端口命名规则如下:
    1. 输出端口的命名格式:o_输出模块_信号名称,输出端口以小写的字母o加下划线开头,后面跟输出模块的模块名,后缀以可表征端口功能的信号名。例如:o_cpm_fc_clk,该名称表示变量是CPM模块的输出,功能是作为FC的clock。
    2. 输入端口的命名格式:i_输出模块_信号名称,输入端口以小写的字母i加下划线开头,后面跟输出模块的模块名,后缀以可表征端口功能的信号名。例如:i_cpm_fc_clk,该名称表示模块的输入,来源是CPM模块,功能是FC clock
  2. 【推荐】输入配置信号:i_scfg信号名称:例如i_scfg_interlve_ena,表示ECC校错码使能。scfg表示static config。在逻辑设计中存在大量静态配置信号(该型号在逻辑/芯片运行过程中不会轻易改变),如果该类信号大量应用在高频时钟域,那么就会造成时序紧张,但实际上对这类信号的时序要求并不需要非常苛刻,通过名称加以区别,在后端中放宽时序约束,有利于提高项目时序性能。

2.4 推荐变量缩写

全称 缩写 中文含义
acknowledge ack 应答
address addr(ad) 地址
arbiter arb 仲裁
check chk 校验,如CRC校验
clock clk 时钟
config cfg Configuration,配置
control ctrl 控制
count cnt 计数
data in din(di) 数据输入
data out dout(do) 数据输出
decode de 译码
decrease dec 减一
delay dly 延迟
disable dis 不使能
error err 错误(指示)
enable ena 使能
frame frm
generate gen 生成,如CRC生成
grant gnt 申请通过
increase inc 加一
input in(i) 输入
length len (帧、包)长
nmport nm 网管相关
output out(o) 输出
packet pkt 与帧相同
priority pri 优先级
pointer ptr 指针
rd enable ren 读使能
read rd 读(操作)
ready rdy 应答信号或准备好
receive rx (帧数据)接受
request req 服务、仲裁请求
reset req
segment seg
source src 源(端口)
statistic stat 统计
timer tmr 定时器
switch sf Switch fabric
temporary tmp 临时
transmit tx 发送(数据帧)相关
Valid vld(v) 有效、校验正确
wr enable wen 写使能
write wr 写操作

3. 编码规范

3.1 缩进&&排版格式说明

  1. 统一缩排取4个空格宽度:Gvim下Tab必须转义成4个空格键,否则禁止使用Tab键
  2. Begin-end排版方式见下图:如果只有一条语句,那么begin-end可以省略
[email protected](posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        mcu_ack_1stg <= 1'b0;
        mcu_ack_2stg <= 1'b0;
    end else begin
    	mcu_ack_1stg <= `TD i_mcu_ack   ;
        mcu_ack_2stg <= `TD mcu_ack_astg;
    end
end

3.2 模块端口声明

模块端口声明推荐采用下图左侧的声明法师,规则为:

  1. 声明应简介高效,对于同一信号的所有属性(如wire/reg、位宽方向)一次性全部声明,避免多处声明

  2. 属于同一组的段楼应排布在一起,组成信号再按照input/output分开声明

  3. 不同组织间信号由空行/注释进行区分

    module Sample(
        input                           clk                      ;
        input                           rst_n                    ;
        
        //DATA RX Access RAM Interface
        output reg                      o_drx_ramce              ;
        output reg                      o_drx_ramwe              ;
        output reg [7:0]                o_drx_rambs              ;
        output reg [31:0]               o_drx_ram_A              ;
        output     [DATA_WID-1:0:0]     o_drx_ram_D              ;
        input      [DATA_WID-1:0:0]     i_drx_ram_Q              ;
        input                           i_drx_ramvalid           ;  
        //Error Correct & Save DMA Status Access RAM Interface
        output reg                      o_esc_ramwe              ;
        output reg [7:0]                o_esc_rambs              ;
        output reg [31:0]               o_esc_ram_A              ;
        output     [DATA_WID-1:0:0]     o_esc_ram_D              ;
        input      [DATA_WID-1:0:0]     i_esc_ram_Q              ;
        input                           i_esc_ramvalid           ;
    );
    

3.3 模块例化

在模块实例化时,端口信号传递必须基于名称,禁止基于顺序。且例化模块名称必须是U_xxx.

fc_syn U_fifo_chk_st0_syn(
    .rst_n_cki       (i_cpm_fc_trx2_rst_n  ),
    .rst_n_cko       (i_cpm_fc_rst_n       ),
    .clki            (i_cpm_fc_trx1_clk    ),
    .clko            (i_cpm_fc_trx1_clk    ),
    .sigi_p          (chk_st0_ok_txrx      ),
    .sigo_s          (chk_st0_ok_sys       )
);

3.4 模块内部变量声明

变量命名见“见信号命名规范”小节

  1. 变量声明时要要注意分组性:有相关性的信号定义在一起,信号组之间可以通过注释或者空行来分隔开。禁止随意地定义和代码中间定义
  2. 全部变量复位处理
  3. 各个模块内部都采用异步复位模式,项目统一在某个特定模块内做异步复位同步化处理

3.5 降序定义数组

使用降序定义向量有效位顺序,最低为为0

wire  [7:0]    rx_ori_data8      ;
wire  [15:0]   rx_ori_data16     ;
reg            rx_ram_data_updata;

3.6 【推荐】禁止使用for、generate、数组

3.7 逻辑复杂度:if…else 和 ?:

“if else”结构:适用于复杂脚尖判断的语句;运算符?:适用于简单条件判断的语句。对于复杂逻辑,采用if…else,对于规则性的或者简单的可以用?:

assign rx_ori_data8 = o_rx_ori_dcnt[1:0] == 2'h0 ? rx_ori_data[7:0]   :
                      o_rx_ori_dcnt[1:0] == 2'h1 ? rx_ori_data[15:8]  :
                      o_rx_ori_dcnt[1:0] == 2'h2 ? rx_ori_data[23:16] :
                                                   rx_ori_data[31:24] ;

3.8 "if…else"结构和“case”结构

if…else结构综合的结构可能时与或非门构成的,也可能是一组多路选择器,而case结构综合的结果一般回事多路选择器,但对于可以优化的case综合工具汇总和出更简单的结构。所以对于可以写成平行结构的条件,优先写成case结构,例如地址译码等,条件之间有重复和嵌套清华则写成if…else结构。

3.9 ()和“”

当逻辑条件比较复杂时,用()来隔开各个分条件的边界,避免由运算符优先级不同导致的歧义

if(&a==1'b1 && !flag==1'b1 || b==1'b1)         ×
if((&a==1'b1) && (!flag==1'b1) || (b==1'b1))   √

3.10 FSM统一使用三段式状态机

3.11 阻塞赋值与非阻塞赋值

  1. 时序电路建模时,用非阻塞赋值
  2. 锁存器电路建模时,用非阻塞赋值
  3. 用always块建立足额逻辑建模时,用阻塞赋值
  4. 禁止在同一个always块中混用非阻塞赋值阻塞赋值
  5. 阻塞赋值时不要使用延迟

3.12 always单变量赋值

  1. 禁止在多个always块中对同一bit变量进行赋值
  2. 推荐在单个always块只驱动同一个变量

3.13 时序逻辑与组合逻辑

  1. 禁止用非时钟信号作为时序敏感列表

    input                    clk                   ;
    input                    rst_n                 ;
    input                    i_rx_vld              ;
    input     [7:0]          i_rx_data             ;
    
    reg       [7:0]          rx_data_1stg          ;
    
    [email protected](posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)
            rx_data_1stg <= `TD 8'h0;
        else if(i_rx_vld == 1'b1)
            rx_data_1stg <= `TD i_rx_data;
        else
            ;
    end															√		
    
    [email protected](posedge i_rx_vld or negedge rst_n)begin
        if(rst_n==1'b0)
            rx_data_1stg <= `TD 8'h0;
        else
            rx_data_1stg <= `TD i_rx_data;						×
    end
    
  2. 时序逻辑需要加延时时,利于仿真查看。组合逻辑禁止使用延时。

    assign rx_ori_data8 = o_rx_ori_dcnt[1:0] == 2'h0 ? rx_ori_data[7:0]   :
                          o_rx_ori_dcnt[1:0] == 2'h1 ? rx_ori_data[15:8]  :
                          o_rx_ori_dcnt[1:0] == 2'h2 ? rx_ori_data[23:16] :
                                                       rx_ori_data[31:24] ;    √
    assign rx_ori_data8 = `TD o_rx_ori_dcnt[1:0] == 2'h0 ? rx_ori_data[7:0]   :
                              o_rx_ori_dcnt[1:0] == 2'h1 ? rx_ori_data[15:8]  :
                              o_rx_ori_dcnt[1:0] == 2'h2 ? rx_ori_data[23:16] :
                                                           rx_ori_data[31:24] ;×
    

    3.14 禁止使用三态信号

    各个以及模块及下级模块内部禁止使用三态信号

    3.15 跨时钟域信号处理

    模块内的所有跨时钟域逻辑集中到同一子模块内,方便维护及后续的流程

    3.16 模块端口例化只能直连

    顶层Asic body不能包含glue logic,只能直连,禁止有组合逻辑,禁止在实例化的时候直接取反

    fc_syn U_fifo_chk_st0_syn(
        .rst_n_cki       (i_cpm_fc_trx2_rst_n  ),
        .rst_n_cko       (i_cpm_fc_rst_n       ),
        .clki            (i_cpm_fc_trx1_clk    ),
        .clko            (i_cpm_fc_trx1_clk    ),
        .sigi_p          (chk_st0_ok_txrx      ),
        .sigo_s          (chk_st0_ok_sys       )         √
    );
    fc_syn U_fifo_chk_st0_syn(
        .rst_n_cki       (i_cpm_fc_trx2_rst_n   ),
        .rst_n_cko       (i_cpm_fc_rst_n        ),
        .clki            (i_cpm_fc_trx1_clk     ),
        .clko            (i_cpm_fc_trx1_clk     ),
        .sigi_p          (chk_st0_ok_txrx       ),
        .sigo_s          ((chk_st0_ok_sys&sig_en))        ×
    );
    

    3.17 FPGA宏

_st0_syn(
.rst_n_cki (i_cpm_fc_trx2_rst_n ),
.rst_n_cko (i_cpm_fc_rst_n ),
.clki (i_cpm_fc_trx1_clk ),
.clko (i_cpm_fc_trx1_clk ),
.sigi_p (chk_st0_ok_txrx ),
.sigo_s ((chk_st0_ok_sys&sig_en)) ×
);


### 3.17 FPGA宏

当有为适配FPGA结构而作的代码修改,要能被全局宏”FPGA“控制,当这个宏没有被定义时,针对FPGA的所有特定逻辑要被屏蔽掉。
相关标签: 芯片 verilog