乐天堂Fun88国际网站 当前位置:首页>乐天堂Fun88国际网站>正文

乐天堂Fun88国际网站

发布时间:2018-09-20

原标题:begin

仇天恨不知道哪来的勇气跟力气,从树后狂奔而出,双手抱住小白狼之后往前一个下坡猛地一跃,差疙瘩怪一掌拍下只间发的距离,几乎快吓破胆的仇天恨却没吓坏理智,小脑袋瓜里不断盘算着该怎么办?往低矮的地方去,对,它身体这般巨大,遇到又低又矮的地形它就没辙了……

真钱盘站

“给我开!”刘皓也知道现在拼的就是谁支持的更久,谁先被击中,谁的气势更强,谁的意志更坚定,因此他也不躲开,直接冲向赤犬,其他人的话哪怕是能元素化的能力者也一般不会选择和赤犬这类熔岩人进行近战,因为比较危险,可是刘皓却不怕,他的心口可是挂着一个勇字的。
看到一个空着的房间,对着身旁的服务的人员说道:“这个房间我要一晚上,明天要多少钱我会付款的!”

正是个袭击的好时候,韩非手下立即撤下来,对韩非报告了那边的情况,韩非当即决定动手,他看了看这些停在外面的卡车,心生一计,对兄弟们说道:“有这些卡车在正好帮了我们的大忙!”

  FPGA设计中,最重要的设计思想就是状态机的设计思想!状态机的本质就是对具有逻辑顺序时序规律的事件的一种描述方法,它有三个要素:状态、输入、输出:状态也叫做状态变量(比如可以用电机的不同转速作为状态),输出指在某一个状态的特定输出,输入指状态机中进入每个状态的条件。根据状态机的输出是否和输入有关,可分为摩尔(Moore)型状态机和米勒型(Mealy)状态机:摩尔型状态机的输出只取决于当前状态,而米勒型状态机的输出不仅取决于当前状态,还与当前输入有关。通常,我们描述状态机有三种方法:状态转移图、状态转移表、HDL描述,状态转移图直观,设计用,而HDL语言方便描述,实现时用。

  那么,如何用HDL描述一个好的状态机呢?主要有以下四点:安全、稳定性高速度快面积小设计清晰在描述过程中,我们会引用两个新的verilog语法:localparam描述参数(等价于parameter)以及用task/endtask将输出功能块封装,增强代码可读性;描述状态机的关键是要描述清楚状态机的三大要素:如何进行状态转移?每个状态的输出?状态输出是否和输入条件相关?通常有三种写法,下面通过一个实例说明;

实例.检测“Hello”序列状态机

  1、功能:在输入一串字符中检测“Hello”序列,检测到后将led状态进行翻转;

  2、根据设计的FSM状态转移图(visio绘制):

  3、一段式描述法:在一个always块里既描述状态转移,又描述状态的输入和输出;

  verilog代码如下:

//检测“Hello”后led状态翻转

module check_hello(
    input  clk,         //50M时钟信号
    input  rst,         //低电平复位
    input  [7:0]asci,   //字符输入
    output reg  led     //控制led
);
    //状态寄存器
    reg  [4:0]NS;     //nextstate
    
    //状态独热编码
    localparam 
        CHECK_H   = 5"b0_0001,
        CHECK_e     = 5"b0_0010,
        CHECK_la  = 5"b0_0100,
        CHECK_lb  = 5"b0_1000,
        CHECK_o   = 5"b1_0000;
        
    //一段式状态机
    always@(posedge clk,negedge rst)
        if(!rst)begin
            NS  <= CHECK_H;
            led <= 1"b1;    //led熄灭
        end
        else begin    
            case(NS)
                CHECK_H:
                    begin
                                led <= 1"b1; 
                        if(asci == "H")
                            NS <= CHECK_e;
                        else
                            NS <= CHECK_H;
                    end
                CHECK_e:
                    begin
                                led <= 1"b1; 
                        if(asci == "e")
                            NS <= CHECK_la;
                        else
                            NS <= CHECK_H;
                    end
                CHECK_la:
                    begin
                                led <= 1"b1; 
                        if(asci == "l")
                            NS <= CHECK_lb;
                        else
                            NS <= CHECK_H;
                    end
                CHECK_lb:
                    begin
                                led <= 1"b1; 
                        if(asci == "l")
                            NS <= CHECK_o;
                        else
                            NS <= CHECK_H;
                    end
                CHECK_o:
                    begin
                        if(asci == "o")
                            led <= 1"b0; 
                        else
                            led <= 1"b1; 
                        NS <= CHECK_H;
                    end
                    //排除任意情况,增强FSM安全性
                default:
                    begin
                        NS  <= CHECK_H;
                        led <= 1"b1;
                    end
            endcase
    end 
