FPGA编程规范

文章目录


前言

每个工程师的代码风格各不一样,当工程较大,面临需要多个工程师共同完成,或者面临当工程师离职后代码交接等问题。若代码规范未统一,阅读代码将是非常吃力的。下文是作者从各大论坛、网站、书籍收集整理的Verilog编程规范。


一、工程命名

工程名体验了机台信息和板卡信息,工程命名规则如下:

机台_板卡

二、设计目录

清晰的文档命名能够让我们思路非常的清晰,所以FPGA工程文件夹的目录要求层次鲜明,归类清晰。以Xilinx工程自带的文件架构为主,此外在工程主界面需创建DOC文件夹,用以存放工程的说明文档。
在这里插入图片描述

三、编程风格

1.命名规则

1.(规则)区分大小写

所有信号(signal),变量(variable)以及模块(module)的名字都用小写字母,常量名(参数(parameter)和宏(macro))用大写字母。不要依赖大小写给标识符增加语义。
说明: 这样做是为了和业界的习惯保持一致,避免了在大小写敏感的工具中可能会遇到的问题(Verilog语言是大小写敏感),同时也可以很容易地从代码中辨认出参数(parameter)。
示例:

不好的代码风格:

parameter width = 16;
input[width - 1 : 0] DataIn;

好的代码风格:

parameter WIDTH= 16;
input[WIDTH- 1 : 0] data_in;

2.(规则)模块命名

  1. 在系统设计阶段应该为顶层文件和每个一级模块进行命名;\
  2. 顶层文件命名方法是:机台名_板卡名_top;
  3. 一级模块命名的方法是:将模块英文名称的单词首字母组合起来,形成3到5个字符的缩写。若模块的英文名只有一个单词,可取该单词的前三个字母。一级模块定义:在顶层文件中例化的模块。
  4. 对于二级以下模块(含二级模块),命名方法是:一级模块名字_ + 模块功能缩写;
    示例:
    a机台,b板卡的顶层文件命名为a_b_top;
    一级模块Arithmetic Logical Unit命名为alu;
    一级模块Data Memory Interface命名为dmi;
    一级模块Decoder命名为dec;
    一级模块CPU里面的二级模块,功能是完成flash的控制功能,命名为:cpu_flash_ctl;

3.(规则)信号命名

  1. 信号的命名由几个单词连接而成,用下划线(‘_’)来分隔名字中的不同部分;
  2. 尽量使用缩写,缩写要求能基本表明本单词的含义;单词除常用的缩写方法外(如:clock->clk,write->wr,read->rd等),一律取该单词的前3-4个字母(如:frequency->freq,variable->var等);
  3. 信号名长度不要太长,原则上不超过28个字符;
  4. 不能用“reg”作为最后的后缀,因为综合工具会给寄存器加上reg,如果命名里就用reg作为后缀,则扰乱了网表的可读性;
    说明: 这样可以增强程序的可读性,并能避免标识符过于冗长。
    示例:
    不好的:
wire [9:0] addresscontrolenable;
reg [15:0] i,q;

好的:

wire [9:0] addr_ctl_en;
reg [15:0] fir_out_datai;
reg [15:0] fir_out_dataq;  

4.(规则)避免关键字

在RTL源码的设计中任何元素包括端口、信号、变量、函数、任务、模块等的命名都不能取Verilog和VHDL语言的关键字。

5.(规则)文件命名

文件名要和模块名相同,在一个文件中只用一个模块,在不同的层级上尽量使用统一的信号名,这样容易跟踪信号,网表调试也容易。

6.(规则)时钟和复位信号命名

对于时钟信号使用前缀clk_,并使用有含义的缩写构成时钟信号,对于同一个时钟信号,在所有的模块中名字保持一致。
对于复位信号使用前缀rst_
,并使用有含义的缩写构成复位信号,对于同一个复位信号,在所有的模块中名字保持一致。
示例:

clk_lbus_33m,clk_base_92m16,rst_lbus_n。

7.(规则)低电平有效信号命名

对于低电平有效的信号,使用后缀*_n;
示例:

rst_lbus_n,ad7680_cs_n;

8.(规则)仿真文件命名

用于仿真测试的文件的名字与被测试模块名字一一对应。添加后缀*_tb;

9.(规则)声明所有使用的信号

模块中所有用到的信号必须在信号声明部分进行声明;如果一个信号名没做声明Verilog将假定他为一位宽的wire变量。

10.(建议)前缀使用

系统级信号命名使用前缀sys_*,时钟和复位信号除外;

