Verilog代码规范

历史修改信息

版本 更改描述 更改人 批准人 修订日期/生效日期
A01 发布首版 ldy

一、目的

作为我司verilog开发过程中的输入文件,用于统一FPGA开发人员的代码风格。从而在满足功能和性能目标的前提下,能够规范代码和优化电路,增强代码的整洁度、可读性、可修改性、可维护性、可重用性、可移植性。另一方面有助于开发人员养成良好的编程习惯并理清思路,同时为FPGA选型提供指导。

二、开发流程

设计时需要把设计文档/方案写好。在设计文档中要把设计思路、 数据通路、状态机状态转移条件实现细节等描述清楚,在经过评审后才能开始编写代码,初看觉得很花费时间,但是从整个项目过程来看,绝对比一上来就编写代码节约时间,而且这种做法可以使项目处于可控、可实现的状态。

三、编写规范

  1. 项目组织形式

项目的文件统一存放在一个统一的文件夹下,根据各自功能的不同,分门别类的存放。由于编译器/开发环境的不同,存储的架构可能有所差别,但总体来说,需要至少包括以下内容:

DOC:存放项目相关的文档,包括该项目用到的datasheet、芯片规格书、设计文档等。

IP:存放官方自带的成熟IP以及项目中自己定义的常用/可复用的代码。

SIM:存放项目的仿真代码。

RTL:存放项目的 rtl 代码。这是项目的核心,文件名与 module 名称应当一致,建议按照模块的层次/子系统分开存放。

因此,组织形式如下所示:

XXX

|-- DOC

|--- IP

|-- SIM

|-- RTL

  1. 工程代码架构

Verilog 设计都是层次型的设计,是由模块一级级搭建出来的,需要按照合理的层次结构组织各个模块。顶层模块一般只实例化子模块,该模块除了内部互连线和模块实例化之外,尽量不要添加其他的逻辑,这样看起来简单直观,同时还有利于分布式的布局布线。即使要添加逻辑,也应该是很简单的 glue logic(胶合逻辑)。

  1. 单个文件构成

Verilog 文件主要有以下几个部分组成:文件声明,module IO 声明,宏定义, wire®定义,parameter 定义,module 具体实现。同时规定:

① 不使用 include 包含文件;

② 一个.V 只包括一个 module;

  1. 文件声明

每一个 Verilog 文件的开头,都必须有一段声明的文字。包括文件的版权,作者,创建日期以及内容介绍等,如下表所示。

// ************************ ***************************************  
// Copyright (C) xx Coporation 
// File name: xx.v  
// Author: xxxxxxxxxx  
// Date: 2012-01-01  
// Version: 1.0  
// Abstract: xxxxxxx  
  
//***************************************************************** 

如果对该文件进行了修改,请在开头声明中添加以下语句,如下表所示。

// Version: 1.1  
// Date: 2012-01-01  
// Abstract: XXX  
// Author: xxxxxxxxxx 
  1. IO/模块输入输出定义

采用 Verilog 2001 语法格式。下面是一个例子,包括 module 名字、输入输出、信号名字、输出类型、注

释。

module divider7_fsm (  
//input  
input sys_clk , // system clock; 
input sys_rst_n , // system reset, low is active; 
//output 
output reg clk_divide_7 // output divide 7 clk  
 );

规范:

① 一行只定义一个信号;

② 信号需要对齐;

③ 同一组的的信号放一起;

  1. Prameter 定义

规范:

① 对于本地的parameter,需要将 Prameter 定义放在紧跟着 module 的输入输出定义之后;

② Prameter 等常量命名全部使用大写。

③ 一行只定义一个信号;

对于状态机状态的parameter定义,为方便与后仿一致(后仿真会将状态优化编码为热独码),采用热独码编码。

如下所示:

// Prameter  
parameter S0 = 7'b0000000; 
parameter S1 = 7'b0000001; 
parameter S2 = 7'b0000010; 
parameter S3 = 7'b0000100; 
parameter S4 = 7'b0001000; 
parameter S5 = 7'b0010000; 
parameter S6 = 7'b0100000;
  1. WIRE/REG 定义

规范:

① 将 reg 与 wire 的定义放在紧跟着 prameter 之后;

② 建议具有相同功能的信号集中放在一起;

③ 信号需要对齐,reg 和位宽需要空 2 格,位宽和信号名字至少空四格;

④ 位宽使用降序描述,[6:0];

⑤ 时钟使用前缀 clk,复位使用后缀 rst;

⑥ 不能使用 Verilog 保留字作为信号名字;

⑦ 一行只定义一个信号;

//reg define  
reg [6:0] curr_st ; // FSM current state 
reg [6:0] next_st ; // FSM next state 
reg clk_divide_7 ; // generated clock,divide by 7 
//wire define  
wire [6:0] aa ; 
  1. 信号命名

WIRE类型一律采用w_xxx命名,如w_fifo_rd_en;

REG类型一律采用r_xxx命名,如r_ram_wr_data;

① 信号命名需要体现其意义,比如 fifo_wr 代表 FIFO 读写使能;

② 可以使用“_”隔开信号,比如 sys_clk;

③ 内部信号不要使用大写,也不要使用大小写混合,建议全部使用小写;

④ 顶层 PAD 信号名字建议全部使用大写,比如 CPU_WR 代表 CPU 写使能管脚;

⑤ 模块名字使用小写;

⑥ 低电平有效的信号,使用_n 作为信号后缀;

⑦纯延迟打拍信号使用_dly 作为后缀;

  1. Always 块描述方式

① if 与end需要结构对齐;

② 一个 always 需要配一个 begin 和 end;

③ always 前面需要有注释;

④ 信号和逻辑运算符有空格;

⑤ beign 建议和 always 放在同一行;

⑥ 一个 always 和下一个 always 空一行即可,不要空多上;

⑦ 时钟复位触发描述使用 posedge sys_clk 和 negedge sys_rst_n

⑧ 一个 always 块只包含一个时钟和复位;

⑨ 时序逻辑使用非阻塞赋值

⑩ 信号逻辑处理的位宽需要一致,即使+1,也要和信号保持一致;

// gen a 1S counter , 1S = 50000000 * 20ns  
always @(posedge sys_clk or negedge sys_rst_n) begin  
 if (sys_rst_n ==1'b0)  
 clk_cnt <= 26'b0; 
 else if ( clk_cnt == 26'd50000000 ) 
 clk_cnt <= 26'b0; 
else 
 clk_cnt <= clk_cnt + 26'b1; 
end 
always @(posedge sys_clk or negedge sys_rst_n) begin  
xxx
  1. 其他规范

  2. 要避免适应锁存器 Latch,也要避免出现因为敏感列表不全而生成的Latch。

如果在 always 中 case 或 if 语句的分支逻辑不全,就会生成锁存器,需注意检查它们的分支结构,将条件赋值语句写全,例如在 if 语句最后加一else,在 case 语句最后加一个 default。

  1. 模块应尽量采用“寄存输入,寄存输出”

即对于模块的输入信号尽量在寄存器锁存之后再用,但是如果输入信号是其他模块的寄存器输出,那么可以不用寄存器锁存;对于模块的输出信号,尽量用寄存器锁存之后再输出。这么做的可以使输入时序、输出强度和输出延迟都得到预测,可以更好的 Timing。

  1. 优先级

在表达式内使用括号表示运算的优先级。

  1. 仿真文件

针对每个模块撰写仿真文件,再进行该功能的聚类仿真,依次类推完成整个顶层仿真。

posted @ 2024-10-17 09:51  羊的第七章  阅读(77)  评论(1编辑  收藏  举报