关于结构体的内容
结构体使用类似于C语言的语法来定义
结构体使用struct关键字声明。结构体内的成员可以是任何数据类型,包括用户自定义类型和其他的结构体类型。
struct{
int a,b; //32位变量
opcode_t opcode;//用户定义类型
logic [23:0] adress;//24位变量
bit error; // 一位两态变量
}Instruction_Word;
结构体是变量和/或常量的集合
结构体是一个名称下的变量和/或常量的集合。整个集合可以用结构体名进行引用。结构体内的每个成员也都有一个名称。它用来从结构体中选择成员。结构体成员与C语言中的引用方式相同。
<结构体名>.<变量名>
例如
Instruction_Word.adress = 32'hF000001E;
结构体可以是变量或线网
结构体不同于数组,数组是同类型同尺寸的元素集合,而结构体是不同类型和尺寸的变量和/或常量的集合。另外一个不同是数组元素是通过索引引用,而结构体成员通过的所有成员名称引用。
1、结构体的声明
变量和线网都可以定义为结构体
结构体是变量的集合,可以单独或者作为一个整体进行访问。作为整体时,结构体可以用var关键字声明为一个变量。结构体还可以使用任意Verilog线网类型如wire或tri定义为一个线网。当定义为线网类型时,结构体的所有成员必须都是四态类型的。
var struct{ //结构体声明
logic [31 : 0 ] a,b;
logic[7 : 0] opcode;
logic[23 : 0] address;
}Instruction_Word_var;
wire struct{ //结构体声明
logic [31 : 0 ] a,b;
logic[7 : 0] opcode;
logic[23 : 0] address;
}Instruction_Word_var;
将结构体声明为变量或线网类型是可选择的。如果未指定,将整个结构体认为是一个变量。
注意,虽然结构体可以被声明为线网类型,但是结构体内不能使用线网类型。多个线网可以用SystemVerilog接口打包成一个组。
自定义和匿名的结构体
可以使用结构体来创建用户自定义数据类型,使用typedef关键字。将一个结构体声明为一个用户自定义类型不分配任何储存区。在声明为用户自定类型的结构体的成员赋值前,必须声明一个这种用户自定义类型的变量。
typedef struct{ //结构体定义
logic [31 : 0 ] a,b;
logic[7 : 0] opcode;
logic[23 : 0] address;
}Instruction_Word_t;
Instruction_Word_t IW; //结构体分配储存区
如果结构体不用typedef声明,它会被当作匿名结构体引用。
struct{ //结构体定义
logic [31 : 0 ] a,b;
logic[7 : 0] opcode;
logic[23 : 0] address;
}Instruction;
局部和共享结构定义
自定义结构可在模块或接口内定义,在整个设计块中都可以使用。如果一个自定义结构体的定义需要在多个模块中,或作为模块或接口的端口使用,那么结构体定义应该放在包中,并导入到设计块或$unint编译单元域中。
2、结构体赋值
结构体可以使用值列表初始化
结构体在实例化的时候可以对其成员初始化。使用大括号’{}内的一组值,大括号之间的值必须与 成员的个数一致。
typedef struct{ //结构体定义
logic [31 : 0 ] a,b;
logic[7 : 0] opcode;
logic[23 : 0] address;
}Instruction_Word_t;
Instruction_Word_t IW = '{100,3,8'hFF,0}; //结构体分配储存区
定义结构体常数和结构体参数也使用类似的语法
注意:SystemVerilog数值列表语法不同于C语言
结构体成员赋值
通过引用结构体成员的名称可以对结构体的任意成员进行赋值。
typedef struct{ //结构体定义
logic [31 : 0 ] a,b;
logic[7 : 0] opcode;
logic[23 : 0] address;
}Instr_t;
Instr_t IW; //结构体分配储存区
always@(posedge clock ,negedge resetN)
if(!resetN)
begin
IW.a = 100;//引用结构体成员
IW.b = 5;
IW.opcode = 8'hFF;
IW.address = 0;
end
else
begin
....
end
结构体表达式在’{}里面
整个结构体可以使用结构体表达式进行赋值。一个结构体表达式由’{}符号内用逗号隔开的一组值构成,跟结构体初始化一样。大括号内必须包含赋给每一个成员的值。
always@(posedge clock, negedge resetN)
if(!resetN) IW = '{100,5,8'hFF,0};
else begin
...
end
结构体表达式可以按照顺序或按成员名称列出
结构体表达式中的值可以按照它们在结构体中定义的顺序列出。或者,结构体表达式也可以指定被赋值的结构体成员名称,使用冒号成员名称和要赋的值。当指定成员名称时,表达式的顺序可以是任意顺序。
IW = '{adrress:0,opcode:8'hFF,a:100,b:5};
结构体的部分或全部成员可以被赋一个默认值。
结构体表达式可以通过指定默认值来指定多个成员为一个值。使用default关键字可以将结构体的所有成员指定为默认值。
IW = '{default,0};//设置IW的所有成员为0
使用数据类型的关键字也可以给结构体中特定的数据类指定默认值。default关键字和数据类型关键字使用冒号和其指定的值隔开。
typedef struct{ //结构体定义
real r0,r1;
int i0,i1;
logic [7:0] opcode;
logic [23:0] address;
}Instruction_word_t;
instruction_word_t IW;
always(posedge clock,negedge resetN)
if(!resetN)
IW = '{real:1.0,default:0};
//指定所有real类型的成员默认值1.0
//指定其他成员默认值0
else
begin
....
end
赋给结构体成员的默认值必须与成员的数据类型兼容。也就是说这些值必须能够转换为成员的数据类型。
成员赋值还有一个优先级的问题。default关键字具有最低的优先级,可以被任意指定数据类型的默认值覆盖。指定数据类型的默认值又会被任意显性使用成员的赋值覆盖。
//赋给r0值1.0,r1值3.1415,结构体的其他成员被赋为0
typedef struct{ //结构体定义
real r0,r1;
int i0,i1;
logic [7:0] opcode;
logic [23:0] address;
}Instruction_word_t;
instruction_word_t IW;
IW = '{real:1.0,default:0,r1:3.1415};
3、压缩和非压缩结构体
可以使用packed关键字显示地声明一个压缩结构体。压缩结构体按照指定的顺序以相邻的位来储存结构体成员。压缩结构体被当作一个向量储存,结构体的第一成员在向量的最左边。向量的最低位是结构体最后一个成员最低位,其位编号为0
struct packed{
logic valid;
logic [7:0] tag;
logic [31:0] data;
}data_word;
压缩结构体的成员可以通过成员名引用,也可以使用结构体向量的相应位来引用。
data_word.tag = 8'hf0;
data_word[39:32] = 8'hf0;//同标识符相同的位
注意:压缩结构体只能包含整数值
压缩结构体必须包含压缩变量
压缩结构体的所有成员都必须是整数值。所谓整数值就是可以表示如byte、int这样的向量以及用bit或logic创建的向量值,如果结构体的任何一个成员不能用向量表示,那么结构体都不能被压缩。也就是说压缩结构体不能包含real或shortreal变量、非压缩结构体、非压缩联合体或非压缩数组。
压缩结构体的操作
压缩结构体被看做向量
由于压缩结构体以向量形式储存,对整个结构体的操作也是以向量的形式,因此对向量的算术操作、逻辑操作以及任何其他操作都可以用于压缩结构体。
typedef struct packed{
logic valid;
logic [7:0] tag;
logic [31:0] data;
}data_word_t;
data_word_t packed_in,pack_out;
always@(posedge clock)
packed_out <= packet_in <<2;
注意,当把’{}符号的一系列值赋给压缩结构体时,列表中的值被赋予结构体的每一个成员,在这种情况下,压缩结构体来对待,而不是一个向量。’{}符号内的值是针对每一个结构体成员的分立值,而不是值的拼接。
有符号的压缩结构体
压缩结构体作为向量使用可以是有符号或者无符号的
压缩结构体可以使用关键字signed或unsigned来声明,这些修饰符影响到整个结构体在算术或相关的操作中,作为一个向量如何识别,但不影响到整个结构体的成员如何识别。结构体的每个成员是有符号的还是无符号,依赖于成员的类型声明。压缩结构体的部分选择始终是无符号的。
typedef struct packed signed{
logic valid;
logic [7:0] tag;
logic [31:0] data;
}data_word_t;
data_word_t A,B;
always@(posedge clock)
if(A<B) //有符号的比较
...
4、通过端口传递结构体
端口可以被声明为结构体类型
结构体可以通过模块和接口的端口传递。结构体必须首先使用typedef定义为用户自定义数据类型,然后才允许将模块或接口的端口声明为结构体类型。
package definitions;
typedef enum{ADD,SUB,MULT,DIV}opcode_t;
typedef struct{
logic[31:0] a,b;
opcode_t opcode;
logic [23:0] adress;
logic error;
}instruction_word_t;
endpackage
module alu
(input definitions::instruction_word_t IW,
inpput wire clock);
...
endmodule
还要一种风格可以将包含typedef定义的包显示命名为模块端口定义的一部分,那就是将包导入到$unint编译单元声明域中。还可以在 $unint域中直接定义用户自定义类型。
当一个非压缩结构体通过模块端口传递时,端口两边连接的必须是同一种类型的结构体。在两个不同模块中声明的匿名结构体,即使它们具有相同的名称,同样的成员及名称,但它们并属于相同的结构体类型。
5、将结构体作为自变量传递至任务和函数
结构体可以传递给任务或者函数
结构体可以作为自变量传递至任务和函数。要做到这一点,必须首先使用typedef将结构体定义为用户自定义数据类型,这样任务和函数的自变量才可以声明为这种结构体的类型。
module processor(...);
...
typedef enum{ADD,SUB,MULT,DIV}opcode_t;
typedef struct{
logic[31:0] a,b;
opcode_t opcode;
logic [23:0] adress;
logic error;
}instruction_word_t;
function alu(input instruction_word_t IW);
...
endfunction
endmodule
当被调用任务或者函数具有非压缩结构体的形式变量时,必须传递完全相同类型的结构体给任务或函数。匿名的结构体,即使具有相同的成员和名称,也不是同种类型的结构体。