高级语法(1):typedef、struct、enum、union
前言
视频语法学习:B站链接
笔记原地址:https://github.com/Tan-YiFan/DigitalLogic-Autumn2020/tree/syntax/syntax网络不好,可能打不开。
数字电路中,万物皆为二进制。
类型统一为logic,符合这一规律。但这对程序员,可能不太友好。
- 需要管理变量的位数
- 同一位数的信号,可能意义完全不同(1010可能是格雷码/正常数值)
对此,引入自定义类型语法typedef。
1 typedef基础
基本格式为:typedef 已有类型 新类型;
一般自定义类型后面添加一个_t,方便区分。
typedef logic[31:0] word_t;//32位的数声明成一个字
typedef logic[5:0] entry_t;//表项是一个类型
typedef entry_t[31:0] table_t;//32位的表项组成一个table,table(是关键字)是已有自定义的类型生成的一个自定义的类型
声明与使用变量的语法:
word_t a, b;//a, b均为32位
assign b = {a[15:0], a[31:16]};
table_t table1; //table1与table_t是一致滴//table1是 logic [31:0][5:0]32个6位的向量
assign table1[1] = '0;// table1[1]是一个6位的向量
assign table1[0][1] = '0;// table1[0][1]可以2级索引
用途举例:变量名的含义,用类型去判断变量的实际意义
//四——十六译码器
typedef logic[3:0] code_t;//输入是编码
typedef logic[15:0] info_t;//输出是信息
typedef logic[31:0] paddr_t; // physical address
typedef logic[31:0] vaddr_t; // virtual address
2 struct
结构体struct可以描述一组相关的数据。
以译码器为例,按以前的写法,可能需要这样写:
logic [3:0] alufunc;
//alufunc_t alufunc;//或者用自定义类型
logic mem_read;
logic mem_write;
logic regwrite;
logic [6:0] control;
assign control = {alufunc, mem_read, mem_write, regwrite};//要把control的位数算出来,多的数据的时候,计算control很麻烦
结构体类型相关的语法如下:
// type definition
//该句中可以把struct看作已有类型名,control_t是新类型名
typedef struct packed { //{}中是信号声明语句,分号结尾
logic [3:0] alufunc;
logic mem_read;
logic mem_write;
logic regwrite;
logic [3:0] reg_addr;//可以任意添加信号,不用修改control_t的位数
} control_t;//control_t是类型名
// variable declaration变量声明
control_t control;//结构体声明变量,control会计算有多少位,存储从高位到低位存储,算出来7位
logic regwrite;
assign regwrite = control.regwrite;//使用的时候:变量.成员
//或者用assign regwrite = control[0];//按位索引也一样;从低位索引
// using structs without typedef匿名类型
struct packed {//类型名+变量名去声明
logic [3:0] alufunc;
logic mem_read;
logic mem_write;
logic regwrite;
} control_without_typedef;//control_without_typedef是一个结构体
struct语法有很多好处,用途也很广。
//流水线寄存器
typedef struct packed {
//省略元素
} pipeline_decode_t;
pipeline_decode_t p, p_nxt;//声明变量
always_ff @(posedge clk) begin
p <= p_nxt;
end
3 enum
枚举类型,要求每一个变量的数据位是一样长
枚举的语法形式为:
typedef enum <datatype> {//<datatype>是已有数据类型,一般是指定位数
IDEN_1, IDEN_2//是常量,可以看作宏,仿真器从低到高,会认为IDEN_1是0, IDEN_2是1
} typename;//typename是自己的命名
举例:
typedef enum logic [3:0] {
ALU_ADD, ALU_AND, ALU_SUB
} alufunc_t;
alufunc_t alufunc;//声明变量
enum logic [3:0] {
ALU_ADD, ALU_AND, ALU_SUB
} alufunc_without_typedef;
enum语法常用于编码(包括状态机的编码)。
enum类型的变量,在Vivado仿真里会显示枚举项,如:显示ALU_ADD, ALU_AND, ALU_SUB,而不是0、1、2。(参考我的视频《编码和译码的应用》)
枚举项被视为常量,各枚举类型的枚举项名字不能冲突。
enum类型的变量,赋值时只能用枚举项。
//状态机
typedef enum logic [1:0] {
STATE_0, STATE_1, STATE_2
} state_t;
state_t state, state_nxt;
always_ff @(posedge clk) begin
if (~resetn) begin
// state <= '0;//此处是枚举类型,会出错
state <= state_t'(0);//state_t'(0)此处是0号状态,即STATE_0
end else begin
state <= state_nxt;
end
end
4 union
联合类型的语法:
typedef union packed {
struct packed {
logic zero;
logic [31:0] aluout;
} alu;
struct packed {
logic branch_taken;
logic [31:0] pcbranch;
} branch;
struct packed {
logic [31:0] addr;
logic mem_read;
} memory;
} result_t;
result_t res;
logic [31:0] addr, aluout;
assign addr = res.memory.addr; // assign addr = res[32:1]//重高到低
assign aluout = res.alu.aluout; // assign aluout = res[31:0]
assign res.alu.aluout = '1;//相当于alu低32位([31:0])全是1,已经有驱动啦,不能在对pcbranch和mem_read在赋值;
//如果再次赋值,则是多驱动了,同一个信号,只能有一个驱动
对union类型的变量进行赋值时,要注意多驱动。
union类型比较占空间