Verilog 编程规范

目录

Verilog 编程规范

1、编程规范重要性

2、工程组织形式

4、输入输出定义

5、parameter 定义

6、wire/reg 定义

7、信号命名

8、always 块描述方式

9、assign 块描述方式

10、空格和 TAB

11、注释

12、模块例化

13、其他注意事项


Verilog 编程规范

本节主要给大家介绍下编程规范,良好的编程规范是一个 FPGA 工程师必备的素质。

1、编程规范重要性

       当前数字电路设计越来越复杂,一个项目需要的人越来越多,当几十号设计同事完成同一个项目时候,大家需要互相检视对方代码,如果没有一个统一的编程规范,那么是不可想象的。大家的风格都不一样,如果不统一的话,后续维护、重用等会有很大的困难,即使是自己写的代码,几个月后再看也会变的很陌生,也会看不懂(您可能不相信,不过笔者和同事交流发现大家都是这样的,时间长不看就忘记了),所以编程规范的重要性显而易见。

       另外养成良好的编程规范,对于个人的工作习惯、思路等都有非常大的好处。可以让新人尽快融入项目中,让大家更容易看懂您的代码。

2、工程组织形式

工程的组织形式一般包括如下几个部分,分别是 docparrtl sim 四个部分。

XX 工程名
|--doc
|--par
|--rtl
|--sim
  • doc:一般存放工程相关的文档,包括该项目用到的 datasheet(数据手册)、设计方案等。不过为了便于大家查看,我们开发板文档是统一汇总存放在资料盘下的;
  • par:主要存放工程文件和使用到的一些 IP 文件;
  • rtl:主要存放工程的 rtl 代码,这是工程的核心,文件名与 module 名称应当一致,建议按照模块的层次分开存放;
  • sim:主要存放工程的仿真代码,复杂的工程里面,仿真也是不可或缺的部分,可以极大减少调试的工作量。

3、文件头声明

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

//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/19 11:56:45
// Design Name: 
// Module Name: shfit_reg4
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
//实验任务:
//本节的实验任务是设计一个 4 级串联寄存器电路。

        建议一个.V 只包括一个 module,这样模块会比较清晰易懂。

4、输入输出定义

        端口的输入输出有 Verilog 95 2001 两种格式,推荐大家采用 Verilog 2001 语法格式。下面是 Verilog 2001 语法的一个例子,包括 module 名字、输入输出、信号名字、输出类型、注释。

module flow_led(
//    input      sys_clk, //50MHz系统时钟(一个周期是20ns:1/50MHz=0.02us=20ns)
    //differential system clocks  //200MHz系统时钟(一个周期是5ns:1/200MHz=0.005us=5ns)
    input      sys_clk_p,       //system clock positive
    input      sys_clk_n,       //system clock negative
    input      sys_rst_n,       //系统复位,低电平有效
    
    output  reg  [3:0]  led     //4个LED输出信号
    );

  建议如下几点:

  • ① 一行只定义一个信号;
  • ② 信号全部对齐;
  • ③ 同一组的信号放在一起。

5、parameter 定义

建议如下几点:

  • ① module 中的 parameter 声明,不建议随处乱放;
  • ② 将 parameter 定义放在紧跟着 module 的输入输出定义之后;
  • ③ parameter 等常量命名全部使用大写。
//parameter define
parameter T = 5;       //黑金FPGA-xc7a5tfgg484-2-时钟周期为5ns 
parameter WIDTH = 25;
parameter COUNT_MAX = 25_000_000; //板载50M时钟=20ns,0.5s/20ns=25000000,需要25bit位宽.

6、wire/reg 定义

一个 module 中的 wire/reg 变量声明需要集中放在一起,不建议随处乱放。

因此,我们建议如下:

  • ① 将 reg wire 的定义放在紧跟着 parameter 之后;
  • ② 建议具有相同功能的信号集中放在一起;
  • ③ 信号需要对齐,reg 和位宽需要空 2 格,位宽和信号名字至少空四格;
  • ④ 位宽使用降序描述,[6:0]
  • ⑤ 时钟使用前缀 clk,复位使用后缀 rst
  • ⑥ 不能使用 Verilog 关键字作为信号名字;
  • ⑦ 一行只定义一个信号。
