新闻中心>>正文

变量类型及使用范围深入探讨

2021-08-19    来自:IC知识库

Verilog变量声明与基本数据类型等章节中介绍变量的声明,也简单介绍了变量的使用及使用情况,随着学习和理解的逐渐深入,有必要将变量的类型作更深入的探讨。

  1. wire型变量声明及使用范围

wire型变量在module内是全局变量,可以在module内任何地方被引用,但对wire型变量的赋值却有不同的要求。

例1: module test_wire ( input [1:0] a, input [1:0] b, output [2:0] c ); assign c=tmp0+tmp1; //tmp0,tmp1未声明先使用, 建议先声明后使用 wire [1:0] tmp0; wire [1:0] tmp1; assign tmp0 =a; assign tmp1 =b; endmodule 例1中赋值语句“assign c=tmp0+tmp1;”在变量声明前就已经使用,在Quartus II综合时没有发现任何相关的warning和error;而且在FII-PRA 006上进行硬件验证,结果也正常,的确实现了两位全加器的结果。实验时管脚锁定如下: 表1

Verilog name a[0] a[1] b[0] b[1] c[0] c[1] c[2]
pra006 name sw0 sw1 sw2 sw3 LED5 LED6 LED7
pin number 80 83 86 87 75 76 77

硬件实验结果如图1,

图1

图1,实现了2+2=4的结果,经过测试,各个输入组合,完全符合2位全加器的结果。 但在Modelsim编译仿真的过程中,却发现有如下错误,如图2

图2

Vivado18.2下编译也没有问题,仅给出一个警告,指示在11行的语句中(assign c=tmp0+tmp1;)tmp0,tmp1在没有声明前使用。 “identifier tmp0 is used before its declaration [D:/FPGA_DDR3_IO/test_auto_func/test_auto_func.srcs/sim_1/new/test_wire.v:11]” 但在Vivado下仿真结果是正确的。如图2, 测试程序如下: `timescale 1 ns /1 ps module tb1; reg [1:0] a,b; wire [2:0] c; initial begin a=0; b=0; #10 a=1; b=0; #10 a=2; b=0; #10; a=3; b=0; #10; a=0; b=1; #10; a=1; b=1; #10; a=2; b=1; #10; a=3; b=1; #10; a=0; b=2; #10; a=1; b=2; #10; a=2; b=2; #10; a=3; b=2; #10; a=0; b=3; #10; a=1; b=3; #10; a=2; b=3; #10; a=3; b=3; #10; end test_wire test_wire_inst ( .a (a), .b (b), .c (c) ); endmodule

图2

虽然在Quartus II和Vivado 18.2下综合都没有问题,但Modelsim下却报语法错误,因此为了得到不同平台下输出结果一致,强烈建议在Verilog编程开发中,变量按照先声明后使用的原则。 2. reg型变量的声明及使用范围 相比较wire类型变量,reg类型变量使用要复杂了许多。reg型变量跟据使用范围可以分为全局变量和局部变量,根据变量的可见性又可以分为静态变量和动态变量。由于整形(integer),字符串,存储类型等最终都可以归结位reg类型,因此也遵循reg类型的共同特性。下面就reg类型的新特性加以介绍并举例说明。

reg类型变量按照可见性又分为static和automatic两种。在变量声明时如果没有指明automatic类型,都默认为static类型。automatic类型具有时效性,例如在过程或函数中声明的automatic变量在每次进入可以初始化和赋值,而static变量只初始化一次,在退出过程或函数时内部变量不可见,但在重新进入过程或函数时时该变量又重新可见,而且保留上次退出时的值,因此static变量初始化只有一次,即第一次进入时对该变量初始化,之后进入该过程将恢复上次退出时的值。

module task_fibonacci ( input rst, input i, input [7:0] in_data, output reg [15:0] out_data, output reg [15:0] last_data0,last_data1 ); task fibo (input rst, input integer i, input[7:0] in_data, output [15:0] out_data); begin:fibo_body reg [15:0] last_data0, last_data1; if(rst) begin last_data0=0; last_data1=0; end   if(in_data<2) out_data=in_data; else out_data=last_data0+last_data1;   last_data1=last_data0; last_data0=out_data; end endtask always@(i, in_data, rst) begin if(rst) begin last_data0=0; last_data1=0; end fibo(rst,i,in_data,out_data); last_data0=last_data0+1; last_data1=last_data0;   end endmodule

  1. 仿真程序

`timescale 1ns /1ps module tb; reg [7:0] in_data; reg rst; integer i; wire [15:0] fibo; wire [15:0] last_data0,last_data1; initial begin rst=1'b1; i=0; in_data=0; #10 rst=0; in_data=1; while(i<256) begin #10 $display ("sequence =%4d fibonacci= %d",i+1,fibo); i=i+1; in_data=i; end #10; end task_fibonacci fibonacci_inst ( .rst (rst), .i (i), .in_data (in_data), .out_data (fibo), .last_data0 (last_data0), .last_data1 (last_data1) ); endmodule

  1. 仿真结果如图3

