原创 那些年,我们拿下了FPGA-第8章拿下变量数据类型(3)

2012-11-4 03:04 4033 18 18 分类: FPGA/CPLD 文集: 那些年,我们拿下了FPGA

wire 和reg才是精干

虽说变量数据类型可以变出很多,但是领导群雄的只有两大门派,一个是wire型,另一个就是reg型,他们才是精干,和他们打交道的机会也是最多的。任何时候,结识一个群体的核心是比较重要的。接下我们就着重就说说这两大门派。

wire型

我该如何声明wire型

wire型数据该如何表示呢?wire型信号的声明是有一定的格式的。

其格式为:

数据类型标示符(wire)  位宽   数据名列表;

如果一次定义多个数据,数据名之间用逗号隔开,声明语句最后要用分号表示语句结束。例如:

         wire [n-1:0] a,b,c;  

或者

         wire [n:1] a,b,c; //定义了三个n位宽的信号a,b,c

wire是wire型数据的标示符;[n-1:0]和[n:1]代表该数据的位宽n;a,b,c是数据的名字。

若信号声明时为位宽缺省,其位宽默认为1位。如:

wire  a;           //定义了一个1位的wire型数据a

该如何使用wire型

1.wire型信号可以用于任何方程式的输入,也可作为用做“assign“语句的或者实例元件(如门)的输出;如

                   wire         ain;

                   wire          bout;

                   reg      cout;

                   assign  bout = ~ ain;// bout作为“assign“语句输出,ain作为输入

                   always @(posedge clk)

                   begin

                            cout<= bout+ ain;    // ain 和bout求和方程式的输入

                   end

         又如:

                   wire         ain;

                   wire          bout;

                            not   uuu(bout,ain);// bout作为非门元件的输出,ain作为输入

2.在verilog HDL程序模块中,输入信号须定义为wire型,同时输入,输出信号若未定义,则默认定义为 wire 类型。

 

reg型

我该如何声明reg型数据

         reg型的变量与wire的声明格式很相似的,可以一次定义多个reg型数据,格式如下:

                   数据类型标示符(reg)  位宽   数据名列表;

         比如:

                   reg [n-1:0] a,b,c; //定义了3个名为a,b,c的reg型数据;

 

“always“的常客

reg型数据常用来表示“always“模块内的制定信号,常代表触发器,同时在“always“模块中被赋值的每一个信号都必须定义成reg型,他是“always“模块中的常客。如

module  sum(ain,bin,cout,dout)

input [3:0]         ain,bin;

output [3:0]  cout;

reg bout;

always @( ain or bin or  cout)

begin

         cout<= ain+bin;  // bout必须定义成reg型

         dout<= cout+ ain; //reg型数据(如 cout)也可以作为运算式的输入

end

endmodule

从例子可以知道,reg型在“always“模块可以作为运算式的输入,也可作为运算式的输出,但运算式的输出(即等号左边)必须为reg型。

reg型的赋值问题

reg型数据可以赋正值,也可赋负值,但是当reg型作为一个表达式中操作数时,其内部数值会被作为无符号数(也就是正值)处理。如何进行转化呢?reg型变量的数值的进行取反,再加1。比如

reg [3:0]          b= -5;

那么b中的无符号数值为多少呢?按照上面的方法,5对应的四位宽的二进制数为0101,将其取反就是1010,再加1,就是1011,也就是10进制的11。

另外,reg型数据的缺省的初始值为不定值x。在顶层测试仿真文件中,初始的信号要定义成reg型,比如:clk,rst。模块间的连接信号和模块的输出信号要定义为wire型。

不要以为reg我就是触发器

reg型信号常常是寄存器或触发器的输出,但不能以为reg我就是触发器;

比如:

module or_test(

         ain,

         bin,

         cout

         );

input ain,bin;

output cout;

reg cout;

always @ (ain or bin or cout)

begin

         cout <= ain | bin;

end

endmodule

综合后的RTL级电路如下:

8.10.jpg

 

 

这是用“always”块实现的or门电路的模块,reg型cout信号并不是触发器的输出。

 

 

memory其实也是reg

memory型可以看做reg型变量的数组,是二维的reg,可以用来描述RAM型或ROM型的存储器。memory型数据类型是通过扩展reg型数据的地址范围来生成的。

我该怎么声明memory型变量?

memory型有特定的表示格式:

reg  [n-1:0]   存储器名    [m-1,:0];

reg  [n-1:0]定义了存储器中每一个单元的大小,也就是存储单元的位宽;[m-1,:0]定义了存储器中有多少个存储单元,地址范围从0到m-1。地址索引的表达式必须为常数表达式,表达式中可以含有参数常量。如:

parameter      memorysize;

reg  [15:0]  memory_a [memorysize -1:0] ;

例子中定义了名字为memory_a,有memorysize个16位宽的存储单元的存储器。地址从0到memorysize-1。

memory型和reg型数据可以在一起定义,如

reg [1:0]   memory_a[255:0],reg_b,reg_c;

我该怎么对memory型变量赋值?

对memory型数据不能像reg型数据一样在一条赋值语句里赋值。如

reg [2:0] reg_a, memory_b[255:0];

reg_a=0;   //合法赋值

memory_b=0;  //非法赋值

要想对一个memory型的数据赋值或者进行读写操作,必须通过指定数据变量的地址分别对每个存储单元进行操作。如:

memory_b[5]=0; //将memory_b地址为5的存储单元赋值为0

我们应该如何对一个memory型变量所有的单元进行访问或操作?那么我们可以通过访问memory型变量的地址索引来访问该变量的每个单元。

例如:

module test_memory_assignment;

         reg clk;                                 //时钟

         reg  mema[7:0];    //memory型变量

         reg [2:0] add_mema; //地址索引寄存器

         reg over; //赋值结束信号

always #1 clk=~clk;           //产生仿真时钟

//地址控制模块

always @(posedge clk)

begin

         if(add_mema==7&&over==0)

                   begin

                            mema[add_mema]<=0;

                   end

         else if(over==0)

                   begin

                            add_mema<=add_mema+1; 

                            mema[add_mema]<=0;

                   end

         else ;

end

 

         initial begin

                   //初始化

                   clk  = 1;

                   over = 0;

                   add_mema =0;

                            #20;

       $stop; //仿真结束函数

         end

     

endmodule

 

仿真结果如下:

8.11.jpg

 

上面的仿真是通过地址索引对memory型变量的单元依次进行初始化。

inout该怎么定义类型

第六章已经讲过模块的端口定义分为input,output和inout是三类。那么inout型的如何使用呢,该怎么定义变量数据类型?前面讲过input只能定义为net型数据, inout型端口也可以作为输入,也同样只能定义为net型数据,一般定义为wire型。

因为inout型变量是输入又是输出,那我们可以用“assign“语句作为inout型变量的驱动源,通过定义一个寄存器io_link作为选择inout口方向,若将io_link置1,则代表为output,若io_link置为0则代表input口。

还是举个例子吧:

         inout io_data;      //inout口

         reg out_data;     //需要输出的数据

         reg io_link;               //inout口方向控制,1为输出,0为输入

         assign  io_data = io_link ? out_data:1'bz;   //这个是关键,没用是一定要拉三态

切记:在没有作为输出时,一定要把它置为高阻态z,别“占着茅坑不拉屎”,只有这样才可以把inout口当作平常的input口用了。

 

文章评论0条评论)

登录后参与讨论
我要评论
0
18
关闭 站长推荐上一条 /2 下一条