自定义类型
通过用户自定义类型,以往Verilog的代码将可以通过更少的代码来表示更多的功能。
用户自定义类型使得代码的可读性更好。
自定义类型的方法:
- 通过typedef来创建用户自定义类型。
- 通过enum来创建枚举类型。
- 通过struct来创建结构体类型。
typedef
Verilog语言无法提供方便的特性来使得用户扩展变量和线网类型。
SV则提供了特性使得用户可以构建更高抽象层的数据类型。
用户可以利用已有的数据类型来定义新的数据类型,一旦定义了新的数据类型,则可利用该类型声明变量。( 就同C语言一样)
typedef int unsigned uint;
...
uint a , b; //uint声明的2个变量
为了使代码更易读和维护,通常我们都习惯添加“_t”的后缀用来表示它是一个自定义类型(type) 。
enum
枚举类型(enum)可以描述变量的合法值范围,其每一个值都需要提供一个用户自定义的名字。
//举例:枚举类型RGB可以拥有red、 green和blue的三个数值:
enum { red ,green ,blue } RGB;
Verilog语言不支持枚举类型,因此为了提供类似于枚举类型设计方式,我们不得不采用参数来表示可取的值范围,或者使用宏来定义各个合法值的宏名称。
举例说明枚举类型的便利:
Verilog代码
`define FETCH 3 'h0
`define WRITE 3 'h1
`define ADD 3 'h2
`define SUB 3 'h3
`define MULT 3'h4
`define DRV 3 'h5
`define SHIET3 'h6
`define NOP 3 'h7
module controller (
output reg read, write ,
input wire [2:0]instruction ,
input wire clock, resetN) ;//instruction依赖于宏定义
parameter WAITE =o ,
LOAD = 1,
STORE= 2;
reg [1∶0] State ,NextState ;//赋值依赖于参数
always @(posedge clock , negedge resetN)
if ( !resetN) state <= WAITE;
else state <=NextState ;
always @ (State) begin
case (State)
WAITE:NextState = LOAD;
LOAD: NextState = STORE;
STORE: NextState = WAITE;
endcase
end
always @ (State, instruction) begin
read = 0 ; write = 0 ;
if (State == LOAD && instruction == `FETCH)
read = 1;
else if (State == STORE && instruction ==`WRITE)
write = 1 ;
end
endmodule
使用SV,代码更加明了,易于维护.
package chip_types;
typedef enum {FETCH,WRITE,ADD,SUB ,MULT,DIV,SHIFT,NOP } instr_t;
Endpackage
import chip_types : :* ; //将包的定义导入到$unit里
module controller (output logic read, write ,input instr_t instruction,input wire clock , resetN) ;#自定义类型instr_t instruction
enum {WAITE,LOAD,STORE} State, Nextstate ;
always_ff @(posedge clock , negedge resetN)
if ( !resetN) State <= WAITE;
else state <= NextState ;
always_comb begin
case (State)
WAITE:Nextstate = LOAD ;
LOAD: Nextstate = STORE;
STORE: NextState = WAITE;
endcase
end
always_comb begin
read = 0 ; write = 0 ;
if (State == LOAD && instruction == FETCH) read = 1;
else if (State == STORE && instruction == WRITE) write = 1;
end
endmodule
默认的枚举类型是int,即32位的二值逻辑数据类型。为了更准确地描述变量,SV允许指明其数据类型,例如:
enum bit {TRUE,FALSE} Boolean ;
enum logic [1:0] {WAITE,LOAD,READY} state ;
如果枚举类型变量被赋值,那么所赋的值应在其数值范围。
enum logic [2:0] {WAITE= 3’ b001, LOAD = 3' b010, READY =3' b100} state ;
如果枚举变量是四值逻辑,那么将其值赋为X或者Z也是合法的。
enum logic {ON=1'b1,OFF=1'bz} out;
枚举类型:
- 匿名枚举类型(anonymous enumerated type:枚举类型并没有伴随着typedef
- 枚举类型也可以声明为自定义类型,这就使得可以用同一个枚举类型来声明多个变量或者线网。
typedef enum {WAITE,LOAD,READY} states_t;
states_t state, next_state ;
枚举类型赋值:
Verilog或者SV可以在不同的数据类型之间通过隐性转换,进行直接赋值,因此Verilog/SV的数据类型转换是宽松的。
枚举类型赋值时则相对严格。
例如下面的例子中,赋值操作符“=”的左右两侧应该尽量为相同的枚举类型。
typedef enum {WAITE,LOAD,READY] states_t;
states_t; state, next state ;
int foo;
state = next state; //合法操作
foo = state +1; //合法操作
state = foo + 1; //非法赋值
state = states_t'(foo + 1)//合法赋值
state = state +1; l/非法赋值
state = states _t'(state + 1)//合法赋值
state++; //非法赋值
next state +=state; //非法赋值
struct
为什么要用结构体:
设计或者验证的数据经常会有逻辑相关的数据信号组,例如一个总线协议的所有控制信号,或者在一个状态控制器中用到的所有的信号。
Verilog语言没有方便的特性可以将相关的信号收集整理到一个信号组中。 SV添加了同C一样的结构体struct。
结构体的成员可以是任何变量类型,包括首定义类型或者其它常量类型。
struct {
int a, b; //32位变量
opcode_t opcode; //用户自定义类型
logic [23:0] address; // 24位变量
bit error; //1位变量
} Instruction_word;
正是由于结构体是变量的合集,因此结构体类型的变量也可以用来索引到其内部的变量,索引方式同C语言一致:
<structure_name>.<variable_name>
Instruction_word .address = 32' hFo00001E ;
结构体类型默认也是变量类型,用户也可以显式声明其为var或者wire类型。
类似于枚举类型,结构体类型也可以伴随着typedef来实现自定义结构体类型。
//自定义结构体
typedef struct {
logic [31:0] a, b;
logic [ 7:0] opcode ;
logic [23:0] address;} instruction_word_t;
instruction_word_t Iw; 1/结构体变量声明
结构体变量可以通过索引其各个成员做依次的成员赋值:
always (posedge clock, negedge resetN) begin
if ( !resetN) begin
Iw.a = 100; //reference structure member
Iw.b = 5;
Iw.opcode = 8' hFF;
Iw.address = 0 ;
end
else begin
...
end
end
也可以通过分号’和花括号{}来实现整体赋初值:
Iw =' {100, 3,8" hFE, 0};
Iw = " {address: 0, opcode : 8* hFF, a:100, b:5};