数字IC手撕代码-有限状态机FSM-饮料机

 前言:

        本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析、代码及波形,所有代码均经过本人验证。

目录如下:

1.数字IC手撕代码-分频器(任意偶数分频)

2.数字IC手撕代码-分频器(任意奇数分频)

3.数字IC手撕代码-分频器(任意小数分频)

4.数字IC手撕代码-异步复位同步释放

5.数字IC手撕代码-边沿检测(上升沿、下降沿、双边沿)

6.数字IC手撕代码-序列检测(状态机写法)

7.数字IC手撕代码-序列检测(移位寄存器写法)

8.数字IC手撕代码-半加器、全加器

9.数字IC手撕代码-串转并、并转串

10.数字IC手撕代码-数据位宽转换器(宽-窄,窄-宽转换)

11.数字IC手撕代码-有限状态机FSM-饮料机

12.数字IC手撕代码-握手信号(READY-VALID)

13.数字IC手撕代码-流水握手(利用握手解决流水线断流、反压问题)

14.数字IC手撕代码-泰凌微笔试真题

15.数字IC手撕代码-平头哥技术终面手撕真题

16.数字IC手撕代码-兆易创新笔试真题

17.数字IC手撕代码-乐鑫科技笔试真题(4倍频)

18.数字IC手撕代码-双端口RAM(dual-port-RAM)

        ...持续更新

为了方便可以收藏导览博客: 数字IC手撕代码-导览目录


目录

有限状态机介绍

有限状态机写法   

三段式状态机基本结构

第一段:状态转移

第二段:根据当前状态,确定下一个状态 

第三段:根据当前状态和输入,确定输出

testbench

波形


有限状态机介绍

        有限状态机(Finite state machine)是为研究有限内存的计算过程和某些语言类而抽象出的一种计算模型。有限状态自动机拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。有限状态自动机可以表示为一个有向图,也就是我们常说的在写FSM之前要先画状态转移图

有限状态机写法   

有限状态机常见的有三种写法:

※ 一段式写法:将下一个状态计算、状态跳转和信号输出在一个进程中完成。

※ 二段式写法:将状态跳转在一个进程中完成,将下一个状态计算和信号输出在一个进程中完成。

※ 三段式写法:将下一个状态计算、状态跳转和信号输出分别在三个进程中完成。

上面所说的进程也就是用一个过程语句块(always)描述。

        这里建议在写状态机的时候,都采用三段式写法,原因很简单,在写只有几个状态的FSM时候,一段式也好两段式也好,都可以快速的写出来而且思路清晰。但是实际工程中的状态机经常会出现有十几个状态相互跳转,甚至状态机嵌套的情况,这时候一段、两段式的状态机书写的思路就不是特别清晰, 更重要的代码维护性不强。所以本文只讲解三段式状态机的写法。

三段式状态机基本结构

三段式的状态机分为两种:Mealy型和Moore型。它们的区别在于摩尔型(Moore)输出只和状态有关而与输入无关,米莉型(Mealy)输出不仅和状态有关而且和输入有关系。

※ 根据状态的个数确定状态机编码。

※ 状态机第一段:时序逻辑,非阻塞赋值,传递状态机的状态(state<=next_state)

※ 状态机第二段:组合逻辑,阻塞赋值,根据当前的状态和输入,确定下一个状态机的状态。

※ 状态机第三段:时序或组合逻辑,非阻塞赋值,根据当前状态当前输入(Mealy型状态机)或者当前状态(Moore型状态机),确定输出信号。

下面以一个饮料机为例子来写一个三段式状态机:

问:请用FSM设计一个饮料机,画出状态转移图并编写代码实现。(其中,投币可以是1元,也可以是五角,当投币满2元就可以出一瓶饮料,饮料机应该还具有找零功能)。

        第一步画状态转移图,我们得先确定有几种状态,状态之间的转换关系是怎么样的,然后再画出状态转移图。

首先分析有几种状态:

IDLE状态:即饮料机的初始状态,此时没有往饮料机里投一毛钱。

GET05状态:饮料机里有五角钱。

GET10状态:饮料机里有一块钱,可能是投两次五角到达该状态,也可以是投一次一元到达该状态。

GET15状态:饮料机里有一块五,可能是投三次五角、一次五角一次一块、一次一块一次五角到达该状态。

上面总共说了四种状态,用两比特即可完全描述状态。

    还有没有别的状态?没有了!饮料机不会有两块的状态!比如在GET15状态,饮料机里有一块五,投入一块,总投入两块五,此时饮料机会给一瓶饮料并找零五角,饮料机回到初始状态。如果投入五角,则总投入两块,此时饮料机会给一瓶饮料且不找零。如果是在GET10状态,投入一块,饮料机也会回到初始状态并给一瓶饮料不找零。综上,我们画出状态转移图。

        根据我们写的状态转移图写出接口定义,输入时钟、复位和投币。输出找零和饮料。如果drink为高,则找零五角,如果drink为高,则给一瓶饮料。

module drink_machine(
  input       clk     ,
  input       rstn    ,
  input [1:0] coin    ,
  //01 for 0.5 yuan, 10 for 1 yuan
  output      change  ,
  //if change is hign ,give 0.5 change
  output      drink
);
//machine state define (one-hot code)
parameter IDLE  = 4'b0001;
parameter GET05 = 4'b0010;
parameter GET10 = 4'b0100;
parameter GET15 = 4'b1000;

