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

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

3天内不再提示

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

FPGA之家 来源:FPGA之家 2022-12-22 14:33 次阅读

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语言学习者与我共同交流,有任何疑问可以到本博“答疑专区”提出,我必知无不言,言无不尽。

​​审核编辑 :李倩


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

    关注

    7

    文章

    2484

    浏览量

    46530
  • Verilog
    +关注

    关注

    28

    文章

    1326

    浏览量

    109302
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66772

原文标题:什么是良好的Verilog代码风格?

文章出处:【微信号:zhuyandz,微信公众号:FPGA之家】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    浅谈Verilog HDL代码编写风格

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

    如何在开始码代码的时候就考虑时序收敛的问题?

    硬件描述语言(verilog,systemVerilog,VHDL等)不同于软件语言(C,C++等)的一点就是,代码对应于硬件实现,不同的代码风格影响硬件的实现效果。
    的头像 发表于 09-21 09:07 677次阅读
    如何在开始码<b class='flag-5'>代码</b>的时候就考虑时序收敛的问题?

    IC设计之Verilog代码规范

    Verilog规范对于一个好的IC设计至关重要。
    的头像 发表于 08-17 10:14 675次阅读
    IC设计之<b class='flag-5'>Verilog</b><b class='flag-5'>代码</b>规范

    FPGA的Verilog代码编写规范

      注:以R起头的是对编写Verilog代码的IP设计者所做的强制性规定,以G起头的条款是建议采用的规范。每个设计者遵守本规范可锻炼命名规范性。
    的头像 发表于 08-15 16:23 1222次阅读

    分享一些优秀的verilog代码 高质量verilog代码的六要素

    高质量的verilog代码至少需要包含以下几个要素:可读性、功能、性能、标准化、稳定性、可定位。
    的头像 发表于 07-18 10:09 698次阅读
    分享一些优秀的<b class='flag-5'>verilog</b><b class='flag-5'>代码</b> 高质量<b class='flag-5'>verilog</b><b class='flag-5'>代码</b>的六要素

    Verilog代码封装后门访问

    关于仿真里的后门访问,之前的文章《三分钟教会你SpinalHDL仿真中的后门读写》中有做过介绍,其针对的都是针对以SpinalHDL中的代码进行的后门访问。今天来看看当封装了Verilog BlackBox时,在SpinalHDL仿真中如何进行后门访问
    的头像 发表于 07-15 10:22 511次阅读
    <b class='flag-5'>Verilog</b><b class='flag-5'>代码</b>封装后门访问

    Verilog中Pmod ALS的SPI接口代码

    电子发烧友网站提供《Verilog中Pmod ALS的SPI接口代码.zip》资料免费下载
    发表于 06-15 09:32 0次下载
    <b class='flag-5'>Verilog</b>中Pmod ALS的SPI接口<b class='flag-5'>代码</b>

    什么是良好Verilog代码风格

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

    基2FFT的verilog代码实现及仿真

    上文基2FFT的算法推导及python仿真推导了基2FFT的公式,并通过python做了算法验证,本文使用verilog实现8点基2FFT的代码
    的头像 发表于 06-02 12:38 696次阅读
    基2FFT的<b class='flag-5'>verilog</b><b class='flag-5'>代码</b>实现及仿真

    Verilog编码风格的建议

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

    FPGA开发环境的搭建和verilog代码的实现

    FPGA需要良好的数电模电基础,verilog需要良好C语言基础。
    发表于 05-22 15:04 510次阅读
    FPGA开发环境的搭建和<b class='flag-5'>verilog</b><b class='flag-5'>代码</b>的实现

    Vivado:ROM和RAM的verilog代码实现

    本文主要介绍ROM和RAM实现的verilog代码版本,可以借鉴参考下。
    的头像 发表于 05-16 16:57 926次阅读

    Verilog边沿检测的基本原理和代码实现

    本文将从Verilog和边沿检测的基本概念入手,介绍Verilog边沿检测的原理和应用代码示例。
    的头像 发表于 05-12 17:05 2347次阅读
    <b class='flag-5'>Verilog</b>边沿检测的基本原理和<b class='flag-5'>代码</b>实现

    FPGA开发环境的搭建和verilog代码的实现

    FPGA需要良好的数电模电基础,verilog需要良好C语言基础。
    的头像 发表于 05-11 17:30 1436次阅读
    FPGA开发环境的搭建和<b class='flag-5'>verilog</b><b class='flag-5'>代码</b>的实现

    如何使用参数化编写可重用的verilog代码

    我们将介绍如何使用verilog参数和generate语句来编写可重用的verilog 代码。 与大多数编程语言一样,我们应该尝试使尽可能多的代码可重用。这使我们能够减少未来项目
    的头像 发表于 05-11 15:59 713次阅读