zl程序教程

您现在的位置是:首页 >  其他

当前栏目

verilog状态机 二段式 状态机 (代码 可以运行)

代码 运行 可以 Verilog 状态机
2023-09-14 09:09:26 时间

link

代码在git

“硬件设计很讲究并行设计思想,虽然用Verilog描述的电路大都是并行实现的,但是对于实际的工程应用,往往需要让硬件来实现一些具有一定顺序的工作,这就要用到状态机思想。什么是状态机呢?简单的说,就是通过不同的状态迁移来完成一些特定的顺序逻辑。硬件的并行性决定了用Verilog描述的硬件实现(臂如不同的always语句)都是并行执行的,那么如果希望分多个时间完成一个任务,怎么办?也许可以用多个使能信号来衔接多个不同的模块,但是这样做多少显得繁琐。状态机的提出会大大简化这一工作。”

——特权同学《深入浅出玩转FPGA》

一、状态机分类:

1.Moore型:状态机的状态变化仅和当前状态有关(特权同学《深入浅出玩转FPGA》);时序逻辑电路的输出只取决于当前状态(夏宇闻《Verilog数字系统设计》)。设计高速电路时常用此类状态机,把状态变化直接用作输出。

2.Mealy型:状态机的状态变化不仅与当前的状态有关,还取决于当前的输入条件(特权同学《深入浅出玩转FPGA》);时序逻辑的输出不但取决于状态还取决于输入(夏宇闻《Verilog数字系统设计》)。平常使用较多的是此类状态机。

“其实这几种状态机之间,只要做一些改变,便可以从一种形式转变为另一种形式。把状态机精确的分为这类或那类,其实并不重要,重要的是设计者如何把握输出的结构能满足设计的整体目标,包括定时的准确性和灵活性。”

——夏宇闻《Verilog数字系统设计》

二、状态机编码:

状态机的参数定义采用的都是独热码,和格雷码相比,虽然独热码多用了触发器,但所用组合电路可以省一些,因而使电路的速度和可靠性有显著提高,而总的单元数并无显著增加。采用独热编码后有了多余的状态,就有一些不可达到的状态。为此在case语句的最后需要增加default分支向。这可以用默认项表示该项,也可以用确定项表示,以确保回到初始状态。一般综合器都可以通过综合指令的控制来合理地处理默认项。

三、实例分析

状态机一般有三种不同的写法,即一段式、两段式和三段式的状态机写法,他们在速度、面积、代码可维护性等各个方面互有优劣,不要对任何一种写法给出“一棍子打死”的定论。手头上刚好有一个状态机的例子,借此记录一下三种状态机的Verilog写法。

要求:

售货机里有价值4元的脉动饮料,支持1元和2元硬币。请设计一个状态机,检测投入的硬币,当累计投入币值大于等于脉动价格时,售货机自动找零并弹出1瓶脉动饮料。硬币和商品都是一个一个的进出,不会出现一次性投很多个硬币弹出很多瓶脉动的情况。

信号含义
clk时钟信号
rst_n复位信号
in输入信号,币值,有1和2两种,投钱
out输出信号,币值,有1和2两种,找零
out_vld输出信号,脉动,为1则输出1瓶脉动

状态转移图:

根据要求,我们先把状态转移图画出来,绘画软件:Visio,如果没有安装也可以用wps自带应用的“流程图”功能:

在这里插入图片描述

//======================================================================
// --- 名称 : FSM_2
// --- 作者 : xianyu_FPGA
// --- 日期 : 2018-12-15
// --- 描述 : 售货机练习,采用二段式状态机
//======================================================================

module FSM_3
//---------------------<端口声明>---------------------------------------
(
input                   clk                 ,
input                   rst_n               ,
input      [1:0]        in                  ,
output reg [1:0]        out                 ,
output reg              out_vld
);
//---------------------<信号定义>---------------------------------------
reg  [3:0]              state_c             ;
reg  [3:0]              state_n             ;
//---------------------<状态机参数>-------------------------------------
localparam S0           = 4'b0001           ;
localparam S1           = 4'b0010           ;
localparam S2           = 4'b0100           ;
localparam S3           = 4'b1000           ;

