0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

什么是良好的Verilog代码风格?

FPGA之家 2022-12-22 14:32 次阅读

1. 前言

之前在公司负责制定代码规范,费了九牛二虎之力,终于整理出来一份文档。由于保密规定的缘故,无法与大家直接分享这份文档,但是文档中的大部分规范都是我自己长期总结出来的,在这里也与大家分享一下。

2. 代码示范

为求直观,首先贴上一份示范代码,然后我再进行逐条详细解释。

以下代码是我之前做的一个同步FIFO模块,代码如下:

001 //======================================================
002 // Copyright (C) 2015 By Kellen.Wang
003 // mail@kellen.wang, All Rights Reserved
004 //======================================================
005 //Module:sync_fifo
006 //Author:KellenWang
007 //Contact:kellen.wang124@gmail.com
008 //Date:Jan.17.2015
009 //=======================================================
010 //Description:
011 //========================================================
012 modulesync_fifo #(
013 parameterDEPTH =32,
014 parameterDATA_W =32
015 ) (
016 inputwireclk ,
017 inputwirerst_n ,
018 inputwirewreq ,
019 inputwire[DATA_W-1:0] wdata ,
020 outputwirefull_flg ,
021 inputwirerreq ,
022 outputwire[DATA_W-1:0] rdata ,
023 outputwireempty_flg
024 );
025 `ifdef DUMMY_SYNC_FIFO
026 assignfull_flg =1'd0;
027 assignrdata =32'd0;
028 assignempty_flg =1'd0;
029 `else
030 `include "get_width.inc"
031 //====================================================
032 //ConstantDefinition:
033 //===================================================
034 localparam DLY =1'd1;
035 localparam FULL =1'd1;
036 localparam NOT_FULL =1'd0;
037 localparam EMPTY =1'd1;
038 localparam NOT_EMPTY =1'd0;
039 localparam ADDR_W = get_width(DEPTH-1);
040 //==================================================
041 //VariableDefinition:
042 //==================================================
043 reg[ADDR_W-1:0] waddr;
044 reg[ADDR_W-1:0] raddr;
045 wire[ADDR_W-1:0] waddr_nxt;
046 wire[ADDR_W-1:0] raddr_nxt;
047 //==================================================
048 //LogicDesign:
049 //==================================================
050 assignwaddr_nxt = waddr +1;
051 assignraddr_nxt = raddr +1;
052 assignfull_flg=(waddr_nxt==raddr)?FULL:NOT_FULL;
053 assignempty_flg=(waddr==raddr)?EMPTY:NOT_EMPTY;
054 assigniwreq = wreq & ~full_flg;
055 assignirreq =1'd1;
056
057 always@(posedgeclkornegedgerst_n)begin
058 if(!rst_n)begin
059 waddr <= #DLY 0;
060 end
061 elseif(wreq & (full_flg == NOT_FULL))begin
062 waddr <= #DLY waddr_nxt;
063 end
064 end
065
066 always@(posedgeclkornegedgerst_n)begin
067 if(!rst_n)begin
068 raddr <= #DLY 0;
069 end
070 elseif(rreq & (empty_flg == NOT_EMPTY))begin
071 raddr <= #DLY raddr_nxt;
072 end
073 end
074
075 //synopsys translate_off
076 `ifdef DEBUG_ON
077 iError_fifo_write_overflow:
078 assert property (@(posedgewclk)disableiff(!rst_n)(iwreq&!full_flg));
079 iError_fifo_read_overflow:
080 assert property (@(posedgerclk)disableiff(!rst_n)(irreq&!empty_flg));
081 `endif
082 //synopsys translate_on
083
084 //====================================================
085 //Sub-Module:
086 //====================================================
087 shell_dual_ram #(
088 .ADDR_W (ADDR_W ),
089 .DATA_W (DATA_W ),
090 .DEPTH (DEPTH )
091 ) u_shell_dual_ram (
092 .wclk (clk ),
093 .write (iwreq ),
094 .waddr (waddr ),
095 .wdata (wdata ),
096 .rclk (clk ),
097 .read (irreq ),
098 .raddr (raddr ),
099 .rdata (rdata )
100 );
101 `endif // `ifdef DUMMY_SYNC_FIFO
102 endmodule