endmodule

  testbench测试文件如下:

 `timescale 1ns/1ps
 `define    clk_period 20
 
 module check_hello_tb();
 
        reg  clk;         //50M时钟信号
        reg  rst;         //低电平复位
        reg  [7:0]asci;   //字符输入
        wire  led;        //控制led
        
        //例化测试模块
        check_hello  check_hello_test(
            .clk(clk),         //50M时钟信号
            .rst(rst),         //低电平复位
            .asci(asci),       //字符输入
            .led(led)          //控制led
        );
        
        //产生50M时钟信号
        initial clk = 1;
        always #(`clk_period / 2)clk <= ~clk;
            
        //开始测试
        initial begin
            rst  = 0;          //系统复位
            asci = 3"bx;
        #(`clk_period * 2);
            rst = 1;
        #(`clk_period);
            asci = "H";
        #(`clk_period);
            asci = "e";
        #(`clk_period);
            asci = "l";
        #(`clk_period);
            asci = "l";
        #(`clk_period);
            asci = "o";
        #(`clk_period);
            asci = "2";
        #(`clk_period);
            asci = "e";
        #(`clk_period);
            asci = "h";
        #(`clk_period);
            asci = "l";    
        #(`clk_period);
            $stop;
        end 
endmodule

  测试结果如下,可以看到,刚开始输入数据是任意数据,FSM为CHECK_H状态,led输出高电平,保持熄灭;当检测到Hello序列时,led输出低电平,状态翻转;

  该测试中隐藏了一个重要的问题,我们在编写testbench的时候,将数据变化与时钟上升沿对齐,但在实际中数据变化会产生滞后,所以这时候我们为了更好地模拟实际情况,编写testbench就有一定的技巧:将输出变化延迟1-2ns,所以我们将testbench中的这行代码进行修改:

initial begin
            rst  = 0;          //系统复位
            asci = 3"bx;
        #(`clk_period * 2);  
            rst = 1;
        #(`clk_period);    //将这行修改为  #(`clk_period + 1); 将整体数据改变时间较时钟上升沿滞后1ns观察
            asci = "H";

   然后再运行仿真观察波形,可以看到,这次的波形更好地说明了实际情况,因为我们编写的一段式FSM整体在一个时序逻辑中,所以FSM只在时钟上升沿检测数据变化,也就是说,在第一个时钟内发生的变化,下一个时钟沿才能检测到

  状态转移图如下,可以看到按照预定设计执行:

  综合出来的电路图如下,可以看到FSM实现的重点在于状态寄存器,耗费资源很少,由综合报告也可看出:

  

  在一段式描述方法中可以看到,虽然一个alaways块就可以解决问题,但描述不清晰,不利于维护修改,并且不利用附加约束,不利于综合其和布局布线器对设计的优化;

 

  4、两段式描述法:一个always块描述状态转移,另一个always块描述状态判断转移条件

  verilog代码如下:

//检测“Hello”后led状态翻转

