入门Verilog—赋值和各种运算符小结
第一次写博客,想要把入门FPGA的一些总结记录下来。如果能对你有帮助,那是最好不过了,哈哈哈。以下很多内容都是借鉴书籍《Xilinx FPGA开发实用教程》而得。
主要包括连续赋值语句、过程赋值,以及这两者的区别,如果不加以区分,写出来的代码是会有bug的(提醒变量类型不对啥的),我吃了好多次亏了。
再总结了关系运算符(8种)、逻辑运算符(3种)、位运算符(7种)、条件运算符、移位运算符、拼接运算符、循环移动。

1赋值

(1)连续赋值-assign
Assign语句(连续赋值语句)只能对线网型变量(wire)进行赋值,而不能对寄存器变量进行赋值(reg),其基本的语法格式如下:
线网型变量类型 [线网型变量位宽] 线网型变量;
assign #(延时量) 线网型变量 = 赋值表达式;
eg:wire a;
assign a=1‘b0;
一个线网型变量一旦被连续赋值语句赋值之后,赋值语句右端赋值表达式的值将持续对被赋值变量产生连续驱动。只要右端表达式任一个操作数的值发生变化,就会立即触发对被赋值变量的更新操作。
(2)过程赋值
过程赋值主要用于两种结构化模块(initial 模块和always模块)中的赋值语句。在过程块中只能使用过程赋值语句(不能再过程块中出现连续赋值语句),同时过程赋值语句也只能用在过程赋值模块中。
过程赋值语句的基本格式为:
<被赋值变量><赋值操作符><赋值表达式>
其中,<赋值操作符>是“=”或“<=”,它分别代表阻塞赋值和非阻塞赋值类型。
小结/
阻塞赋值:“=” 组合电路 顺序执行
非阻塞赋值:“<=” 时序电路 并行执行
链接:阻塞赋值与非阻塞赋值区别
//

过程赋值语句只能对寄存器类型的变量(reg、integer、real和time)进行赋值。

2运算符小结

(1)关系运算符(8种)

大于
= 大于等于
< 小于
<= 小于等于
== 逻辑相等
!= 逻辑不相等
=== 实例相等
!== 实例不相等

在进行关系运算符时,如果操作数之间的关系成立,返回值为1;如果关系不成立,返回值为0;若某一个操作数的值不定,则关系是模糊的,返回的是不定值X.
所有关系运算符具有相同的优先级,但关系运算符的优先级低于算术运算符的优先级。

(2)逻辑运算符(3类)

&&  逻辑与   二目运算符,要求有两个操作数
||    逻辑或     二目运算符,要求有两个操作数
!  逻辑非   单目运算符,只要求一个操作数

“&&”和“||”的优先级高于算术运算符

由(1)和(2)得出,逻辑与、逻辑或的优先级>算法运算符>关系运算符
(3)位运算符(7类)

~ 非
& 与
| 或
^ 异或 两个输入相同为0,相异为1 ,相当于半加运算(不 带进位的二进制加法)
^~ 同或
~& 与非
~| 或非

上述7种位运算,除了非“~” 都是二目运算符。
位运算对其自变量的每一位进行操作,例如S1&S2的含义就是S1和S2的对应位相与。
当两个操作数的长度不相等时,将会对较短的数高位补零,然后进行对应位运算,使输出结果的长度与位宽较长的操作数的长度保持一致。
注意区分:位运算符中的&、|与逻辑运算符中的&&、||

(4)条件运算符
条件运算符的格式:
y = x ? a : b
条件运算符有3个操作数,若第一个操作数x的值时True(1),算子返回第二个操作数a,否则返回第三个操作数b
例如:
wire y;
assign y=(s1==1)?a:b;

嵌套的条件运算符可以实现多路选择。例如:
wire [1:0] s;
assign s=(a>=2) ? 1 : (a<0) ? 2:0;
//当a>=2时,输出为1;当a<0时,输出为2,其余情况为0.

(5)移位运算符
移位运算符只有两种: << - 左移; >> - 右移
左移一位相当于乘2,右移一位相当于除2
例如:
s1<<N;
s1>>N;
其含义是将第一个操作数S1向左或右移位,所移位的位数由第二个操作数N决定,且都用0来填补移出的空位。
在实际运算中,经常通过不同移位数的组合来计算简单的乘法和除法。
例如:s1*24,因为20=16+8,所以可以通过s1<<4+s1<<3来实现。

(6)拼接运算符
拼接运算符可以将两个或多个信号的某些位并接起来进行运算操作。其使用格式如下:
{s1,s2,……,sn}
将某些信号的某些位详细地列出来,中间用逗号隔开,最后用一个大括号表示一个整体信号。
在实际工程中,拼接运算受到了广泛使用,特使是在描述移位寄存器时。
例如:
reg [n:0] shiftreg;
always @(posedge clk)
shiftreg [n:0] <= {shiftreg [n-1:0],data_in};

还有一个应用:
比如要把a[31:0]扩大2^32倍,可以这样写
wire b[63:0];
assign a[31:0]=32’h12345678;
assign b[63:0]={a[31:0],32’h00000000};//相当于a[31:0]*232

也可以这样写:
wire b[63:0];
b[63:0]=a[31:0]<<32;

(7)循环移动
循环右移:
reg [n:0] shifter;
always@(posedge clk)
begin
shifter <={shifter[0],shifter[n:1]};
end
//每一次都要原来的最低位,往最高位搬移一次。形象理解为整体往右移动,把最低位挤到最高位去了。

循环左移
reg [n:0] shifter;
always @(posedge clk)
begin
shifter<={shifter[n-1:0],shifter[n]};
//每一次都把原来的最高位,往最低位搬移一次。形象理解为整体往左移一位,把最高位挤到最低位去了

上述循环左移和右移应用了拼接符{}实现了循环移动,循环移动可以想象为把一串数当做项链上的珠子,不过珠子有序号,往左这样更容易理解。