[转载].怎样在有限状态机中延时
原文地址 http://www.cnblogs.com/yuphone/archive/2010/05/12/1733867.html#
感谢亚锋前辈的精彩文章.
本文借一小例,来示范在FSM中如何延时。
案例
(1)fsm_with_delay_demo.v
001 |
module fsm_with_delay_demo( |
002 |
input CLOCK_50, |
003 |
input RST_N, |
004 |
|
005 |
output reg LED |
006 |
); |
007 |
008 |
//++++++++++++++++++++++++++++++++++++++ |
009 |
// 定时器1 开始 |
010 |
// 时长:1秒 |
011 |
//++++++++++++++++++++++++++++++++++++++ |
012 |
parameter TIMER1_VAL = 50_000_000; // 50M/50M = 1s |
013 |
014 |
reg timer1_enable; |
015 |
reg [25:0] timer1_cnt; |
016 |
wire timer1_done; |
017 |
018 |
always @( posedge CLOCK_50) |
019 |
if (~timer1_enable) |
020 |
timer1_cnt <= 0; |
021 |
else if (~timer1_done) |
022 |
timer1_cnt <= timer1_cnt + 1'b1 ; |
023 |
|
024 |
assign timer1_done = (timer1_cnt == TIMER1_VAL - 1); |
025 |
//-------------------------------------- |
026 |
// 定时器1 结束 |
027 |
//-------------------------------------- |
028 |
029 |
030 |
//++++++++++++++++++++++++++++++++++++++ |
031 |
// 定时器2 开始 |
032 |
// 时长:4秒 |
033 |
//++++++++++++++++++++++++++++++++++++++ |
034 |
parameter TIMER2_VAL = 200_000_000; // 200M/50M = 4s |
035 |
036 |
reg timer2_enable; |
037 |
reg [27:0] timer2_cnt; |
038 |
wire timer2_done; |
039 |
040 |
always @( posedge CLOCK_50) |
041 |
if (~timer2_enable) |
042 |
timer2_cnt <= 0; |
043 |
else if (~timer2_done) |
044 |
timer2_cnt <= timer2_cnt + 1'b1 ; |
045 |
|
046 |
assign timer2_done = (timer2_cnt == TIMER2_VAL - 1); |
047 |
//-------------------------------------- |
048 |
// 定时器2 结束 |
049 |
//-------------------------------------- |
050 |
051 |
052 |
//++++++++++++++++++++++++++++++++++++++ |
053 |
// 状态机部分 开始 |
054 |
//++++++++++++++++++++++++++++++++++++++ |
055 |
parameter S0 = 0; |
056 |
parameter S1 = 1; |
057 |
058 |
059 |
reg [5:0] current_state, next_state; // 现态、次态 |
060 |
061 |
always @ ( posedge CLOCK_50, negedge RST_N) |
062 |
begin :fsm_always_block1 |
063 |
if (!RST_N) |
064 |
current_state <= S1; |
065 |
else |
066 |
current_state <= next_state; |
067 |
end |
068 |
069 |
// 根据条件转移状态,并给出相应逻辑 |
070 |
always @ * |
071 |
begin :fsm_always_block2 |
072 |
/* 用户变量初始化 开始*/ |
073 |
LED = 1'b0 ; |
074 |
/* 用户变量初始化 结束*/ |
075 |
|
076 |
/* 清定时器使能 开始 */ |
077 |
timer1_enable = 1'b0 ; |
078 |
timer2_enable = 1'b0 ; |
079 |
/* 清定时器使能 结束 */ |
080 |
case (current_state) |
081 |
S0 : |
082 |
begin |
083 |
timer1_enable = 1; // 使能定时器1 |
084 |
|
085 |
/* 用户代码 开始*/ |
086 |
LED = 1'b0 ; |
087 |
/* 用户代码 结束*/ |
088 |
|
089 |
if (timer1_done /* && 用户条件 */ ) |
090 |
next_state = S1; |
091 |
else |
092 |
next_state = S0; |
093 |
end |
094 |
S1 : |
095 |
begin |
096 |
timer2_enable = 1; // 使能定时器1 |
097 |
|
098 |
/* 用户代码 开始*/ |
099 |
LED = 1'b1 ; |
100 |
/* 用户代码 结束*/ |
101 |
|
102 |
if (timer2_done /* && 用户条件 */ ) |
103 |
next_state = S0; |
104 |
else |
105 |
next_state = S1; |
106 |
end |
107 |
endcase |
108 |
end |
109 |
//-------------------------------------- |
110 |
// 状态机部分 结束 |
111 |
//-------------------------------------- |
112 |
113 |
endmodule |
(2)QII综合的FSM
图1 QII综合的FSM
(3)实验现象
该例中,FSM有两个状态,S0持续1s,S1持续4s,然后循环交替。
假设案例中的LED是送1亮,送0灭,那么LED灭1秒亮4秒,如此循环交替。
分析
众所周知,FSM有三种写法:1个always块;两个always块;三个always块。其中1个always块的fsm,由于各种弊端,为大家所不齿。然而加入定时器这一动作在3个always块又不能很好地实现,于是退而求其次,使用2个always块的fsm。
这样一来,我们只需在第一个always块内实现现次态交替转变即可。在第二个always块内,根据状态迁移,来写各状态的逻辑。由于定时器的加入,而其使能又是reg变量,所以在初始化时,必须将使能清零,以免无法实现定时效果。在各状态中,如果只需持续一个clock,则无需使能定时器。倘若需要持续一段时间,使能定时器即可,然后等定时完毕,再转移状态;未定时完毕,则保持当前状态。至于其他的用户条件,可与定时完毕标志逻辑与在一起,以实现所需功能。