前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一周掌握FPGA Verilog HDL语法 day 5

一周掌握FPGA Verilog HDL语法 day 5

作者头像
FPGA技术江湖
发布2020-12-29 16:51:22
1.1K0
发布2020-12-29 16:51:22
举报
文章被收录于专栏:FPGA技术江湖FPGA技术江湖

大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。

今天给大侠带来的是一周掌握FPGA Verilog HDL 语法,今天开启第五天。

上一篇提到了case语句、循环语句(forever、repeat、while、for)、结构说明语句(initial、always、task、 function)等,此篇我们继续来看编译预处理,结合实例理解理论语法,会让你理解运用的更加透彻。下面咱们废话就不多说了,一起来看看吧。

编译预处理

Verilog HDL语言和C语言一样也提供了编译预处理的功能。“编译预处理”是Verilog HDL编译系统的一个组成部分。Verilog HDL语言允许在程序中使用几种特殊的命令(它们不是一般的语句)。Verilog HDL编译系统通常先对这些特殊的命令进行“预处理”,然后将预处理的结果和源程序一起在进行通常的编译处理。

在Verilog HDL语言中,为了和一般的语句相区别,这些预处理命令以符号“ `”开头(注意这个符号是不同于单引号“ '”的)。这些预处理命令的有效作用范围为定义命令之后到本文件结束或到其它命令定义替代该命令之处。Verilog HDL提供了以下预编译命令:

`accelerate,`autoexpand_vectornets,`celldefine,`default_nettype,`define,`else,`endcelldefine,`endif,`endprotect,`endprotected,`expand_vectornets,`ifdef,`include,`noaccelerate,`noexpand_vectornets , `noremove_gatenames , `noremove_netnames ,`nounconnected_drive , `protect , `protecte , `remove_gatenames , `remove_netnames ,`reset,`timescale,`unconnected_drive

在这里只对常用的`define、`include、`timescale进行介绍,其余的请自行查阅资料。

宏定义 `define

用一个指定的标识符(即名字)来代表一个字符串,它的一般形式为:

`define 标识符(宏名) 字符串(宏内容)

如:`define signal string

它的作用是指定用标识符signal来代替string这个字符串,在编译预处理时,把程序中在该命令以后所有的signal都替换成string。这种方法使用户能以一个简单的名字代替一个长的字符串,也可以用一个有含义的名字来代替没有含义的数字和符号,因此把这个标识符(名字)称为“宏名”,在编译预处理时将宏名替换成字符串的过程称为“宏展开”。`define是宏定义命令。

例1:

代码语言:javascript
复制
`define WORDSIZE 8 
module 
reg[1:`WORDSIZE] data; //这相当于定义 reg[1:8] data;
……

关于宏定义的八点说明:

1) 宏名可以用大写字母表示,也可以用小写字母表示。建议使用大写字母,以与变量名相区别。

2) `define命令可以出现在模块定义里面,也可以出现在模块定义外面。宏名的有效范围为定义命令之后到原文件结束。通常,`define命令写在模块定义的外面,作为程序的一部分,在此程序内有效。

3) 在引用已定义的宏名时,必须在宏名的前面加上符号“`”,表示该名字是一个经过宏定义的名字。

4) 使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量。而且记住一个宏名要比记住一个无规律的字符串容易,这样在读程序时能立即知道它的含义,当需要改变某一个变量时,可以只改变 `define命令行,一改全改。如例1中,先定义WORDSIZE代表常量8,这时寄存器data是一个8位的寄存器。如果需要改变寄存器的大小,只需把该命令行改为:`define WORDSIZE 16。这样寄存器data则变为一个16位的寄存器。由此可见使用宏定义,可以提高程序的可移植性和可读性。

5) 宏定义是用宏名代替一个字符串,也就是作简单的置换,不作语法检查。预处理时照样代入,不管含义是否正确。只有在编译已被宏展开后的源程序时才报错。

6) 宏定义不是Verilog HDL语句,不必在行末加分号。如果加了分号会连分号一起进行置换。如:

例2:

代码语言:javascript
复制
module test;

    reg a, b, c, d, e, out; 
    `define expression a+b+c+d; 
    assign out = `expression + e; 
     ... 
   
endmodule

经过宏展开以后,该语句为:

assign out = a+b+c+d;+e;

显然出现语法错误。

7) 在进行宏定义时,可以引用已定义的宏名,可以层层置换。如:

例3:

代码语言:javascript
复制
module test;

    reg a, b, c; 
    wire out; 
    `define aa a + b 
    `define cc c + `aa 
    assign out = `cc; 
    
endmodule

这样经过宏展开以后,assign语句为:

assign out = c + a + b;

8) 宏名和宏内容必须在同一行中进行声明。如果在宏内容中包含有注释行,注释行不会作为被置换的内容。如:

例4:

代码语言:javascript
复制
 module 
 
 `define typ_nand nand #5 //define a nand with typical delay 
 `typ_nand g121(q21,n10,n11); 
 ………
 
 endmodule

经过宏展开以后,该语句为:

nand #5 g121(q21,n10,n11);

