这一章,我们来介绍怎么在Verilog HDL中自己定义一个元件。以前我们使用过实例调用,就是在模块里调用库里的元件。这些库,一般是一些公司开发的(具有知识产权)。直接在模块里调用可以省去重复设计的浪费。这些库元件,有些是组合逻辑电路,有些是时序逻辑电路。有些是门级的元件,有些是开关级的元件。实际上,大多数仿真的情况下,我们自己也可以使用Verilog来定义一些元件,方法类似于真值表的形式,用真值表来描述元件的功能。
我们先来自定义一个四选一的多路选择器mux4to1。
例12.1:四选一的多路选择器mux4to1
上面这个例子使用了用户定义原语。
可以看到,在一个用户定义原语里,一定要把UDP的范围用关键词primitive和endprimitive标识出来。和模块的结构类似,UDP也由两部分组成:输入输出端口说明、器件功能描述。比如格式如下:
在上面这个例子里,定义i1~i4为4个输入端口,定义out为输出,定义ctrl1和ctrl2为控制信号。ctrl的变化范围为00~11,共四种变化,分别对应着把i1~i4的信号输出给out。用//标注的语句为注释,没有实际意义。我们在table表内使用了八行语句,这是个类似于真值表的东西,它实际上定义了这个四选一多路选择器的功能。比如我们看如下这个语句:
它指的是ctrl1=0,ctrl2=0时,i1=0,不管其他输入端口是什么,都让输出out=0。table表中的问号表示不必关心此时的这个变量的值。
下面我们来看看该如何使用一个UDP模块,先给一个错误的例子。
例12.2:一个错误的UDP模块。
这个例子里,UDP元件udp1写错了位置。UDP被包含在一个模块wrong_mod之内,这种结构是错误的。
那么,正确的写法该如何呢?请看下面的一个正确的例子。
例12.3:一个正确的UDP模块。
也就是说,UDP模块不能够出现在其他模块之内。UDP模块必须与其他的模块独立并列,处于相同级别的结构地位。
接下来,再看一个错误的例子:
例12.4:另一个错误的UDP。
上面这个例子里,它的写法是错误的。它的格式和前面的四选一多路器的不同之处在于,上面的这个例子把之前的两个一位输入端口ctrl1和ctrl2合并为一个2位的矢量信号ctrl。
input[1:0]ctrl这条语句错误的。
请记住:UDP的写法要求,无论是输入端口的定义,还是输出端口的定义,端口只能是一位宽度的标量。
在UDP里,当被定义的元件是时序逻辑元件时,输出端口寄存器变量的类型说明可以定义为reg类型。比如reg sum将输出端口sum定义为一个寄存器类型的变量。也就是说,这种寄存器类型的变量是被用来描述时序电路UDP的内部状态。
在UDP的定义里,我们也可以使用初始化initial语句。比如:initial sum=x,这个语句用initial过程块来实现元件初始状态的说明,将sum的初始状态设置为未知x。元件一般有几个初始状态0、1、?和未知x。不得出现高阻态z。
在primitive右边括号里,最左边第一个位置只能是输出端口;在table列表项里,最右边一列只能是输出端口。
table表项中,各个输入列的值用空格隔开。
table表项中,输入列的值与输出列的值用英文冒号隔开。
可以在table表项的第一行加入一段注释,标识清楚各列的参数,有利于观察输入和输出的信号的变化。
有table,就别忘了endtable。有primitive,就别忘了endprimitive。
如果table的输入列表没有把所有的输入信号列完备,在恰好出现这种例外的输入信号的时候,输出为x。我们在table进行列表的时候,要尽可能把所有的输入可能性都列出来。这样模拟的时候才不会出错。机器有的时候是很笨的,你只有帮它考虑的很清楚很完备了,它才不会出错。
再看一个全加器的UDP的例子。
例12.5:用UDP描述一个全加器。
全加器的真值表如下:
表12.1 全加器的真值表描述
其中,a、b是输入信号,cin是输入的进位信号,sum是一位加法的本位和的输出结果,cout是输出的进位位。UDP元件的语法规则要求只能有一个一位的输出。因此,我们必须要分别定义两个UDP元件,来分别对应全加器的本位和与进位位。
先来看进位位的定义:
我们在table里的第一句语句是:000:0;;这句话编译器是如何知道哪个数对应着哪个端口呢?实际上,UDP语法规定,table表项里的冒号前的信号是输入信号,冒号后的是输出信号,冒号前各个信号的顺序严格对应着primitive端口列表的输入端口顺序。在这个例子里,primitive carry(cout,cin,a,b)语句告诉我们,table表项的第一列信号对应着cin,第二列信号对应着a,第三列信号对应着b。
我们再来看看本位和sum的UDP定义。
以上,我们使用UDP对一个全加器组合逻辑电路进行了描述。
对于全加器来说,当输入a、b和cin中的任意两个为1时,无论第三个输入是什么,全加器的进位位cout都是1,由此,我们再来看全加器进位位cout的另一种UDP描述。(www.xing528.com)
例12.6:全加器进位位cout的另一种UDP描述。
上面这个例子使用了状态“?”,可以看作一种简略的写法。
例12.7:2选1多路选择器的UDP描述
例12.8:对低电平触发的D锁存器进行UDP建模。低电平时,输入信号传送到输出;高电平时,输出值锁存。
其中,out(state)表示out的当前状态,out(next)表示out的下一状态。-表示保持值不变。
这个例子是一个时序逻辑的例子。
例12.9:对边沿触发的D触发器进行UDP建模。
其中,(01)表示clk的信号从0变为1,(?0)表示从任意值(0、1或x)转换到0,(??)表示任意转换。
这也是一个时序逻辑的例子。时序逻辑电路内部有内部状态。因此,我们要给它定义寄存器来保存状态。它的特点就是:时序逻辑的UDP描述,其输出应该被定义为寄存器类型。
同时,还要给时序逻辑电路的输出设置一个初始值,比如设置为0。如果不进行初始状态的初始化,默认为x。
例12.10:对具有异步清零功能的D触发器进行UDP描述。
这个例子同时具有reset高电平触发清零和clk上升沿触发送数的特点。
例12.11:一个公司有3个股东,各占三分之一投票权,公司章程规定每个股东单独拥有一票否决权。在没有行使否决权的情况下,3个股东一致赞成,则议事通过。使用UDP描述该投票表决器。
例12.12:如下图所示原理图,列出其真值表,用UDP对其进行描述。
图12.1 示例原理图
其真值表如下:
图12.2 示例真值表
其UDP描述如下:
如果c=0,无论a、b是什么,输出都是0。
如果c=1,只要a、b有一个是1,输出都是1。
如果考虑到输入信号存在x的情况,可以把上面这个描述修改如下:
实际上,UDP和模块是同一个层次。不能把UDP写在模块语句里面。
下面,我们给出一个表格,列出了UDP的各种状态及相应的含义。
表12.2 UDP状态表格
例12.13:重写2选1多路选择器,并进行实例调用和仿真。
对其进行实例调用的过程如下:
意思是不支持的开关或者UDP原语。这很正常,因为我们设计的UDP,肯定没法在工程指定的器件上形成(该器件都是已经定型好的芯片)。
但是这不影响其仿真。sel_ex为0时,把in1_ex的值传送给out_ex;当sel_ex为1时,把in2_ex的值传送给out_ex。图12.3是它的仿真结果。
图12.3 2选1多路选择器仿真结果
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。