11.(建议)后缀使用

异步信号命名使用后缀*_a;
三态信号命名使用后缀*_z;
多周期路径第n个周期使用的信号使用后缀*_pn;
使用触发器或者SRL延迟n clk cycle的信号使用后缀*_ln;

12.(建议)模块之间接口信号命名

模块之间的接口信号,命名分为两个部分,第一部分表明数据方向,其中数据发出方在前,数据接收方在后,第二部分为数据名称:若某个信号从一个模块传递到多个模块,其命名应视信号的主要路径而定,在不同的子模块中尽量采用相同的名字;端口和连接端口的信号尽可能采用相同的名字。
示例:
cpu_mmu_wr_req,该信号含义为CPU模块到MMU的写请求。

13.(建议)跨时钟域信号命名

用于跨时钟域信号传递消除亚稳态的两级寄存器,前一个寄存器输出信号命名为*_meta,第二个寄存器使用*_meta作为输入,*_sync作为输出;

2 注释和文本规范

1.(规则)文件头

每个设计文件开头应包含如下注释内容:公司名称、作者、创建时间、文件名、所属项目、顶层模块、所需库、使用的仿真器和综合工具(运行平台和版本)、模块名称及实现功能和关键特性描述、文件创建和修改记录(包括修改版本号、修改时间、修改人名字、修改内容)。

2.(规则)注释使用

1.使用//进行的注释行在//后加一个空格。并以分号结束;
2.使用/* */进行的注释,//各占用一行,并且顶头。
3.尽量在每个always块之间加一段注释,增加可持续性和便于调试;
4.注释应该与代码一致,修改程序的时候一定要修改相应的注释;
5.注释不应该重复代码已表明的内容,而是简介式点明程序的突出特征。
示例:

// Edge detector used to synchronize the input signal;

/*
Edge detector used to synchronize the input signal;
*/

3.(规则)端口定义

1.端口定义按照功能块划分,每个功能块中按照输入、输出、双向的顺序,各个功能块之间要有空行或注释为间隔;
2.每行声明一个端口并有注释,注释在同一行;
3.用下述顺序声明端口,不同类型的端口声明使用一个空行间隔;

Input:
clocks
resets
enables
other control signals
data and address lines
Outputs:
clocks
resets
enables
other control signals
data and address lines

4.(规则)时延单位和精度定义

在模块端口声明之间定义时延单位和时延精度,和文件头及端口声明各有一个空行间隔,格式为:timescale 1ns / 100ps;

5.(规则)模块区域划分

模块按照下列功能块顺序组织:
文件头
时延单位和时延精度
端口声明
参数声明
信号声明
逻辑功能
各个功能块之间要有空行或注释作为间隔;

6.(规则)独立成行

每一行语句独立成行。尽管VHDL和Verilog都允许一行可以写多个语句,但每个语句独立成行可以增加可读性和可维护性。

7.(规则)缩进

1.用缩进提高行和嵌套语句的可读性;
2.缩进一般采用制表符Tab对语句对齐和缩进,Tab键采用4个字符宽度,可在编辑器中设置;
3.不要使用连续的空格来进行语句的对齐;
4.各种嵌套语句尤其是if…else语句,必须严格的逐层缩进对齐。

8.(规则)总线顺序

总线的有效位顺序定义从MSB到LSB,如data[4:0];

9.(规则)例化

1.模块名、模块例化名统一,例化前加大写“Un_”区分,其中n表示多次例化标识;
2.使用名字相关的显示映射而不要采用位置相关的映射;
3.输入和输出每类端口之间一个空行来提高可读性;
4.模块例化时不允许存在未连接的信号;
这样可提高代码的可读性和方便debug连线错误。

10.(规则)空行

分节书写,各节之间加1到多个空行。如每个always,initial语句都是一节。每节基本上完成一个特定的功能,即用于描述某几个信号的产生。在每节之前有几行注释对该节代码加以描述,至少列出本节中描述的信号的含义。

11.(规则)空格

1.不同变量,以及变量与符号、变量与括号之间都应当保留一个空格;
2.Verilog关键字与其他任何字符串之间都应当保留一个空格;
3.逻辑运算符,算术运算符,比较运算符的两侧各留一个空格,与变量分割开来;
4.单操作数运算符例外,直接位于操作数前,不使用空格;

12.(规则)语句对齐

1.同一个层次的所有语句左端对齐;
2.Initial、always等语句块的begin关键词跟在本行的末尾,相应的end关键词与initial、always对齐。这样做的好处是避免因begin独占一行而造成行数太多;
3.Initial、always等语句块的关键词顶头书写;

