FPGA状态机

 状态机电路框图

 注:第三段状态机的变化方式较多:

1、输入可以是next_state也可以是cur_state,next_state的化,输出可提前一个周期,但第三段状态机的电路类型必须是时序逻辑电路,否则,组合(输入)+组合(输出)的方式容易出现一些错误;

2、输入是cur_state的话,电路类型可以是组合或时序,因为cur_state是时序输出,因此第三段状态机的电路的时序必然会和cur_state的时序保持一致,因此无需在电路类型。

题目

以一个简单的例子来测试一下3段式状态机的使用

假如有一个饮料机,需要投入3个硬币,才会输出一瓶可乐

其中data_in表示是否投入硬币,data_out表示是否输出一瓶可乐

mealy型

状态转移图

首先根据题目要求,画一个状态转移图。

idle,one:当没有硬币投入的时候,饮料机处于空闲状态,下一状态就会一直维持在当前状态,当有硬币投入的时候就会跳转到下一状态。

two:当投入了2个硬币的情况下,需要判断,是否还会有第3个硬币投入的情况,如有,输出可乐,并重新进入空闲状态,否则,等待(不能说没有硬币投入,就把前两个硬币给取消了吧)

 时序图

        next_state的输入信号有两个:1、data_in;2、cur_state

        具体:next_state根据cur_state和data_in来变化,如果data_in为1,则跳转到下一状态,否则保持当前状态

比如:当cur_state==idle     if(data_in==1)   next_state:idle->one else next_state:idle->idle

data_out:当前状态是two,且data_in等于1,输出1。

由于是组合逻辑电路,所以是同时变化的,不存在延迟一个周期的情况

mealy型时序图

 仿真图

通过仿真验证,可以发现仿真图的时序和画出来的时序一致。

注:在仿真时要将next_state_name和cur_state_name的格式调成ACSII码,才会显示字符。

mealy型仿真图​​

 RTL代码

module mealy_3
(
    input       clk,
    input       rst_n,
    input       data_in,
    output reg  data_out
);

//3个状态
localparam  idle = 3'b001;
localparam  one  = 3'b010;
localparam  two  = 3'b100;

//时序电路的输入输出
reg [2:0]   next_state;
reg [2:0]   cur_state; 

//第一段状态机,输入next_state,输出cur_state,时序电路
always@(posedge clk or negedge rst_n)
if(!rst_n)
    cur_state <= idle;
else 
    cur_state <= next_state;
    
//第二段状态机,输入有data_in和cur_state,输出有next_state,组合电路
always@(*)
begin
    case(cur_state)
    idle:   next_state = (data_in)?one:idle;
    one:    next_state = (data_in)?two:one;
    two:    next_state = (data_in)?idle:two;
    default:next_state = idle;
    endcase
end

//第三段状态机,输入有data_in和cur_state,输出有data_out,时序电路
always@(posedge clk or negedge rst_n)
begin
    case(cur_state)
        two:if(data_in)
                data_out <= 1'b1;
    default:data_out <= 1'b0;
    endcase
end
endmodule

 仿真代码

