Verilog HDL语法参考

简单记录一下verilog的语法,用于日后复习或者参考。

模块框架

module 模块名 (端口列表);
  // 端口声明
  input input_port;
  output output_port;
  // 参数声明
  parameter param;
  // 数据流语句
  assign a = b & c;
  // 模块实例和门原语
  subModule Mod1(port1, port2);
  subModule Mod2(.port1(PORT1),.port2(PORT2));
  nand (out, in1, in2);
  // 过程语句块
  always @(*)
    过程语句
  initial
    过程语句

endmodule

基本概念

数字声明:<size>’<base format><number>

可以在’之后加s表示有符号数

建议使用_分割为四个二进制数一组

使用[begin:end]选择向量的部分位

使用{a,b,c[3:0]}表示变量拼接

位宽大的数向位宽小的数赋值:获得低位并截断

位宽小的数向位宽大的数赋值:高位补0。例如reg[4:0]a;a = 2’bx;得到00xx。

位扩展时根据最高位扩展x和z。例如4’bx0和4’bz1得到xxx0和zzz1。

常用系统函数:

$ time 返回仿真时间

$ display 显示输出

$ monitor 监控值的变化,通常第一个参数为$ time,格式类似printf

$finish 结束仿真

结构域描述

子模块连接

端口连接规则:

  1. 输入:外界net或reg,内部net
  2. 输出:外界net,内部net或reg
  3. 双向:外界net,内部net
  4. 允许按端口列表顺序相连或者按名字相连

模块也可以定义成实例数组

通常设计测试台包括四个部分:定义外部与端口相连的变量(通常输入用reg输出用wire),定义模块并相连,initial初始化并控制驱动信号,显示monitor信息等。

门级建模

门原语属于模块一级,相当于并发语句。

只能驱动wire型的变量。

and/or门

包括and,nand,or,nor.xor,xnor六种

一个输出端和多个输入端,第一个参数为输出端

真值表中有x/z时,输出通常为x

buf/not门

包括buf,not两种

一个输入端和多个输出端,最后一个参数为输入端

三态门

包括bufif1,bufif0,notif1,notif0四种

控制信号是最后一个参数,输入信号是倒数第二个参数

***要求:***当控制信号关断时,输出信号必须为z。

控制信号为x/z时,或者输入信号为x/z时,输出通常为x。

延迟指定

门原语后使用#(上升 下降 关断)制定

使用#(最小:典型:最大 最小:典型:最大 最小:典型:最大)指定延迟

上升延迟指变为1,下降延迟指变为0,关断延迟指变为z

行为域描述

数据流级

连续赋值

连续赋值语句assign,左值一定为net类型

总是激活,时刻监控,描述组合逻辑

普通延迟

在assign之后使用#指定延迟

惯性延迟右端信号保持的持续时间必须大于延时宽度才能使左值改变

线网延迟

声明wire变量时使用#指定线网延迟

实际延迟为线网延迟和赋值语句延迟之和

信号保持的时间要大于两者的最大值

操作符

向量参与逻辑运算时,首先进行缩减或操作得到逻辑值,使用逻辑值运算

逻辑等价(== !=)对于含有x/z的操作数结果为x。

case等价(=== !==)对于含有x/z的操作数严格的逐位比较。

三目条件运算符(cond)?expr1:expr2;若cond为1或0,则分别执行expr1和expr2。若cond为x,则诸位比较expr1和expr2,相同的位得到该位的值,不同的位得到x。

行为级

过程语句块中绝对不能出现连续赋值语句

过程语句

主要有两种过程语句initial和always

相互不能嵌套,都是模块级的语句

要求:initial用于仿真,always用于设计

不要在不同的语句块中对同一变量进行操作(过程语句的并发性)

  • initial
    • 从时刻0开始执行,只执行一次
    • 所有的initial语句块并发执行
    • 可以使用initial foreveer实现always的功能
  • always
    • 从时刻0开始执行,无限循环
    • 可以定义局部变量,但是不能定义的同时赋初值
    • 可以用always @(*)实现assign的功能

语句块

不建议两种块混合使用

可以在begin/fork后用:为块命名

可以使用disable禁止块的运行

  • 顺序块
    • begin-end
    • 顺序执行,延迟是相对于上一语句结束时
  • 并行块
    • fork-join
    • 并发执行,延迟是相对于整个块

时序控制

基于延迟
  • 常规延迟:
    • #delay a = b;
    • 遇到该语句时,等待delay时间后执行
  • 内嵌延迟
    • a = #delay b+c
    • 遇到该语句时计算右边,等待delay时间后赋值
  • 零延迟:
    • #0
    • 表示在当前时间步结束时执行
基于事件
  • 常规事件控制(边沿触发)
    • @(posedge clock)、@(negedge clock)、@(clock)
    • 表示正向跳变、反向跳变、值改变
    • 数组中任意一个值变化
  • OR事件控制
    • @(cond1 or cond2 or cond3)三个条件满足其一
    • @(*)语句块中任意元素值改变
  • 命名事件
    • 定义一个事件event aEvent;
    • 满足条件触发事件-> aEvent;
    • 事件触发执行always @(aEvent)
电平敏感(电平触发)

等待条件为真时 wait(cond)

cond为数组时,使用逻辑值判断

过程控制

只能用在过程语句块中,不能用于模块一级

  • if…else…
    • 多条语句使用begin-end组成一块
  • case…cond1:…default…endcase
    • 建议必须添加default
    • 逐位比较
  • casex…cond1:..default…endcase
    • 候选式使用x和z表示无关值
  • casez…cond1:..default…endcase
    • 候选式使用z表示无关值
  • while(condition)
  • for(count = 0; count < 128;count = count+1)
  • repeat(times)
    • 以上三种都要在外部声明reg型的循环变了
  • forever
    • 遇到$finish停止
    • 可以被disable关闭

赋值语句

仅能实现对寄存器的操作

要求:时序逻辑电路使用非阻塞赋值,组合逻辑电路使用阻塞赋值

严禁:在always块中混合使用阻塞和非阻塞

顺序块中的非阻塞赋值和并行块中的阻塞赋值的效果类似,推荐前者。

  • 阻塞赋值
    • 串行块中,按序执行,有延迟时停止
    • 并行块中,并发执行
  • 非阻塞赋值
    • 串行块中,并发执行,不影响后面的语句
    • 内嵌赋值语句被延迟到在目标时间步结束时执行赋值
    • 并行块中,并发执行

任务和函数

定义在模块内部

内部不能出现always或initial,只有行为语句

  • 函数(相当于同名的reg变量 要声明位宽)
    • 可以调用函数,不可以调用任务,出现在assign、always、initial中
    • 遇到即执行,一定不包含延迟和时序控制
    • 返回一个值,不能有output
  • 任务
    • 可以调用函数和任务,出现在initial、always中
    • 可以延迟或时序控制
    • 不返回值,有多个output
    • 行为级语句,处理reg型变量

生成块

  • 在模块一级,使用if,case,while等过程控制来控制门原语、模块调用、数据流赋值等语句生成
  • 使用临时变量genvar控制
  • 使用generate和endgenerate表示开始和结束
  • 循环生成时内部的begin-end块需要命名,以便使用层次命名访问

UDP用户定义原语

  • 格式
primitive upd_and(output y, input a);
  table
    //  a b : y
        0 0 : 0;
        0 1 : 0;
        1 0 : 0;
        1 1 : 1;
  endtable
endprimitive
  • 可以使用?表示无关匹配
Lei Yang
Lei Yang
PhD candidate

My research interests include visual speech recognition and semantics segmentation.