fpga-状态机的设计及应用

目录

状态机的概念与分类

状态机的描述方法

交通灯控制器的设计


状态机的概念与分类

时序电路在时钟脉冲的作用下,在有限个状态之间进行转换,以完成某种特定的功能,所以将时序电路又称为有限机状态机(Finite State Machine),简称为状态机,用FSM表示。

但广义地讲,状态机不单单是指时序逻辑电路,而是指任何有逻辑顺序和时序规律的事件。

状态机有三个基本要素:状态、输出和输入。

(1) 状态: 用于划分逻辑顺序和时序规律。(2) 输出: 在某个状态下发生的特定事件。(3) 输入:状态机中进入每个状态的条件。

根据状态机的输出是否与输入有关,将状态机分为摩尔(Moore)型状态机和米里(Mealy)型状态机两大类。

一般地,Moore型状态机也可能有输入,只是输出不直接与输入信号相关,因此,在数字电路中,Moore型状态机表示为图6-3所示的结构形式,其中输出z(t)=f[s(t)],而与输入x(t)无关。

Mealy型状态机

Mealy型状态机的输出不仅依赖于当前状态,而且与输入有关。

Mealy型状态机的输出既与状态有关,也与输入有关,因此,在数字电路中,Mealy型状态机表示结构形式,其中输出z(t)=f[x(t),s(t)]。

状态机的描述方法

时序逻辑电路常用的功能描述有状态转换表、状态转换图和波形图三种方法。

以表格的形式描述时序电路的次态Q*以及外部输出信号Y与外部输入信号X和现态Q之间的关系。

状态转换图

以图形的方式描述时序电路的状态Q和输出Y在输入信号X的作用下随时钟CLK的变化关系。

用随时间变化的波形来描述在输入信号的作用下,输出Y及状态Q与时钟脉冲CLK之间的对应关系。

状态机有三种常用的表示方法:状态转换图、状态转换表和HDL描述。

状态机的HDL描述方法结构清晰,灵活规范,而且安全、高效和易于维护,因此是本章重点的讲述内容。

状态机设计的基本步骤是: (1) 定义状态; (2) 建立状态转换图; (3) 完成状态编码; (4) 应用HDL描述状态转换关系以及输出。

  1. 定义状态

由于检测器用于检测"1111"序列,所以电路需要识别和记忆连续输入1的个数,因此,预定义电路内部有S0、S1、S2、S3和S4共五个状态,其中S0表示当前还没有接收到一个1,S1表示已经接收到一个1,S2表示已经接收到两个1,S3表示已经接收到三个1,而S4表示已经接收到四个1。

  1. 建立状态转换图

状态定义完成之后,需要分析在时钟脉冲的作用下,状态转移的方向以及输出,画出状态转换图。通常从系统的初始状态、复位状态或者空闲状态开始分析,标出每个状态的转换方向、转换条件以及输出。

  1. 状态编码

在状态机设计中,建议使用参数定义语句parameter/localparam来定义状态编码,以增强代码的可阅读性。

无效状态的处理大致有如下三种方式:(1)转入空闲状态,等待下一个任务的到来(2)转入指定的状态,去执行特定任务;(3)转入预先定义的专门处理错误的状态,如预警状态等。

  1. 状态机的描述

Verilog HDL用过程语句描述状态机。由于时序电路的次态是现态以及输入的逻辑函数,因此需要将现态和输入信号作为过程的敏感事件或者边沿触发事件,结合case和if等高级程序语句等实现逻辑功能。

有限状态机的描述可采用一段式、两段式和三段式三种描述方式。

三段式状态机的描述模板如下:

复制代码
// 第一个过程语句,时序逻辑,描述状态转换关系
always @ ( posedge clk or negedge rst_n )   
     if ( !rst_n )                                     // 复位信号有效时
            current_state <= IDLE;
     else  current_state <= next_state;          // 状态转换
  // 第二个过程语句,组合逻辑,描述次态
  always @ ( current_state,input_signals )       // 电平敏感条件
         case ( current_state )
             S1: if (...)  next_state = ...;       // 阻塞赋值
                 else next_state = ...; 
             S2: if (...)  next_state = ...; 
                 else next_state = ...; 
             ...... 
         default: ...;
        endcase
// 第三个过程语句描述输出(1),应用组合逻辑,阻塞赋值
always @ ( current_state,
                  input_signals )
   case ( current_state )
      S1: if ( cond_expression ) 
             out = ...;  
           else out = ...;                       
      S2: if ( cond_expression ) 
               out = ...; 
           else out = ...; 
       ......
       default: ...;         
    endcase
// 第三个过程语句描述输出(2),
应用时序逻辑,非阻塞赋值
always @ ( posedge clk or negedge rst_n )
  if ( !rst_n )                                  
     out <= ...;                        
   else
     case ( current_state )
        S1: if (cond_expression )  out <= ...; 
             else out <= ...; 
        S2: if ( cond_expression )  out <= ...; 
             else out <= ...; 
         ......
         default: ...;         
      endcase

设计例1的"1111"序列检测器,应用三段式状态机Verilog描述代码参考如下:

复制代码
  module serial_detor ( input  clk,          // 检测器时钟
                          input  rst_n,        // 复位信号
                          input  x,             // 串行数据输入
                          output wire y   );   // 检测结果输出
      localparam S0 = 5'b00001,            // 状态定义及编码
                   S1 = 5'b00010,
                   S2 = 5'b00100, 
                   S3 = 5'b01000,
                   S4 = 5'b10000;
      // 内部状态变量定义
      reg [4:0] current_state,next_state;    // 现态和次态
      // 组合逻辑,定义输出
      assign y = ( current_state == S4 );         
      // 时序逻辑过程,描述状态转换
     always @( posedge clk or negedge rst_n) 
         if ( !rst_n )  current_state <= S0;
            else       current_state <= next_state;      
      //  组合逻辑过程,确定次态
      always @( current_state,x )               
         case ( current_state )
              S0 : if (x) next_state = S1; else next_state = S0;
              S1 : if (x) next_state = S2; else next_state = S0;
              S2 : if (x) next_state = S3; else next_state = S0;
              S3 : if (x) next_state = S4; else next_state = S0;
              S4 : if (x) next_state = S4; else next_state = S0;
         default : next_state = S0;
       endcase
   endmodule

将上述代码经过编译与综合后,建立向量波形文件进行功能仿真,得到的时序波形如图所示。

基于状态机设计按键消抖电路时,需要将按键的一次动作分解为:按下前、按下时、稳定期和释放时4个状态,分别用 KEY_IDLE、KEY_PRESSED、KEY_ACTIVE和 KEY_RELEASE表示。设按键输入用key_in表示,低电平有效,设计消抖时间为20ms,则按键消抖电路的状态转换关系如图所示。

应用系统函数random产生随机整数的语法格式为: num = random%b // num为-(b-1) ~ (b-1)范围内的随机整数 num ={$random}%b // num为0 ~ (b-1)范围内的随机整数

交通灯控制器的设计

交通灯控制器用于控制十字路口交通信号灯的状态,引导车辆和行人通行。交通灯控制器是时序逻辑电路,可以应用MCU/状态机设计方法实现。

设计过程:交通灯控制器应由状态控制电路和计时电路两部分构成。控制电路用于切换主、支干道绿灯、黄灯和红灯的状态,计时电路用于控制通行时间。

主支干道的绿、黄、红灯正常工作时共有四种组合,分别用S0、S1、S2和S3表示,状态的具体含义如表所示。

根据状态转换图设计交通灯控制电路的Verilog代码参考如下:

复制代码
 module traffic_controller( clk,rst_n,t45,t5,t25,traffic_state,mG,mY,mR,sg,sy,sr );
     input clk,rst_n;
     input t45,t5,t25;
     output wire [3:0] traffic_state;   // 需要显示计时时间时,状态需要输出
     output reg mG,mY,mR;              // 主干道绿、黄、红灯
     output reg sg,sy,sr;                 // 支干道绿、黄、红灯
     // 状态及编码定义,一位热码方式
     localparam S0=4'b0001, S1=4'b0010, S2=4'b0100, S3=4'b1000;      
     // 内部变量定义
     reg [3:0] current_state,next_state;    // 现态和次态    
     // 状态输出描述
     assign traffic_state = current_state;
     // 时序逻辑过程,描述状态转换
     always @(posedge clk or negedge rst_n)   
        if ( !rst_n )                                 // 异步复位
            current_state <= S0;  
         else               // 状态切换
            current_state <= next_state;    
     // 组合逻辑过程,确定次态
     always @(current_state,t45,t25,t5)    
        case (current_state)
            S0 : if (t45) next_state = S1; else next_state = S0;
            S1 : if ( t5) next_state = S2; else next_state = S1;
            S2 : if (t25) next_state = S3; else next_state = S2;   
            S3 : if ( t5) next_state = S0; else next_state = S3;
       default :          next_state = S0;
       endcase
     // 组合逻辑过程,描述输出
     always @( current_state )                       
        case ( current_state )
            S0: begin
                 mG = 1; mY = 0; mR = 0;     // 主干道绿灯
                 sg = 0; sy = 0; sr = 1;  end  // 支干道红灯          
            S1: begin
                 mG = 0; mY = 1; mR = 0;     // 主干道黄灯
                 sg = 0; sy = 0; sr = 1; end   // 支干道红灯
            S2: begin
                 mG = 0; mY = 0; mR = 1;     // 主干道红灯
                 sg = 1; sy = 0; sr = 0; end    // 支干道绿灯
             S3: begin
                 mG = 0; mY = 0; mR = 1;     // 主干道红灯
                 sg = 0; sy = 1; sr = 0; end    // 支干道黄灯
        default:  begin                   // 其它取值时
                 mG = 1; mY = 0; mR = 0;
                 sg = 0; sy = 0; sr = 1; end
        endcase
 endmodule

如果不要求显示计时时间,则计时电路的设计比较简单。

取计时电路的时钟周期为5秒时,则45秒、5秒、25秒和5秒计时共需要9+1+5+1=16个时钟周期。