宏内容可以是空格,在这种情况下,宏内容被定义为空的。当引用这个宏名时,不会有内容被置换。

注意:组成宏内容的字符串不能够被以下的语句记号分隔开的。

  • 注释行
  • 数字
  • 字符串
  • 确认符
  • 关键词
  • 双目和三目字符运算符

如下面的宏定义声明和引用是非法的。

`define first_half "start of string

$display(`first_half end of string");

注意在使用宏定义时要注意以下情况:

1) 对于某些 EDA软件,在编写源程序时,如使用和预处理命令名相同的宏名会发生冲突,因此建议不要使用和预处理命令名相同的宏名。

2) 宏名可以是普通的标识符(变量名)。例如signal_name 和 'signal_name的意义是不同的。但是这样容易引起混淆,建议不要这样使用。

“文件包含”处理`include

所谓“文件包含”处理是一个源文件可以将另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。Verilog HDL语言提供了`include命令用来实现“文件包含”的操作。其一般形式为:如下图1

上图表示“文件包含”的含意。(a)为文件File1.v,它有一个`include "File2.v"命令,然后还有其它的内容(以A表示)。(b)为另一个文件File2.v,文件的内容以B表示。在编译预处理时,要对`include命令进行“文件包含”预处理:将File2.v的全部内容复制插入到 `include "File2.v"命令出现的地方,即File2.v 被包含到File1.v中,得到(c)所示的结果。在接着往下进行的编译中,将“包含”以后的File1.v作为一个源文件单位进行编译。

“文件包含”命令是很有用的,它可以节省程序设计人员的重复劳动。可以将一些常用的宏定义命令或任务(task)组成一个文件,然后用`include命令将这些宏定义包含到自己所写的源文件中,相当于工业上的标准元件拿来使用。另外在编写Verilog HDL源文件时,一个源文件可能经常要用到另外几个源文件中的模块,遇到这种情况即可用`include命令将所需模块的源文件包含进来。

例1:(1)文件aaa.v

代码语言:javascript
复制
module aaa(a,b,out); 

    input a, b; 
    output out; 
    wire out; 
    
    assign out = a^b; 
    
endmodule

(2)文件bbb.v

代码语言:javascript
复制
`include "aaa.v" 

module bbb(c,d,e,out);

    input c,d,e; 
    output out; 
    wire out_a; 
    wire out; 
    
    aaa aaa(
              .a(c),
              .b(d),
              .out(out_a)
             ); 
             
    assign out=e&out_a;

endmodule

在上面的例子中,文件bbb.v用到了文件aaa.v中的模块aaa的实例器件,通过“文件包含”处理来调用。模块aaa实际上是作为模块bbb的子模块来被调用的。在经过编译预处理后,文件bbb.v实际相当于下面的程序文件bbb.v:

代码语言:javascript
复制
module aaa(a,b,out);

    input a, b; 
    output out; 
    wire out; 
    
    assign out = a ^ b;

endmodule
代码语言:javascript
复制
module bbb( c, d, e, out);

    input c, d, e; 
    output out; 
    wire out_a; 
    wire out; 
    
    aaa aaa(
            .a(c),
            .b(d),
            .out(out_a)
           ); 
    
    assign out= e & out_a; 
    
endmodule

关于“文件包含”处理的四点说明:

1) 一个`include命令只能指定一个被包含的文件,如果要包含n个文件,要用n个`include命令。注意下面的写法是非法的`include"aaa.v""bbb.v"

2) `include命令可以出现在Verilog HDL源程序的任何地方,被包含文件名可以是相对路径名,也可以是绝对路径名。例如:'include"parts/count.v"

3) 可以将多个`include命令写在一行,在`include命令行,只可以出空格和注释行。例如下面的写法是合法的。

'include "fileB" 'include "fileC" //including fileB and fileC

4) 如果文件1包含文件2,而文件2要用到文件3的内容,则可以在文件1用两个`include命令分别包含文件2和文件3,而且文件3应出现在文件2之前。例如在下面的例子中,即在file1.v中定义:

代码语言:javascript
复制
`include"file3.v" 
`include"file2.v" 

module test(a,b,out);

    input[1:`size2] a, b; 
    output[1:`size2] out; 
    wire[1:`size2] out; 
    
    assign out= a+b; 
    
endmodule

file2.v的内容为:

`define size2 `size1+1 . . .

file3.v的内容为:

`define size1 4 . . .

这样,file1.v和file2.v都可以用到file3.v的内容。在file2.v中不必再用 `include "file3.v"了。

5) 在一个被包含文件中又可以包含另一个被包含文件,即文件包含是可以嵌套的。例如上面的问题也可以这样处理,见下图2、图3。

它的作用和下图,图3的作用是相同的。

时间尺度 `timescale

`timescale命令用来说明跟在该命令后的模块的时间单位和时间精度。使用`timescale命令可以在同一个设计里包含采用了不同的时间单位的模块。例如,一个设计中包含了两个模块,其中一个模块的时间延迟单位为ns,另一个模块的时间延迟单位为ps。EDA工具仍然可以对这个设计进行仿真测试。

`timescale 命令的格式如下:

