试想一下:想给数值一个label时,有没有好的方法呢 ?
一 基本知识
Verilog/SV会采取不同的方法。
用parameter/`define来定义一组常数或者宏名。
`define FETCH 3'h7 //宏定义
module controller (output reg read...);
parameter LOAD = 0; //常数定义
always @(State) begin
if(State == LOAD && instruction == `FETCH) //使用label
...
end
endmodule
今天,我们介绍另外一种方法,成为 枚举类型 enum 。
package chip_types;
typedef enum {
FETCH,
WRITE,
ADD,
SUB,
MULT,
DIV,
SHIFT,
NOP
} instr_t;
//用户自定义枚举类型instr_t
endpackage
import chip_types::*; //import包定义到$unit编译单元域
module controller (input instr_t instruction,...); //input instr_t类型instruction
enum { WAITE, LOAD, STORE} State, NextState; //枚举类型State, NextState。两者值只能是{}中的label
always_comb begin
if(State == LOAD && instruction == FETCH) //使用label
...
end
endmodule
1.3. 枚举类型标签序列
state | 创建单个标签state |
state[N] | 创建标签序列 state0,state1, ... stateN-1 |
state[N:M] | 创建标签序列,由stateN开始,到stateM(正反向不存在) |
1.4. 枚举类型标签作用域
enum列表中的label在其作用域内必须是唯一的(label唯一)
这里的作用域包括:$unit, modules, interfaces, programs,begin...end blocks, fork...join blocks, tasks and functions
1.5. 枚举类型值
- 枚举类型值在没有显示指定时,默认int从0自加。且label的值也要唯一(value唯一)
enum {A=1, B, C, X=24, Y, Z} list; //A=1,B=2,C=3,X=24,Y=25,Z=26
1.6. 枚举类型的基类
- 枚举类型的默认基类(base type)是int,也可以对枚举类型的基类进行显示声明
enum logic [1:0] {WAITE, LOAD, READY} state; //2位宽的枚举类型,四态基类
- 注意label数涉及到value的值,要与基类类型匹配。
1.7. 自定义枚举类型
typedef enum {WAITE, LOAD, READY} states_t; //自定义枚举类型states_t
states_t state, next_state; //声明枚举state,next_state
(同类型枚举变量可以互相赋值)
1.8. 枚举类型的专用系统任务(system tasks)和方法(method)
二 注意细节
enum是system verilog中使用频率极高的数据类型之一,验证中作内置判断类型,设计时作为状态机类型都很常见,平时一直在用但是对于细节一直把控不准,因此总结一下。
常见的枚举类型定义和使用方式,假如我们要定义一个状态机:
initial begin
typedef enum {idle, st1, st2, st3} state_s;
state_s state, next_state;
$display("state -> %0d, %0d, %0d, %0d", idle, st1, st2, st3);
end
打印效果如下:
细节1:枚举定义的idle/st1/st2/st2类似于于parameter idle = 0,在当前命名空间域具有独占性;
例如以下的定义方式,编译时候就会报error:
typedef enum {idle, st1, st2, st3} state_s;
typedef enum {idle, st1} state_t;
那么当然了,下面这种写法也是不行的,哪怕你定义的值是一样的:
parameter idle = 0;
typedef enum {idle, st1, st2, st3} state_s;
细节2:枚举内定义的参数值并不是一个完备的取值空间;
这句话有点绕,简单来说就是,枚举变量可以取枚举以外的值!例如下面这段代码,用state_s做强类型转换就可以了:
initial begin
typedef enum {idle, st1, st2, st3} state_s;
state_s state, next_state;
next_state = st1;
$display("next_state = %0d", next_state);
next_state = state_s'(10);
$display("next_state = %0d", next_state);
end
当然了,我是没有想出来什么场景下会这样用,但是这个还是提醒了我们一下,当把枚举类型用作状态机编码时候,不要认为只枚举了三个内置参量,之后写状态或者case时状态机就只能有这三种case,该写default还是要写。
细节3:枚举参量默认是int型,也可以显性的写其他类型;
比如,我要作一个独热码状态机,就可以这么些:
initial begin
typedef enum logic[2:0]{idle = 3'b000, st1 = 3'b001, st2 = 3'b010, st3 = 3'b100} state_s;
state_s state, next_state;
next_state = st1;
$display("next_state = %0d, state = %0d", next_state, state);
next_state = state_s'(10);
$display("next_state = %0d", next_state);
$finish;
end
值得注意的是,在这个独热码状态机中,由于使用的是logic四态数据类型同时state没有初始化,因此state是X态;此外还是刚刚那个话题,哪怕你就规定了四种参数值,我还是能给你整出来第五个,因此default很关键。
细节4:枚举参量时,你甚至可以写数组;
代码示意如下:
initial begin
typedef enum {idle, st[3], rt[2]} state_s;
state_s state, next_state;
$display("%0d, %0d, %0d", st0, st1, st1);
$display("%0d, %0d", rt0, rt1);
end
根据sv标准,st[3]会自动被编译为st0,st1,st2:
这个写法吧,感觉没啥用,估计能用来唬唬人或者用来节省状态机编码时间吧。
细节5:枚举还内置了很多方法,妈妈再也不怕我忘记怎么用了;
内置方法包括:first()/last()/next(N)/prev(N)/num()/name(),具体用法敲在下面,大家自行感受:
initial begin
typedef enum logic[3:0]{idle = 4'b0000, st1 = 4'b0001, st2 = 4'b0010, st3 = 4'b0100, st4 = 4'b1000} state_s;
state_s state;
state = state.first();
$display("state.first() = 'b%4b", state);
state = state.last();
$display("state.last() = 'b%4b", state);
state = state.next(2);
$display("state.next(2) = 'b%4b", state);
state = state.prev(2);
$display("state.prev(2) = 'b%4b", state);
$display("state.num() = %0d", state.num());
$display("state.name() = %s", state.name());
end
打印结果如下: