【Verilog】状态机代码风格


前言

最近刚刚接触到状态机代码的编写,一个好的状态机的代码风格易于更简洁地实现功能、便于修改内容和查找bug,所以在刚开始学习时养成一个很好的代码风格是很有必要的,本文用简单的售货机作为例子,分享一下自己在状态机代码编写时的一些理解。


一、状态机是什么?

状态机全称为同步有限状态机,简写为FSM(Finite State Machine),是指状态机不同状态下的转换与时钟同步的并且其状态数量有限。状态机分为米利型状态机和穆尔型状态机,其中米利型状态机的输出和当前状态以及当前输入有关,而穆尔型状态机的输出仅仅与当前状态有关。


二、状态机代码风格

状态机的写法主要分为一段式、两段式和三段式状态机,接下来以简单的售货机为例子,介绍三种状态机的代码风格与优缺点。
售货机中只卖一种3元的可乐,每次只能投入一枚1元,投入3次硬币即可出货。
根据需求可知输入输出及状态有以下可能:

状态投入0元、投入1元、投入2元、投入3元
输入投入1元、不投币
输出出货、不出货

根据需求可以分别绘制出状态转换图,考虑到米利型状态机的状态数量更少,采用米利型状态机编写代码。
在这里插入图片描述

1.一段式

一段式状态机中在一个always语句完成整个状态机编写,既包含了组合逻辑,也包含了时序逻辑。其中状态转移、结果输出均采用时序逻辑。

//一段式状态机
module  simple_fsm
(
    input   wire    sys_clk     ,
    input   wire    sys_rst_n   ,
    input   wire    pi_money    ,//投币,1为投入1元,0为未投币

    output  reg     po_cola     //输出,出货为1,未出货为0
);

//采用独热码的方式表示状态
parameter   IDLE    =   3'b001;
parameter   ONE     =   3'b010;
parameter   TWO     =   3'b100;

reg     [2:0]   state;

always@(posedge sys_clk or negedge sys_rst_n)begin
    if(sys_rst_n == 1'b0)begin
        state   <=  IDLE;
        po_cola <=  1'b0;
    end
    else
        case(state)
        IDLE    :   begin
                        if(pi_money == 1'b1)begin
                            state   <=  ONE;
                            po_cola <=  1'b0;
                        end
                        else begin
                            state   <=  IDLE;
                            po_cola <=  1'b0;
                        end
                    end
        ONE     :   begin
                        if(pi_money == 1'b1)begin
                            state   <=  TWO;
                            po_cola <=  1'b0;
                        end
                        else begin
                            state   <=  ONE;
                            po_cola <=  1'b0;
                        end
                    end
        TWO     :   begin
                        if(pi_money == 1'b1)begin
                            state   <=  IDLE;
                            po_cola <=  1'b1;
                        end
                        else begin
                            state   <=  TWO;
                            po_cola <=  1'b0;
                        end
                    end
        default :   begin
                        state   <=  IDLE;
                        po_cola <=  1'b0;
                    end
        endcase
end
endmodule

2.两段式

两段式状态机引入了下一个状态,采用时序逻辑描述状态转移,采用组合逻辑描述下一状态和输出结果,其中组合逻辑的敏感列表为状态和输入信号(穆尔式状态机仅为状态)。将组合逻辑与时序逻辑区分开,提高了代码的可读性。

//两段式状态机
module  simple_fsm
(
    input   wire    sys_clk     ,
    input   wire    sys_rst_n   ,
    input   wire    pi_money    ,//投币,1为投入1元,0为未投币

    output  reg     po_cola     //输出,出货为1,未出货为0
);
//采用独热码的方式表示状态
parameter   IDLE    =   3'b001;
parameter   ONE     =   3'b010;
parameter   TWO     =   3'b100;

reg     [2:0]   state;
reg     [2:0]   next_state;
//第一段:描述状态转移
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else 
        state   <=  next_state;

//第二段:描述下一状态和输出结果
always@(state,pi_money)
    case(state)
    IDLE    :   if(pi_money == 1'b1)begin
                    next_state  =   ONE;
                    po_cola     =   1'b0;
                end
                else begin
                    next_state  =   IDLE;
                    po_cola     =   1'b0;
                end
    ONE     :   if(pi_money == 1'b1)begin
                    next_state  =   TWO;
                    po_cola     =   1'b0;
                end
                else begin
                    next_state  =   ONE;
                    po_cola     =   1'b0;
                end
    TWO     :   if(pi_money == 1'b1)begin
                    next_state  =   IDLE;
                    po_cola     =   1'b1;
                end
                else begin
                    next_state  =   TWO;
                    po_cola     =   1'b0;
                end
    default :   begin
                    next_state  =   IDLE;
                    po_cola     =   1'b0;
                end
    endcase
endmodule

3.三段式

三段式状态机主要分为三个部分,用三个always语句描述状态的转移、描述下一个状态、描述输出结果。在第一段描述状态转移中采用时序逻辑描述赋值当前状态,而在第二段采用组合逻辑判断状态变化,,在第三段中采用时序逻辑描述输出结果。整体逻辑非常清晰明了,也解决了两段式状态机在描述输出信号时组合逻辑描述的一些困难。

//三段式状态机
module  simple_fsm
(
    input   wire    sys_clk     ,
    input   wire    sys_rst_n   ,
    input   wire    pi_money    ,//投币,1为投入1元,0为未投币

    output  reg     po_cola     //输出,出货为1,未出货为0
);
//采用独热码的方式表示状态
parameter   IDLE    =   3'b001;
parameter   ONE     =   3'b010;
parameter   TWO     =   3'b100;

reg     [2:0]   state;
reg     [2:0]   next_state;

//第一段:描述状态转移
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else 
        state   <=  next_state;

//第二段:描述下一状态(米利型状态机这里的敏感列表是状态和输入)
always@(state,pi_money)
    case(state)
    IDLE    :if(pi_money == 1'b1)
                next_state  =   ONE;
            else 
                next_state  =   IDLE;
    ONE     :if(pi_money == 1'b1)
                next_state  =   TWO;
            else 
                next_state  =   ONE;
    TWO     :if(pi_money == 1'b1)
                next_state  =   IDLE;
            else 
                next_state  =   TWO;
    default :next_state  =   IDLE;
    endcase

//第三段:描述结果输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_cola <=  1'b0;
    else if((state == TWO)&&(pi_money == 1'b1))
        po_cola <=  1'b1;
    else
        po_cola <=  1'b0;
endmodule

总结

本文对常见的几种状态机编写方法作了比较,从编写结果来看,三段式状态机虽然代码量相较略大,但有着代码逻辑清晰、区分了组合逻辑和时序逻辑、并且消除了两段式状态机中可能存在的输出毛刺以及表达寄存器型变量存在困难的问题等优点,是编写状态机的理想风格。

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值