一、写在前面
本专栏为作者在 【数字IC手撕代码】 【数字IC笔试面经分享】 【数字IC工具解析】 以外开设的第四个独立专栏,旨在学习并提供有关Verilog硬件描述语言中非基础性的高阶语法特性知识,因本身专栏的独特定位,因此作者并不会涉及基础Verilog语言如阻塞式非阻塞赋值,过程块,数据类型等内容;同时受限于作者知识有限,本专栏也不会涉及System Verilog的相关内容,若按照IEEE的相关标准来看,本专栏将会聚焦Verilog-2005,即“IEEE Std 1364™-2005”以及之前的有关内容,提供相关的IC设计领域语法特性。以下为Verilog的进阶框图,有更多学习需求的读者可以检索相关英文标准进行学习。
二、什么是UDP用户原语
UDP用户原语是一种嵌入在IEEE Verilog Standard中的建模方法,我们在【Verilog高级教程】灭霸打个响指的功夫,看懂Verilog多维数组的举例中,在wire型数组中对门电路(异或门,与门,或门)进行例化连接,实际上,Verilog内嵌了诸如非门,异或门,与非门等基础门电路
那假如我们需要调使用一个复杂门电路(比如十输入与非门)呢?这里我们就需要使用到UDPs了。UDPs的出现,给予了IC工程师定义自己设计的基本逻辑元件的能力。但是UDP语句没有办法综合,因此它更多的使用在验证功能上。
UDPs可以分为以下三类,他们三类的区别主要体现在对于table的描述上,后文就讲解什么是table。
- 组合逻辑UDP
- 时序逻辑UDP
- 混合型UDP
三、UDP的构成
一个基本的UDP结构如下,primitive和endprimitive类似于module和endmodule,端口声明的两种方式与module中的端口声明也是一样的,唯一的不同是primitive中的table和endtable,table和endtable中罗列的内容描述了UDP的功能,相当于从输入到输出的真值表,在组合逻辑UDP和时序逻辑UDP中我们还有更为详细的例子
primitive name(/*在括号内定义端口*/); table /*在这里定义输入与输出的的关系*/ endtable endprimitive
- 需要注意的是,UDP可以有多个输入,但是只能存在一个输出
- 同时在端口声明的时候,输出端口需要处在第一个的位置上
- inout双向端口在UDP中不被允许,同时无论是输入端口还是输出端口都必须是标量(即一位位宽)
- 时序逻辑UDP,输出端口都需要定义成reg型,这是语法的规定
- UDP支持0,1,x三个状态,但是不支持高阻态z
3.1 组合逻辑UDP
以下的例子定义了一个multiplexer,在control的控制下,选择dataA或dataB作为输出,以下的自定义原语也同样存在更简单的写法,我们可以通过引入"?"符号 来减少所需语句数量,但作者在这里不再赘述,需要注意的是,IEEE-1364中我们同样规定没有必要定义每一种组合逻辑的结果,没有列举的输入情况,默认输出为不定态x
primitive multiplexer (mux, control, dataA, dataB); output mux; input control, dataA, dataB; table // control dataA dataB mux 0 1 0 : 1 ; 0 1 1 : 1 ; 0 1 x : 1 ; 0 0 0 : 0 ; 0 0 1 : 0 ; 0 0 x : 0 ; 1 0 1 : 1 ; 1 1 1 : 1 ; 1 x 1 : 1 ; 1 0 0 : 0 ; 1 1 0 : 0 ; 1 x 0 : 0 ; x 0 0 : 0 ; x 1 1 : 1 ; endtable endprimitive
3.2 时序逻辑UDP
以下的例子定义了一个上升沿触发的D触发器,以下例子的具体含义为当clock信号的上升沿到来的时候,data值被输送到q,(01)为从0到1,其他的符号含义可以参考文章最后的UDP符号表进行参考
- 对比组合逻辑UDP,我们可以发现,时序逻辑UDP的table中多了一列初态(对应q的那一列),这就是二者之间的区别
primitive d_edge_ff (q, clock, data); output q; reg q; input clock, data; table // clock data q q+ // obtain output on rising edge of clock (01) 0 : ? : 0 ; (01) 1 : ? : 1 ; (0?) 1 : 1 : 1 ; (0?) 0 : 0 : 0 ; // ignore negative edge of clock (?0) ? : ? : - ; // ignore data changes on steady clock ? (??) : ? : - ;
3.3 UDP的初始化
同时,我们在时序逻辑UDP中可以定义输出的初始化值,使用initial定义输入初始化值的案例如下,这其中需要解释的是r和f的含义,这里的r与f意味着rising/falling edge on input.即上升沿与下降沿,其他的符号同样可以参考最后罗列UDP符号表。
primitive srff (q, s, r); output q; reg q; input s, r; initial q = 1'b1; table // s r q q+ 1 0 : ? : 1 ; f 0 : 1 : - ; 0 r : ? : 0 ; 0 f : 0 : - ; 1 1 : ? : 0 ; endtable endprimitive
四、UDP符号表
五、往期Verilog高级教程文章
- 多维数组:灭霸打个响指的功夫,看懂Verilog多维数组
- clog2系统函数: 关于Verilog自动计算位宽的系统函数$clog2,这些是你不得不知道的
- UDP用户原语:玩转UDP用户原语,这篇文章就够了
- $monitor系统函数:放学前的最后几分钟,看懂Verilog中的monitor系统函数
- generate语句:一把王者的时间,学会Verilog中的generate语句
- parameter常量:玩转parameter与localparameter,这篇文章就够了
- inout双向端口:通俗易懂的带你解读inout双向端口
- task与function区别:芯片人必会的task与function区别详解