下面详细讲解一下我在进行这个模块设计的时候遵循了哪些希望向大家推荐的代码风格。

3. 代码风格

3.1 规则总览

在设计这个模块的时候,我主要遵从了以下几条规则:

  1. Verilog2001标准的端口定义

  2. DUMMY模块

  3. 逻辑型信号参数赋值

  4. 内嵌断言

  5. memory shell

3.2 规则解释

接下来我们逐一解释以下为什么要这么做。

3.2.1 Verilog2001标准的端口定义
01 modulesync_fifo #(
02 parameterDEPTH =32,
03 parameterDATA_W =32
04 ) (
05 inputwireclk ,
06 inputwirerst_n ,
07 inputwirewreq ,
08 inputwire[DATA_W-1:0] wdata ,
09 outputwirefull_flg ,
10 inputwirerreq ,
11 outputwire[DATA_W-1:0] rdata ,
12 outputwireempty_flg
13 );

相对于verilog1995的端口定义,这种定义方式将端口方向,reg或wire类型,端口位宽等信息都整合到了一起,减少了不必要的重复打字和出错几率,也使得代码长度大大缩短,非常紧凑。另外,用于控制模块编译的例化参数都被放置于端口定义之前,有利于在模块例化时进行配置,也是IP化模块最好的编写方式。例如在这个同步fifo设计中,我希望这个模块的深度和数据位宽是可以配置的,那么我就把这2个参数放在端口声明的前面。另外要说明的一点是,一旦在模块中出现了可以配置的例化参数,最好在文件头的描述部分增加有关这些参数有效值范围的说明。

3.2.2 DUMMY模块

在做项目的时候,一个大的系统会被分割成很多细小的部分,由不同的人负责,设计完成后上传到具有版本管理功能的服务器上。有时候有的人忘记在上传代码之前进行严格测试,或者根本传错了版本,就会造成其他人仿真报错。有时候我们希望用FPGA进行原型验证,但是有的模块设计根本还没有完成,而反复修改FPGA顶层文件又会显著提高版本出错的几率,最好的办法就是将这些有问题的模块临时替换成dummy模块。dummy模块不仅可以隔离问题模块,还可以显著加速仿真过程,可谓一举两得。传统上大家在完成设计之后会另外建立一个只有接口代码的空文件,例如dummy_sync_fifo.v,当需要将sync_fifo变成dummy的时候,就将文件清单中的文件名改掉,但这样的方式会增加文件,容易造成管理的混乱,反复修改文件清单显然也不是一个好的做法。我推荐的dummy方式如下所示:

1

`ifdef DUMMY_SYNC_FIFO

2

assignfull_flg =1'd0;

3

assignrdata =32'd0;

4

assignempty_flg =1'd0;

5

`else

6

...

7

`endif // `ifdef DUMMY_SYNC_FIFO

这里推荐的方式是在模块的顶层文件中写一个宏控制的综合控制逻辑,当DUMMY_SYNC_FIFO宏被定义的时候,综合工具就只会将整个模块综合成没有任何逻辑的dummy模块了。

3.2.3 逻辑型信号用参数赋值

很多人做RTL设计的时候为了省事,在代码中对数值型信号和逻辑型信号完全不做区分,用同样的方式赋值。如果这种时候稍微做一点点改变,就能让你的代码可读性大大提高,例如:

1 assignfull_flg = (waddr_nxt == raddr);

1 localparam FULL =1'd1;
2 localparam NOT_FULL =1'd0;
3 assignfull_flg=(waddr_nxt==raddr)?FULL:NOT_FULL;

