FPGA设计技巧与案例开发详解
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.2 Verilog HDL设计

3.2.1 Verilog HDL与VHDL的对比

1.VHDL与Verilog HDL的诞生与发展

VHDL诞生于1982年。在1987年年底,VHDL被IEEE和美国国防部确认为标准硬件描述语言。自IEEE公布了VHDL的标准版本IEEE-1076(简称87版)后,各EDA公司相继推出了自己的VHDL设计环境,或者宣布自己的设计工具可以和VHDL结合。此后,VHDL在电子设计领域被广泛接受,并逐步取代了原有的非标准的硬件描述语言。

Verilog HDL是由GDA(Gateway Design Automation)公司的PhilMoorby在1983年年末首创的,最初只设计了一个仿真与验证工具,之后又陆续开发了相关的故障模拟与时序分析工具。1985年,Moorby公司推出了第三个商用仿真器Verilog-XL,获得了巨大的成功,从而使得Verilog HDL迅速得到推广和应用。1989年,CADENCE公司收购了GDA公司,使得Verilog HDL成为该公司的独家专利。1990年,CADENCE公司公开发行了Verilog HDL,并成立了OVI(Open Verilog International)组织来负责Verilog HDL语言的发展,并促进Verilog HDL成为IEEE标准,即IEEE Standard 1364—1995。

2.Verilog HDL与VHDL的通用性对比

笔者当年在学校学习的是VHDL,那是在2009年的春天,当时FPGA还不像现在这样应用广泛,基于VHDL设计的电路严谨而且富有章程,只是逻辑性不够灵活。一年后,由于众多网友的推荐,笔者在一周内迅速转战Verilog HDL。有VHDL与C语言基础,学Verilog HDL语言会很快,但是重要的是不要把C语言的线程思路带到Verilog HDL中,因为FPGA是并行的。笔者认为,Verilog HDL目前具有更大的广泛性与通用性。

3.Verilog HDL与VHDL的关系就如同C语言与汇编的关系

Verilog HDL比较接近寄存器传输级,语法比较灵活,比较适合底层电路的描述。而VHDL比较死板,更适合系统级描述。笔者当年从VHDL转战Verilog HDL后,瞬间感受到由Verilog HDL带来的轻松愉快!对于初学者而言,从Verilog HDL入门较好,而且目前一些大企业,如华为等,都采用Verilog HDL设计电路。但同时学会或能看懂VHDL语言也非常重要。

4.硬件电路综合能力

由于GDA公司偏重硬件,所以Verilog HDL也不可避免地偏重硬件一些。因此,Verilog HDL的底层综合做得非常好。而VHDL由于严谨的风格,逻辑综合能力要比Verilog HDL出色一些。总之,Verilog HDL着重强调集成电路的综合,而VHDL强调组合逻辑的综合。

3.2.2 Verilog HDL的发展

Verilog HDL语言具有下述描述能力:设计的行为特性;设计的数据流特性;设计的结构组成;响应监控和设计验证方面的延时和波形产生机制。此外,Verilog HDL语言提供了编程语言接口,通过该接口可以在模拟期间和验证期间从设计外部访问设计,包括模拟的具体控制和运行。

Verilog目前已经发展到了System Verilog,尽管现在System Verilog用得还不是很多,但是针对系统级别设计的Verilog HDL,也许会是未来的一种趋势。如图3.18所示为Verilog HDL的发展历史和发展前景。

图3.18 Verilog HDL的发展历史和发展前景

笔者在本书中采用Verilog HDL语言进行逻辑设计,以独特的架构来完成基础及进阶的FPGA设计,希望能带给读者独特的思维及崭新的FPGA设计模式。

3.2.3 Verilog HDL的应用

代码风格就如同开发者其人,爱整洁的人必然不会写出邋遢的代码!好的代码不仅能在功能上实现要求,而且还能在风格上做到结构统一。永远记住,代码是写给别人看的,真正的“完美主义者”会对代码风格细节苛刻到极致。