`timescale 1ns/1ns
module tb_mealy_3();

reg     clk,rst_n   ;
reg     data_in     ;
wire    data_out    ;


initial 
begin
    clk = 1'b0;
    rst_n <= 1'b0;
    data_in <= 1'b0;
    #30
    rst_n <= 1'b1;
    #20
    data_in <= 1'b1;//拉高40ns,两个周期
    #40
    data_in <= 1'b0;//拉低20ns,一个周期
    #20
    data_in <= 1'b1;//拉高20ns,一个周期
    #20
    data_in <= 1'b0;//拉低20ns,一个周期
    #20
    data_in <= 1'b1;//拉高60ns,三个周期
    #60
    data_in <= 1'b0;//接下来,一直拉低
end

always #10 clk = ~clk;


//在仿真中显示状态的名字,状态名称最长有4位,每位占1个字节
reg [31:0]  cur_state_name;
reg [31:0]  next_state_name;

always@(*)
begin
    case(u_mealy_3.cur_state)
    3'b001:cur_state_name = "idle";
    3'b010:cur_state_name = "one";
    3'b100:cur_state_name = "two";
    default:cur_state_name = "idle";
    endcase
end

always@(*)
begin
    case(u_mealy_3.next_state)
    3'b001:next_state_name = "idle";
    3'b010:next_state_name = "one";
    3'b100:next_state_name = "two";
    default:next_state_name = "idle";
    endcase
end

mealy_3 u_mealy_3
(
    .clk(clk),
    .rst_n(rst_n),
    .data_in(data_in),
    .data_out(data_out)
);

endmodule

 进一步对第三段状态机进行讨论

 1、时序+cur_state

时序逻辑电路+cur_state
always@(posedge clk or negedge rst_n)
begin
    case(cur_state)
        two:if(data_in)
                data_out <= 1'b1;
            else
                data_out <= 1'b0;
    default:data_out <= 1'b0;
    endcase
end

2、组合+cur_state

组合逻辑电路+cur_state
always@(*)
begin
    case(cur_state)
        two:if(data_in)
                data_out <= 1'b1;
            else
                data_out <= 1'b0;
    default:data_out <= 1'b0;
    endcase
end

从第1和第2种方式可以看出,组合会比时序提前一个周期输出。

3、时序+next_state

always@(posedge clk or negedge rst_n)
begin
    case(next_state)
        two:if(data_in)
                data_out <= 1'b1;
            else
                data_out <= 1'b0;
    default:data_out <= 1'b0;
    endcase
end

 4、组合+next_state

always@(*)
begin
    case(next_state)
        two:if(data_in)
                data_out <= 1'b1;
            else
                data_out <= 1'b0;
    default:data_out <= 1'b0;
    endcase
end

可以看到用next_state输出的data_out的时机并不对,理论上应该是在第三个硬币投入的时候,才能进行输出,但第3和第4种方式都是在第2个硬币投入时输出,所以用next_state做第3段状态机的方式并不合适。

moore型

idle,one,two:当没有硬币投入的时候,饮料机处于空闲状态,下一状态就会一直维持在当前状态,当有硬币投入的时候就会跳转到下一状态。

three:当投入了3个硬币的情况下,需要判断,是否还会有第4个硬币投入的情况,如有,输出可乐,并进入one状态,否则,直接输出一瓶可乐,并进入空闲状态

时序图 

 next_state的输入信号有两个:1、data_in,2、cur_state

 具体:next_state根据cur_state和data_in来变化

当cur_state为idle、one、two:如果data_in为1,则跳转到下一状态,否则保持当前状态

当cur_state为three状态:如果data_in为1,next_state就会变成one,如果是0,那么next_state就变成idle

data_out:当前状态为three时,输出1

moore时序图​​​​​

 仿真图

moore仿真图

RTL代码 

module moore_3
(
    input       clk,
    input       rst_n,
    input       data_in,
    output reg  data_out
);

//3个状态
localparam  idle = 4'b0001;
localparam  one  = 4'b0010;
localparam  two  = 4'b0100;
localparam  three= 4'b1000;
//时序电路的输入输出
reg [3:0]   next_state;
reg [3:0]   cur_state; 

//第一段状态机,输入next_state,输出cur_state,时序电路
always@(posedge clk or negedge rst_n)
if(!rst_n)
    cur_state <= idle;
else 
    cur_state <= next_state;
    
//第二段状态机,输入有data_in和cur_state,输出有next_state,组合电路
always@(*)
begin
    case(cur_state)
    idle:   next_state = (data_in)?one:idle;
    one:    next_state = (data_in)?two:one;
    two:    next_state = (data_in)?three:two;
    three:  next_state = (data_in)?one:idle;
    default:next_state = idle;
    endcase
end

//第三段状态机,输入只有cur_state,输出有data_out,这里用组合电路
always@(posedge clk or negedge rst_n)
begin
    case(cur_state)
        three:  data_out <= 1'b1;
    default:data_out <= 1'b0;
    endcase
end
endmodule

 仿真代码

`timescale 1ns/1ns
module tb_moore_3();

reg     clk,rst_n   ;
reg     data_in     ;
wire    data_out    ;


initial 
begin
    clk = 1'b0;
    rst_n <= 1'b0;
    data_in <= 1'b0;
    #30
    rst_n <= 1'b1;
    #20
    data_in <= 1'b1;//拉高40ns,两个周期
    #40
    data_in <= 1'b0;//拉低20ns,一个周期
    #20
    data_in <= 1'b1;//拉高20ns,一个周期
    #20
    data_in <= 1'b0;//拉低20ns,一个周期
    #20
    data_in <= 1'b1;//拉高60ns,三个周期
    #60
    data_in <= 1'b0;//接下来,一直拉低
end

always #10 clk = ~clk;


//在仿真中显示状态的名字,状态名称最长有5位,每位占1个字节
reg [39:0]  cur_state_name;
reg [39:0]  next_state_name;

always@(*)
begin
    case(u_moore_3.cur_state)
    4'b0001:cur_state_name = "idle";
    4'b0010:cur_state_name = "one";
    4'b0100:cur_state_name = "two";
    4'b1000:cur_state_name = "three";
    default:cur_state_name = "idle";
    endcase
end

always@(*)
begin
    case(u_moore_3.next_state)
    4'b0001:next_state_name = "idle";
    4'b0010:next_state_name = "one";
    4'b0100:next_state_name = "two";
    4'b1000:next_state_name = "three";
    default:next_state_name = "idle";
    endcase
end

moore_3 u_moore_3
(
    .clk(clk),
    .rst_n(rst_n),
    .data_in(data_in),
    .data_out(data_out)
);

endmodule

 进一步对第三段状态机进行讨论

 1、时序+cur_state

always@(posedge clk or negedge rst_n)
begin
    case(cur_state)
        three:  data_out <= 1'b1;
    default:data_out <= 1'b0;
    endcase
end

2、组合+cur_state

always@(*)
begin
    case(cur_state)
        three:  data_out <= 1'b1;
    default:data_out <= 1'b0;
    endcase
end

从第1和第2种方式可以看出,组合会比时序提前一个周期输出。

3、时序+next_state

always@(posedge clk or negedge rst_n)
begin
    case(next_state)
        three:  data_out <= 1'b1;
    default:data_out <= 1'b0;
    endcase
end

 4、组合+next_state

always@(*)
begin
    case(next_state)
        three:  data_out <= 1'b1;
    default:data_out <= 1'b0;
    endcase
end

对于moore状态机的第三段状态机的输入并没有因为是next_state而出现错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值