module check_hello(
    input  clk,         //50M时钟信号
    input  rst,         //低电平复位
    input  [7:0]asci,//字符输入
    output reg led     //控制led
);
    //状态寄存器
    reg  [4:0]NS;     //nextstate
    reg  [4:0]CS;    //currentstate
    
    //状态独热编码
    localparam 
        CHECK_H   = 5"b0_0001,
        CHECK_e   = 5"b0_0010,
        CHECK_la  = 5"b0_0100,
        CHECK_lb  = 5"b0_1000,
        CHECK_o   = 5"b1_0000;
        
    //两段式状态机
    //第一个always块描述状态转移
    always@(posedge clk,negedge rst)
        if(!rst)
            CS  <= CHECK_H;
        else
            CS  <= NS;        //状态转移到下一状态
            
    //第二个always块描述状态输出以及判断状态转移        
    always@(CS,asci)
        case(CS)
            CHECK_H:
                begin
                    led = 1"b1;
                    if(asci == "H")
                        NS <= CHECK_e;
                    else
                        NS <= CHECK_H;
                end
            CHECK_e:
                begin
                    led = 1"b1;
                    if(asci == "e")
                        NS <= CHECK_la;
                    else
                        NS <= CHECK_H;
                end
            CHECK_la:
                begin
                    led = 1"b1;
                    if(asci == "l")
                        NS <= CHECK_lb;
                    else
                        NS <= CHECK_H;
                end
            CHECK_lb:
                begin
                    led = 1"b1;
                    if(asci == "l")
                        NS <= CHECK_o;
                    else
                        NS <= CHECK_H;
                end
            CHECK_o:
                begin
                    if(asci == "o")
                        led = 1"b0;
                    else
                        led = 1"b1;
                    NS <= CHECK_H;
                end
            default: 
                begin
                    NS  <= CHECK_H;
                    led = 1"b1;
                end
        endcase
endmodule

   用之前修改后的testbench测试文件进行测试,测试结果如下,可以看到,两段式状态机描述结果和之前一段式描述并没有差异:

  综合后状态转移图如下,与之前也没有差异:

 

  再查看综合后的电路图,与之前有了较大的差异,可以看到,这次led输出采用组合逻辑输出,之前采用一个带有使能端的D触发器输出:

  再来分析一下资源占用情况,在上次设计中,总共占用了14个LE,6个寄存器资源,而这次占用了12个LE,5个寄存器资源,因为两段式描述更清晰,便于软件进行分析优化,所以设计也更加优良:

  

  接下来我们再次人为上演上一个testbench中的错误,将数据改变与时钟上升沿对齐,测试波形如下:

  可以看出来,因为我们描述输出采用组合逻辑,所以这样的测试是错误的,不仅NS与CS变化没有同步,而且状态也没有翻转,但在实际中,数据变化与时钟上升沿有可能会同时发生,显然这个设计就会出错。所以为了从根本上避免这种情况,一般在输出部分插入一级额外的时钟信号,用来保证信号稳定性,所以我们引入接下来的三段式FSM描述。

  

  5、三段式描述法:一个always块采用时序逻辑描述状态转移,一个always块采用组合逻辑判断状态转移条件,一个always块采用时序逻辑描述状态输出

  verilog代码如下:

//检测“Hello”后led状态翻转