`timescale<时间单位>/<时间精度>

在这条命令中,时间单位参量是用来定义模块中仿真时间和延迟时间的基准单位的。时间精度参量是用来声明该模块的仿真时间的精确程度的,该参量被用来对延迟时间值进行取整操作(仿真前),因此该参量又可以被称为取整精度。如果在同一个程序设计里,存在多个`timescale命令,则用最小的时间精度值来决定仿真的时间单位。另外时间精度至少要和时间单位一样精确,时间精度值不能大于时间单位值。

在`timescale命令中,用于说明时间单位和时间精度参量值的数字必须是整数,其有效数字为1、10、100,单位为秒(s)、毫秒(ms)、微秒(us)、纳秒(ns)、皮秒(ps)、毫皮秒(fs)。这几种单位的意义说明见下表。

下面举例说明`timescale命令的用法。

[例1]: `timescale 1ns/1ps

在这个命令之后,模块中所有的时间值都表示是1ns的整数倍。这是因为在`timescale命令中,定义了时间单位是1ns。模块中的延迟时间可表达为带三位小数的实型数,因为 `timescale命令定义时间精度为1ps.

[例2]:`timescale 10us/100ns

在这个例子中,`timescale命令定义后,模块中时间值均为10us的整数倍。因为`timesacle 命令定义的时间单位是10us。延迟时间的最小分辨度为十分之一微秒(100ns),即延迟时间可表达为带一位小数的实型数。

例3:

代码语言:javascript
复制
`timescale 10ns/1ns

module test; 
    reg set; 
    parameter d=1.55; 
    
    initial 
        begin 
        #d set=0; 
        #d set=1; 
        end 
        
endmodule

在这个例子中,`timescale命令定义了模块test的时间单位为10ns、时间精度为1ns。因此在模块test中,所有的时间值应为10ns的整数倍,且以1ns为时间精度。这样经过取整操作,存在参数d中的延迟时间实际是16ns(即1.6×10ns),这意味着在仿真时刻为16ns时寄存器set被赋值0,在仿真时刻为32ns时寄存器set被赋值1。仿真时刻值是按照以下的步骤来计算的。

1) 根据时间精度,参数d值被从1.55取整为1.6。

2) 因为时间单位是10ns,时间精度是1ns,所以延迟时间#d作为 时间单位的整数倍为16ns。

3) EDA工具预定在仿真时刻为16ns的时候给寄存器set赋值0 (即语句 #d set=0;执行时刻),在仿真时刻为32ns的时候给 寄存器set赋值1(即语句 #d set=1;执行时刻),

注意:如果在同一个设计里,多个模块中用到的时间单位不同,需要用到以下的时间结构。

1) 用`timescale命令来声明本模块中所用到的时间单位和时间精度。

2) 用系统任务$printtimescale来输出显示一个模块的时间单位和时间精度。

3) 用系统函数$time和$realtime及%t格式声明来输出显示EDA工具记录的时间信息。

条件编译命令`ifdef、`else、`endif

一般情况下,Verilog HDL源程序中所有的行都将参加编译。但是有时希望对其中的一部分内容只有在满足条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足条件时对一组语句进行编译,而当条件不满足时则编译另一部分。

条件编译命令有以下几种形式:

1)

`ifdef 宏名 (标识符)

程序段1

`else

程序段2

`endif

它的作用是当宏名已经被定义过(用`define命令定义),则对程序段1进行编译,程序段2将被忽略;否则编译程序段2,程序段1被忽略。其中`else部分可以没有,即:

2)

`ifdef 宏名 (标识符)

程序段1

`endif

这里的 “宏名” 是一个Verilog HDL的标识符,“程序段”可以是Verilog HDL语句组,也可以是命令行。这些命令可以出现在源程序的任何地方。

注意:被忽略掉不进行编译的程序段部分也要符合Verilog HDL程序的语法规则。

通常在Verilog HDL程序中用到`ifdef、`else、`endif编译命令的情况有以下几种:

• 选择一个模块的不同代表部分。

• 选择不同的时序或结构信息。

• 对不同的EDA工具,选择不同的激励。

总结

Verilog HDL的语法与C语言的语法有许多类似的地方,但也有许多不同的地方。我们学习Verilog HDL语法要善于找到不同点,着重理解如:阻塞〔Blocking〕和非阻塞〔Non-Blocking〕赋值的不同;顺序块和并行块的不同;块与块之间的并行执行的概念;task和function的概念。Verilog HDL还有许多系统函数和任务也是C语言中没有的如:monitor、readmemb、

Day 5 就到这里,到这里,经过五天的Verilog HDL基础语法的学习,基本语法差不多都在这里了,从Day 6 继续开始,最后两天将推出思考题(附参考答案),大侠可以自行思考,检测一下自己这一周的语法学习效果,大侠保重,告辞。

END

后续会持续更新,带来Vivado、 ISE、Quartus II 、candence等安装相关设计教程,学习资源、项目资源、好文推荐等,希望大侠持续关注。

大侠们,江湖偌大,继续闯荡,愿一切安好,有缘再见!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FPGA技术江湖 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档