赞
踩
硬件设计很讲究并行设计思想,虽然verilog描述的电路大多是并行的,但是对于实际的工程应用中,往往需要设计硬件来实现一些具有顺序的工作,就需要用到状态机的思想。
状态机是数字系统设计中非常重要的组成部分,状态机的设计对系统的高可靠性、高速性有着至关重要的作用。
状态机由多个相互跳转的状态组成,用于对具有逻辑顺序和时序规律的事件进行描述,在任意时刻,状态机只能处于某一个状态;状态机的功能可以发分解为两个部分:第一部分根据外部输入实现状态转移;第二部分根据特定状态和输入来驱动输出。
状态机可以将待实现的复杂功能分解在各个状态中,使得整个系统的控制流程的实现变得简单化。
状态机有四个要素:现态、次态、输入、输出;
现态:状态机当前所处的状态;
次态:根据现态、输入、状态转移条件所得到的状态机将要跳转至的新状态;
输入:一般指外部事件,当一个外部事件发生后,状态机便会根据状态转移函数发生响应;
输出:由现态或现态和输入共同决定的,但是有时某一状态或者某一输入改变,输出并不一定会发生变化,带来的仅仅是状态迁移而已。
摩尔型状态机:
米勒型状态机:
由于摩尔型状态机的输出来自组合逻辑,可能会产生毛刺,从而产生不利的影响;
而米勒型状态机由于输入与输出有关,且输出也是组合逻辑,输出时序不佳,限制了系统的工作频率;
更好的方法时把摩尔型状态机和米勒型状态机混合起来使用,也即输出既和状态有关又与输入有关,采用时序逻辑输出。
寄存器型输出:具有更好的时序性能。
注意:一个健壮的状态机应当具备初始化状态或默认状态;即芯片上电或复位之后,状态机能够自动将所有判断条件复位,并进入初始状态。
大多数FPGA都有GSR(全局复位信号),当FPGA上电后,GSR有效,对所有寄存器、RAM等单元复位/置位。
状态机描述方法:一段式、两段式、三段式
一段式状态机:所有的状态变化以及输出变化都写在一个always块中在该always块中既描述状态的同步转移,又描述状态的输入条件和输出。
两段式状态机:用两个always块来描述状态机。其中一个模块采用同步时序逻辑描述状态转移,另一个模块采用组合逻辑判断状态转移条件。它需要两个状态——现态和次态,然后通过现态和次态的转换来实现时序逻辑。输出采用组合逻辑。
三段式状态机:用三个always块来描述状态机。其中一个模块采用同步时序逻辑描述状态转移,另一个模块采用组合逻辑判断状态转移条件(注意和两段式的区别)。第三个模块描述状态的输出(既可以用组合逻辑也可以用时序逻辑)。
可见,三段式描述法是最简单的。
状态机编码方法:独热码编码(只有一位是高电平),自然二进制编码,格雷码编码。
FPGA提供的触发器资源比组合逻辑资源多,所以使用独热码编码较好,节省组合逻辑资源,简化了比较逻辑,状态译码方便。
总结:
推荐使用独热码编码方式与三段式描述方式设计状态机。
1.三段always块中,第一个和第三个(多个)always块都是同步时序逻辑,用非阻塞(<=)赋值;第二个always块是组合逻辑,用阻塞赋值(=) ;
2.第二部分为组合逻辑always块,建议敏感列表使用always@(*)的方式;
3.第二部分组合逻辑always块中的判断条件一定要包含所有情况;
4.第二部分组合逻辑always块中的case中的条件应该为当前状态-state c;
5.状态转移的条件可以在第二段组合逻辑always块之外单独设计。
基于状态机的概念,设计下面一个状态图:
此状态图是要在一段序列101101101101中检测出1101的序列,可重复检测3次1101。
设计随机输入一段序列,这个序列我们可以用按键去传达,比如这里,由于序列是二进制序列只有0个1组成,因此设计两位按键用高位和低位分别表示是否输出1/0,高位表示是否输出1,低位表示是否输出0。输出dout则规定4位,检测是否有1101的序列输出。
- module detect (
- input clk ,
- input rst_n ,
- input [1:0] key_in ,
- output reg [3:0] dout
- );
- //参数定义
- parameter S1 = 4'b0001 ,//检测‘1’
- S2 = 4'b0010 ,//检测‘1’
- S3 = 4'b0100 ,//检测‘0’
- S4 = 4'b1000 ;//检测‘1’
-
- //状态机信号
- reg [3:0] state_c ;
- reg [3:0] state_n ;
-
- wire s12s2 ;
- wire s22s3 ;
- wire s22s1 ;
- wire s32s4 ;
- wire s42s2 ;
- wire s42s1 ;
-
- //第一段 时序逻辑 状态的转移
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- state_c <= S1 ;
- end
- else begin
- state_c <= state_n ;
- end
- end
-
- //第二段 组合逻辑 描述状态转移的规律
- always @(*) begin
- case (state_c)
- S1:begin
- if(s12s2)
- state_n = S2 ;
- else
- state_n = state_c ;
- end
- S2:begin
- if(s22s3)
- state_n = S3 ;
- else if(s22s1)
- state_n = S1 ;
- else
- state_n = state_c ;
- end
- S3:begin
- if(s32s4)
- state_n = S4 ;
- else
- state_n = state_c ;
- end
- S4: begin
- if(s42s2)
- state_n = S2 ;
- else if(s42s1)
- state_n = S1 ;
- else
- state_n = state_c ;
- end
- default:state_n = S1 ;
- endcase
- end
-
- //状态之间的条件(状态之间有多少箭头就有多少条件)
- assign s12s2 = state_c == S1 && (key_in[1]);//1
- assign s22s3 = state_c == S2 && (key_in[1]);//1-1
- assign s22s1 = state_c == S2 && (key_in[0]);//1-0
- assign s32s4 = state_c == S3 && (key_in[0]);//1-1-0
- assign s42s2 = state_c == S4 && (key_in[1]);//1-1-0-1
- assign s42s1 = state_c == S4 && (key_in[0]);//1-1-0-0
-
- //dout
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- dout <= 'd0;
- end
- else if (s42s2) begin
- dout <= dout + 1'b1; //输出需要的结果1101
- end
- end
-
- endmodule