module check_hello(
    input  clk,         //50M时钟信号
    input  rst,         //低电平复位
    input  [7:0]asci,//字符输入
    output reg led     //控制led
);
    //状态寄存器
    reg  [4:0]NS;     //nextstate
    reg  [4:0]CS;    //currentstate
    
    //状态独热编码
    localparam 
        CHECK_H   = 5"b0_0001,
        CHECK_e   = 5"b0_0010,
        CHECK_la  = 5"b0_0100,
        CHECK_lb  = 5"b0_1000,
        CHECK_o   = 5"b1_0000;
        
    //三段式状态机
    //第一个always块描述状态转移
    always@(posedge clk,negedge rst)
        if(!rst)
            CS  <= CHECK_H;
        else
            CS  <= NS;        //状态转移到下一状态
            
    //第二个always块判断状态转移        
    always@(CS,asci)
        case(CS)
            CHECK_H:
                begin
                    if(asci == "H")
                        NS <= CHECK_e;
                    else
                        NS <= CHECK_H;
                end
            CHECK_e:
                begin
                    if(asci == "e")
                        NS <= CHECK_la;
                    else
                        NS <= CHECK_H;
                end
            CHECK_la:
                begin
                    if(asci == "l")
                        NS <= CHECK_lb;
                    else
                        NS <= CHECK_H;
                end
            CHECK_lb:
                begin
                    if(asci == "l")
                        NS <= CHECK_o;
                    else
                        NS <= CHECK_H;
                end
            CHECK_o:
                begin
                    NS <= CHECK_H;
                end
            default: 
                begin
                    NS  <= CHECK_H;
                end
        endcase
    
    //第三个always块描述状态输出
    always@(posedge clk,negedge rst)
        if(!rst)
            led <= 1"b1;    //led熄灭
        else begin
            case(CS)
                CHECK_H:        
                    led <= 1"b1;
                CHECK_e:    
                    led <= 1"b1;
                CHECK_la:
                    led <= 1"b1;
                CHECK_lb:
                    led <= 1"b1;
                CHECK_o:  
                    if(asci == "o")
                        led <= 1"b0;
                    else
                        led <= 1"b1;
                default:
                    led <= 1"b1;
                endcase
        end
endmodule 

   testbench依然采用之前修改后的测试文件(数据整体延迟时钟1ns)进行测试,结果如下,测试结果和之前相同:

  再次人为进行查错,将数据与时钟对齐进行测试,结果如下:

  结果是不是很令人惊喜^_^,可以看到当数据变化与时钟上升沿对齐的时候,结果依然正确,这是因为状态输出不是组合逻辑,而是时序逻辑,在输出前面插入了一级时钟信号就有效的解决了问题,所以一般描述FSM时选用三段式描述法描述

  再来看看状态转移图,与之前两种描述也没有差异:

  综合后的电路如下,可以验证之前说的,在输出前插入了一级时钟信号:

  再来对比一下资源占用,可以看到三段式描述法中和了前面两种描述方法的优点,总共耗费了12个LE,6个寄存器资源,虽然多了一个寄存器资源,却有效的避免了重大错误,这也暗示了一个数字设计里最重要的思想,中和设计思想有的时候可以需要性能的提升,但在提升性能的同时也会增加资源占用,设计面积增大,所以两者中和往往是最优秀的设计;

  

  

  至此,一个完整的示例就设计实现完成了,最后再进行几点补充:

  1、因为这个示例中输出只有led,所以我们采用了直接写在FSM描述里面,如果输出较多,可以利用task/endtask将输出进行封装

  2、在整个设计中,不管是任何一种描述方式,只要case,就会写入default选项,这一点也一直在设计过程中没有提到;

   default选项是必须要写入的,这样就符合了刚开始提到的状态机评判标准最重要的一点——安全性,因为不管我们采用二进制编码还是one-hot编码,在实际应用中可能会由于其他因素(比如噪声)产生突变,这时候就会进入default选项,然后重新启动状态机,可谓优点多多;

  3、这一点也是最重要的一点,状态机设计不是一种具体的事物,比如说verilog语法就是固定的,它更多的是一种对具有逻辑规律和时序逻辑事件的一种描述思想,所以,即使前面提到了一段式,两段式,三段式FSM描述方法,在实际中,如果需要,我们可以分离出来4个always块,5个always块等等,这里的一段式,两段式,三段式反映的只是一种设计思想,希望在以后的数字设计中有更多的体会!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

    

  

编辑:文纯石

发布:2018-09-20 00:24:12

当前文章:http://www.jwgmjj.com/array/63918.html

挣钱最快的方法 纽约国际娱乐场送体验金 老虎机通用干扰器软件下载 注册首存一元送19 澳门新濠天地3559 tt娱乐场要怎么开户 刮刮乐游戏下载 鸿利亚洲娱乐场官方网站

56795 87252 99476 74998 95328 5282930679 79993 19879

责任编辑:安华华

随机推荐