Verilog HDL入门(2)
Verilog 基础语言2
门级建模
门级建模 内建基元(原语)门
Verilog HDL 提供下列内建基元门
| 门类型 | 内建基元门 |
|---|---|
| 多输入门 | 与门(and)、与非门(nand)、或门(or)、或非门(nor)、异或门(xor)、同或门(xnor) |
| 多输出门 | 缓冲器(buf)、非门(not) |
| 三态门 | bufif0、bufif1、notif0、notif1 |
| 上拉、下拉门 | pullup、pulldown |
| MOS 开关 | cmos、nmos、pmos、rnmos、rpmos |
| 双向开关 | tran、tranif0、tranif1、rtran、rtranif0、rtranif1 |
设计中,可以用实例引用语句来描述具体的门。
1 | gate_type [instance_name] (term1,term2,...,termN); |
instance_name:可选的(实例名称)gate_type:上面列出的某种门term1,term2,...,termN:与名instance_name、类型gate_type门的输入/输出端口相连的线网或变量(模块引脚名称)
同一类型门的多个实例能够在一条语句结构中定义。
1 | gate_type |
多输入门
内置的多输入门:与门(and)、与非门(nand)、或门(or)、或非门(nor)、异或门(xor)、同或门(xnor)
这些逻辑门只有单个输出,1 个或多个输入。多输入门实例引用语法:
1 | muliple_input_gate_type[instance_name](OutputA,Input1,Input2,...,InputN); |
例子:
1 | and uland(out1,in1,in2); //定义名为uland的双输入与门,输出out1,两个输入 |
1 | xor |
多输入门的真值
多输出门:缓冲器(buf)、非门(not)
这两种类型门只有一个输入,输出可以是一个或者几个,实例引用语法:
1 | muliple_input_gate_type [instance_name](out1,out2,...,outN,InputA); |
最后的端口是输入端口,其余的端口全部为输出端口
例子:
1 | buf u5buf(clk_tx,clk_rx,clk_io1,clk_spi,clk_core); //缓冲门语句, |
clk_core 是 u5buf 缓冲门的输入,该缓冲门有四个输出 clk_tx、clk_rx、clk_io1、clk_spi。
1 | not u8not(phase_a_rdy,phase_b_rdy,ready); |
ready 是 u8not 非门唯一的输入端口。有两个输出 phase_a_rdy、phase_b_rdy。
多输出门真值
buf 门真值
| buf | 0 | 1 | x | z |
|---|---|---|---|---|
| 输出 | 0 | 1 | x | z |
not 门真值
| not | 0 | 1 | x | z |
|---|---|---|---|---|
| 输出 | 1 | 0 | x | x |
三态门
bufif0、bufif1、notif0、notif1
4 种门均有一个输出、一个数据输入和一个控制输入,三态门基本语法:
1 | tristate_gate [instance_name](OutputA,InputB,ControlC); |
第一个端口 OutputA 是输入端口,第二端口 InputB 是数据输入,第三端口 ControlC 是控制输出
控制输入,输出可被驱动到高阻态,即值 Z。
bufif0:控制端输入为 1,输出为 Z;否则数据从输入传输至输出;bufif0其中的 0 就是条件bufif1:控制端输入为 0,输出为 Z;否则数据从输入传输至输出;bufif1其中的 1 就是条件notif0:控制端输入为 1,输出为 Z;否则输入数据的非传输至输出;notif0其中的 0 就是条件notif1:控制端输入为 0,输出为 Z;否则输入数据的非传输至输出;notif1其中的 1 就是条件
例:
1 | bufif1 u7bufif1(dbus,mem_data,strode); //strode为0,名u7bufif1的三态门(bufif1)输出线dbus驱动到高阻态;否则mem_data的值被传输至dbus。 |
高阻态(可能是高电压可能是低电压)
bufif0 的真值
| bufif0 | control | ||||
|---|---|---|---|---|---|
| 0 | 1 | x | z | ||
| DATA | 1 | 0 | z | x | x |
| 0 | 0 | z | x | x | |
| x | x | z | x | x | |
| z | x | z | x | x |
bufif1 的真值
| bufif1 | control | ||||
|---|---|---|---|---|---|
| 0 | 1 | x | z | ||
| DATA | 1 | z | 1 | x | x |
| 0 | z | 0 | x | x | |
| x | z | x | x | x | |
| z | z | x | x | x |
notif0 的真值
| notif0 | control | ||||
|---|---|---|---|---|---|
| 0 | 1 | x | z | ||
| DATA | 1 | 0 | z | x | x |
| 0 | 1 | z | x | x | |
| x | x | z | x | x | |
| z | x | z | x | x |
notif1 的真值
| notif1 | control | ||||
|---|---|---|---|---|---|
| 0 | 1 | x | z | ||
| DATA | 1 | z | 0 | x | x |
| 0 | z | 1 | x | x | |
| x | z | x | x | x | |
| z | z | x | x | x |
上拉门和下拉门(电阻):pullup、pulldown
这两种门只有一个输出,没有输入,上拉门(电阻)将输出置 1;下拉门(电阻)输出置 0
语法:
1 | pull_gate [instance_name](OutputA); |
该门的端口列表只包含 1 个输出。
1 | pullup u0pullup(core_pwr); //此上拉门(电阻)实例名为u0pullup,其输出core_pwr被连接到高电平1. |
MOS 开关:cmos、nmos、pmos、rnmos、rpmos
可以用来给单向开关建模,也就是说,通过设置控制输入的值(1/0)可以接通或者关闭从输入流向输出的数据流
pmos(p 类型 MOS 管)、nmos(n 类型 MOS 管)、rnmos(r 代表电阻)和 rpmos 开关有一个输出、一个输入和一个控制输入。
示例引用这类门语法:
1 | gate_type [instance_name] (OutputA,InputB,ControlC); |
第一个端口为输出,第二个端口为输入,最后一个端口是控制输入。
若 nmos 和 rnmos 开关的控制输入为 0,pmos 和 rpmos 开关的控制输入为 1,则开关关闭,输出 z;
若 nmos 和 rnmos 开关的控制输入为 1,pmos 和 rpmos 开关的控制输入为 0,则开关打开,输入数据传输至输出。
rnmos 和 rpmos 在输入引线和输出引线之间存在比较高的阻抗(电阻)。当数据从输入传输至输出时,由于开关阻抗的存在,数据信号的强度会出现衰减。
实例:
1 | pmos u9pmos(iol_bus,read_data,gate_ctrl); //实例名u9pmos的pmos开关,输入read_data,输出iol_bus,控制信号gate_ctrl. |
MOS 开关的真值
pmos 和 rpmos 的真值
| pmos | control | ||||
|---|---|---|---|---|---|
| rpmos | 0 | 1 | x | z | |
| Data | 0 | 0 | z | x | x |
| 1 | 1 | z | x | x | |
| x | x | z | x | x | |
| z | z | z | z | z |
nmos 和 rnmos 的真值
| nmos | control | ||||
|---|---|---|---|---|---|
| rnmos | 0 | 1 | x | z | |
| Data | 0 | z | 0 | x | x |
| 1 | z | 1 | x | x | |
| x | z | x | x | x | |
| z | z | z | z | z |
cmos(是互补型 MOS)和 rcmos(电阻型 MOS)开关具有一个数据输出、一个数据输入和两个控制信号输入,语法:
1 | (r)cmos [instance_name](OutputA,InputB,NControlC,PControlD); |
第一端口输出,第二端口输入,第三个端口为 N 沟道控制输入,第四个端口 P 沟道控制输入。
双向开关:
tran、rtran、tranif0、tranif1、rtranif0、rtranif1
双向开关,数据可以双向流动,并且数据通过开关传播时没有延时。
后四个开关设置合适的控制信号而关闭,但 tran 和 rtran 这两种开关不能关闭。
语法(r 是有电阻的开关):
1 | (r)tran [instance_name](SignalA,SignalB); //端口只有两个端口,并且无条件地双向流动, |
其他双向开关实例:
1 | gate_type [instance_name](SignalA,SignalB,ControlC); //第三个端口是控制端口 |
tranif0、rtranif0 设置 ControlC 为 1,双向数据禁止流动;为 0,则流动。tranif1、rtranif1 设置 ControlC 为 0,双向数据禁止流动;为 1,则流动。
rtran、rtranif0、rtranif1 这三种阻抗双向开关,信号经过时,信号强度会出现衰减。
门延迟(在测试文件使用 tb)
信号从任何门的输入到其输出传输延迟可以用门延迟来定义,在门实例引用语句中可以指定门延迟。实例引用带延迟参数的门语句如下:
1 | gate_type [delay][instance_name](terminal_list); |
delay 门延迟,输入到输出延迟,没有指定,则为 0
门延迟最多由 3 类延迟值组成:上升延迟、下降延迟、截止延迟
门延迟可指定包含 0、1、2、3 个延迟值,下表列出了指定不同延迟值个数时,delay 的 4 种表示方式
实例数组
需要多次实例引用时,门实例应用语句中可以指定一个范围,生成多个实例
1 | gate_type [delay] instance_name [leftbound:rightbound](list_of_terminal_names); |
leftbound:rightbound 是任意常数表达式(可以为负数)。
1 | wire[3:0] irq,ctrl,sense; |
语句等价于
1 | nand |
注意:当指定实例数组时,必须明确地定义实例名
隐含线网
没有特别声明的线网被默认为 1 位线网,但是用户可以用 `default_nettype 编译指令设置默认(缺省)的线网类型。编译指令格式:
1 |
说明:
1 |
有了这条编译指令,所有后续未声明的线网全部被定义为 wand 类型。
`default_nettype 编译指令必须出现在模块定义的外面,并且在遇到下一个同样的编译指令或遇到 `resetall 编译指令之前一直保持有效。
`default_nettype 编译指令后面跟着一个 none 值,就可以把已默认的线网定义取消掉,这种情况下,编译器若发现没有声明类型的任何线网都将报告出错。
示例:
1 | module mux4x1(y,d0,d1,d2,d3,s0,s1) |
2-4 编码器举例(译码器电路)
1 | module dec2x4(a,b,enable,y); |
主从触发器
1 | module ms_dflip_flop(d,c,q,qbar); |
奇偶校验电路 9 位奇偶校验
1 | module parity_9_bit(d,even,odd); |
用户定义的原语(基元 UDP)
UDP 的实例语句与基本门的实例语句完全相同,即 UDP 实例语句的语法与基本门的实例语句语法一致。
UDP 的定义
UDP 可以使用具有如下语法的 UDP 语句来定义:
1 | primitive UDP_name(OutputName,List_of_inputs); |
可以使用如下形式的语句来定义:
1 | primitive UDP_name(Output_declarations,List_of_input_declarations); |
UDP 的定义不依赖于模块定义,因此出现在模块定义以外。也可以在单独的文本文件中定义 UDP。
UDP 只能有一个输出和一个或多个输入。第一个端口必须是输出端口。此外,输出取值 0、1、x(不允许 z 值),输入中出现 z 以 x 处理,UDP 的行为以表的形式描述。
UDP 中可以描述下面两类行为:
(1)组合电路
(2)时序电路(沿触发和电平触发)
组合逻辑的 UDP
表规定了不同的输入组合和相对应的输出值。没有指定的任意组合输出为 x。以多路选择器为例说明:
1 | primitive mux_2by1(y,a,b,select); |
字符 ? 代表不必关心相应变量的具体值,可以是 0、1、x。输入端口的次序必须与表中各项的次序匹配,即表中的第一列对应于原语端口队列的第一个输入(例子中为 a),第二列是 b,第三列是 select。在该多路选择器的表中没有一项的输入组合为 01x(还缺少其他一些组合项);这种情况,输出的缺省值为 x(对其他未定义的输入组合项也是如此)。
1 | module mux_4by1(y,a,b,c,d,select); |
在 UDP 实例中,总共可以指定 2 个延迟,这是由于 UDP 的输出可以取值 0、1、x(无截止延迟)。
时序逻辑 UDP
时序逻辑 UDP 中,使用 1 位寄存器描述内部状态。该寄存器的值是时序电路 UDP 的输出值。
共有两种不同类型的时序 UDP;一种时序 UDP 是电平敏感行为的模型;另一种是跳变沿触发行为模型。
时序电路 UDP 使用寄存器当前值和输入值决定寄存器的下一状态(和后继的输出)
状态变量的初始化
使用一条过程性赋值语句对时序逻辑 UDP 的状态进行初始化:
1 | initial reg_name = 0,1,or x; |
初始化语句在 UDP 定义中出现。
电平触发的时序逻辑 UDP
下面是 D 锁存器建模的电平触发的时序逻辑的 UDP 举例,只要时钟为低电平 0,数据就从输入传递到输出;否则输出值被锁存。
1 | primitive latch(q,clk,d); |
- 字符表示值“无变化”。注意 UDP 的状态存储在变量 q 中。
沿触发的时序逻辑 UDP
用跳变沿触发的时序逻辑 UDP 为 D 型沿触发的触发器建模。一条初始化语句用于初始化该触发器的状态。
1 | primitive d_edge_flip_flop(q,clk,data); |
表项(01)表示 0 转换到 1,表项(0x)表示从 0 转换到 x,表项(?0)表示从任意值(0、1、x)转换到 0,表项(??)表示任意切换,对任意未定义的转换,输出缺省为 x。
假定上面的 UDP 定义了 d_edge_flip_flop,现在就能够在模块中像基元门那样,实例引用该原语(基元):
1 | module register4(clk,data_in,data_out); |
沿触发的和电平敏感的混合行为
在同一个表中能够混合电平触发和沿触发项。这种情况下,边沿变化在电平触发之前处理,即电平触发项覆盖边沿触发项。
1 | primitive d_async_ff(q,clk,clear,data); |
表项的总结
所有可以用于 UDP 原语表项中的值:
| 符号 | 意义 |
|---|---|
0 | 逻辑 0 |
1 | 逻辑 1 |
x | 未知值 |
? | 0、1、x 中的任一个 |
b | 0 或 1 中任选一个 |
- | 不变 |
(AB) | 值由 A 变到 B |
* | 与 (??) 相同,任意值变换到任一值 |
r | 上跳变沿,与 (01) 相同 |
f | 下跳变沿,与 (10) 相同 |
p | (01)、(0x) 和 (x1) 的任一种 |
n | (10)、(1x) 和 (x0) 的任一种 |
negedge 下降沿,posedge 上升沿