可综合的有限状态机(1)

1.0状态机分类

狀態機分兩種,一種為Moore狀態機,一種為Mealy狀態機。
Moore狀態機: 同步輸出狀態機,輸出僅為當前狀態的函數,當時鐘使狀態發生變化時,才導致輸出的變化,所以要比Mealy狀態機要多等待一個週期。
Mealy狀態機的輸出是當前狀態和所有輸入信號的函數,它的輸出變化不需要依賴時鐘同步,自發變化。

可合成状态机可以通过多种方式进行编码。两种最常见、易于理解和有效的方法是two-always block 和 one-always block
理解和实现的最简单方法是两个总是块状态机,其输出分配包含在组合下一状态always模块或单独的连续赋值输出模块。
主要分为三个模块:
1.clocked present state logic;
2.next state combinational logic;
3.output combinational logic;.在这里插入图片描述
狀態機除了按照輸出方式分,還可以按照編碼方式進行分類,比較常見的編碼分類為:
1. highly-encoded binary (or binarysequential),
2. gray-code
3. Johnson,
4. one-hot,
5. almost one-hot
6. one-hot with zero-idle
本文將利用图2的Moore FSM狀態圖,进行讲解。
在这里插入图片描述

2.0基本准则

准则1:一个Verilog模块至多描述一个有限状态机。
准则2:使用具有符号状态名称的参数进行状态赋值。
准则3:仅在时序逻辑模块中使用Verilog非阻塞赋值。仅在用于生成时序逻辑的always块中使用Verilog非阻塞赋值。
准则4:用always模块写组合逻辑时,采用阻塞赋值。
准则5:将输出逻辑编码为单独的模块或在组合逻辑模块中。

3.0 准则解释

准则1:一个Verilog模块至多描述一个有限状态机。

这样不仅可以简化状态的定义、修改和调试,还可以利用一些EDA工具来协助设计。

准则2:使用具有符号状态名称的参数进行状态赋值。

定义和使用符号状态名称使verilog代码更具可读性,并在必要时简化了重新定义状态的任务。

Example1、2、3显示了图2中FSM状态图的binary, one-hot and one-hot with zeroidle参数定义。

  parameter [2:0] // synopsys enum code
    IDLE = 3'd0,
    S1 = 3'd1,
    S2 = 3'd2,
    S3 = 3'd3,
    ERROR = 3'd4;

Example 1 - Parameter definitions for binary encoding

parameter [4:0] 
IDLE = 5'b00001,
S1 = 5'b00010,
S2 = 5'b00100,
S3 = 5'b01000,
ERROR = 5'b10000;

Example 2 - Parameter definitions for verbose one-hot encoding

parameter [4:0] IDLE = 5'd0,
S1 = 5'd1,
S2 = 5'd2,
S3 = 5'd3,
ERROR = 5'd4;

Example 3 - Parameter definitions for simplified one-hot encoding
示例3所示的简化的Code使用十进制数索引到状态寄存器中。它允许比较单个位,而不是使用示例2中所示的完整状态参数与整个状态向量进行比较。

parameter [4:1] // ERROR is 4'b0000
IDLE = 4'd1,
S1 = 4'd2,
S2 = 4'd3,
S3 = 4'd4;

Example 4 - Parameter definitions for one-hot with zero-idle encoding

