本文所有源码可通过后台回复 “结构体”获得
结构体由关键字struct
声明,且结构体中的成员可以是任何数据类型
如下定义一个结构体:
struct{
int a, b; //32位int类型
bit c; //1位bit类型
logic [7:0] din; //8位logic类型
opcode_t opcode; //用户自定义类型
} Instrution_Word;
结构体就如同一个集合,集合中包含各种变量和常量,并且这些变量和常量可以用结构体名进行引用,引用方式为<结构体名>.<变量名>
如果要用到上面定义的结构体中的变量c,可以这样引用Instruction_Word.c
结构体也可以整体显示声明为变量整体或者线网整体
var struct { //结构体变量
logic [31:0] a, b;
logic [7:0] opcode;
} Instruction_Word_var;
wire struct { //结构体线网
logic [31:0] a, b;
logic [7:0] opcode;
} Instruction_Word_net;
结构体整体可以声明为线网类型,但结构体内部不能使用线网类型
像上面这个例子,我们把结构体名定义的比较长,每次引用结构体内的变量,会显得冗长,这里可以利用typedef
将结构体声明为用户自定义类型
typedef struct { //用户自定义类型的结构体
logic [7:0] a, b;
logic [31:0] address;
} Instruction_word_t;
Instruction_word_t IW; //用IW代替结构体名
//调用结构体内部变量
IW.a
IW.adress
这里有两点需要说明
Instruction_word_t IW;
会给结构体分配存储空间typedef
声明,它会被当作匿名结构体引用可以在实例化结构体时对内部的变量进行初始化,方法是用'{}
,大括号里的数值个数及次序必须与成员个数及次数一致
看个例子
module struct_tb;
typedef struct{
logic [7:0] a, b;
logic [3:0] all_one;
bit flag;
}Instruction_t;
Instruction_t IW = '{100, 8'hF0, '1, 0}; //实例化
initial begin //打印结构体中的内容
$display ("\n\t the a is %b", IW.a);
$display ("\n\t the b is %b", IW.b);
$display ("\n\t the all_one is %b", IW.all_one);
$display ("\n\t the flag is %b", IW.flag);
end
endmodule
运行结果如下
注意:大括号前的符号是 英文的单撇号
刚开始以为是Esc键下面的那个按键,结果就运行失败了
尝试一下在实例化的时候删除一个赋值
Instruction_t IW = '{100, '1, 0}; //实例化
看看编译会出现什么情况
看来赋值时的个数确实要与结构体内部变量的个数一致
如果我们一时间只想改变结构体中某个成员的值,可以单独给这个成员赋值
就像上面这个例子,在初始化后,我们只想改变变量flag
的值,可以这样
module struct_tb;
typedef struct{
logic [7:0] a, b;
logic [3:0] all_one;
bit flag;
}Instruction_t;
Instruction_t IW = '{100, 8'hF0, '1, 0}; //实例化
initial begin
$display ("\n\t the a is %b", IW.a);
$display ("\n\t the b is %b", IW.b);
$display ("\n\t the all_one is %b", IW.all_one);
$display ("\n\t the flag is %b", IW.flag);
IW.flag = 1; //改变flag的值
$display ("\n\t the flag is %b", IW.flag); //再次打印flag
end
endmodule
从运行结果中可以看到,初始化时flag为0,通过单独给flag赋值后,变为1
结构体表达式赋值与初始化类似
一个结构体表达式由 '{}符号用逗号隔开的一组值构成
有两种形式
IW = '{100, 8'hF0, '1, 0};
IW = '{a:10, b:8'h0F, all_one:2, flag:0};
我们试试两种方式赋值是否都可行
module struct_tb;
typedef struct{
logic [7:0] a, b;
logic [3:0] all_one;
bit flag;
}Instruction_t;
Instruction_t IW = '{100, 8'hF0, '1, 0}; //实例化
initial begin
$display ("\n\t the a is %b", IW.a);
$display ("\n\t the b is %b", IW.b);
$display ("\n\t the all_one is %b", IW.all_one);
$display ("\n\t the flag is %b", IW.flag);
IW.flag = 1; //改变flag的值
$display ("\n\t the flag is %b", IW.flag); //再次打印flag
IW = '{a:10, b:8'h0F, all_one:2, flag:0}; //结构体表达式赋值
/****再次打印所有成员****/
$display ("\n\t ******再次打印所有成员******");
$display ("\n\t the a is %b", IW.a);
$display ("\n\t the b is %b", IW.b);
$display ("\n\t the all_one is %b", IW.all_one);
$display ("\n\t the flag is %b", IW.flag);
end
endmodule
可见两种方式都可行!
那么这两种方式能不能混合使用呢?
我们在显示后面再加两句
IW = '{a:10, 8'h0F, all_one:2, flag:0}; //混合方式赋值
$display ("\n\t the flag is %b", IW.flag);
运行结果显示 不能混合使用
结构体表达式的方式是不是类似于Verilog中module的调用,分为按位置调用和按名称调用,只不过不能有缺省
关键字default
可以将结构体的所有成员指定为默认值,像这样'{default:0}
module default_tb;
typedef struct{ //声明结构体
logic [7:0] a, b;
logic [7:0] all;
bit flag;
} Instruction_t;
Instruction_t IW;
initial begin
IW = '{default : 0}; //设置为默认值
/***************打印所有成员************/
$display("\n\t a is %b", IW.a);
$display("\n\t b is %b", IW.b);
$display("\n\t all is %b", IW.all);
$display("\n\t flag is %b", IW.flag);
end
endmodule
可以看到运行结果均为对应位宽的默认数值
优先级排序:default赋值 < 数据类型赋值 < 成员名称赋值
优先级高的赋值会覆盖掉优先级低的赋值
用一个例子测试一下
module default_tb;
typedef struct{ //声明结构体
logic [7:0] a, b;
logic [7:0] all;
bit flag;
} Instruction_t;
Instruction_t IW;
initial begin
IW = '{default : 1, a : 2, logic : 3};
$display("\n\t a is %b", IW.a);
end
endmodule
可以看到,最终输出的结果是 成员名赋值的结果,因为按成员名称赋值的优先级最高
默认情况下结构体是非压缩的,虽然结构体的成员使用一个共同的结构体名称,但他们被当作是独立的变量或常量
可以使用关键字
packed
显式声明一个压缩结构体,压缩结构体按照指定的顺序以相邻的位来存储结构体成员
压缩结构体就像一个向量,所有的成员都是向量中的元素,成员按照在结构体中的顺序,依次是向量的高位和低位
比如下面这个压缩结构体:
struct packed{
logic [3:0] title;
logic middle;
logic [1:0] endle;
} data_word;
按照压缩变量的说法,title, middle, endle
会组成一个向量,其顺序如下
为了验证这一点,同样做一个小测试
module compress_tb;
/*******声明一个压缩结构体********/
struct packed {
logic [3:0] title;
logic middle;
logic [1:0] endle;
} data_word;
initial begin
data_word = 100; //给这个结构体赋值
end
initial begin //打印
$display("\n\t print this struct %b", data_word);
$display("\n\t the \"title\" is %b", data_word.title);
$display("\n\t the \"middle\" is %b", data_word.middle);
$display("\n\t the \"endle\" is %b", data_word.endle);
end
endmodule
从结果中可以看到,title
是高四位,middle
是中间位,endle
是低两位,与上述吻合
有一点需要注意,这里是packed不是定义包的那个package ,并且顺序不能搞错,一定是struct packed,而不是packed struct
压缩结构体只能包含整数值
压缩结构体也可以定义为有符号的压缩结构体,只需要加一个
singed
像上面例子中定义的结构体,可以改为struct packed singed
便是有符号的压缩结构体
本文主要参考《SystemVerilog硬件设计及建模》