Verilog HDL能干啥?
Verilog HDL的特色:node
-
可描述顺序执行或并行执行的程序结构 -
用延迟表达式或事件表达式来明确地控制过程的启动时间 -
经过命令的事件来触发其余过程的激活行为或中止行为 -
提供了条件/循环等逻辑控制结构 -
提供了可带参数且非零延续时间的任务程序机构 -
提供了用于创建表达式的算术运算符、逻辑运算符和位运算符 -
实现了完整的表示组合逻辑基本元件的原语 -
提供了双向通路和电阻器的描述 -
可创建MOS器件的电荷分享和衰减模型 -
可经过结构性语句精确地创建信号模型
在学习Verilog HDL以前,先明确一下FPGA的设计抽象层次:web

基本程序结构
module module_name(port_list)
//声明各类变量、信号
reg //寄存器
wire //线网
parameter //参数
input //输入信号
output //输出信号
inout //输入输出信号
function //函数
task //任务
....
//程序代码
initial assignment
always assignment
module assignment
gate assignment
UDP assignment
continous assignment
endmodule
启示:描述的是模块,其本质是数字电路:编程
-
组合逻辑电路模块:组合逻辑电路的特色是输入的变化直接反映了输出的变化,其输出的状态仅取决于输入的当前的状态,与输入、输出的原始状态无关。微信
-
时序逻辑电路模块:时序电路具备记忆功能。时序电路的特色是:输出不只取决于当时的输入值,并且还与电路过去的状态有关。时序逻辑电路又称时序电路,主要由存储电路和组合逻辑电路两部分组成。app
数据类型及运算符
变量名
变量名相似C语言,以一组字母、数字、下划线和$符号的组合,且首字符须为字母或者下划线。如编辑器
input ctrl_1;
数据类型
将四种基本数据类型整理成一张导图:svg

其中须注意的是,对于memory型存储单元进行读写,须指定地址,如:函数
reg[15:0] addr; //定义addr为16位位宽的存储器变量
addr = 1; //ok
reg addr[15:0]; //定义addr为1位位宽的16个存储器变量
addr = 1; //错误
addr[0] = 1; //正确
//又如:
reg[15:0] addr[3:1]; //定义3个位宽为16位存储器
addr[1] = 16'h0 //16'指定位宽,h 表示16进制,0
addr[2] = 16'b011 //b表示二进制
对于parameter变量的实用价值可读性比较好理解,那么可维护性怎么体现呢?工具
熟悉C语言编程的,联想一下宏,若是宏变了,有宏的地方全替换,这里parameter变量做用相似,如:布局
module Decode(A,F);
parameter Width=1, Polarity=1;
……………
endmodule
module Top;
wire[3:0] A4;
wire[4:0] A5;
wire[15:0] F16;
wire[31:0] F32;
Decode #(4,0) D1(A4,F16);
Decode #(5) D2(A5,F32);
Endmodule
常量
parameter定义常量,那么对于常数,整型常量即整常数有如下四种进制表示形式:
-
二进制整数(b或B) -
十进制整数(d或D) -
十六进制整数(h或H) -
八进制整数(o或O)
数字表达方式有如下三种:
-
<位宽><进制><数字>这是一种全面的描述方式。 -
<进制><数字>在这种描述方式中,数字的位宽采用缺省位宽(这由具体的机器系统决定,但至少32位)。 -
<数字>在这种描述方式中,采用缺省进制十进制。
x和z值
在数字电路中,x表明不定值,z表明高阻值。不肯定是啥?高阻又是啥?记住verilog描述的数字电路,那么对于一个模块的I/O就有多是高阻,或者状态不肯定。
负数:
一个数字能够被定义为负数,只需在位宽表达式前加一个减号,减号必须写在数字定义表达式的最前面。注意减号不能够放在位宽和进制之间也不能够放在进制和具体的数之间。
-8'd7 //-号直接放在最前面
8'd-7 //这样则不正确
实数
实数可用十进制方式表述或者科学计数法描述,如:
//十进制表示
1.0
20.234
//科学计数法表示
6e-4
模块端口
-
input:模块从外界读取数据的接口,在模块内 可读不可写 -
output:模块向外部输出数据的接口,模块内部 可写不可读 -
inout:可读写数据,数据双向流动。
学习硬件描述语言,必定要时刻记住,这是描述的是电路,风格类C,但不是C!
表达式及运算符
和C语言相似,运算符也有三种:
-
单目运算符(unary operator):能够带一个操做数,操做数放在运算符的右边。 -
二目运算符(binary operator):能够带二个操做数,操做数放在运算符的两边。 -
三目运算符(ternary operator):能够带三个操做,这三个操做数用三目运算符分隔开。
对于运算符,整理了一张导图:

