FPGA之道(34)Verilog初始化与操作符号

更新时间:2023-05-07 13:17:06 阅读: 评论:0

FPGA之道(34)Verilog初始化与操作符号
⽂章⽬录
前⾔
Verilog的初始化可以在定义变量时候赋值,也可在initial语句块内赋值,当然也可以在always内的复位时候初始化。
⾄于Verilog的操作符采⽤与VHDL操作符并⾏的⽅式来描述。
本⽂摘⾃《FPGA之道》,⼀起学习下。
Verilog初始化
初始化主要是针对FPGA内部有记忆的单元,例如寄存器、BLOCK RAM等,⽽对于⽆记忆的单元,例如硬件连线,没有必要也⽆法对它们赋初值。⽬前来说,并不是所有的FPGA芯⽚都⽀持赋初值的,那么对于那些不⽀持赋初值的FPGA芯⽚,我们⼀定要设计好复位电路;⽽对于⽀持赋初值的FPGA芯⽚,我们可以利⽤这⼀特性让FPGA在上电后就处于⼀个可被确定的状态。除此以外,赋初值对于仿真也有类似的影响。
在Verilog语⾔中,有两种初始化的⽅法:分布式和集中式,分别介绍如下:
分布式赋初值就是在声明变量的时候顺便赋初值,例如:
reg [7:0] data = 8’h7f;
⽽集中式赋初值使⽤了initial语法,这种⽅法的好处是可以将所有赋初值操作集中在⼀起,⽅便⽇后的维护和修改,因此集中式赋初值的⽅法也是我们所推荐的赋初值⽅法。initial语法也是程序块的⼀种,它和always是程序块“唯⼆”的两个分⽀,所不同的是always中的语句是永久的循环执⾏,⽽initial中的语句只在程序最开始执⾏⼀次。它的语法如下:
initial begin
<statements>;
end
具体使⽤请参考下例:
wire a;
reg [7:0] b;
reg [7:0] c[99:0];
integer i;
initial begin
a =1'b1;// wrong
b <=8'h7E;
for(i=0; i <100; i = i+1) begin
c[i]= i;
end
end
从上例可以看出,线⽹类型不⽀持赋初值。从语法上来说,这是由于程序块中被赋值的只能是寄存器
类型的变量;从物理意义上来说,就是只有有记忆的单元才能被赋初值。另外,由于initial中的语句仅执⾏⼀次,且不涉及具体功能电路的实现,所以它内部的寄存器类型变量使⽤阻塞和⾮阻塞赋值都⾏,没有什么区别。
Verilog的操作符号
Verilog赋值运算符
连续赋值符号
连续赋值即continues assignments,它常配合assign关键字使⽤,仅能⽤于线⽹类型的变量赋值,语法如下:
assign <variable_name> = ;
例如:
input b;
wire a;
assign a = b;
有时候,为了简便起见,上述我们可以把变量声明和连续赋值合并起来,简写成这样:
wire a = b;
相⽐之下,我们可以称带有assign关键字的赋值⽅式为显式的连续赋值,⽽简化后的赋值⽅式为隐式的连续赋值。为什么要叫连续赋值呢?这是因为该赋值语句所描述的硬件功能赋值电路,不需要等待任何事件的触发,也不会受任何事件的影响⽽中断,会连续不断的执⾏(当然了,在仿真的时候显然不能这样,否则将进⼊仿真死循环)。显然,这种模式只能描述组合逻辑,故只能⽤于线⽹类型变量的赋值。
阻塞赋值符号
阻塞赋值主要⽤于组合逻辑,它只能操作寄存器类的变量,赋值符号为“=”。⼀般来说,当我们期望变量被综合成为FPGA中的硬件连线时,我们常常会对变量⽤阻塞赋值,⽽对应的赋值语句⼀般要写在表⽰组合逻辑的always程序段中。在always程序段中,语句的执⾏⼀般都是顺序的,阻塞赋值符号的作⽤就是保证上⼀条语句已执⾏并且赋值操作完成后才开始下⼀条语句的执⾏,也因此得名阻塞赋值。例如:
always@(c)
begin
b = c;
a = b;
end
如果当前状态c为逻辑1,b为逻辑1,a也为逻辑1。那么若此时c变为逻辑0,那么则在本次c改变后,b为逻辑0,a也为逻辑0。从硬件实现上来说,上述代码就是三段⾸尾相连的硬件连线,其实就是⼀根线,你想,如果连线的输⼊端值改变了,那么整个连线的值肯定也都跟着变了,所以c处的值传给b,同时经过b也传给了a;从阻塞赋值符号的作⽤来说,由于在第⼀条语句的赋值完成后才开始执⾏第⼆条语句,所以第⼆条语句中的b是b的新值。
⾮阻塞赋值符号
⾮阻塞赋值主要⽤于时序逻辑,它也只能操作寄存器类的变量,赋值符号为“<=”。⼀般来说,当我们期望变量被综合成为FPGA中的寄存器时,我们常常会对变量⽤⾮阻塞赋值符号,当然了,对应的赋
值语句也应该写在表⽰时序逻辑的always程序段中。在always程序段中,语句的执⾏⼀般都是顺序的,⾮阻塞赋值符号的作⽤就是可以在上⼀条语句已执⾏但是赋值操作还没完成前就开始下⼀条语句的执⾏。例如:
always@(podge clk)
begin
b <= c;
a <= b;
end
如果在上⼀次时钟上升沿触发后,b为逻辑1,a也为逻辑1。那么若此时c保持为逻辑0,那么在本次时钟上升沿触发后,b为逻辑0,a为上⼀次b的值,即逻辑1。从硬件实现上来说,上述代码就是⼀个移位寄存器,每⼀次时钟触发后,c处的值传给b,b处的值传给a,值的传递只能跨越1个寄存器;从⾮阻塞赋值符号的作⽤来说,由于在第⼀条语句的赋值还没有完成时,就开始执⾏第⼆条语句,所以第⼆条语句中的b仍然使⽤的是b的旧值。
映射赋值符号
Veriog中的映射赋值符号为“.”,它不能算是⼀种纯粹的赋值运算符,因为它只是在两个变量之间建⽴⼀种连接的关系,⽽本⾝并不明确表明赋值的⽅向关系,即到底是把左边的数据写到右边去还是把右边的数据写到左边去。最常见的例⼦就是模块实例化语句,⽆论端⼝⽅向是input还是output,都⼀律使⽤“.”来映射赋值,⾄于赋值的⽅向,编译器会根据上下⽂⾃⼰去推断。例如:
wire line0, line1, result;
cellAnd m0(.a (line0),
.b (line1),
.c (result));
位置赋值
位置赋值没有任何显式的操作符号,它仅仅是根据变量书写的位置来确定赋值的对应关系,因此,位置赋值其实是映射赋值的⼀个简化版本。例如,映射赋值⼩节⾥⾯的例⼦也可以写成这样:
cellAnd m0(line0,line1, result);
不过对于凡是⽀持“.”符号来进⾏映射赋值地⽅,建议⼤家还是不要使⽤位置赋值,因为映射赋值是位置⽆关的,这样会在很⼤程度上减少程序出错的可能性,并且⽅便⽇后的修改与维护。
最后注意,位置赋值和映射赋值不能混合使⽤!
Verilog按位运算符
按位运算符是⼀类最基本的运算符,可以认为它们直接对应数字逻辑中的与、或、⾮门等逻辑门,在Verilog中它们的描述如下:
~
“~”是逻辑⾮运算符,它是⼀个单⽬运算符,对被操纵数做取反逻辑;若被操作数是具有⼀定位宽的向量,则对该向量按位取反。例如:
wire a, b;
assign b =1'b0;
assign a =~ b;// a =1'b1
wire [7:0] busA, busB;
assign busB =8'h00;
assign busA =~ busB;// busA =8'hFF;
&
“&”是逻辑与运算符,它是⼀个双⽬运算符,对两个被操纵数做取与逻辑;若被操作数是具有⼀定位宽的向量,则对两个向量按位取与操作。例如:
wire a, b, c;
assign a =1'b1;
assign b =1'b1;
assign c = a & b;// c =1'b1
wire [7:0] busA, busB, busC;
assign busA =8'hF1;
assign busB =8'h1F;
assign busC = busA & busB;// busC =8'h11;
|
“|”是逻辑或运算符,它是⼀个双⽬运算符,对两个被操纵数做取或逻辑;若被操作数是具有⼀定位宽的向量,则对两个向量按位取或操作。例如:
wire a, b, c;
assign a =1'b0;
assign b =1'b1;
assign c = a | b;// c =1'b1
wire [7:0] busA, busB, busC;
assign busA =8'hF1;
assign busB =8'h1F;
assign busC = busA | busB;// busC =8'hFF;
^
“^”是逻辑异或运算符,它是⼀个双⽬运算符,对两个被操纵数做取异或逻辑;若被操作数是具有⼀定位宽的向量,则对两个向量按位取异或操作。例如:
wire a, b, c;
assign a =1'b1;
assign b =1'b1;
assign c = a ^ b;// c =1'b0
wire [7:0] busA, busB, busC;
assign busA =8'hF1;
assign busB =8'h1F;
assign busC = busA ^ busB;// busC =8'hEE;
“~^”是逻辑异或⾮运算符(也即同或运算),它是⼀个双⽬运算符,对两个被操纵数做取同或逻辑;若被操作数是具有⼀定位宽的向量,则对两个向量按位取同或操作。例如:
wire a, b, c;
assign a =1'b0;
assign b =1'b0;
assign c = a ~^ b;// c =1'b1
wire [7:0] busA, busB, busC;
assign busA =8'hF1;
assign busB =8'h1F;
assign busC = busA ~^ busB;// busC =8'h11;
Verilog归约运算符
归约运算符,都是单⽬运算符号,它们的被操作数都是有⼀定位宽的向量,⽽操作的结果都是1bit的。由于它们和按位运算符⾮常的相似,所以使⽤的时候⼀定要注意区分。分别介绍⼊下:
&
在这⾥,“&”是归约与运算符,它对⼀个被操纵数的所有位做逻辑与操作。例如:
wire [3:0] bus = 4’hF;
wire result = & bus; // result = 1’b1;
如果⽤按位运算符来表⽰,上述赋值相当于:
assign result = bus[0] & bus[1] & bus[2] & bus[3];
~&
在这⾥,“~&”是归约与⾮运算符,它对⼀个被操纵数的所有位先做归约与操作,然后对结果取逻辑反操作。例如:
wire [3:0] bus = 4’hF;
wire result = ~& bus; // result = 1’b0;
如果⽤按位运算符来表⽰,上述赋值相当于:
assign result = ~ (bus[0] & bus[1] & bus[2] & bus[3]);
|
在这⾥,“|”是归约或运算符,它对⼀个被操纵数的所有位做逻辑或操作。例如:
wire [3:0] bus = 4’h7;
wire result = | bus; // result = 1’b1;
如果⽤按位运算符来表⽰,上述赋值相当于:
assign result = bus[0] | bus[1] | bus[2] | bus[3];
~|
在这⾥,“~|”是归约或⾮运算符,它对⼀个被操纵数的所有位先做归约或运算,然后对结果取逻辑反操作。例如:
wire [3:0] bus = 4’h7;
wire result = ~| bus; // result = 1’b0;
如果⽤按位运算符来表⽰,上述赋值相当于:
assign result = ~ (bus[0] | bus[1] | bus[2] | bus[3]);
^
在这⾥,“^”是归约异或运算符,它对⼀个被操纵数的所有位做逻辑异或操作。例如:
wire [3:0] bus = 4’h7;
wire result = ^ bus; // result = 1’b1;
如果⽤按位运算符来表⽰,上述赋值相当于:
assign result = bus[0] ^ bus[1] ^ bus[2] ^ bus[3];
在这⾥,“~^”是归约异或⾮运算符(注意,只有当操作数的位宽等于2时,异或⾮才等同于同或),它对⼀个被操纵数的所有位先做归约异或运算,然后对结果取逻辑反操作。例如:
wire [3:0] bus = 4’h7;
wire result = ~^ bus; // result = 1’b0;
如果⽤按位运算符来表⽰,上述赋值相当于:
assign result = ~ (bus[0] ^ bus[1] ^ bus[2] ^ bus[3]);
Verilog算数运算符
算数运算符,即我们常说的加、减、乘、除等,这类运算符的抽象层级较⾼,从数字逻辑电路实现上来看,它们都是基于与、或、⾮等基础门逻辑组合实现的。FPGA中的算数运算符主要有两种⽤途:⼀种是作⽤于具体的硬件的信号上,完成相关的运算功能;另⼀种是作⽤于Verilog程序的某些参数或者语句结构上,仅仅起到帮助⽤户更⽅便的编写代码和帮助编译器更好的理解代码的作⽤。
它们在Verilog中的描述如下:
+
“+”是加法运算符,它是⼀个双⽬运算符,即必须有两个操作数,对它们进⾏加法运算。例如:
wire [3:0] a = 4’d3;
wire [3:0] b = 4’d2;
wire [3:0] c = a + b; // c = 4’d5
wire [3:0] d = 4’d15;
wire [3:0] e = b + d; // e = 4’d1, 溢出的⾼位被丢弃
-
“-”是减法运算符,它是⼀个双⽬运算符,即必须有两个操作数(两个操作数的地位不⼀样,⼀个是被减数,⼀个是减数,不能颠倒),对它们进⾏减法运算。例如:
wire [3:0] a = 4’d3;
wire [3:0] b = 4’d2;
wire [3:0] c = a - b; // c = 4’d1
wire [3:0] d = b - a; // c = 4’d15,如果以有符号2进制来理解,4’d15等价于-1
*
“*”是乘法运算符,它是⼀个双⽬运算符,即必须有两个操作数,对它们进⾏乘法运算。例如:
wire [7:0] a = 8’d10;
wire [7:0] b = 8’d12;
wire [7:0] c = a * b; // c = 8’d120
/
“/”是除法运算符,它是⼀个双⽬运算符,即必须有两个操作数(两个操作数的地位不⼀样,⼀个是被除数,⼀个是除数,不能颠倒),对它们进⾏除法运算。例如:
wire [7:0] a = 8’d140;
wire [7:0] b = 8’d20;
wire [7:0] c = a / b; // c = 8’d7
其实FPGA中的乘、除法都是按照⼆进制数来进⾏的,关于⼆进制数的乘、除法的详细阐述。
%
“%”是求余数运算符,它是双⽬运算符,且操作数不能任意颠倒,例如:

本文发布于:2023-05-07 13:17:06,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/865665.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:赋值   逻辑   例如   操作   运算符   变量
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图