图3

从图3的仿真结果可以看出,实体程序中分别在module内声明的reg型变量与在task内声明的变量互不影响。可见在task内声明的变量作用范围,仅限于task内部使用。而且在没有automatic修饰符的情况下默认为静态(static)变量。本例中 last_data0, last_data1最终以锁存器的方式保存变量值。

修改例2中的实体程序如下: module task_fibonacci ( input rst, input i, input [7:0] in_data, output reg [15:0] out_data, output reg [15:0] last_data0,last_data1 ); task fibo (input rst,input integer i,input[7:0] in_data, output [15:0] out_data);begin :fibo automatic reg [15:0] last_data0,last_data1;   if(rst) begin last_data0=0; last_data1=0; end   if(in_data<2) out_data=in_data; else out_data=last_data0+last_data1;   last_data1=last_data0; last_data0=out_data; end endtask always@(i,in_data,rst,last_data0,last_data1) begin if(rst) begin last_data0=0; last_data1=0; end else begin fibo(rst,i,in_data,out_data); last_data0=last_data0+1; last_data1=last_data0; end   end endmodule

图3

从图3中显示的结果可以看出,由于task内的自动(automatic)变量没有初始化,因此last_data0,last_data1将X值传递给out_data, 而外部的last_data0,last_data1值没有受到影响。修改上面的程序如例4, 例4, module task_fibonacci ( input rst, input i, input [7:0] in_data, output reg [15:0] out_data, output reg [15:0] last_data0,last_data1 ); task fibo (input rst,input integer i,input[7:0] in_data, output [15:0] out_data);begin :fibo automatic reg [15:0] last_data0=0,last_data1=0;     if(rst) begin last_data0=0; last_data1=0; end   if(in_data<2) out_data=in_data; else out_data=last_data0+last_data1;   last_data1=last_data0; last_data0=out_data; end endtask always@(i,in_data,rst,last_data0,last_data1) begin if(rst) begin last_data0=0; last_data1=0; end else begin fibo(rst,i,in_data,out_data); last_data0=last_data0+1; last_data1=last_data0; end   end endmodule 仿真结果如图4

图4

从图4可以看出,除了in_data为1时,in_data直接赋值给out_data外,其它时刻在调用task时,由于”out_data=last_data0+last_data1;” , out_data返回结果都为0; 可见使用自动变量,每次调用task都会对自动变量赋初值。 例5: module tb2; reg [6:0] a; reg [15:0] acc; initial begin a=0; #10 a=10; #10 a=20; #10 a=30; #10 a=40; #10 a=50; #10 a=60; #10 a=70; #10 a=80; #10 a=90; #10 a=100; #10; end always@(*)begin :acc_sum reg [6:0] tmp; //不能在声明时赋初值 tmp=0; acc=0; while(tmp<=a)begin acc=acc+tmp; tmp=tmp+1'b1; end end endmodule

图5

例5程序中在always内定义了tmp变量,初始化并参与运算可以有较好的封装性,由于该变量在always快外不可见,可以自由使用不会与always快外的同名变量冲突或赋值的相互覆盖。 3.reg变量声明和使用总结如下(以modelsim编译器为准):

 

automatic只能在function或task内声明和使用,不能always,initial过程内使用。

   

上一条: Verilog 可变的向量域选择

下一条: Verilog实现16进制到10进制(BCD)转换(1)加减计数法