大部分与C语言相似,除了等式运算符、位拼接运算符、缩减运算符,这里放点例子方便理解:
//缩减运算符
reg [3:0] B;
reg C;
C = &B;
//至关于:
C =( (B[0]&B[1]) & B[2] ) & B[3];
//位拼接运算符
{a,b[3:0],w,3’b101}
//至关于:
{a,b[3],b[2],b[1],b[0],w,1’b1,1’b0,1’b1}
运算符优先级:
赋值语句
-
非阻塞(Non_Blocking)赋值方式, 如 b <= a; 加粗是非阻塞的含义 -
块结束后才完成赋值操做。 -
b的值并非马上就改变的。 -
这是一种比较经常使用的赋值方法。 -
阻塞(Blocking)赋值方式,如 b = a; -
赋值语句执行完后,块才结束。 -
b的值在赋值语句执行完后马上就改变的。 -
可能会产生意想不到的结果。
块语句
块语句有两种,一种是begin_end语句,一般用来标识顺序执行的语句,用它来标识的块称为顺序块。一种是 fork_join语句,一般用来标识并行执行的语句,用它来标识的块称为并行块。
顺序块
-
块内的语句是按顺序执行的,即只有上面一条语句执行完后下面的语句才能执行。 -
每条语句的延迟时间是相对于前一条语句的仿真时间而言的。 -
直到最后一条语句执行完,程序流程控制才跳出该语句块。
begin
语句1;
语句2;
......
语句n;
end
并行块
-
块内语句是同时执行的,即程序流程控制一进入到该并行块,块内语句则开始同时并行地执行。 -
块内每条语句的延迟时间是相对于程序流程控制进入到块内时的仿真时间的。 -
延迟时间是用来给赋值语句提供执行时序的。 -
当按时间时序排序在最后的语句执行完后或一个disable语句执行时,程序流程控制跳出该程序块。
fork
语句1;
语句2;
.......
语句n;
join
流控语句
流控语句风格与C语言相似,仅仅须要注意的有下面几点:
-
if 语句别忘了考虑else的状况,如忘了处置则最终硬件会最终产生意想不到的后果 -
多条语句在条件内部须要用begin/end对包起来。 -
case语句与C语言也有default分支,实际使用注意处置default分支
结构说明语句
Verilog语言中的任何过程模块都从属于如下四种结构的说明语句:
-
initial说明语句:只执行一次 -
always说明语句 :是不断地重复执行 -
task说明语句 -
function说明语句
对于task/function的不一样点,使用时须要注意:
-
函数只能与主模块共用同一个仿真时间单位,而任务能够定义本身的仿真时间单位。函数的定义不能包含有任何的时间控制语句,即任何用#、@、或wait来标识的语句。 -
函数不能启动任务,而任务能启动其它任务和函数。 -
函数至少要有一个输入变量,而任务能够没有或有多个任何类型的变量。 -
函数返回一个值,而任务则不返回值。 -
函数的目的是经过返回一个值来响应输入信号的值。任务却能支持多种目的,能计算多个结果值,这些结果值只能经过被调用的任务的输出或总线端口送出 -
在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内 部变量具备和函数名相同的名字。
系统函数和任务
在Verilog HDL语言中每一个系统函数和任务前面都用一个标识符$来加以确认,有这些系统函数和任务。
rtoi, setup, skew, setuphold, strobe, time, timefoemat, width, write, $recovery,
按字面意思理解,须要用到时查询手册便可。
编译预处理
宏定义 `define
用法:
`define 标识符(宏名) 字符串(宏内容)
如:
//相似C宏替换
`define signal hello
与C语言宏相似,除了关键字不同,也支持嵌套。组成宏内容的字符串不可以被如下的语句记号分隔开的,下面几点须要注意:
-
注释行
-
数字
-
字符串
-
确认符
-
关键词
-
双目和三目字符运算符
“文件包含”处理`include
用法:`include “文件名”
四点说明:
-
一个`include命令只能指定一个被包含的文件,若是要包含n个文件,要用n个`include命令。注意下面的写法是非法的`include"aaa.v""bbb.v" -
`include命令能够出如今Verilog HDL源程序的任何地方,被包含文件名能够是相对路径名,也能够是绝对路径名。例如:'include"parts/count.v" -
能够将多个`include命令写在一行,在`include命令行,只能够出空格和注释行。 -
若是文件1包含文件2,而文件2要用到文件3的内容,则能够在文件1用两个`include命令分别包含文件2和文件3,并且文件3应出如今文件2以前
时间尺度 `timescale
`timescale命令用来讲明跟在该命令后的模块的时间单位和时间精度。使用`timescale命令能够在同一个设计里包含采用了不一样的时间单位的模块。用法:
`timescale<时间单位>/<时间精度>
//模块中全部的时间值都表示是1ns的整数倍
//1ns/ps:1纳秒/脉冲
`timescale 1ns/1ps
注意:若是在同一个设计里,多个模块中用到的时间单位不一样,须要用到如下的时间结构:
-
用`timescale命令来声明本模块中所用到的时间单位和时间精度。 -
用系统任务$printtimescale来输出显示一个模块的时间单位和时间精度。 -
用系统函数 realtime及%t格式声明来输出显示EDA工具记录的时间信息。
条件编译命令
`ifdef、`else、`endif
这与C语言用法相似,这里就不赘述了。
总结一下
Verilog HDL的语法与C语言的语法相似,可是必定要意识到Verilog HDL描述的是电路,光有代码还不够,器件可能运行的结果并非代码想要的效果。另外要注意理解并行的概念,这里的并行是硬件在时钟驱动真的同时按照所设计的逻辑运行。一些重要的概念:
-
阻塞〔Blocking〕和非阻塞〔Non-Blocking〕赋值的不一样 -
顺序块和并行块的不一样 -
块与块之间的并行执行的概念; -
task和function的概念。
那么最好的学习办法是什么呢?写代码、仿真、综合、优化布局布线,挖坑、踩坑、填坑,在错误中总结,渐进明晰、不断实践总结。
本文辛苦原创分享,若是以为有价值也请帮忙点赞/转发支持,不胜感激!
参考资料:
《夏宇闻-Verilog经典教程》,如须要本电子书,关注后发送Verilog,可领取pdf。
—END—

推荐阅读
点击上方字体便可跳转阅读哟
本文分享自微信公众号 - OpenFPGA(OpenFPGA)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。