FPGA——Vivado 状态机设计

本文为 FPGA 学习总结,欢迎分享交流。

运行环境

  • windows10
  • Vivado 2018.3
  • Modelsim 10.7

状态机是数字逻辑系统的核心,是重要的时序电路。通常包括三个部分:一是下一个状态的逻辑 电路,二是存储状态机当前状态的时序逻辑电路,三是输出组合逻辑电路。状态机的结构如下图:

在这里插入图片描述

根据状态机的输出信号是否与电路的输入有关分为 Mealy 型状态机和 Moore 型状态机。电路的输出信号与电路当前状态和电路的输入有关,称为 Mealy 型状态机;电路的输出信号仅与电路当前状态有关,称为 Moore 型状态机。

一段式状态机

// detect1.v
module detect_1(
input clk_i,
input rst_n_i,
output out_o
 );
reg out_r;
//状态声明和状态编码
reg [1:0] state;
parameter [1:0] S0=2'b00;
parameter [1:0] S1=2'b01;
parameter [1:0] S2=2'b10;
parameter [1:0] S3=2'b11;
always@(posedge clk_i)
begin
	if(!rst_n_i)begin
		state<=0;
 		out_r<=1'b0;
 	end
	else
		case(state)
			S0 :
			begin
 				out_r<=1'b0; // 输出和状态转移在一个always模块中
 				state<= S1;
			end
			S1 :
			begin
				out_r<=1'b1;
 				state<= S2;
			end
			S2 :
			begin
				out_r<=1'b0;
				state<= S3;
			end
			S3 :
			begin
				out_r<=1'b1;
			end
		endcase
	end
assign out_o=out_r;
endmodule

两段式状态机

// detect2.v
`timescale 1ns / 1ps

module detect_2(
     input clk_i,
     input rst_n_i,
     output out_o
);
    reg out_r;
    // 状态声明和状态编码
    reg [1:0] Current_state;
    reg [1:0] Next_state;
    parameter [1:0] S0=2'b00;
    parameter [1:0] S1=2'b01;
    parameter [1:0] S2=2'b10;
    parameter [1:0] S3=2'b11;
    
    // 时序逻辑:描述状态转换
    always@(posedge clk_i)
    begin
        if(!rst_n_i)
           Current_state<=0;
        else
            Current_state<=Next_state;
    end
    
    // 组合逻辑:描述下一状态和输出
    always@(*)
    begin
        case(Current_state)
            S0 : 
                begin
               out_r=1'b0;
               Next_state= S1;
                end
            S1 :
                begin
               out_r=1'b1;
               Next_state= S2;
                end
            S2 :
                begin
               out_r=1'b0;
               Next_state= S3;
                end
            S3 :
                 begin
                out_r=1'b1;
                 Next_state=Next_state;
                 end
            endcase
    end
    assign out_o=out_r;    
endmodule

三段式状态机

// detect3.v
module detect_3(
        input clk_i,
        input rst_n_i, 
        output out_o
        );
    reg out_r;
    // 状态声明和状态编码
    reg [1:0] Current_state;
    reg [1:0] Next_state;
    parameter [1:0] S0=2'b00;
    parameter [1:0] S1=2'b01;
    parameter [1:0] S2=2'b10;
    parameter [1:0] S3=2'b11;
    
    // 时序逻辑:描述状态转换
    always@(posedge clk_i)
    begin
        if(!rst_n_i)
            Current_state<=S0;
        else
            Current_state<=Next_state;
    end
    
    // 组合逻辑:描述下一状态
    always@(*)
    begin
        case(Current_state)
            S0: 
                Next_state = S1;
            S1:
                Next_state = S2;
            S2:
                Next_state = S3;
            S3:
                Next_state = Next_state;
			default : 
				Next_state = S0;
          endcase
    end
    // 输出逻辑:让输出 out,经过寄存器 out_r 锁存后输出,消除毛刺
    always@(*)
    begin
        case(Current_state)
               S0,S2: 
                    out_r<=1'b0;
               S1,S3: 
                   out_r<=1'b1;
			   default :
					  out_r<=out_r;
            endcase
    end
    
    assign out_o=out_r;    
endmodule

例程

我们用一个例程对三种状态机的设计进行进一步的理解。下面为以上三种方式的仿真文件:

// tb_detect.v
`timescale 1ns / 1ps
module tb_detect();

reg clk_i;
reg  rst_n_i;
wire out_o_1,out_o_2,out_o_3;
detect_1  detect_1(
	.clk_i(clk_i),
	.rst_n_i(rst_n_i),
	.out_o(out_o_1)
    );
    
detect_2  detect_2(
        .clk_i(clk_i),
        .rst_n_i(rst_n_i),
        .out_o(out_o_2)
        );
    
 detect_3  detect_3(
                .clk_i(clk_i),
                .rst_n_i(rst_n_i),
                .out_o(out_o_3)
                );  
                
      initial begin
                 clk_i =0;
                 rst_n_i =0;
                 #20 
                 rst_n_i =1;
      end
            
                always #5 clk_i = ~clk_i;     
endmodule

在 vivado 中点击 SIMULATION 运行代码进行仿真,三种状态机得到的结果都为 0101:

在这里插入图片描述

我们添加断点进行深入研究。给每个文件的 always 模块添加断点,并将对应的信号加入到仿真窗口中进行观察:

在这里插入图片描述

对于一段式状态机,在复位完成后才开始输出 0101;对于两段和三段式状态机,因为输出部分为组合逻辑,复位状态时,当前状态为 0,因此在复位之前全部输出 0,在复位完成的第一个状态中输出为 1,再输出 01。

编写 TESTBENCH 的目的是为了对使用硬件描述语言设计的电路进行仿真验证,测试设计电路的功能、性能与设 计的预期是否相符。通常,编写测试文件的过程如下:

  • 产生模拟激励(波形文件);
  • 将产生的激励加入到被测试模块中并观察其响应;
  • 将输出响应与期望值相比较

状态机的设计是时序电路的核心内容,要仔细把握三种状态机的区别与练习,并熟练运用。

猜你喜欢

转载自blog.csdn.net/weixin_44413191/article/details/107516559