verilog设计与实现状态机-基于售货机实例

实验目标1:

用状态机分析和实现一个简单的售货机系统,可乐机每次只能投入 1 枚 1 元硬币,且每瓶可乐卖 3 元钱,即投入 3 个硬币就可以让可乐机出可乐,如果投币不够 3 元想放弃投币需要按复位键,否则之前投入的钱不能退回。

状态机的基本结构与组成:

1 、输入:根据输入可以确定是否需要进行状态跳转以及输出,是影响状态机系统执行过程
的重要驱动力;
2 、输出:根据当前时刻的状态以及输入,是状态机系统最终要执行的动作;
3 、状态:根据输入和上一状态决定当前时刻所处的状态,是状态机系统执行的一个稳定的
过程。

把实例抽象为状态机:

结构组成

1 、输入:投入 1 元硬币、没有投入硬币;
2 、输出:出可乐、不出可乐;
3 、状态:可乐机中有 0元、可乐机中有 1 元、可乐机中有 2元、可乐机中有 3 元。

状态明确

可乐机中有 0 元的状态是最原始的状态我们称之为 IDLE 状态,可乐机中有 1 元的状态我们就取名为 ONE,可乐机中有 2 元的状态取名为 TWO,可乐机中有 3元的状态取名为 THREE。

没有投入硬币作为0,投入一元硬币作为1

状态机设计

在TWO状态时可能会发现问题,这时可乐机中已经有 3 元钱了,是可以出可乐了,但是出了可乐后我们的状态应该是回到 IDLE 状态才正确,为什么又多出来一个 THREE 呢?把 THREE状态去掉,变为下图,此为 Moore 型状态机,即输出只与状态有关而与输入无关。

也可以按照上面四种状态的状态转移图来分析,TWO 状态下的第二种情况时虽然可乐机中有
了 3 元钱,但是和上面分析不同的是可乐机器此时不会立刻出可乐,此为 Mealy 状态机,输出不仅与状态有关还和输入有关,可以看到3状态后面根据不同的输入跳转了两种不同的结果。

最后设计的时候大家往往更喜欢把状态的个数化简到最简的状态,因此参照moore型状态机

编写代码

首先需要对于三种状态使用参数化进行编码,一般使用独热码。同时定义一个state寄存器表示状态。

parameter   IDLE = 3'b001;
parameter   ONE  = 3'b010;
parameter   TWO  = 3'b100;
//reg   define
reg     [2:0]   state

状态机的代码编写,设计完成状态机后可以参照模板直接编写代码,这就是状态机方便的地方。

状态机模板使用case语句逻辑,对于每个case下的状态,都会根据输入条件来确定下一时刻的状态。此时只需要关注输入与下一时刻的状态。