//input define
//reg          sys_clk;   //时钟信号
reg        sys_clk_p;  //时钟信号
wire       sys_clk_n;
reg        sys_rst_n;  //复位信号

//output define
wire  [3:0]   led;

7、信号命名

大家对信号命名可能都有不同的喜好,我们建议如下:

  • ① 信号命名需要体现其意义,比如 fifo_wr 代表 FIFO 读写使能;
  • ② 可以使用 “_” 隔开信号,比如 sys_clk
  • ③ 内部信号不要使用大写,也不要使用大小写混合,建议全部使用小写;
  • ④ 模块名字使用小写;
  • ⑤ 低电平有效的信号,使用_n 作为信号后缀;
  • ⑥ 异步信号,使用_a 作为信号后缀;
  • ⑦ 纯延迟打拍信号使用_dly 作为后缀。

8、always 块描述方式

always 块的编程规范,我们建议如下:

  • ① if 需要空四格;
  • ② 一个 always 需要配一个 begin end
  • ③ always 前面需要有注释;
  • ④ beign 建议和 always 放在同一行;
  • ⑤ 一个 always 和下一个 always 空一行即可,不要空多行;
  • ⑥ 时钟复位触发描述使用 posedge sys_clk negedge sys_rst_n
  • ⑦ 一个 always 块只包含一个时钟和复位;
  • ⑧ 时序逻辑使用非阻塞赋值。
//计数器对系统时钟计数,计时0.2秒,仿真时间设为10
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        counter <= 24'd0;
    else if (counter<24'd1000_0000)
    //else if (counter<24'd9) //仅用于仿真  
        counter <= counter + 1'b1;
    else
        counter <= 24'd0;
end

9、assign 块描述方式

assign 块的编程规范,我们建议如下:

  • ① assign 的逻辑不能太复杂,否则易读性不好;
  • ② assign 前面需要有注释;
  • ③ 组合逻辑使用阻塞赋值。
//计数到最大值时产生高电平使能信号
assign counter_en = (counter == (COUNT_MAX - 1'b1)) ? 1'b1 : 1'b0;

10、空格和 TAB

由于不同的解释器对于 TAB 翻译不一致,所以建议不使用 TAB,全部使用空格。

11、注释

添加注释可以增加代码的可读性,易于维护。我们建议规范如下:

  • ① 注释描述需要清晰、简洁;
  • ② 注释描述不要废话,冗余;
  • ③ 注释描述需要使用 “//” ;
  • ④ 注释描述需要对齐;
  • ⑤ 核心代码和信号定义之间需要增加注释。
//用于产生 0.5 秒使能信号的计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (sys_rst_n == 1'b0)
        counter <= 1'b0;
    else if (counter_en) // counter_en 为 1 时,counter 清 0
        counter <= 1'b0;
    else
        counter <= counter + 1'b1; 
end

12、模块例化

模块例化我们建议规范如下:

① moudle 模块例化使用 u_xx 表示。

//例化计时模块
time_count #(
    .MAX_NUM (TIME_SHOW)
) u_time_count(
     .clk (sys_clk ),
     .rst_n (sys_rst_n),
     
     .flag (add_flag )
);

//例化数码管静态显示模块
seg_led_static  u_seg_led_static (
    .clk         (sys_clk      ),
    .rst_n       (sys_rst_n    ),
    
    .add_flag    (add_flag     ),
    .sel         (sel          ),
    .seg_led     (seg_led      )
);

13、其他注意事项

其他注意事项如下:

  • ① 代码写的越简单越好,方便他人阅读和理解;
  • ② 不使用 repeat 等循环语句;
  • ③ RTL 级别代码里面不使用 initial 语句,仿真代码除外;
  • ④ 避免产生 Latch 锁存器,比如组合逻辑里面的 if 不带 else 分支、case 缺少 default 语句;
  • ⑤ 避免使用太复杂和少见的语法,可能造成语法综合器优化力度较低。

良好的编程规范是大家走向专业 FPGA 工程师的必备素质,希望大家都能养成良好的编程规范。

你可能感兴趣的:(fpga开发,dsp开发,嵌入式硬件,硬件工程)