Verilog HDL语言技术要点

基于FPGA的SOC在嵌入式系统应用越来越广了 , 往往一个复杂系统使用一个单芯片基于FPGA的SOC就搞定了 。比较流行的方案主要有Altera/xilinx两家的方案 。要用这样的方案 , 首要需要掌握的是硬件描述语言 。最为流行的硬件描述语言有两种Verilog HDL/VHDL , 均为IEEE标准 。Verilog HDL具有C语言基础就很容易上手 , 而VHDL语言则需要Ada编程基础 。另外Verilog HDL语言具有大量成熟的模块 , 从某种角度说Verilog HDL更具生命力 。本文整理了一下Verilog HDL语言技术要点 , 并分享给大家 。如发现有错误 , 欢迎留言指正 。
Verilog HDL能干啥?
Verilog HDL的特点:
可描述顺序执行或并行执行的程序结构
用延迟表达式或事件表达式来明确地控制过程的启动时间
通过命令的事件来触发其他过程的激活行为或停止行为
提供了条件/循环等逻辑控制结构
提供了可带参数且非零延续时间的任务程序机构
提供了用于建立表达式的算术运算符、逻辑运算符和位运算符
实现了完整的表示组合逻辑基本元件的原语
提供了双向通路和电阻器的描述
可建立MOS器件的电荷分享和衰减模型
可通过结构性语句精确地建立信号模型
在学习Verilog HDL之前 , 先明确一下FPGA的设计抽象层次:

Verilog HDL语言技术要点

文章插图
其中须注意的是 , 对于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);parameterWidth=1, Polarity=1;……………endmodulemoduleTop;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值
负数:
一个数字可以被定义为负数,只需在位宽表达式前加一个减号,减号必须写在数字定义表达式的最前面 。注意减号不可以放在位宽和进制之间也不可以放在进制和具体的数之间 。
-8'd7//-号直接放在最前面 8'd-7//这样则不正确
实数
实数可用十进制方式表述或者科学计数法描述 , 如:
//十进制表示 1.0 20.234 //科学计数法表示 6e-4
模块端口
input:模块从外界读取数据的接口 , 在模块内可读不可写
output:模块向外部输出数据的接口 , 模块内部可写不可读
inout:可读写数据 , 数据双向流动 。
学习硬件描述语言 , 一定要时刻记住 , 这是描述的是电路 , 风格类C , 但不是C!
表达式及运算符
和C语言类似 , 运算符也有三种:
单目运算符(unary operator):可以带一个操作数,操作数放在运算符的右边 。
二目运算符(binary operator):可以带二个操作数,操作数放在运算符的两边 。
三目运算符(ternary operator):可以带三个操作,这三个操作数用三目运算符分隔开 。
对于运算符 , 整理了一张导图:

Verilog HDL语言技术要点

文章插图
赋值语句
非阻塞(Non_Blocking)赋值方式 ,  如 b <= a; 加粗是非阻塞的含义
块结束后才完成赋值操作 。
b的值并不是立刻就改变的 。
这是一种比较常用的赋值方法 。
阻塞(Blocking)赋值方式 , 如 b = a;
赋值语句执行完后,块才结束 。
b的值在赋值语句执行完后立刻就改变的 。
可能会产生意想不到的结果 。
块语句
块语句有两种 , 一种是begin_end语句 , 通常用来标识顺序执行的语句 , 用它来标识的块称为顺序块 。一种是fork_join语句 , 通常用来标识并行执行的语句 , 用它来标识的块称为并行块 。
顺序块
块内的语句是按顺序执行的 , 即只有上面一条语句执行完后下面的语句才能执行 。
每条语句的延迟时间是相对于前一条语句的仿真时间而言的 。
直到最后一条语句执行完 , 程序流程控制才跳出该语句块 。
begin语句1;语句2;......语句n;end
并行块
块内语句是同时执行的 , 即程序流程控制一进入到该并行块 , 块内语句则开始同时并行地执行 。
块内每条语句的延迟时间是相对于程序流程控制进入到块内时的仿真时间的 。
延迟时间是用来给赋值语句提供执行时序的 。
当按时间时序排序在最后的语句执行完后或一个disable语句执行时 , 程序流程控制跳出该程序块 。
fork语句1;语句2;.......语句n;join
流控语句

Verilog HDL语言技术要点 `include命令可以出现在Verilog HDL源程序的任何地方 , 被包含文件名可以是相对路径名 , 也可以是绝对路径名 。例如:'include"parts/count.v"
可以将多个`include命令写在一行 , 在`include命令行 , 只可以出空格和注释行 。
如果文件1包含文件2 , 而文件2要用到文件3的内容 , 则可以在文件1用两个`include命令分别包含文件2和文件3 , 而且文件3应出现在文件2之前
时间尺度 `timescale
`timescale命令用来说明跟在该命令后的模块的时间单位和时间精度 。使用`timescale命令可以在同一个设计里包含采用了不同的时间单位的模块 。用法:
`timescale<时间单位>/<时间精度>

Verilog HDL语言技术要点

文章插图
//模块中所有的时间值都表示是1ns的整数倍 //1ns/ps:1纳秒/脉冲`timescale1ns/1ps
注意:如果在同一个设计里 , 多个模块中用到的时间单位不同 , 需要用到以下的时间结构:
用`timescale命令来声明本模块中所用到的时间单位和时间精度 。
用系统任务$printtimescale来输出显示一个模块的时间单位和时间精度 。
用系统函数realtime及%t格式声明来输出显示EDA工具记录的时间信息 。
条件编译命令
`ifdef、`else、`endif
这与C语言用法类似 , 这里就不赘述了 。
总结一下
Verilog HDL的语法与C语言的语法类似 , 但是一定要意识到Verilog HDL描述的是电路 , 光有代码还不够 , 器件可能运行的结果并不是代码想要的效果 。另外要注意理解并行的概念 , 这里的并行是硬件在时钟驱动真的同时按照所设计的逻辑运行 。一些重要的概念:
阻塞〔Blocking〕和非阻塞〔Non-Blocking〕赋值的不同
顺序块和并行块的不同
块与块之间的并行执行的概念;
task和function的概念 。
那么最好的学习办法是什么呢?写代码、仿真、综合、优化布局布线 , 挖坑、踩坑、填坑 , 在错误中总结 , 渐进明晰、不断实践总结 。

    推荐阅读