13.(建议)保持注释比例

使用适当的注释来解释所有的always进程、函数、端口定义、信号含义、变量含义或信号组、变量组以及常量的意义等。注释应该放在它所注释的代码附近,要求简明扼要,只要住够说明设计意图即可,避免过于复杂;保持20%-25%的注释率。注释描述代码的功能,而不是行为。
示例:
这样的注释纯粹是废话

//increment addr
addr = addr + 1’b1;

这样的注释就好得多

//Move the read addr to the next element
addr = addr + 1’b1;

14.(建议)无效代码

无效代码删除掉,不要注释;

15.(建议)模块互联

1.顶层模块应只是内部模块间的互联,尽量避免再做逻辑,如不能出现对reg变量赋值等。这样做的目的是为了能更有效的综合,因为在顶层模块中出现中间逻辑,综合工具不能把子模块中的逻辑综合到最优。
2.端口连接时避免使用表达式;

16.(建议)数值分组

书写数值时每隔4个bit用下划线(“_”)隔开,如32’h0000_0000。

17.(建议)不直接使用数字

在设计中不要直接使用数字,作为例外,可以使用0和1。建议采用参数定义代替直接数字。同时,在定义常量时,如果一个常数依赖另一个常数,建议在定义该常量时用表达式表示出这种关系。用parameter或`define来定义变量,禁止使用defparams。
示例:

不好的编码风格:

wire [7:0] my_in_bus;
reg [7:0] my_out_bus;

好的编码风格:

`define MY_BUS_SRIE	8
wire [MY_BUS_SRIE - 1:0] my_in_bus;
reg [MY_BUS_SRIE - 1:0] my_out_bus;

18.(建议)行长度

保持每行小于或等于80个字符,如超出,则要换行。这样做都是为了提高代码的可读性,保留边空,方便打印,保持代码的清晰,美观和层次感。

19.(建议)代码拷贝

代码需要拷贝时,注意修改相应的注释,以免产生错误的指导,导致理解上的错误。

3 编码原则

1.(规则)避免使用latch

避免使用产生任何latch。产生latch的情况有:组合逻辑中if语句缺乏else字句,case语句中各个条件所处理的变量不同;避免方法是:对所有输入条件都给出输出,在最终优先级的出发上使用else语句而不用else if。

2.(规则)定义完整的敏感表

1.对于组合模块,敏感表中必须包含被always所利用的所有信号,这通常意味着所有出现的赋值语句右边和条件表述式中的信号,可使用alwys @ *;
2.对于时序模块,敏感表必须包含时钟、异步复位信号;
3.确保敏感列表中不包含不必要的信息,否则会降低仿真性能;

3.(规则)模块输出寄存器化

对所有模块输出加一级寄存器。这样做使输出驱动强度和输入延迟可预测,使得模块的综合过程更简单。

4.(规则)优先级

用括号来表示执行的优先级,而不是依靠操作符本身的优先顺序;

5.(规则)根据功能选用条件语句

有优先级的建议使用if语句,case语句用于描述平行逻辑,即须确保不同的条件是互斥的;

6.(规则)总线对齐

赋值或者条件判断时要注明比特宽度,注意表达式的位宽匹配;

7.(规则)禁止组合环

禁止组合换(就是没有寄存器的反馈环路。这种结构在仿真和综合的时候都会有问题)组合环非常难以测试,因为他很难设计成一个已知的状态;

8.(规则)阻塞赋值和非阻塞赋值

1.时序逻辑使用非阻塞赋值;
2.Latch使用非阻塞赋值;
3.组合逻辑使用阻塞赋值;
4.同一个always模块不允许同时有阻塞赋值和非阻塞赋值;
5.不要在不同的always中对同一变量赋值;
6.如果要用always语句同时进行时序和组合逻辑建模时,一定使用非阻塞赋值;

9.(规则)禁止使用内部三态

禁止使用内部三态电路,建议用多路选择电路代替内部三态电路;

10.(规则)禁止使用任务

RTL级代码禁止使用task。原因是task根据调用的情况不同,可综合成组合逻辑电路,也可综合成时序逻辑电路,增加了电路的歧义性;

11.(规则)case语句

1.case语句中如果不需要优先级,那么必须确保不同的条件是互斥的;
2.case语句中必须覆盖所有的条件,不管是指定还是用default语句,如果可能的话在default语句中吧x值赋给输出;
3.在每个case语句中都要使用begin/end结构,并使用缩格;

