用户自定义和枚举数据类型
用户自定义
1、typedef定义用户自定义类型
SystemVerilog同C一样,使用typedef关键字来建立用户自定义类型。用户自定义类型允许使用现有的数据类型建立新的数据类型。新的数据类型定义后,可以声明这个类型的变量
typedef int unsigned uint;
...
unint a,b;//unint类型的两个变量
2、局部使用typedef
用户自定义类型可以在局部定义,也可以在编译单元域进行外部定义。当一个用户自定义数据类型只用于设计的特定部分时,typedef的定义可以在模块或者设计的接口部分。
module alu(...);typedef logic[3:0] nibble;nibble opA,opB;//nibble类型的变量nibble [7:0] data;//由8个nibble类型组成的32位向量
3、共享typedef定义
(1)包中的typedef定义
如果一个用户自定义类型会在很多不同的模型中用到,那么typedef声明可以在包中内声明。然后这些定义就可以被直接引用,或导入到每个使用该用户自定义类型的模块、接口或程序块中。
(2)$unint中的typedef
typedef定义也可在外部,即编译单元域中声明。外部声明就是将typedef语句放在任何模块、接口或程序块的外面。
//直接从包中引用用户自定义类型
package chip_types;`ifdef TWO_STATEtypedef bit dtype_t;`elsetypedef logic dtype_t;`endif
endpackage
module counter
(output chip_types::dtype_t[15:0] count,input chip_types::dtype_t clock,resetN
);
always@(posedge clock , negedge resetN)if(!resetN) count <= 0;else count <= count +1;
endmodule
将包定义导入到$unint
当模块多个端口都是用户自定义类型,而每个端口声明直接引用包名又显得繁琐时,可以用这一方法。
//将包中的typedef 定义导入到$unint中
package chip_types;`ifdef TWO_STATEtypedef bit dtype_t;`elsetypedef logic dtype_t;`endif
endpackage
import chip_types::dtype_t;//导入定义到$unint
module counter
(output dtype_t[15:0] count,input dtype_t clock,resetN
);
always@(posedge clock , negedge resetN)if(!resetN) count <= 0;else count <= count +1;
endmodule
如果包中包含许多typedef ,可以通过通配符导入到$unint中,而不是将包中特定子项导入到 $unint
import chip_types::*;//通配符导入
枚举数据类型
1、枚举数据类型提供了一种方式来声明一个具有特定允许值列表的抽象变量,每一个值都有一个确定的用户自定义名字,即标签(label)。
//变量RGB有red、green、bule三个值
enum {red,greed,bule} RGB;
枚举值通过标签来区别
enum{WAITE,LOAD,STORE}State,NextState;
枚举数据类型提供了一种方法将一个变量的值对应到一个有意义的标签上,这可以是模型或者测试代码更易读,也可以使代码更容易自成文档,并更容易纠错。
package chip_typed;typedef enum{FETCH,WRITE,ADD,SUB,MULT,DIV,SHIFT,NOP} instr_t;
endpackage
import chip_types::*;//导入包定义到$unint里
module controller(output logic read,write,input instr_t instruction,input wire clock,resetN
);
enum{WAITE,LOAD,STORE}State,NextState;
always_ff@(posedge clock,negedge resetN)if(!resetN) State <= WAITE;else State <= NextState;
always_comb
begincase(State)WAITE: NextState = LOAD;LOAD: NextState = STORE;STORE: NextState = WAITE;endcase
end
always_comb
beginread = 0;write = 0;if(State == LOAD && instruction == FETCH)read = 1;else if(State == STORE && instruction == WRITE)write = 1;
end
注意:导入枚举类型定义名时不会自动导入枚举值标签
2、枚举类型标签序列
指定一个枚举列表标签序列
state | 创建单个标签state |
state[N] | 创建标签序列,state0,state1,…stateN |
state[N:M] | 创建标签序列,由stateN开始,stateM结束。如果N小于M,序列由N增长至M。如果N大于M,序列由N减少至M |
enum {RESET,S[5],W[6:9]}state;
3、枚举类型标签作用域
枚举标签必须唯一
枚举类型列表中的标签在其作用域内必须是唯一的。可以包含枚举类型声明的作用域包括编译单元、模块、接口、程序、begin…end块、fork…join块、任务和函数。
4、枚举类型值
枚举类型标签有一个默认值
缺省情况下,枚举类型列表中的标签代表的实际数值是一个int类型的整数。枚举列表中的第一标签表示数值0,第二个名称表示1,第三个表示数值2,以此类推
用户可以指定标签的值
SystemVerilog可以显示地说明枚举列表中的标签表示的数值。这允许抽象的枚举类型在需要时进行修改以便描述更具体的硬件特性。
//枚举列表中的每一个标签都有一个与其对应的整数值。
enum{
ONE = 0,
FIVE =5,
TEN =10
}state;
标签值必须是唯一
枚举列表中的各个标签必须具有唯一的值。如果有两个标签具有相同的值就会出现错误。
5、枚举类型的基类
枚举类型的默认基类为int类型
枚举类型是具有以系列标签值的变量或线网。因此,枚举类型具有一个Verilog或SystemVerilog基类。枚举类型的默认基类是int,它是32位两态类型。
基类可以显示定义
为了更具体的描述硬件,SystemVerilog允许对枚举类型的基类进行显示的声明
//1位宽的枚举类型,两态基类
enum bit{TURE,FALSE} Boolean;//2位宽的枚举类型,四态基类
enum logic[1:0] {WAIT,LOAD,READY}state;
枚举值宽度
如果对显示定义枚举类型的枚举标签赋值,那么这个值的宽度必须与基类宽度相符
enum logic[2:0] {WAIT =3'b001,LOAD = 3'b010,READY = 3'b100}state;
四态枚举类型
如果枚举值的基类是四态数据类型,将枚举标签赋为X或则Z是合法的。
enum logic{ON = 1'b1,OFF = 1'bZ}out;
6、自定义和匿名枚举
自定义枚举类型使用typedef定义
枚举类型可以声明为一个用户自定义类型。这提供了一个便捷方式来声明几个具有相同枚举值的变量或线网。由typedef声明的枚举类型一般称作自定义枚举类型,不使用typedef定义的则称作匿名枚举类型
typedef enum{WAITE,LOAD,REDAY}states_t;
states_t state,next_state;
7、枚举类型操作的强类型检验
大部分变量类型都是弱类型
也就是说,任何类型的任何值都可以赋给一个变量。数值将会自动地转换成变量的数据类型,遵循Verilog或SystemVerilog标准指定的转换原则。
枚举类型是强类型
枚举类型的类型是半强类型。一个枚举类型只可以进行下列赋值:
- 枚举类型列表中的一个标签。
- 同类枚举类型的其他变量(即用同样枚举类型声明的变量)
- 通过cast转换成枚举类型变量的数值
操作使用标签的基类
当对一个枚举类型数据进行操作时,枚举数值自动转换成代表枚举类型列表中标签的基类和内部值。如果枚举类型标签的基类没有显式地声明,则默认基类和标签为int类型。
typedef enum {WAIT,LOAD,READY}states_t;
states_t state,next_state;
int foo;
8、将表达式强制转换为枚举类型
将值强制转换成枚举类型
一个操作的结果可以转换成一个枚举类型,然后赋给同种类型的一个枚举类型变量。SystemVerilog的强制转换操作符或者动态$cast系统函数都可以使用。
typedef enum {WAIT,LOAD,READY}states_t;
states_t state,next_state;
next_state = states_t(state+1); //合法的强制类型转换
$cast(next_state,state+1); //合法的动态的$cast系统函数
强制转换操作符总是进行强制转换操作和赋值,而不检查被赋的值是否在枚举类型设置的合法范围内。
如果state有一个值READY,它代表2,加1结果为整数3.将这个数值赋给next_state将会超出next_state枚举类型列表的数值范围。
动态的$cast系统函数在修改目标变量之前检查表达式的结果是否是一个合法值。如果state加上1对于next_state是一个范围外的值,那么 $cast(next_state,state+1)不会修改next_state,并且会报告一个运行错误。
9、枚举类型的专用系统任务和方法
SystemVerilog提供了一些内置函数,称为方法(method),可以循环访问枚举类型列表中的值。这些方法能自动处理枚举类型的半强类型特性,使一些操作变得简单,如递增到枚举类型列表下一个值、跳到列表的开头、跳到列表的结尾。使用这些方法,不必知道列表的中的名称和值。
这些处理枚举列表的专用方法的调用方法类似于C++类方法的调用,即方法的名称附在在枚举变量名字的后面,用一个“.”分开。
<枚举变量名>.first --返回指定变量枚举列表中的第一个成员的值。
<枚举变量名>.last–返回枚举列表最后一个成员的值。
<枚举变量名>.next()–返回枚举列表中下一个成员的值。可以用一个整数值作为next的参数。在这种情况下,从枚举变量的当前位置算起,返回后面第N个成员的值。如果到达了枚举列表的末尾,则会返回到列表的开头。如果枚举变量的当前值不在枚举列表中,则返回列表中第一个成员的值。
<枚举变量名>.prev()–返回枚举列表中前一个成员的值。同next的方法一样。
<枚举变量名>.num–返回变量的枚举列表中元素个数
<枚举变量名>.name–返回枚举变量中代表这个值的字符串。如果这个值不在枚举变量列表中,则返回一个空字符串。
module confidence_counter(input logic synced,compare,resetN,clock,ouptut logic in_sync
);
enum{cnt[0:15]}State,Next;always_ff@(posedge clock , negedge resetN)if(!resetN) State <= cnt0;else State <= Next;always_comb
beginNext = State;//default NextState valuecase(State)cnt0:if(compare && synced) Next = State.next;cnt1:beginif(compare && synced) Next = State.next;if(compare && !synced) Next = State.first;endcnt15:if(compare && !synced) Next = State.prev(2);default:beginif(compare && synced) Next = State.next;if(compare && !synced) Next = State.prev(2);endendcase
endalways_ff@(posedge clock , negedge resetN)if(!resetN) in_sync <=0;else beginif(State == cnt8) in_sync <= 1;if(State == cnt0) in_sync <= 0;end
10、打印枚举类型
打印枚举类型标签和值
枚举类型值可以作为标签的内部值或标签名打印。直接打印枚举变量会打印变量的内部值。使用枚举类型的方法name可以读取当前值的标签名称,并返回包含名称的字符串这个字符串可以用$display打印出来。
module FSM(input logic clock,resetN,output logic[3:0]control);enun logic[2:0]{WAITE=3'b001,LOAD=3'b010,READY=3'b010} State,Next;always @(posedge clock, negedge resetN)if(!resetN)State<=WAITE;else State<=Next;
always_comb begin$display("\nCurrent state is 8s(号b)",State.nane,State);case(State)WAITE:Next=LOAD;LOAD:Next=READY; READY:Next=WAITE; endcase$display("Next state will be 号s(告b)",Next.name,Next); endassign control=State;
endmodule