你觉得哪一个阅读起来更直观?而将所有逻辑型信号的数值参数化的另外一个好处,就是在如veridi这样业界良心的仿真软件中,你可以在仿真波形中直接看到FULL或NOT_FULL这样的文字参数,大大提高了波形的友好程度,比起你在那痛苦地目测这根线到底是高电平还是低电平轻松多了。

3.2.4内嵌断言

有的IC设计工程师觉得断言是验证工程师才需要学习的东西,其实不然,好的模块内嵌断言可以及时发现模块内部的错误状态,防止模块的不当使用,极大地提高模块的验证效率。但是,断言属于不可综合的语句(在ZEBU这种变态系统中使用除外),直接放在模块设计代码中需要进行必要的特殊处理,如下所示:

1 //synopsys translate_off
2 `ifdef DEBUG_ON
3 iError_fifo_write_overflow:
4 assert property (@(posedgewclk)disableiff(!rst_n)(iwreq&!full_flg));
5 iError_fifo_read_overflow:
6 assert property (@(posedgerclk)disableiff(!rst_n)(irreq&!empty_flg));
7 `endif
8 //synopsys translate_on

首先使用了综合指令的注释synopsys translate_off以防综合工具对这段语句进行综合,然后再加上一个DEBUG_ON的宏进行二次保护。上例中的断言可以保证这个sync_fifo在使用过程中一旦发生“过读”或者“过写”就会立刻打印报错信息。

3.2.5memory shell

IC设计中经常需要用到memory,memory通常不是用verilog描述实现的(这种方式实现不是不可以,而是性价比太低了),而是需要调用FPGA里的存储资源,或是由后端生成。但是在进行仿真的时候,我们不妨用verilog写一个行为模型来替代实现。这种原型验证和仿真验证的不一致,导致了跟dummy模块设计一样的麻烦,那就是需要对代码进行反复修改。另外,在不同项目中有可能根据不同的情况采用不同的后端物理层来生成memory,或者由于不同的工艺生成不同的memory,这种memory的接口协议可能多少会有一些不一样,同样会导致需要在不同工艺和项目中修改IP代码,造成出错的风险。比较好的做法就是像以下例子中那样使用一个memory shell来隔离这种修改。

01 shell_dual_ram #(
02 .ADDR_W (ADDR_W ),
03 .DATA_W (DATA_W ),
04 .DEPTH (DEPTH )
05 ) u_shell_dual_ram (
06 .wclk (clk ),
07 .write (iwreq ),
08 .waddr (waddr ),
09 .wdata (wdata ),
10 .rclk (clk ),
11 .read (irreq ),
12 .raddr (raddr ),
13 .rdata (rdata )
14 );

这个memory shell定义了一组标准的接口,用于在IP模块中进行例化。而在这个memory shell模块内部,可使用宏控制的综合分支控制语句根据不同情况综合不同的memory或仿真模型。当同一个size的memory被多个模块调用的时候,这种设计的好处更加明显,因为当接口协议变化时,你只需要改动memory shell文件内部的连接逻辑就可以了,这个shell在不同模块中的例化语句都是不需要改动的。

4. 总结

良好的代码风格可以提高代码的可读性,减少犯错机会,也可以提高代码调试的效率,但积累良好的代码风格不是一朝一夕的事,需要一步一个脚印,一点点积累。本文长期更新,如果你有好的想法和建议,欢迎在本文底部留言。另外也欢迎其他verilog语言学习者与我共同交流,


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Verilog
    +关注

    关注

    28

    文章

    1327

    浏览量

    109312
  • 代码
    +关注

    关注

    30

    文章

    4556

    浏览量

    66814