//----------------------------------------------------------------------
//--   状态机第1段
//----------------------------------------------------------------------
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        state_c <= S0;
    else
        state_c <= state_n;
end

//----------------------------------------------------------------------
//--   状态机第2段
//----------------------------------------------------------------------
always@(*)begin
    case(state_c)
        S0: begin
            if(in==1)begin
                state_n = S1;
            end
            else if(in==2)begin
                state_n = S2;
            end
            else begin
                state_n = state_c;
                out     = 0 ;
                out_vld = 0 ;
            end
        end
        S1: begin
            if(in==1)begin
                state_n = S2;
            end
            else if(in==2)begin
                state_n = S3;
            end
            else begin
                state_n = state_c;
            end
        end
        S2: begin
            if(in==1)begin
                state_n = S3;
            end
            else if(in==2)begin
                state_n = S0;
                out_vld = 1 ;
            end
            else begin
                state_n = state_c;
                out_vld = 0;
            end
        end
        S3: begin
            if(in==1)begin
                state_n = S0;
                out_vld = 1 ;
            end
            else if(in==2)begin
                state_n = S0;
                out     = 1 ;
                out_vld = 1 ;
            end
            else begin
                state_n = state_c;
                out     = 0;
                out_vld = 0;
            end
        end
        default:state_n = S0;
    endcase
end


endmodule

`timescale 1ns/1ps  //时间精度
`define    Clock 20 //时钟周期

module FSM_3_tb;
//--------------------< 端口 >------------------------------------------
reg                     clk                 ;
reg                     rst_n               ;
reg  [1:0]              in                  ;
wire [1:0]              out                 ;
wire                    out_vld             ;

//----------------------------------------------------------------------
//--   模块例化
//----------------------------------------------------------------------
FSM_3 u_FSM_3
(
    .clk                (clk                ),
    .rst_n              (rst_n              ),
    .in                 (in                 ),
    .out                (out                ),
    .out_vld            (out_vld            )
);

//----------------------------------------------------------------------
//--   状态机名称查看器
//----------------------------------------------------------------------
localparam S0           = 4'b0001           ;
localparam S1           = 4'b0010           ;
localparam S2           = 4'b0100           ;
localparam S3           = 4'b1000           ;
//2字符16位
reg [15:0]              state_name          ;

always@(*)begin
    case(u_FSM_3.state_c)
        S0:     state_name = "S0";
        S1:     state_name = "S1";
        S2:     state_name = "S2";
        S3:     state_name = "S3";
        default:state_name = "S0";
    endcase
end

//----------------------------------------------------------------------
//--   时钟信号和复位信号
//----------------------------------------------------------------------
initial begin
    clk = 1;
    forever
    #(`Clock/2) clk = ~clk;
end

initial begin
    rst_n = 0; #(`Clock*20+1);
    rst_n = 1;
end

//----------------------------------------------------------------------
//--   设计输入信号
//----------------------------------------------------------------------
initial begin

    $dumpfile("test.vcd");
    $dumpvars(0,FSM_3_tb);
    #1;
    in = 0;
    #(`Clock*20+1); //初始化完成
//情况1--------------------------
    in = 1;         //1块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*1);
    in = 1;         //1块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*1);
    in = 1;         //1块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*1);
    in = 1;         //1块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*10);
//情况2--------------------------
    in = 1;         //1块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*1);
    in = 1;         //1块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*1);
    in = 1;         //1块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*1);
    in = 2;         //2块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*10);
//情况3--------------------------
    in = 1;         //1块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*1);
    in = 1;         //1块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*1);
    in = 2;         //2块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*10);
//情况4--------------------------
    in = 1;         //1块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*1);
    in = 2;         //2块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*1);
    in = 2;         //2块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*10);
//情况5--------------------------
    in = 2;         //2块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*1);
    in = 2;         //2块钱
    #(`Clock*1);
    in = 0;
    #(`Clock*10);


    $stop;
end


endmodule

我用的是 vivado 21 秒学会 vivado 仿真

在这里插入图片描述