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口用了。
文章评论(0条评论)
登录后参与讨论