有限状态机建模
1、使用枚举类型建立状态机模型
枚举类型有固定的数值
枚举类型提供了一种定义一个具有有限合法数值集合的变量的方法。数值是用标签而不是数字逻辑值表示的。
枚举类型支持抽象FSM类型
枚举类型支持更高抽象层次的建模,并且能描述精确的、可综合的硬件行为。
使用了枚举变量后,变量State和Next的值只可能在其枚举列表中。在状态逻辑中的case语句前的修饰符unique有助于保证case语句能覆盖量State和Next所有可能值(unique case)
//使用枚举类型建模的有限状态机
module traffic_light(
output logic green_light,
yellow_light,
red_light,
input sensor,
input[15:0] green_downcnt,
yellow_downcnt,
input clock,resetN
);
enum{RED,GREEN,YELLOW}State,Next;//使用枚举默认值
always_ff@(posedge clock,negedge resetN)
if(!resetN) State <= RED;//复位为红灯
else State<=Next;
always_comb
begin:set_next_state
Next=State;//下面每个分支的缺省状态
unique case(State)
RED: if(sensor) Next=GREEN;
GREEN:if(green_downcnt==0) Next = YELLOW;
YELLOW:if(yellow_downcnt==0)Next=RED;
endcase
end:set_next_state
always_comb
begin:set_outputs
{green_light,yellow_light,red_light} = 3'b000;
unique case(State)
RED:red_light = 1'b1;
GREEN:green_light=1'b1;
YELLOW:yellow_light=1'b1;
endcase
end:set_outputs
endmodule
该例使用默认的枚举基类int和每个枚举值标签(分别为0、1、2)的默认值。这些默认值可能不能精确反映仿真的硬件行为。int类型是一个32位两态类型。上例的实际硬件只有三个状态,只需一个两态或三位向量,这取决于这三种状态如何编码。实际硬件实现的门级模型将具有四态语义。
1.1、使用枚举类型表示状态编码
枚举类型可以具有显式基类
SystemVerilog还允许定义枚举变量的基类。这就使四态类型如logic可用作基类,这可以在RTL仿真中更精确地描述硬件行为。
枚举类型可以具有显示值
SystemVerilog的枚举类型也支持低抽象建模,那样可以描述特定的状态机结构。枚举类型列表中的每个标签的逻辑值都可以指定,这就可以显示表示one-hot、one-cold、格雷码或其他需要的任意类型的状态序列编码。
//指定使用枚举类型指明用one-hot码进行建模
module traffic_light(
output logic green_light,
yellow_light,
red_light,
input sensor,
input[15:0] green_downcnt,
yellow_downcnt,
input clock,resetN
);
enum bit[2:0]{RED=3'b001,//显示枚举定义
GREEN=3'b010,
YELLOW=3'b100}State,Next;//使用枚举默认值
always_ff@(posedge clock,negedge resetN)
if(!resetN) State <= RED;//复位为红灯
else State<=Next;
always_comb
begin:set_next_state
Next=State;//下面每个分支的缺省状态
unique case(State)
RED: if(sensor) Next=GREEN;
GREEN:if(green_downcnt==0) Next = YELLOW;
YELLOW:if(yellow_downcnt==0)Next=RED;
endcase
end:set_next_state
always_comb
begin:set_outputs
{green_light,yellow_light,red_light} = 3'b000;
unique case(State)
RED:red_light = 1'b1;
GREEN:green_light=1'b1;
YELLOW:yellow_light=1'b1;
endcase
end:set_outputs
endmodule
本例中,表示状态序列的枚举标签值在RTL模型中被显示指定。综合编译器将门级实现中保持这些值。这有助于比较综合前/后模型的功能。它也使指定综合前/后模型都适用的验证断言更容易。(综合编译器可能为了优化门级实现而提供一种重载显示枚举标签值的方法,这种优化会消除很多指定显示枚举值的优点)。
1.2使用枚举类型的反向case语句
case语句的典型应用是先指定一个变量为条件表达式,然后列出确定值来与条件选项列表匹配。
one-hot状态机可以使用反向case语句
one-hot状态机建模的另一种风格是采用反向case语句。在这种建模风格中,条件表达式和条件选项的位置颠倒了。将条件表达式指定为匹配的文本值,对于one-hot状态机是一个1的1.而条件选项是状态变量的每一位。对于一些综合编译器来说,使用反向case风格的one-hot码状态机会得到比标准的case语句更优化的综合结果。
module traffic_light(
output logic green_light,
yellow_light,
red_light,
input sensor,
input[15:0] green_downcnt,
yellow_downcnt,
input clock,resetN
);
enum{R_BIT=0,//状态寄存器中RED状态的索引
G_BIT=1,//状态寄存器中GREEN状态的索引
Y_BIT=2}state_bit;//使用枚举默认值
//将1移到表示每个状态的位上
enum bit[2:0]{RED=3'b001<<R_BIT,
GREEN=3'b001<<G_BIT,
YELLOW=3'b001<<Y_BIT}State,Next;
always_ff@(posedge clock,negedge resetN)
if(!resetN) State <= RED;//复位为红灯
else State<=Next;
always_comb
begin:set_next_state
Next=State;//下面每个分支的缺省状态
unique case(State)
RED: if(sensor) Next=GREEN;
GREEN:if(green_downcnt==0) Next = YELLOW;
YELLOW:if(yellow_downcnt==0)Next=RED;
endcase
end:set_next_state
always_comb
begin:set_outputs
{green_light,yellow_light,red_light} = 3'b000;
unique case(State)
RED:red_light = 1'b1;
GREEN:green_light=1'b1;
YELLOW:yellow_light=1'b1;
endcase
end:set_outputs
endmodule
在one-hot FSM模型中使用枚举类型的巧妙编码技巧
使用这一看似复杂的方案来指定oe-hot状态值有两个重要目的:
- 在两个枚举类型定义中定义不同的one-hot位,不可能产生编码错误。
- 如果设计规范改变了one-hot定义,只需改变指定位的位置的枚举类型。定义状态名的枚举类型将自动反映这一变化。
1.3枚举类型与unique case语句
unique case语句减少了case语句的不确定性
在上面的例子里为case语句增加修饰符unique是十分重要的。由于one-hot码状态机的状态寄存器在某一时刻只有一位是1,也只有一个条件选项与值为1的条件表达式匹配。case语句中的unique修饰符说明了三件事情。
第一,unique case指定所有条件选项必须并行求值,而不采用带优先级的编码方式。像综合编译器这样的软件工具可以优化这些条件选项的译码逻辑,从而得到更小、更有效的实现结果。在这一点上unique case与综合的parallel case附注是一样的。
第二,unique case指定条件选项中不应该有重叠项。例如在仿真过程中,如果条件表达式的值满足两个或更多条件选项,则会产生运行期警告。这种语法上的检查可以帮组在设计过程早期发现设计错误。而综合的parallel_case附注不提供这一重要的语法的检查功能。
第三,unique case指定条件选项必须涵盖在仿真中会产生条件表达式是所有值。对unique case,如果一个条件表达式的值不会引起一条case语句分支的执行,则会产生运行期警告。这种语法检查也可以帮组在设计过程中更早地发现设计错误。这与综合的full_case附注的功能类似,但是综合附注不要求其他工具进行任何检查。
1.4指定未使用的状态值
表明条件表达式中有一些未使用的值有两种常用的建模风格;一是使用逻辑X赋值指定默认的条件选项,另一种是使用专用的综合full_case附注。
使用X作缺省赋值可以覆盖未使用的状态
将枚举类型和unique case语句结合起来可以消除对于Verilog的case语句常用的编码风格的要求。
//带x默认值的verilog风格的条件语句
reg[2:0] State,Next;//3位变量
case(State)
3'b001:Next=3'b010;
3'b010:Next=3'b100;
3'b100:Next=3'b001;
default:Next=3'bxxx;
endcase
若在default选项赋X值,综合编译器将认为所有落在默认条件内的条件表达式值都是未使用值。
枚举类型不能直接赋X值
当使用枚举类型时,逻辑X赋值就不是合法赋值了。枚举类型只能从它的枚举列表中赋值。如果需要X赋值,枚举类型的基类必须定义为四态类型,如logic并且枚举标签必须用显示X值定义。
//带枚举X默认值的条件语句
enum logic[2:0] {RED =3'b001,
GREEN = 3'B010,
YELLOW = 3'B100,
BAD_STATE = 3'bxxx;}State,Next;
case(State)
RED:Next=GREEN;
GREEN:Next=YELLOW;
YELLOW:Next=RED;
default:Next=BAD_STATE'
endcase
枚举类型不能直接赋X值
SystemVerilog不再需要BAD_STATE 枚举值和default条件选项。将枚举类型和unique case语句结合起来消除了需要使用逻辑X赋值以表明存在未使用的条件表达式值的情况。因为枚举类型将其变量的值限制为只能是其枚举值集中的数值,而在case语句中也只需要列出这些数值。对枚举类型可以具有的值的定义,再加上unique case的语法检查功能,有助于确保综合前的RTL模型和综合后的门级模型在仿真和等价检查时都是相同的。
1.5将状态值赋给枚举类型变量
枚举类型变量只能被赋其类型集合中的值
枚举类型时比其他Verilog和SystemVerilog变量更强的类型,只能赋值为该枚举类型列表里成员的数值。枚举类型可以赋其他枚举类型的值,但只有这两个枚举类型来自同一定义时才行。
使用one-hot码状态序列时,另一种Verilog编码风格是首先将次态变量清零,然后再将次态变量中对应下一个状态的位置位,这种风格不能用于枚举类型变量
尽量使用枚举列表中的标签而不是具体数值对枚举变量赋值
1.6对枚举类型变量的操作
使用类型强制转换就能将不同类型的是赋给枚举类型。SystemVerilog即支持静态强制转换操作符又支持动态强制转换系统函数。
typedef enum{RED,GREEN,YELLOW}states_t;
states_t State,Next;
Next=states_t'(State+1);//静态强制转换
$cast(Next,State+1);//动态强制转换
静态强制转换操作会强制将一个表达式变为新的类型,而对转换后的值是否是新的类型所规定的的有效值不作任何检查。SystemVerilog标准允许软件工具以不确定方式处理越界赋值。也就是说前面的静态强制转换赋值操作中变量Next的新值可能,或者说很可能在不同软件工具中是不同的。
动态强制转换对正要转换的值执行运行期检测。如果值越界,会报告出错误信息,而且目标变量的值不会变化。因此使用动态强制转换可以发现这种无意中造成的设计错误,并可以修改设计以避免值的越界。
SystemVerilog还提供了许多针对枚举类型变量的方法,可以完成枚举类型变量的基本操作。这些方法可以对枚举类型合法值列表中的值作递增和递减操作。
Next=State.next;//枚举方法