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)相关信号必须写在一起,并且要有注释。