一段、两段及三段式状态机的写法——售货机的verilog实现

本文介绍了Verilog状态机的基本概念,包括Moore型与Mealy型的区别,以及一段式、两段式和三段式的写法。并通过售货机状态机的例子详细展示了如何基于状态转移图来设计状态机。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、 状态机的Verilog分类、写法、编码方式

状态机:简单说是通过不同的状态迁移来完成一些特定的顺序逻辑。

状态机的分类

(1)Moore型:状态机的输出仅和当前状态有关。
(2)Mealy型:状态机的输出不仅与当前的状态有关,还取决于当前的输入条件。
.
当两者实现同一种功能的时候,Moore型状态机需要比Mealy型状态机多定义一个状态,且输出也比米利型延后一个时钟周期。这里之所以多一个状态,是因为Moore型的输出只取决于当前状态,因此需要有一个中间状态来产生一个输出。

用代码来区别两种状态机:

// Moore型
assign out = state == B ;//表示,输出只和状态B有关,当状态为B的时候,不管输入是多少,都输出1。
// Mealy型
assign out = ((state == B) && (in == 1)) ; //表示输出和状态以及输入有关,当状态为B且输入等于1的时候输出1.

用状态图来区别两种状态机:
比如用用两种方式,来实现同一功能的状态机。
Mealy型如下:
在这里插入图片描述

Moore型如下:(多一个状态)
在这里插入图片描述

状态机的写法

状态机一般包括三种写法:一段式、两段式和三段式。
(1)一段式:整个状态机写到一个always块里面,在该块中既描述状态寄存器,又描述状态转移和输出;

(2)二段式:用两个always块来描述状态机,一个always块采用同步时序描述状态转移;另一个块采用组合逻辑判断状态转移条件和状态输出;

(3)三段式:使用三个always块,一个always块采用同步时序描述状态转移,一个always采用组合逻辑判断状态转移条件,描述状态转移规律,另一个always块描述状态输出(可以用组合电路输出,也可以时序电路输出)。

状态机编码方式

独热码、格雷码。
独热码永远只有一位为1,缺点:用独热码后会有多余的状态;优点:可减少组合逻辑资源的消耗,比如在描述输出的时候,当采用组合逻辑进行比较的时候,可以只用其中的一位。assign state = S1[0] ;
格雷码相邻两个数只有一位不同;优点:状态少,而且可以避免毛刺出现;缺点:消耗组合逻辑资源多。assign state = S1;

注意:写case语句时,需要要加上default的情况,不然的话,会生成锁存器(跟电平有关),但在时序逻辑中,不加default生成的不是锁存器,是触发器。其中default分支可以用默认项表示,也可以用确定向来表示,以确保能最初状态。


2、根据状态转移图写状态机

小例子:
假如有三个状态,IDLE,S1,S2,在IDLE状态的时候,若en=1,则进入到S1状态,否则保持原状态不变。在S1状态的时候,若en=1,则进入到S2状态,否则保持原状态不变。在S2状态的时候,若en=1,则进入到IDLE状态,同时输出vld=1,否则保持原状态不变。
在这里插入图片描述

  module state(clk,rst_n,en,vld);
   input clk,rst_n;
	input en;
	output reg vld;
	
   reg [2:0]  state;
	
   localparam IDLE= 3'b001;    //parameter声明全局常量,可用在整个工程中,localparam仅在当前module中有效
	localparam S1=   3'b010;
	localparam S2=   3'b100;
   
	reg [2:0]  state_n;
	reg [2:0]  state_c;
	
	 always @ (posedge clk or negedge rst_n)begin
	    if(!rst_n)
		    state_c <= IDLE;
		 else 
		   state_c <= state_n; 
		 
	end
	always @(*) begin
	  case(state_c)
	    IDLE:begin
		    if(en) 
			    state_n = S1;
			 else
			   state_n = IDLE; 
	      end
	   S1:begin
		    if(en) 
			    state_n = S2;
			 else
			   state_n = S1; 
	      end
	    S2:begin
		    if(en) 
			    state_n = IDLE;
			 else
			   state_n = S2; 
	      end
	  endcase
	end
	
 always @(posedge clk or negedge rst_n)begin
       if(!rst_n)
		    vld <= 0;
		 else if (state_c == S2 && en==1)
		       vld<=1;
		 else
		    vld <= 0;	 

 end   
endmodule 

testbench

`timescale 1ns/1ps //时间精度
`define Clock 20 //时间周期
module state_tb;

   reg clk,rst_n;
	reg en;
	wire vld;
state state1(
     .clk(clk),
	  .rst_n(rst_n),
	  .en(en),
	  .vld(vld)   
	 );

//--   状态机名称查看器

localparam S0           = 4'b0001           ;
localparam S1           = 4'b0010           ;
localparam S2           = 4'b0100           ;
//2字符16位  这里是用ASICC表示,其中的S0就是IDLE
reg [15:0]              state_name          ;

