SystemVerilog 结构体

本文所有源码可通过后台回复 “结构体”获得

结构体

1. 基本信息

结构体由关键字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声明,它会被当作匿名结构体引用

2. 结构体赋值

2.1 结构体初始化

可以在实例化结构体时对内部的变量进行初始化,方法是用'{},大括号里的数值个数及次序必须与成员个数及次数一致

看个例子

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}; //实例化

看看编译会出现什么情况

运行结果

看来赋值时的个数确实要与结构体内部变量的个数一致

2.2结构体成员赋值

如果我们一时间只想改变结构体中某个成员的值,可以单独给这个成员赋值

就像上面这个例子,在初始化后,我们只想改变变量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

2.3 结构体表达式赋赋值

结构体表达式赋值与初始化类似

一个结构体表达式由 '{}符号用逗号隔开的一组值构成

有两种形式

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的调用,分为按位置调用和按名称调用,只不过不能有缺省

2.4 默认值

关键字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

运行结果

可以看到运行结果均为对应位宽的默认数值

2.5 赋值的优先级

优先级排序: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

运行结果

可以看到,最终输出的结果是 成员名赋值的结果,因为按成员名称赋值的优先级最高

3. 压缩和非压缩

默认情况下结构体是非压缩的,虽然结构体的成员使用一个共同的结构体名称,但他们被当作是独立的变量或常量

可以使用关键字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硬件设计及建模》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值