收藏 人收藏

    评论

    相关推荐

    FPGA工程的Verilog HDL初学者设计要点

    要养成良好Verilog代码风格,要先有硬件电路框图之后再写代码的习惯,设计出良好的时序,这样
    的头像 发表于 11-19 13:54 3061次阅读
    FPGA工程的<b class='flag-5'>Verilog</b> HDL初学者设计要点

    vhdl良好代码风格25点要求

    良好代码编写风格可以满足信、达、雅的要求。在满足功能和性能目标的前提下,增强代码的可读性、可移植性,首要的工作是在项目开发之前为整个设计团队建立一个命名约定和缩略语清单,以文档的形式记
    发表于 02-06 11:48

    国外经典verilog代码

    Language",by D.E.Thomas and P.R. Moorby例子代码比较实在,可以看下国外的verilog代码风格。缺点是没有注释,大家不明白可以提出来。
    发表于 11-02 14:05

    VHDL+Verilog良好代码编写风格(二十五条)

    良好代码编写风格可以满足信、达、雅的要求。在满足功能和性能目标的前提下,增强代码的可读性、可移植性,首要的工作是在项目开发之前为整个设计团队建立一个命名约定和缩略语清单,以文档的形式记
    发表于 08-15 17:53

    勇敢的芯伴你玩转Altera FPGA连载35:Verilog代码风格概述

    `勇敢的芯伴你玩转Altera FPGA连载35:Verilog代码风格概述特权同学,版权所有配套例程和更多资料下载链接:http://pan.baidu.com/s/1i5LMUUD 所谓
    发表于 12-27 10:07

    Linux内核编码风格(编程代码风格推荐)

    这是翻译版本,英文原版是linux源码Documentation文件夹下的CodingStyle一个良好风格的程序看起来直观、美观,便于阅读,还能有助于对程序的理解,特别在代码量比较大情况下更显
    发表于 08-24 09:45

    什么是良好Verilog代码风格

    推荐的代码风格。3、代码风格1、规则总览在设计这个模块的时候,我主要遵从了以下几条规则:Verilog2001标准的端口定义DUMMY模块逻
    发表于 06-02 14:48

    Verilog HDL代码描述对状态机综合的研究

    有许多可综合状态机的Verilog代码描述风格,不同代码描述风格经综合后得到电路的物理实现在速度和面积上有很大差别。优秀的
    发表于 12-24 00:52 30次下载
    <b class='flag-5'>Verilog</b> HDL<b class='flag-5'>代码</b>描述对状态机综合的研究

    Altera代码风格讲义--作者:骏龙小马

    一个讲解Altera代码风格的讲义,适合初学者看看,verilog代码风格
    发表于 11-17 18:07 0次下载

    verilog_代码资料

    verilog_代码资料,非常实用的代码示例。
    发表于 02-18 15:00 36次下载

    Verilog HIDL的RTL设计风格指南资源下载

    Verilog HIDL的RTL设计风格指南资源下载
    发表于 04-13 10:09 9次下载

    什么样的Verilog代码风格是好的风格

    代码是给别人和多年后的自己看的。 关于Verilog代码设计的一些风格和方法之前也写过一些Verilog有什么奇技淫巧?
    的头像 发表于 10-24 15:23 1085次阅读

    什么是良好Verilog代码风格

    相对于verilog1995的端口定义,这种定义方式将端口方向,reg或wire类型,端口位宽等信息都整合到了一起,减少了不必要的重复打字和出错几率,也使得代码长度大大缩短,非常紧凑。
    的头像 发表于 12-22 14:33 596次阅读

    Verilog编码风格的建议

    良好的编码风格,有助于代码的阅读、调试和修改。虽然 Verilog 代码可以在保证语法正确的前提下任意编写,但是潦草的编码
    的头像 发表于 06-01 16:27 496次阅读
    <b class='flag-5'>Verilog</b>编码<b class='flag-5'>风格</b>的建议

    浅谈Verilog HDL代码编写风格

    深层次的问题,对于这个行业来说可能我才是一直脚踩在门外面。所以这篇文章是写给一些刚开始学习FPGA、Verilog HDL的同学,我看过一些大神写的代码,然后尽量模仿大神写法,经过好几个大神的影响和自己
    的头像 发表于 11-20 10:04 320次阅读
    浅谈<b class='flag-5'>Verilog</b> HDL<b class='flag-5'>代码</b>编写<b class='flag-5'>风格</b>