always@(*)begin
    case(state1.state_c)
        S0:     state_name = "S0";
        S1:     state_name = "S1";
        S2:     state_name = "S2";
        default:state_name = "S0";
    endcase
end	
	 
initial		
    clk = 0;
always #(`Clock/2) clk= ~clk;

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

initial begin
    en=0;
	 #(`Clock);en=1;#(`Clock);en=0;#(`Clock);en=0;
	 #(`Clock*2); en=1;#(`Clock*2);en=1;#(`Clock*2);en=0;
	 #(`Clock*2);en=1;#(`Clock*2);en=1;#(`Clock*2);en=1;
	 #(`Clock*2);en=0;#(`Clock*2);en=1;#(`Clock*2);en=1;
	 #(`Clock*2);en=0;#(`Clock*2);en=0;#(`Clock*2);en=1;
	 #(`Clock*2); en=0;#(`Clock*2);en=1;#(`Clock*2);en=0;
	 #(`Clock*50);
	 $stop;
end
endmodule

在这里插入图片描述


3、售货机的状态机设计

举例子:设计一个关于售货机的状态机,要求只能投入一元和两元钱,脉动饮料的价格是四元,若超出则找零,否则不找,当达到四元时,给出脉动饮料,否则不给。

IDLE:初始状态,未投币或者已取出饮料。

S0 :投币一元的状态。

S1:投币两元的状态。

S2:投币3元的状态。

S3:投币4元的状态。(对应输出饮料)

S4:投币5元的状态。(对应找零)

在这里插入图片描述


三段式的写法进行verilog编写:

module Sys_Rst(
    input           clk,
    input           rst_n,
    input   [1:0]   data,
    output          flag_0, //找零
    output          flag_1 //出饮料
    );

parameter   IDLE = 0,
            S0   = 1,
            S1   = 2,
            S2   = 3,
			   S3   = 4,
			   S4   = 5;

reg     [3:0]   cur_state,next_state;


//第一段
always @(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
        cur_state <= IDLE;
    else
        cur_state <= next_state;

//第二段
always @(*)
    case(cur_state)
          IDLE : begin
            if(data == 2'b01 )
                next_state <= S0;
            else if(data == 2'b10) 
                next_state <= S1;
            else 
                next_state <= IDLE;
              end
		  S0   : begin
            if(data == 2'b01 )
                next_state <= S1;
            else if(data == 2'b10) 
                next_state <= S2;
            else 
                next_state <= S0;
          end
		  S1   : begin
            if(data == 2'b01 )
                next_state <= S2;
            else if(data == 2'b10) 
                next_state <= S3;
            else 
                next_state <= S1;
          end
		  S2   : begin
            if(data == 2'b01 )
                next_state <= S3;
            else if(data == 2'b10) 
                next_state <= S4;
            else 
                next_state <= S2;
          end
		  S3   : next_state <= IDLE; 
		  S4   : next_state <= IDLE;
          default: next_state <= IDLE;
    endcase

//输出
assign  flag_0 = (cur_state == S4)? 1'b1: 1'b0;  //找零
assign  flag_1 = (cur_state == S3)? 1'b1: 1'b0;  //出饮料
endmodule

testbench编写:

`timescale 1ns/1ns

module Sys_Rst_tb;

	 reg clk;
	 reg rst_n;
	 reg[1:0] d;        //序列输入 
	 wire flag_0 ; 
	 wire flag_1 ; 



 Sys_Rst u1 (
    
	 .clk(clk),
	 .rst_n(rst_n),
	 .data(d),         //序列输入
	 .flag_0(flag_0),
	 .flag_1(flag_1)

);


parameter   IDLE = 0,
            S0 = 1,
            S1 = 2,
            S2 = 3,
				S3   = 4,
			   S4   = 5;


//4字符16位
reg [31:0]              state_name  ;

always@(*)begin
    case(u1.cur_state)
        IDLE:     state_name = "IDLE";
        S0:       state_name = "S0";
        S1:       state_name = "S1";
        S2:       state_name = "S2";
        S3:       state_name = "S3";
        S4:       state_name = "S4";		  
        default:  state_name = "IDLE";
    endcase
end

//
initial clk = 0;
    always #1 clk = ~clk;
//
initial begin
    rst_n = 0;
    #1;
    rst_n = 1;
end
//
initial begin
    d = 2'b01; //1元
    #2;
    d = 2'b10; //2元
    #2;
    d = 2'b01; //1元	
    #2;
    d = 2'b01; //1元
	 #2;
    d = 2'b01; //1元
    #2;
	 d = 2'b01; //1元
	 #2;
    d = 2'b10; //2元 
	 #2;
    d = 2'b01; //1元
	 #3;
	 d= 2'b10; //2元
	 #20;
	 $stop;
	 	 
end

endmodule

波形如下:
其中S3表示投入共四元,因此出饮料,因此flag_1拉高。
S3表示投入超过了四元,因此找零,因此flag_0拉高。


在这里插入图片描述
售货机如果有错误请指正~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Fighting_FPGA

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

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

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

打赏作者

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

抵扣说明:

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

余额充值