12.(规则)数值指定宽度

对于数值一律指定进制和宽度,否者默认数值宽度是32bit,会导致比预想的大得多的算数单元;

13.(规则)显示表明条件

显示表明判决条件。
示例:

不好的编码:

if(signal_ctl)
……

好的编码:

if(signal_ctl == 1’b1)
……

14.(建议)区分关键路径

关键路径和非关键路径逻辑放在不同模块;

15.(建议)保持一个模块一个时钟

尽可能在整个设计中只使用一个主时钟,同时只使用同一个时钟沿,主时钟走全局时钟网络,跨时钟域信号使用两级reg去除亚稳态。这样可以让综合器综合出更优的结果。

16.(建议)时钟使用

1.时钟信号选用全局时钟缓冲器BUFG;
2.避免使用门控时钟;
3.避免使用内部产生时钟信号;
4.不要用时钟或复位信号作数据或使能信号,也不能用数据信号作为时钟或复位信号;
5.时钟信号必须连接到全局时钟管脚上,禁止用计数器分频后的信号做其他模块的时钟,而要用改成时钟使能的方式,否则这种时钟满天飞的方式对设计的可靠性极为不利,也大大增加了静态时序分析的复杂性;

17.(建议)复位信号

1.复位信号采用异步低电平有效信号,外部复位信号连接到芯片全局复位输入端(这些管脚提供较低的抖动),复位使初始化状态可预测,防止出现禁用状态,为避免释放复位时和时钟冲突,复位信号要同步化;
2.确保所有寄存器只被简单的复位信号控制,尽可能避免内部产生的条件复位信号,建议模块内部所有寄存器应在同一时间内被复位;
3.复位的条件表达式及命名要和always敏感列表中的描述相统一。

18.(建议)避免例化具体门级电路

在设计中避免实例化具体的门级电路。门级电路可读性差,且难于理解和维护,如果使用特定工艺的门电路,设计将变得不可移植,如果必须实例化门电路,建议采用独立于工艺库的门电路。

19.(建议)避免例化具体门级电路

使用函数
如果同一段代码需要重复多次,尽可能使用函数,以避免冗长的逻辑和子表达式。如果有可能,可以将函数通用化,以使得它可以复用。注意,内部函数的定义一般要添加注释,这样可以提高代码的可读性;

4 状态机设计规范

1.(规则)状态定义

状态定义用parameter定义,不要使用define宏定义的方式。 define宏定义在编译时自动替换整个设计中所定义的宏,而parameter仅仅定义模块内部的参数,定义的参数不会与模块外的其他状态机混淆。

2.(规则)状态编码

使用独热码来定义状态机状态。

3.(规则)保持层次

使有限状态机FSM保持在层次中的自己所在的那一级,不允许综合工具在输出和下一个状态译码逻辑之间共享资源。

4.(规则)处理所有状态

必须包含对所有状态都处理,不能出现无法处理的状态使状态机失控。

5.(规则)三段式状态机

状态机要写成三段式的,第一个always块,描述对应当前状态的状态寄存器,非阻塞赋值;第二个always块,描述下一状态的状态寄存器,阻塞赋值;第三个always块,描述输出,阻塞赋值;
示例:
第一段:我要去哪儿?(时序电路,由时钟触发)

always @(posedge clk or negedge rst_n) begin       
    if(!rst_n) begin          
        current_state <= IDLE; // 复位后状态机处于空闲态           
end       
    else begin           
        current_state <= next_state; // 更新状态           
end   
end

第二段:我怎样去?(组合电路,条件符合立刻就变)

always @(*) begin 
      if("进入状态的条件") begin
           next_state = "想要进入的状态";
      end
      else if("进入状态的条件") begin 
          next_state = "想要进入的状态"; 
      end 
      ...... 
      ...... 
      ...... 
end

第三段:去了干啥?(时序电路,时钟触发)
al

ways @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        "复位状态机中使到的变量(寄存器)";
    end
    else begin
        case(current_state)
            "状态1": begin
                "要干的事";
                ......
            end
            "状态2": begin
                "要干的事";
                ......
            end
            ......
            ......
            ......
            default: begin
                "默认情况下要干的事";
            end
        endcase
    end
end

6.(规则)状态机完备性

