首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

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

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

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级电路如下:



这是用“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


仿真结果如下:

上面的仿真是通过地址索引对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口用了。
返回列表