//machine state
reg [3:0] next_state;
reg [3:0] current_state;

        上面为状态机的状态编码,我们采取独热码的形式,即one-hot编码,每个状态只有1bit为1。再定义当前状态和下个状态的变量。 

第一段:状态转移

//(1) state transfer
always @(posedge clk)begin
  if(!rstn)begin
    current_state <= IDLE;
  end
  else begin
    current_state <= next_state;
  end
end

第二段:根据当前状态,确定下一个状态 

//(2)Determine the next state according to the current state
always @(*)begin
  next_state = current_state; // 防止综合出Latch
  case(current_state)
    IDLE:
      case(coin)
        2'b01:  next_state = GET05;
        2'b10:  next_state = GET10;
      endcase
    GET05:
      case(coin)
        2'b01:  next_state = GET10;
        2'b10:  next_state = GET15;
      endcase
    GET10:
      case(coin)
        2'b01:  next_state = GET15;
        2'b10:  next_state = IDLE;
      endcase
    GET15:
      case(coin)
        2'b01,2'b10:
                next_state = IDLE;
      endcase
    default:    next_state = IDLE;
  endcase
end

第三段:根据当前状态和输入,确定输出

//(3)Determine the output according to the current state and input
reg drink_r ;
reg change_r;
always @(posedge clk)begin
  if(!rstn)begin
    drink_r   <= 1'b0;
    change_r  <= 1'b0;
  end
  else if((current_state == GET10 && coin == 2'b10) || (current_state == GET15 && coin == 2'b01))begin
    drink_r   <= 1'b1;
    change_r  <= 1'b0;
  end
  else if(current_state == GET15 && coin == 2'b10)begin
    drink_r   <= 1'b1;
    change_r  <= 1'b1;
  end
  else begin
    drink_r   <= 1'b0;
    change_r  <= 1'b0;
  end
end

assign drink  = drink_r;
assign change = change_r;

        第三段控制输出,如果当前状态为GET10且投币一元,或者当前状态为GET15且投币五角,则饮料一瓶不找零。如果当前状态为GET15且投币一元,则饮料一瓶找零五角。其他情况均不出饮料且不找零。

总代码

module drink_machine(
  input       clk     ,
  input       rstn    ,
  input [1:0] coin    ,
  //01 for 0.5 yuan, 10 for 1 yuan
  output      change  ,
  //if change is hign ,give 0.5 change
  output      drink
);
//machine state define (one-hot code)
parameter IDLE  = 4'b0001;
parameter GET05 = 4'b0010;
parameter GET10 = 4'b0100;
parameter GET15 = 4'b1000;

//machine state
reg [3:0] next_state;
reg [3:0] current_state;

//(1) state transfer
always @(posedge clk)begin
  if(!rstn)begin
    current_state <= IDLE;
  end
  else begin
    current_state <= next_state;
  end
end

//(2)Determine the next state according to the current state
always @(*)begin
  next_state = current_state; // 防止综合出Latch
  case(current_state)
    IDLE:
      case(coin)
        2'b01:  next_state = GET05;
        2'b10:  next_state = GET10;
      endcase
    GET05:
      case(coin)
        2'b01:  next_state = GET10;
        2'b10:  next_state = GET15;
      endcase
    GET10:
      case(coin)
        2'b01:  next_state = GET15;
        2'b10:  next_state = IDLE;
      endcase
    GET15:
      case(coin)
        2'b01,2'b10:
                next_state = IDLE;
      endcase
    default:    next_state = IDLE;
  endcase
end

//(3)Determine the output according to the current state and input
reg drink_r ;
reg change_r;
always @(posedge clk)begin
  if(!rstn)begin
    drink_r   <= 1'b0;
    change_r  <= 1'b0;
  end
  else if((current_state == GET10 && coin == 2'b10) || (current_state == GET15 && coin == 2'b01))begin
    drink_r   <= 1'b1;
    change_r  <= 1'b0;
  end
  else if(current_state == GET15 && coin == 2'b10)begin
    drink_r   <= 1'b1;
    change_r  <= 1'b1;
  end
  else begin
    drink_r   <= 1'b0;
    change_r  <= 1'b0;
  end
end

assign drink  = drink_r;
assign change = change_r;

endmodule

testbench

module drink_machine_tb();

reg clk,rstn;
reg [1:0] coin;
always #5 clk = ~clk;

wire drink,change;

initial begin
  clk   <= 1'b0;
  rstn  <= 1'b0;
  #25
  rstn  <= 1'b1;
  // four times input 0.5 yuan
  coin  <= 2'b01;
  #10
  coin  <= 2'b01;
  #10
  coin  <= 2'b01;
  #10
  coin  <= 2'b01;
  // two times input 1 yuan
  #10
  coin  <= 2'b10;
  #10
  coin  <= 2'b10;
  // 1yuan 0.5yuan  1yuan
  #10
  coin  <= 2'b10;
  #10
  coin  <= 2'b01;
  #10
  coin  <= 2'b10;
  #100
  $stop();
end

drink_machine u_drink_machine(
  .clk      (clk)     ,
  .rstn     (rstn)    ,
  .coin     (coin)    ,
  .change   (change)  ,
  .drink    (drink)
);

endmodule

波形

        以上为饮料机的FSM。记住三段式即可,第一段:状态转移。第二段:根据当前状态和输入确定下一个状态。第三段:根据当前状态和输入,确定输出。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不吃葱的酸菜鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值