- `timescale 1ns/1ps
- module detect_tb();
- reg clk ;
- reg rst_n ;
- reg [1:0] key ;//两个按键0/1表示的是这两个按键是否按下,如0/1表示低位按键按下而高位按键没按下
-
- wire [3:0] dout ;
-
- //参数
- parameter CYCLE = 20 ;//50M时钟
-
-
- //模块例化
- detect u_detect(
- .clk (clk ),
- .rst_n (rst_n ),
- .key_in (key ),
- .dout (dout)
- );
-
- //激励
- initial begin //产生时钟
- clk = 1'b1 ;
- forever begin
- #(CYCLE/2);
- clk = ~clk ;
- end
- end
-
- integer i;
- initial begin
- rst_n = 1'b1;
- #(CYCLE);
- rst_n = 1'b0;
-
- key = 2'b00;
- #(CYCLE*2);
- #2;
- rst_n = 1'b1;
- #(CYCLE*4)
-
- for (i = 0 ;i < 10 ; i = i + 1 ) begin
- key[0] = {$random}%2 ;
- #(CYCLE);
- key = 2'b00;
- #(CYCLE);
- key[1] = {$random}%2 ;
- #(CYCLE);
- key = 2'b00;
- #(CYCLE*2);
- end
-
- key = 2'b00;
-
- #(CYCLE*10);
- $stop;
- end
- endmodule

仿真结果显示,此段随机序列没有一个符合要求的1101序列。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。