一个one-hot with zero-idle encoding可以为状态机产生非常有效的fsm,这些状态机有许多与复杂方程的互连,包括到一个特定状态的大量连接。通常,会多次转换到IDLE state或其他常見状态(例如本例中的ERROR State)。
使用参数给状态赋值,而不是用宏定义(‘define)。因为’define宏定义在编译时自动替换整个设计中所定义的宏,而parameter仅仅定义模块内部的参数,定义的参数不会与模块外的其它状态机混淆。
也可以使用宏定义编译器指令(define)定义符号状态名,但`define创建全局定义(从在verilog代码输入流中读取定义的点开始)。与“define constants”不同,参数是声明它们的模块的本地常量,这允许设计具有多个具有重复状态名(如空闲或读取)的fsm,每个fsm都具有唯一的状态编码。
有时,fsm代码是用参数定义的状态定义编写的,但随后的的代码仍然含有二进制状态编码。这违背了使用符号标记参数的目的。仅将预定义的参数名称用于状态测试和下一个状态赋值。

准则3:仅在时序逻辑模块中使用Verilog非阻塞赋值。仅在用于生成时序逻辑的always块中使用Verilog非阻塞赋值。

这样才能保证有限状态机综合前和综合后仿真的一致性。

时序逻辑

当实现Binary encoded或复杂的one-hot encoded 状态机时,在重置时,状态寄存器将被分配IDLE state (或 equivalent )(示例5)。

always @(posedge clk or posedge rst)
if (rst) state <= #1 IDLE;
else state <= #1 next;

Example 5 - Sequential always block for binary and verbose one-hot encoding

当实现简化的one-hot encoded FSM时,复位时,状态寄存器将被清零,然后立即重新分配状态寄存器的idle位(示例6)。

always @(posedge clk or posedge rst)
if (rst) begin
state <= 5'b0;
state[IDLE] <= 1'b1;
end
else state <= next;

Example 6 - Sequential always block for simplified one-hot encoding

当实现one-hot with zero-idle encoded fsm时,复位时,状态寄存器将被清零(例7)。

always @(posedge clk or posedge rst)
if (rst) state <= 4'b0;
else state <= next;

Example 7 - Sequential always block one-hot with zero-idle encoding

准则4:用always模块写组合逻辑时,采用阻塞赋值。

组合逻辑

编写组合always模块以更新下一个状态值。alway模块由一个敏感度列表触发,该敏感度列表对来自同步always block的状态寄存器和状态机的所有输入敏感。
在“always模块敏感度”列表后面的行上放置默认的下一个状态分配。此默认分配由case语句中的next state赋值更新。
有三种常用的默认next state赋值类型:
(1)Next设置为所有X,
(2)Next设置为预定的恢复状态,如Idle,
(3)Next设置为状态寄存器的值。
通过对x进行默认的下一个状态分配,如果case语句中没有分配所有状态转换,则仿真将导致状态机输出变为未知。这是一种调试状态机设计的有用技术,而且X将被合成工具视为“不关心”。
有些设计需要赋值到已知状态,而不是赋值X。包括:卫星应用、医疗应用、使用FSM触发器作为诊断扫描链一部分的设计,以及使用正式验证工具检查等效性的设计等。
将默认的下一个状态赋值设为Idle或0通常满足这些设计要求,并使初始默认赋值比在case语句中对所有next state转换赋值进行编码更容易。
使默认的下一个状态分配等于当前状态是PLD设计人员多年来使用的一种编码样式。
在这里插入图片描述

 always @(state or i1 or i2 or i3 or i4) begin
    next = 3'bx;
    case (state)
    IDLE: begin
    next = ERROR;
    if (!i1) next = IDLE;
    if (i1 & i2) next = S1;
    if (i1 & !i2 & i3) next = S2;
    end
    S1: ...

Example 8 - Next state assignments for binary and verbose one-hot encoding

always @(state or i1 or i2 or i3 or i4) begin
next = 5'b0;
case (1'b1) // synopsys full_case parallel_case
state[IDLE]: begin
if (!i1) next[IDLE] = 1'b1;
else if ( i2) next[S1] = 1'b1;
else if ( i3) next[S2] = 1'b1;
else next[ERROR] = 1'b1;
end
state[S1]: ...

Example 9 - Next state assignments for simplified one-hot encoding

always @(state or i1 or i2 or i3 or i4) begin
next = 4'b0;
case (1'b1) // synopsys full_case parallel_case
~|state: begin // IDLE
if ( i1 & i2) next[S1] = 1'b1;
if ( i1 & !i2 & i3) next[S2] = 1'b1;
if ( i1 & !i2 & !i3) next[ERROR] = 1'b1;
end
state[S1]: ...

Example 10 - Next state assignments for simplified one-hot with zero-idle encoding
在这里插入图片描述

准则5:将输出逻辑编码为单独的模块或在组合逻辑模块中。

输出模块

如果输出分配被编码为combinational logic always中,则输出分配也可以放入具有有意义名称的Verilog任务中,如图5所示。从case语句中的每个状态内调用任务。
如果需要修改,隔离输出分配可以很容易地更改输出逻辑。它还有助于避免合成工具产生额外的不需要的锁存。
将输出分配放置在Two-Always Block状态机的 combinational always block 内时,在Always Block的顶部进行默认输出分配,然后在case语句中修改相应的输出分配。
在这里插入图片描述
通常,此方法需要的编码少于为每个状态(Case)进行所有输出分配,并在输出应该更改时突出显示。

Mealy and Registered Outputs

Mealy 输出可以通过连续的输出赋值实现:

assign rd_out = (state == READ) & !rd_strobe_n;

或者combinational always block中实现:

case (state)
...
READ: if (!rd_strobe_n) rd_out = 1'b1;

已经定义的输出可以用非阻塞赋值进sequential always block中。 FSM可以编码在应该sequential always block 或以第二个 sequential always block添加到设计中。

一个always模块的状态机

通常,One-Always Block比Two-Always Block仿真效率高,因为输入仅在时钟变化时检查; 但是,这种状态机可能更难以修改和调试。
将输出分配放在One-Always Block状态机的始终块内时,必须考虑以下因素:
1.将输出分配放在always块中将模拟出触发器。
2.置于always block内的输出分配是“next state”,这可能更容易出错。
注意: sequential always block内的输出不能是Mealy输出。

半满全满

case语句,在条件出发时顺序执行Case语句,剩下的case将被跳过知道循环结束。

case (case_expression (with 2n
 possible combinations))
 case_item1 : <action #1>;
 case_item2 : <action #2>;
 case_item3 : <action #3>;
 ...
 case_item2n-1: <action #2n-1>;
 case_item2n   : <action #2n>;
 default: <default action>;
endcase

full case语句被定义为case语句,其中定义了每个可能的条件。
Parallel case语句被定义为case语句,没有重复条件。
VHDL语言陈述必须是完整的,这需要明确定义每一个条件或者加一个"others =>" ,同时,他的语言陈述必须是并行的,所以在case的编写中不能有重叠的条件。
Verilog 不需要"full" 或者 “parallel.”。
在case语句的末尾添加“// synopsys full_case”
(在表达case之前)通知综合工具,没有声明的case的所有输出都应被视为“无关紧要”以用于综合目的。
将“// synopsys parallel_case”添加到case语句的末尾
(在表达case之前)通知综合工具,即使条件重复,也应单独执行每个条件。

在使用one-hot 或者 one-hot with zero-idle FSMs时,
将“// synopsys full_case parallel_case”指令中的任何一个或两者添加到Verilog FSM源代码中通常是有益的
在这些情况下,给出只设置状态向量的一位,并且所有其他位模式组合应被视为“不关心”。 还假设在案例列表中应该没有重叠。
请注意,使用full_case并行情况可能会导致预合成设计模拟与合成后设计模拟不同,因为这些指令有效地为Synopsys工具提供了有关未包含在原始Verilog模型中的设计的信息。
不建议在设计中的每个case语句中添加full_case parallel_case。这种做法可以改变设计的功能,也可以使一些二进制编码的FSM设计实际上变得越来越大。

综合工具

注意事项

Synopsys FSM工具可用于试验不同的状态编码样式,例如binary, gray 和 one-hot codes。 为了使用FSM工具,Verilog代码必须包含Synopsys综合注释,以及一些不寻常的Verilog代码语句。 Synopsys FSM工具对于如何对这些注释和代码段进行排序非常严格,并且很容易对FSM工具进行错误编码。
首先,参数必须包含一个范围
如果参数声明中未包含范围,则将报告错误消息“ Declaration of enumeration type requires range specification"。
在这里插入图片描述
其次,必须定义数字参数的大小
否则FSM工具会将所有数字解释为32位数字并报告无效的编码错误。
在这里插入图片描述
第三,Synopsys综合评论所需的位置完全如图所示。
必须在参数范围声明之后和声明任何参数之前放置“// synopsys enum ”,
在这里插入图片描述
a“/ synopsys state_vector <state_vector_name>”Synopsys注释必须紧接在state-reg声明之前,并且上面使用的完全相同的“// synopsys enum ”注释必须放在reg范围声明之后但在 state(和nest)声明之前。
在这里插入图片描述
下面是用于在状态机设计上调用Synopsys FSM工具的示例dc_shell命令。

(read the design)
compile
extract
set_fsm_encoding_style binary
compile
write -f db -hier -output "db/" + DESIGN + "_fsm_binary.db"
report_area > "rpt/" + DESIGN + "_fsm_binary.rpt"
create_clock -p 0 clk
report_timing >> "rpt/" + DESIGN + "_fsm_binary.rpt"
(read the design)
compile
extract
set_fsm_encoding_style gray
compile
write -f db -hier -output "db/" + DESIGN + "_fsm_gray.db"
report_area > "rpt/" + DESIGN + "_fsm_gray.rpt"
create_clock -p 0 clk
report_timing >> "rpt/" + DESIGN + "_fsm_gray.rpt"
(read the design)
compile
extract
set_fsm_encoding_style one_hot
compile
write -f db -hier -output "db/" + DESIGN + "_fsm_onehot.db"
report_area > "rpt/" + DESIGN + "_fsm_onehot.rpt"
create_clock -p 0 clk
report_timing >> "rpt/" + DESIGN + "_fsm_onehot.rpt"

Example 11 - FSM Tool - dc_shell script

在这里插入图片描述

Example 1 - FSM Tool synthetic comments

module bm1_s (err, n_o1, o2, o3, o4,
 i1, i2, i3, i4, clk, rst);
 output err, n_o1, o2, o3, o4;
 input i1, i2, i3, i4, clk, rst;
 reg err, n_o1, o2, o3, o4;
 parameter [2:0] // synopsys enum code
 IDLE = 3'd0,
 S1 = 3'd1,
 S2 = 3'd2,     //Highly encoded stateparameter definitions
 S3 = 3'd3,
 ERROR = 3'd4;
 // synopsys state_vector state
 reg [2:0] // synopsys enum code    ///Synopsys FSM Toolsynthetic comments
 state, next;
 always @(posedge clk or posedge rst)
 if (rst) state <= IDLE;
 else state <= next;
 always @(state or i1 or i2 or i3 or i4) begin
 next = 3'bx;                  //next = 2'bx (synthesis "don't care" assignment)
 err = 0; n_o1 = 1;        //Initial default output assignments
 o2 = 0; o3 = 0; o4 = 0;
 case (state)
 IDLE: begin
 next = ERROR;
 if (!i1) next = IDLE;
 if (i1 & i2) next = S1;
 if (i1 & !i2 & i3) next = S2;
 end
 S1: begin
 next = ERROR;                 //   Default assignment followed by parallel if statements
 if (!i2) next = S1;
 if (i2 & i3) next = S2;
 if (i2 & !i3 & i4) next = S3;
 n_o1 = 0;
 o2 = 1;
 end
 S2: begin
 next = ERROR;
 if (i3) next = S2;
 if (!i3 & i4) next = S3;
 o2 = 1;
 o3 = 1;
 end
 S3: begin
 next = S3;
 if (!i1) next = IDLE;
 if (i1 & i2) next = ERROR;
 o4 = 1;
 end
 ERROR: begin
 next = IDLE;
 if (i1) next = ERROR;
 err = 1;
 end
 endcase
 end
endmodule

Example 2 - Verbose one-hot FSM

module bm1_1afp (err, n_o1, o2, o3, o4,
 i1, i2, i3, i4, clk, rst);
 output err, n_o1, o2, o3, o4;
 input i1, i2, i3, i4, clk, rst;
 reg err, n_o1, o2, o3, o4;
 parameter [4:0] IDLE = 5'b00001,
 S1 = 5'b00010,    //Verbose one-hot stateparameter definitions
 S2 = 5'b00100,
 S3 = 5'b01000,
 ERROR = 5'b10000;
 reg [4:0] state, next;
 always @(posedge clk or posedge rst)
 if (rst) state <= IDLE;
 else state <= next;
 always @(state or i1 or i2 or i3 or i4) begin
 next = 5'bx;   //next = 5'bx (synthesis "don't care" assignment)
 err = 0; n_o1 = 1;   //Initial default output assignments
 o2 = 0; o3 = 0; o4 = 0;
 case (state) // synopsys full_case parallel_case
 IDLE: begin
 if (!i1) next = IDLE;
 else if ( i2) next = S1;  //If/else-if statements
 else if ( i3) next = S2;
 else next = ERROR;  //Final else statements
 end
 S1: begin
 if (!i2) next = S1;
 else if ( i3) next = S2;
 else if ( i4) next = S3;
 else next = ERROR;
 n_o1 = 0;  Only update output assignments that change in each state
 o2 = 1;
 end
 S2: begin
 if ( i3) next = S2;
 else if ( i4) next = S3;
 else next = ERROR;
 o2 = 1;
 o3 = 1;
 end
 S3: begin
 if (!i1) next = IDLE;
 else if ( i2) next = ERROR;
 else next = S3;
 o4 = 1;
 end
 ERROR: begin
 if (i1) next = ERROR;
 else next = IDLE;
 err = 1;
 end
 endcase
 end
endmodule

Example 3 - Simplified one-hot FSM w/task outputs (cont.)

  module bm1_1fpt (err, n_o1, o2, o3, o4,
     i1, i2, i3, i4, clk, rst);
     output err, n_o1, o2, o3, o4;
     input i1, i2, i3, i4, clk, rst;
     reg err, n_o1, o2, o3, o4;
     parameter [4:0] IDLE = 5'd0,    //Simplified one-hot stateparameter definitions (to index into the state vector)
     S1 = 5'd1,
     S2 = 5'd2,
     S3 = 5'd3,
     ERROR = 5'd4;
     reg [4:0] state, next;
     always @(posedge clk or posedge rst)
     if (rst) begin
     state <= 5'b0;  //On reset, state <= 5'b0 followed by state[IDLE] <= 1'b1
     state[IDLE] <= 1'b1;
     end
     else state <= next;
     always @(state or i1 or i2 or i3 or i4) begin
     next = 5'b0;  //next is set to all 0's
     drive_defaults;
     case (1'b1) // synopsys full_case parallel_case  //Case "if true" (1'b1) ...//Synopsys full_case parallel_case helps infer a more efficient onehot implementation
     state[IDLE]: begin  //... match a single state bit
     if (!i1) next[IDLE] = 1'b1;
     else if ( i2) next[S1] = 1'b1;  //Only set the "one-hot" bit in the next register
     else if ( i3) next[S2] = 1'b1;
     else next[ERROR] = 1'b1;
     end
     state[S1]: begin
     if (!i2) next[S1] = 1'b1;
     else if ( i3) next[S2] = 1'b1;
     else if ( i4) next[S3] = 1'b1;
     else next[ERROR] = 1'b1;
     init_S1;
     end
     state[S2]: begin  
     if ( i3) next[S2] = 1'b1;
     else if ( i4) next[S3] = 1'b1;
     else next[ERROR] = 1'b1;
     set_S2;                        //Output task call
     end
     state[S3]: begin
     if (!i1) next[IDLE] = 1'b1;
     else if ( i2) next[ERROR] = 1'b1;
     else next[S3] = 1'b1;
     drive_S3;
     end
     state[ERROR]: begin
     if (i1) next[ERROR] = 1'b1;
     else next[IDLE] = 1'b1;
     error_state;
     end
     endcase
     end
     task drive_defaults;  //Descriptive output task names
     begin
     err = 0;
     n_o1 = 1;
     o2 = 0;
     o3 = 0;
     o4 = 0;
     end
     endtask
     task init_S1;   // Descriptive output task name
     begin
     n_o1 = 0;
     o2 = 1;
     end
     endtask
     task set_S2;
     begin
     o2 = 1;
     o3 = 1;
     end
     endtask
     task drive_S3;
     o4 = 1;
     endtask
     task error_state;
     err = 1;
     endtask
    endmodule

Example 4 - One-hot with zero-idle FSM

module bm1o_0bfp (err, n_o1, o2, o3, o4,
 i1, i2, i3, i4, clk, rst);
 output err, n_o1, o2, o3, o4;
 input i1, i2, i3, i4, clk, rst;
 wire err, n_o1, o2, o3, o4;
 parameter [4:1] // ERROR  //ERROR state was selected to be the all 0's state
 IDLE = 4'd1,
 S1 = 4'd2,  //Other states are "one-hot" states
 S2 = 4'd3,
 S3 = 4'd4;
 reg [4:1] state, next;
 always @(posedge clk or posedge rst)
 if (rst) begin
 state <= 4'b0;  //On reset, state <= 4'b0 followed by state[IDLE] <= 1'b1
 state[IDLE] <= 1'b1;
 end
 else state <= next;
 always @(state or i1 or i2 or i3 or i4) begin
 next = 4'b0;    //next is set to all 0's
 case (1'b1) // synopsys full_case parallel_case  //Case "if true" (1'b1) ...//Synopsys full_case parallel_case helps infer a more efficient onehot implementation
 state[IDLE]: begin  //... match a single state bit
 if (!i1) next[IDLE] = 1'b1;
 if ( i1 & i2) next[S1] = 1'b1;
 if ( i1 & !i2 & i3) next[S2] = 1'b1;
 end
 state[S1]: begin
 if (!i2) next[S1] = 1'b1;
 if ( i2 & i3) next[S2] = 1'b1;
 if ( i2 & !i3 & i4) next[S3] = 1'b1;
 end
 state[S2]: begin
 if ( i3) next[S2] = 1'b1;
 if (!i3 & i4) next[S3] = 1'b1;
 end
 state[S3]: begin
 if (!i1) next[IDLE] = 1'b1;
 if ( i1 & !i2) next[S3] = 1'b1;
 end
 ~|state: begin // ERROR    Decode all 0's state (must match case(1'b1))
 if (!i1) next[IDLE] = 1'b1;
 end
 endcase
 end
 assign err = !(|state);
 assign n_o1 = !(state[S1]);
 assign o2 = ((state[S1]) || (state[S2]));
 assign o3 = (state[S2]);               //Continuous output assignments
 assign o4 = (state[S3]);
endmodule
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值