Verilog中wire、reg关键字使用、过程赋值语句、竞争冒险说明
(2021-02-14 12:06:27)分类: Verilog基础知识 |
一、wire、reg变量用法
1、wire类型变量线网类型表示一个或多个门或者其它类型的信号源驱动的硬件连线。如果没有驱动源,则线网的默认值为z。
verilog中定义的线网类型有以下几种:wire,tri,wor,trior,wand,triand,trireg,tri1,tri0,supply0,supply1。其中最主要的是wire/tri,其它的类型都是综合中用不到的线网。
wire线网用来连接线路中一个逻辑模块的输出和另一个逻辑模块的输入,通常用来表示单个门驱动或连续赋值语句驱动的连线型数据,即wire类型信号相当于一条导线(或任意位宽总线)。使用wire变量(信号)有如下规则:
(1)wire信号可用于连接模块实例化的输入、输出端口;
(2)wire信号可用于实际模块声明中的input和output;
(3)wire信号必须被驱动,且未被驱动时不能存储数值;
(4)wire信号在always@块中不能用作“=”或“<=”的左值;
(5)wire信号是assign语句中左值的唯一合法信号类型;
(6)wire信号可以被多重驱动,也就是可以再多个连续赋值语句中驱动同一个线网(但最好不要这样使用,在综合工具眼中,这样的语法是错误的);
(7)wire信号只能用于组合逻辑建模。wire变量经综合器综合后是一根导线。
tri类型线网表示电路的连接以三态方式进行。它和wire是等价的,它只是用来提高三态门代码的可读性。
2、reg类型变量
在Verilog中,reg类型信号能够用于存储信息(状态)。reg可以综合成register、latch甚至wire(当其仅为中间变量时)。可用于组合逻辑或时序逻辑,能存储数据,有驱动能力,在always@块中可用于左值。详细用法如下:
(1)reg类型信号可用于连接模块实例化的输入端口;
(2)reg类型信号不能用于连接模块实例化的输出端口;
(3)reg类型信号可用于实际模块声明中的输出;
(4)reg类型信号不能用于实际模块声明中的输入;
(5)reg类型信号是always@块语句中“=”和“<=”左侧变量的唯一合法类型;
(6)reg类型信号是initial语句(testBench中使用)中“=”左侧变量的唯一合法类型;
(7)reg类型信号不能用于assign语句的左值;
(8)reg类型信号在always@语句块中使用时,可用于创建寄存器;
(9)reg类型信号不能被多个不同的行为进程(例如always语句块)驱动;
(10)reg类型信号可用于创建组合逻辑和时序逻辑。reg类型变量在综合过程中不一定会综合为一个实实在在的D触发器(时序电路中才会综合为D触发器),寄存器变量的特点是需要在程序运行中过程保存其值,也就是这个变量在程序运行时需要占用内存空间。
3、模块声明、例化的接口变量类型
(1)模块声明
- 输入端口(input):模块内部只能是wire类型;
- 输出端口(output):模块内部可以为wire类型、reg类型;
- inout端口:模块内部为wire类型,是双向信号,一般定义为tri。
(2)模块例化
- 与模块输入(input)端口相连:可以为wire类型、reg类型;
- 与模块输出(output)端口相连:只能为wire类型,驱动到一个线网;
- 与模块inout端口相连:输入时从一个wire线网驱动来,输出时驱动到一个wire线网。
二、过程赋值语句(阻塞赋值、非阻塞赋值、过程连续赋值)
所谓过程赋值语句,就是在initial、always语句块中的赋值语句。赋值对象只能是寄存器变量,右边表达式可以是任意操作符的表达式。过程赋值语句有阻塞赋值、非阻塞赋值、过程连续赋值(不常用)三种。为避免因不当使用阻塞赋值与非阻塞赋值导致的歧义与错误,推荐以下使用方法:
- 对于时序逻辑,即always块的敏感列表为沿敏感信号(多位时钟或复位的上升或下降沿),统一使用非阻塞赋值“<=”;
- 对于always块的敏感列表为电平敏感的信号的组合逻辑,统一使用阻塞赋值“=”;
- 对于assign关键字描述的组合逻辑(通常称为连续赋值语句),统一使用阻塞赋值“=”。
(一)阻塞赋值
1、阻塞赋值的概念理解
语法:寄存器变量 = 表达式;
- 阻塞:在本语句中“右式计算”和“左式更新”完全完成之后,才开始执行下一条语句。
- 阻塞赋值对应的电路往往与触发沿无关,仅与输入电平的变化有关。
- 一般在为组合逻辑建模时使用阻塞赋值语句,并且在assign语句中必须使用阻塞赋值语句。
- 阻塞赋值语句中,右边表达式的计算与左边寄存器变量的赋值是2个动作,这2个动作间不能再插入其他动作。也就是,赋值前先计算等号右边表达式的值,于此同时赋值语句不允许任何其他语句干扰,直到赋值语句完成 即把等号右边表达式的值赋值给左边寄存器变量,才允许下面的赋值语句(包括语句中等号右边表达式的计算)执行。;
- 如果有多个阻塞赋值语句顺序出现在begin...end语句中,前面语句在执行时会完全阻塞后面的语句,直到前面赋值语句执行完成后,才会执行下一句赋值语句中右边表达式的计算,即阻塞赋值是在本语句中“右式计算”和“左式更新”完成之后,才开始执行下一条语句;
- 阻塞赋值可以看成是一步完成的,即计算等号右边表达式并同时赋值给左边寄存器变量。
- 阻塞赋值示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17wireA_in, B_in, C_in;
regTemp, D_out;
//always1
always@(A_in or B_in or C_in) begin
Temp = A_in & B_in;
D_out = Temp | C_in;
end
//always2:当A_in、B_in发生变化时,D_out不变。
always@(A_in or B_in or C_in) begin
D_out = Temp | C_in;
Temp = A_in & B_in;
end
//always3:完成功能与always1一致;因为A_in、B_in发生变化导致Temp变化。Temp变化会触发新一次的@事件语句,从而D_out值
always@(A_in or B_in or C_in or Temp) begin
D_out = Temp | C_in;
Temp = A_in & B_in;
end
(二)非阻塞赋值
1、非阻塞赋值概念理解
语法:寄存器变量 <= 表达式;
- 非阻塞赋值,顾名思义,就是指当前语句的执行不会阻塞下一条语句的执行。即,与阻塞赋值不同的是,多个非阻塞赋值语句顺序出现在begin...end语句中时,前面语句的执行并不会阻塞后面语句的执行。
- 非阻塞赋值对应的电路往往与触发沿相关,只有在触发沿时才有可能发生赋值操作。
- 在时序逻辑电路中一般使用非阻塞赋值。
- 可将非阻塞赋值理解为表达式计算、赋值动作2个过程:1)计算等号右边表达式的值。可理解为,在进入进程后,首先同时计算所有非阻塞赋值语句的右边表达式。2)将等号右边表达式的值赋给等号左边寄存器变量。可理解为,所有非阻塞赋值右侧表达式计算完成后,顺序从上到下执行赋值过程。
- 比如如下代码:
1 {Co,Sum} <= A + B; -
比如以下代码:首先执行q1<=d,产生一个更新事件,将d的当前值赋给q1,但这个赋值过程并没有立刻执行,而是在事件队列中处于等待状态;然后执行q2<=q1,同样产生一个更新事件,将q1的当前值(注意上一语句中将d的值赋给q1的过程还未完成,这里的q1还是旧值)赋给q2,这个赋值事件同样也将在事件队列中处于等待状态;再执行q3<=q2,将q2的当前值(旧值)赋值给q3,这个赋值事件也将在事件队列中等待。这是always语句执行完成,开始对下一个sclk上升沿敏感。只有当当前仿真时刻内的所有活跃事件、非活跃事件全部执行完成后,才开始执行这些非阻塞赋值的更新事件。这样就相当于将d、q1、q2的值同时赋值给了q1、q2、q3。
1
2
3
4
5always@(posedge sclk) begin
q1 <= d;
q2 <= q1;
q3 <= q2;
end
(三)过程连续赋值
某些综合器不支持,待完善。