设计一个16进制加法计数器,状态编码为0~15。当状态为8时令t45=1,状态为9时令t5=1,状态为14时令t25=1,状态为15时令t5=1。因此,计时电路的Verilog描述参考如下:

复制代码
 module traffic_timer(clk,rst_n,t45,t5,t25);
     input clk,rst_n;
     output wire t45,t5,t25;
      // 定义内部计数器
      reg [3:0] timer; 
      // 描述16进制计数器
      always @(posedge clk or negedge rst_n) 
         if (!rst_n)
             timer <= 4'd0;
          else
             timer <= timer+1'd1;
     // 描述输出
     assign t45 = ( timer == 4'd8 ) ;
     assign t5  = ( timer == 4'd9 ) || ( timer == 4'd15 ) ;
     assign t25 = ( timer == 4'd14 ) ;
 endmodule

将交通灯控制模块traffic_controller和计时模块traffic_timer连成图所示的顶层设计电路即可实现简单的交通信号灯控制器,并将时钟脉冲clk和复位信号rst_n连接在一起以控制两个子模块之间的同步。

若需要显示当前状态的剩余时间,则需要重新设计计时电路,而且主支干道计时时间(main_timer,sub_timer)的显示与状态有关。

以倒计时方式分别显示主干道和支干道状态的剩余时间时,则计时电路设计的Verilog 描述代码参考如下:

复制代码
module traffic_timer (clk,rst_n,state,t45,t5,t25,main_timer,sub_timer);
    input clk;
    input rst_n;  
    input [3:0] state;                           // 状态信息,来自于traffic_controller
    output wire t45,t5,t25;                    // 计时到输出信号,用于控制状态切换
    output reg [5:0] main_timer,sub_timer;  // 主干道和支干道计时信息,用于显示
    // 状态定义及编码,独热码方式
    localparam S0=4'b0001, S1=4'b0010, S2=4'b0100, S3=4'b1000;
    // 组合逻辑,计时时间到逻辑     
    assign t45 = (state==S0) && (main_timer == 0);
    assign t5  = (main_timer == 0) && (sub_timer==0);
    assign t25 = (state==S2) && (sub_timer==0 );
    // 计时过程
    always @(posedge clk or negedge rst_n)  
       if ( !rst_n ) begin                   // 复位有效时
            main_timer <= 45;             // 从主干道通行开始
            sub_timer  <= 50; end   
         else                                 // 否则,在时钟作用下        	case ( state )                   // 分情况讨论
	    S0: if ( t45 ) begin        // 主干道通行时间到
 	            main_timer <= 4; 
	            sub_timer  <= sub_timer - 1; end
	         else begin
		 main_timer <= main_timer - 1; 
		 sub_timer  <= sub_timer - 1;  end
	    S1: if ( t5 ) begin     // 主干道停车时间到
	             main_timer <= 29; 
                         sub_timer  <= 24;  end
	         else begin
		  main_timer <= main_timer - 1; 
                          sub_timer  <= sub_timer - 1;  end
                S2: if ( t25 ) begin    // 支干道通行时间到
		  main_timer <= main_timer - 1;
		  sub_timer <= 4; end
	         else begin
		   main_timer <= main_timer - 1; 
		   sub_timer  <= sub_timer - 1;  end
               S3: if ( t5 ) begin    // 支干道停车时间到
	              main_timer <= 44;
                          sub_timer  <= 49; end
	         else begin
		   main_timer <= main_timer - 1; 
		   sub_timer  <= sub_timer - 1;  end
	   default:  begin  main_timer <= 45; 	
                                  sub_timer <= 50;  end 
           endcase
endmodule

由于计时时间是以二进制方式计数的,因此需要显示主、支干道计时时间时,还需要设计6位二进制数到两组BCD码的转换电路,才能驱动数码管进行显示。 带计时显示的交通灯控制器顶层测试电路如图所示。由按键产生脉冲,经过模块KEY_debounce消抖后作为交通灯计时电路的时钟。在计时电路traffic_timer的控制下,模块traffic_controller驱动交通灯显示状态,模块BinarytoSEG驱动数码管显示主、支干道状态的剩余时间。

相关推荐
nanxl13 小时前
FPGA-DDS信号发生器
fpga开发·verilog·vivado
黄埔数据分析4 小时前
RecoNIC 入门:SmartNIC 上支持 RDMA 的计算卸载-FPGA-智能网卡-AMD-Xilinx
fpga开发
nanxl16 小时前
FPGA-数字时钟
fpga开发·verilog·vivado
尤老师FPGA20 小时前
LVDS系列9:Xilinx 7系可编程输入延迟(二)
单片机·嵌入式硬件·fpga开发
内有小猪卖1 天前
时序约束 记录
fpga开发
Cao1234567893211 天前
FPGA时钟设计
fpga开发
JNTeresa1 天前
锁存器知识点详解
fpga开发
Cao1234567893211 天前
FPGA基础之基础语法
fpga开发
一大Cpp1 天前
通过Quartus II实现Nios II编程
fpga开发
7yewh1 天前
Verilog 语法 (二)
fpga开发