一个完备的状态机应该具备初始化(reset)状态和默认(default)状态。

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: FPGA Verilog编程规范是指在使用Verilog进行FPGA设计时应遵循的一系列规范和准则。通过遵守这些规范,可以提高设计的可读性、可维护性和可重用性,从而更好地完成FPGA设计任务。 以下是一些常见的FPGA Verilog编程规范: 1. 注释规范:对于每个模块、端口和信号,都应添加详细的注释,以便理解其功能和作用。 2. 端口规范:对于每个模块的输入输出端口,应指定其方向(输入/输出)和宽度(位数)。 3. 变量命名规范:变量的命名应具有描述性,并使用驼峰命名法或下划线分隔单词。 4. 模块规范:模块应具有清晰的输入输出接口、良好结构和适当的功能划分。 5. 忽略无用的警告:避免设计中出现不必要的警告,以确保代码的整洁和可靠性。 6. 合适的时间和空间建模:根据设计需求,选择适当的时间和空间的建模方法和语法。 7. 参数化设计:合理使用参数化设计,以便在不同场景下方便地修改设计参数。 8. 文件和文件目录管理:确保代码和文件结构的整洁,使用合理的文件名和文件目录组织方式。 9. 避免使用不推荐的语言特性:避免使用已被弃用或不推荐使用的语言特性,以提高代码的可移植性和兼容性。 10. 遵循公司或项目的编程规范和工作流程:如果有特定的公司或项目编程规范和工作流程,应遵守并在设计中加以体现。 总之,遵循FPGA Verilog编程规范可以提高设计的质量和效率,并促使代码更易于维护和理解。 ### 回答2: FPGA Verilog编程规范是指在使用Verilog进行FPGA设计时需要遵守的一些规范和约定。这些规范旨在提高代码的可读性、可维护性和可重用性,同时还可以确保设计的正确性和性能。 首先,命名规范编程规范中的重要部分。命名应该具有一定的描述性,能够准确反映变量、信号或模块的用途。变量和信号应该使用小写字母和下划线,而模块名采用大写字母开头的驼峰命名法。此外,命名应该具备一致性和易读性,以便在团队协作中更方便地理解代码。 其次,代码布局也是一个重要的方面。应该使用缩进和合适的空格来使代码具有良好的层次结构和可读性。同时,应该避免使用过长的代码行,可以使用换行符将代码分成多行,方便查看和理解。 第三,模块化设计是FPGA Verilog编程规范中的关键概念。应该将复杂的功能划分成多个模块,每个模块负责一个特定的任务。模块应该按照功能和层次进行组织,并且应该编写清晰的接口定义和文档注释,以方便其他人使用和理解。 此外,应该注意避免使用不明确的语法和技术。应该优先选择可读性好、简洁明了的语法和技术,以避免产生歧义和错误。 最后,对于代码的注释和文档也是必不可少的。应该为代码添加适量的注释,解释代码的作用、原理和使用方法,以方便后续维护和团队交流。同时,还应该编写清晰的文档,用于记录设计的性能要求、接口定义和使用方法等重要信息。 在FPGA设计中,遵守Verilog编程规范可以提高代码的质量和可维护性,减少设计错误和调试时间。因此,遵守编程规范FPGA设计者应该重视的一个方面。 ### 回答3: FPGA Verilog编程规范是一种用于FPGA设计的编码规范,旨在提高代码的可读性、可维护性和可重用性。它定义了设计工程师在编写Verilog代码时应遵循的一系列规则和标准。 首先,FPGA Verilog编程规范要求采用模块化设计方法。这意味着将设计划分为多个模块,每个模块负责一个特定的功能或任务。每个模块应该有清晰的输入和输出接口,以便于与其他模块的集成。 其次,规范强调代码的结构和命名的重要性。代码应该有清晰的缩进和层次结构,以增强可读性。变量和信号的命名应该具有描述性,以便于理解其用途和功能。 此外,规范要求遵循良好的命名约定。比如,模块名称应该以大写字母开头,信号和变量名称应该以小写字母开头。常量应该使用全大写字母表示。这些命名约定有助于提高代码的可读性和可维护性。 规范还要求进行适当的注释。注释应该解释代码的功能、用途和设计意图。它们可以帮助其他开发人员理解代码,并在维护或修改代码时提供指导。 此外,规范还包括一些特定的Verilog编码实践,例如使用非阻塞赋值语句(<=)来避免时序问题,以及使用延迟语句(#)来控制时序信号的生成等。 通过遵循FPGA Verilog编程规范,可以提高代码质量和可维护性,减少错误和调试时间,并支持代码重用。这样的规范在团队开发中尤为重要,可以提高团队成员之间的合作效率和代码交付的质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭郭的柳柳在学FPGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值