//第一段状态机,描述当前状态state如何根据输入跳转到下一状态
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state <= IDLE;  //任何情况下只要按复位就回到初始状态
    else    case(state)
                IDLE    :   if(pi_money == 1'b1)//判断输入情况
                                state <= ONE;
                            else
                                state <= IDLE;

                ONE     :   if(pi_money == 1'b1)
                                state <= TWO;
                            else
                                state <= ONE;

                TWO     :   if(pi_money == 1'b1)
                                state <= IDLE;
                            else
                                state <= TWO;
                //如果状态机跳转到编码的状态之外也回到初始状态
                default :       state <= IDLE;
            endcase

接下来编写输出逻辑代码,此时需要关注输入和状态如何影响输出,我们给出一个波形图直观一些

这里需要注意观察状态和输出影响的是下一时刻的输出,比如po_cola=1时的输入和状态要看上一个时钟周期的状态和输入即为(state == TWO) && (pi_money == 1'b1)

//第二段状态机,描述当前状态state和输入pi_money如何影响po_cola输出
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

接下来是完整代码:

module  simple_fsm
(
    input   wire    sys_clk     ,   //系统时钟50MHz
    input   wire    sys_rst_n   ,   //全局复位
    input   wire    pi_money    ,   //投币方式可以为:不投币(0)、投1元(1)

    output  reg     po_cola         //po_cola为1时出可乐,po_cola为0时不出可乐
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
//只有三种状态,使用独热码
parameter   IDLE = 3'b001;
parameter   ONE  = 3'b010;
parameter   TWO  = 3'b100;

//reg   define
reg     [2:0]   state;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//第一段状态机,描述当前状态state如何根据输入跳转到下一状态
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state <= IDLE;  //任何情况下只要按复位就回到初始状态
    else    case(state)
                IDLE    :   if(pi_money == 1'b1)//判断输入情况
                                state <= ONE;
                            else
                                state <= IDLE;

                ONE     :   if(pi_money == 1'b1)
                                state <= TWO;
                            else
                                state <= ONE;

                TWO     :   if(pi_money == 1'b1)
                                state <= IDLE;
                            else
                                state <= TWO;
                //如果状态机跳转到编码的状态之外也回到初始状态
                default :       state <= IDLE;
            endcase

//第二段状态机,描述当前状态state和输入pi_money如何影响po_cola输出
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

接下来稍微复杂一些的实现继续练习

实验目标2:

可乐定价为 2.5 元一瓶,可投入 0.5 元、1 元硬币,投币不够 2.5 元需要按复位键退回钱款,投币超过 2.5 元需找零。

抽象为状态机

输入:不投(00),投入 0.5 元(01)或1 元硬币(10)

输入有两个信号可以拼接成一个信号来判断

//wire  define
wire    [1:0]   pi_money;
//pi_money:为了减少变量的个数,我们用位拼接把输入的两个1bit信号拼接成1个2bit信号
//投币方式可以为:不投币(00)、投0.5元(01)、投1元(10),每次只投一个币
assign pi_money = {pi_money_one, pi_money_half};

输出:投出可乐和找零具体为不出可乐/不找零(00)、出可乐/不找零(01)、出可乐/找零(10);

输出话波形图看关系

状态:可乐机中有 0 元(IDLE)、可乐机中有 0.5 元(HALF)、可乐机中有 1 元(ONE)、可乐机中有 1.5 元(ONE_HALF)、可乐机中有 2 元(TWO)、可乐机中有 2.5 元(TWO_HALF)、可乐机中有 3 元(THREE)。

对状态进行编码:

//parameter define
//只有五种状态,使用独热码
parameter   IDLE     = 5'b00001;
parameter   HALF     = 5'b00010;
parameter   ONE      = 5'b00100;
parameter   ONE_HALF = 5'b01000;
parameter   TWO      = 5'b10000;

注意:画状态转移图时容易出现状态跳转情况遗漏的问题,这里我们给大家总结一个
小技巧:我们可以观察到,输入有多少种情况(上一节是两种输入情况,本节是三种输入
情况),每个状态的跳转就有多少种情况(上一节每个状态都有两种跳转情况,本节每个
状态都有三种跳转情况),这样根据输入来确定状态的跳转就能够保证我们不漏掉任何一
种状态跳转。

输入有三个所以每个状态的下一个状态都有三种情况

//第一段状态机,描述当前状态state如何根据输入跳转到下一状态
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state <= IDLE;  //任何情况下只要按复位就回到初始状态
    else	case(state)
                IDLE    : if(pi_money == 2'b01)   //判断一种输入情况
                              state <= HALF;
                          else    if(pi_money == 2'b10)//判断另一种输入情况
                              state <= ONE;
                          else
                              state <= IDLE;
    
                HALF    : if(pi_money == 2'b01)
                              state <= ONE;
                          else    if(pi_money == 2'b10)
                              state <= ONE_HALF;
                          else
                              state <= HALF;
    
                ONE     : if(pi_money == 2'b01)
                              state <= ONE_HALF;
                          else    if(pi_money == 2'b10)
                              state <= TWO;
                          else
                              state <= ONE;
    
                ONE_HALF: if(pi_money == 2'b01)
                              state <= TWO;
                          else    if(pi_money == 2'b10)
                              state <= IDLE;
                          else
                              state <= ONE_HALF;
    
                TWO     : if((pi_money == 2'b01) || (pi_money == 2'b10))
                              state <= IDLE;
                          else
                              state <= TWO;
        //如果状态机跳转到编码的状态之外也回到初始状态
                default :       state <= IDLE;
            endcase

如何根据波形图观察输出,由状态信号和输入信号决定

po_cola为高的条件是:(state == TWO && pi_money == 2'b01) || (state == TWO && pi_money == 2'b10) || (state == ONE_HALF && pi_money == 2'b10

po_money为高的条件是:(state == TWO) && (pi_money == 2'b10)

//第二段状态机,描述当前状态state和输入pi_money如何影响po_cola输出
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 == 2'b01) || (state == TWO && 
          pi_money == 2'b10) || (state == ONE_HALF && pi_money == 2'b10))
        po_cola <= 1'b1;
    else
        po_cola <= 1'b0;

//第二段状态机,描述当前状态state和输入pi_money如何影响po_money输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n ==	1'b0)
        po_money <= 1'b0;
    else if((state == TWO) && (pi_money == 2'b10))
        po_money <= 1'b1;
    else
        po_money <= 1'b0;

完整代码如下:

module  complex_fsm
(
    input   wire    sys_clk         ,   //系统时钟50MHz
    input   wire    sys_rst_n       ,   //全局复位
    input   wire    pi_money_one    ,   //投币1元
    input   wire    pi_money_half   ,   //投币0.5元
                    
    output  reg     po_money        ,   //po_money为1时表示找零
                                        //po_money为0时表示不找零
    output  reg     po_cola             //po_cola为1时出可乐
                                        //po_cola为0时不出可乐
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
//只有五种状态,使用独热码
parameter   IDLE     = 5'b00001;
parameter   HALF     = 5'b00010;
parameter   ONE      = 5'b00100;
parameter   ONE_HALF = 5'b01000;
parameter   TWO      = 5'b10000;

//reg   define
reg     [4:0]   state;

//wire  define
wire    [1:0]   pi_money;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//pi_money:为了减少变量的个数,我们用位拼接把输入的两个1bit信号拼接成1个2bit信号
//投币方式可以为:不投币(00)、投0.5元(01)、投1元(10),每次只投一个币
assign pi_money = {pi_money_one, pi_money_half};

//第一段状态机,描述当前状态state如何根据输入跳转到下一状态
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state <= IDLE;  //任何情况下只要按复位就回到初始状态
    else	case(state)
                IDLE    : if(pi_money == 2'b01)   //判断一种输入情况
                              state <= HALF;
                          else    if(pi_money == 2'b10)//判断另一种输入情况
                              state <= ONE;
                          else
                              state <= IDLE;
    
                HALF    : if(pi_money == 2'b01)
                              state <= ONE;
                          else    if(pi_money == 2'b10)
                              state <= ONE_HALF;
                          else
                              state <= HALF;
    
                ONE     : if(pi_money == 2'b01)
                              state <= ONE_HALF;
                          else    if(pi_money == 2'b10)
                              state <= TWO;
                          else
                              state <= ONE;
    
                ONE_HALF: if(pi_money == 2'b01)
                              state <= TWO;
                          else    if(pi_money == 2'b10)
                              state <= IDLE;
                          else
                              state <= ONE_HALF;
    
                TWO     : if((pi_money == 2'b01) || (pi_money == 2'b10))
                              state <= IDLE;
                          else
                              state <= TWO;
        //如果状态机跳转到编码的状态之外也回到初始状态
                default :       state <= IDLE;
            endcase

//第二段状态机,描述当前状态state和输入pi_money如何影响po_cola输出
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 == 2'b01) || (state == TWO && 
          pi_money == 2'b10) || (state == ONE_HALF && pi_money == 2'b10))
        po_cola <= 1'b1;
    else
        po_cola <= 1'b0;

//第二段状态机,描述当前状态state和输入pi_money如何影响po_money输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n ==	1'b0)
        po_money <= 1'b0;
    else if((state == TWO) && (pi_money == 2'b10))
        po_money <= 1'b1;
    else
        po_money <= 1'b0;

endmodule

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值