www.pudn.com网站下载一份利用Verilog HDL实现流水灯的代码,代码如下所示。

/*跑马灯实验:利用计数器轮流点亮LED灯,实现各种动态效果*/
module ledwater(clk, rst, dataout);

input clk, rst;
output[11:0] dataout;
reg[11:0] dataout;

reg[22:0] cnt;

always@(posedge clk or negedge rst)
begin
    if(! rst) begin
      cnt<=0;
      dataout<=12'b111110_011111;     //为0的位代表要点亮的LED的位置
    end
    else begin
      cnt<=cnt+1;
      if(cnt==23'h7fffff) begin
          dataout[4:0]<=dataout[5:1];
          dataout[5]<=dataout[0];
          dataout[11:7]<=dataout[10:6];
          dataout[6]<=dataout[11];
        end
    end
end

endmodule

试问,这样的代码风格,您看了以后,心情如何?笔者修改后的代码如下所示:

module led_water
(
    //global clock
    input                  clk,
    input                  rst_n,

    //led interface
    output   reg   [11:0]    data_out
);
//--------------------------------
//Generate 4 modes led display, step 1
reg[22:0] cnt;
always@(posedge clk or negedge rst_n)
begin
    if(! rst_n)
      cnt <=0;
    else
      cnt <=cnt + 1'b1;
end

//--------------------------------
//Generate 4 modes led display, step 2
always@(posedge clk or negedge rst_n)
begin
    if(! rst_n)
      data_out <=12'b111110_011111;
    else if(cnt==23'h7fffff)
      begin
      data_out[4:0]   <= data_out[5:1];
      data_out[5]     <= data_out[0];
      data_out[11:7]   <= data_out[10:6];
      data_out[6]     <= data_out[11];
      end
    else
      data_out        <= data_out;
end

endmodule

试问,看了修改后的风格,您的心情又如何(当然如果实际运行时还需再加入文件头,注释等信息)?

一个优秀的代码风格,必须在满足功能和性能的前提下,增强代码的可读性、可移植性,Coding有风格的HDL。业界有很多公司规定了开发团队的Verilog HDL设计规范,Quartus II本身也有推荐的代码风格。为避免这些规范间的冲突,尤其是在命名方面,笔者在此主要通过多年来总结并且积累的Verilog HDL文件架构方面的经验,来阐述自成体系的、良好代码风格的架构。

1.Verilog HDL文件头编写规范

无论是一个Teamwork,还是一个工程师的设计,在HDL的编写中难免都会遇到版本修改、更新等问题,这就需要有一个规范的文件头。文件头必须包括设计者、设计时间、模块功能、修改、版本记录等内容。

每次查看Altera Handbook时,都可以看到清晰的版本备注信息,这样的做法便于版本的修正、管理等,如图3.19所示是Altera Handbook某一章节的版本注释。

图3.19 Altera Handbook某一章节的版本注释

笔者刚开始设计Verilog HDL时也没有注意这一点,但随着开始设计大型的工程,长时间不断地进行修正、升级,整个过程非常混沌,笔者才发现文件头极其重要。经过参照、总结,不断地修改、升级,到目前为止,笔者设计的文件头如下所示:

/*------------------------------------------------------------------------------------------
                            \\\|///
                          \\  --  //
                          (  @@  )
+---------------------------------------oOOo-(_)-oOOo-------------------------------+
CONFIDENTIAL IN CONFIDENCE
This confidential and proprietary software may be only used as authorized
by a licensing agreement from CrazyBingo (Thereturnofbingo).
In the event of publication, the following notice is applicable:
Copyright (C) 2012-20xx CrazyBingo Corporation.
The entire notice above must be reproduced on all authorized copies.
Author                :    CrazyBingo
Official Websites         :   http://www.crazyfpga.com
Email Address           :   crazyfpga@qq.com
Filename              :   Verilog_Template.v
Data                  :   2013-07-17
Description             :   The function of the Template.
Modification History      :
Data          Author      Version        Change Description
=======================================================
13/07/17       CrazyBingo   1.0            Original
---------------------------------------------------------------------------------------------
|                              Oooo                    |
+-----------------------------------------oooO--(   )----------------------------------+
                        (   )   )/
                          \(   (_/
                          \_)
--------------------------------------------------------------------------------------------*/

如上所示,文件头最开始包含版权信息(这对于规范而言极其重要),接着包含了个人信息及文件描述,最后包含版本的记录(这对于版本修订而言非常重要)。

无论代码量有多大,电路如何简单或复杂,代码都必须包含一个最基本的文件头。这里不仅关注代码功能的问题,而且还关注代码风格的优化问题。

2.Module列表编写规范

Module列表编写规范如下:

`timescale 1ns/1ns
module Verilog_Template
(
    //global clock
    input          clk,         //50MHz
    input          rst_n,        //global reset

    //user interface
    output   [7:0]   led_data     //board test led
);
endmodule

早期的Verilog HDL不支持将信号输入/输出列表放在module中,而是要求必须在后面进行定义。但随着Verilog HDL的发展,目前支持将信号输入/输出列表直接放在module中。笔者认为这样的设计可以减少添加或删除信号的累赘操作,同时可读性更好、可移植性更强,因此,Verilog HDL一如既往地采用这种写法,一劳永逸!

与此同时,还必须遵循以下原则。

(1)无论是否进行仿真,module前都必须写`timescale 1ns/1ns。

(2)全局时钟及复位写在最前面,分别命名为clk及rst_n(多个时钟时可用clk_50, clk_24区分)。

(3)相关信号根据时钟、复位、使能、控制端的顺序规划在一起,同时必须有注释。

(4)所有输入/输出、信号命名等,都必须严格对齐。

(5)所有信号注释必须全部对齐。

(6)为了解决不同编辑器的兼容,TAB用4个空格来代替。

(7)禁止使用中文注释。

备注:笔者曾见过某公司的规定,其中甚至要求所有逗号都必须对齐!

3.always模块编写规范

always模块编写规范如下:

//---------------------------------------
//Generate for 1s delay signal
localparam   DELAY_TOP=28'd50_000000;   //1s
reg[27:0]   delay_cnt;
always @(posedge clk or negedge rst_n)
begin
    if(! rst_n)
      delay_cnt <=0;
    else if(delay_cnt < DELAY_TOP -1'b1)
      delay_cnt <=delay_cnt + 1'b1;
    else
      delay_cnt <=0;
end
//counter for 1s delay is completed
wire   delay_1s=(delay_cnt==DELAY_TOP-1'b1)?1'b1:1'b0;

每一个always模块都必须自成体系,还必须遵循以下规范。

(1)每个always都必须有功能介绍。

(2)相关的寄存器定义必须写在当前always模块前。

(3)相关的宏定义也必须写在当前always模块前。

(4)所有if、else都必须对齐。

(5)else必须写完整。

(6)信号位宽必须写完整。

(7)每个逻辑符号前后各空一格。

(8)单/总线连出(delay_1s)写在always模块后。

4.Module的例化编写规范

Module的例化编写规范如下:

//---------------------------------------
//Generate water led display
led_input_display
#(
    .LED_WIDTH       (8)
)
u_led_input_display
(
    //global clock
    .clk               (clk),
    .rst_n             (rst_n),

    //led interface
    .led_en            (1'b1),
    .led_value          (led_value),
    .led_data           (led_data)
);

对于Module的例化,相当于C语言中的函数调用,需要注意信号列表的格式。

(1)每个Module在例化前都必须有module的功能说明。

(2)Module的例化名采用u_xx的格式。

(3)信号列表必须完全对齐。

(4)相关信号必须写在一起,并且要有注释。