月曜日, 8月 20, 2007

ステートマシン その4

前回の続き。

私がステートマシンを記述する場合、大きく分けて2通りの方法があります。

1つは出力レジスタが無い記述で、もう一つは出力レジスタ付きの記述です。

どのような感じか、ちょっと書いてみると

出力レジスタ無しの場合は、

always @(posedge clock or posedge reset) begin
if (reset)
state <= INITIAL_STATE;
else
state <= next_state;
end

always @* begin
next_state = state;
case(state)

INITIAL_STATE: begin
output_signal = 1'b0;
if (input_signal==1'b1) begin
output_signal = 1'b1;
next_state = SECOND_STATE;
end
end

SECOND_STATE: begin
output_signal = 1'b1;
if (input_signal==1'b0)
next_state = LAST_STATE;
end

LAST_STATE: begin
output_signal = 1'b1;
next_state = INITIAL_STATE;
end

default: begin // Recovery from illegal state
next_state = INITIAL_STATE;
output_signal = 1'b0;
end

endcase
end


出力レジスタ付きの場合は、

always @(posedge clock or posedge reset) begin
if (reset) begin
state <= INITIAL_STATE;
output_signal <= 1'b0;
end
else begin
case(state)

INITIAL_STATE: begin
output_signal <= 1'b0;
if (input_signal==1'b1) begin
output_signal <= 1'b1;
state <= SECOND_STATE;
end
end

SECOND_STATE: begin
output_signal <= 1'b1;
if (input_signal==1'b0)
state <= LAST_STATE;
end

LAST_STATE: begin
output_signal <= 1'b1;
state <= INITIAL_STATE;
end

default: begin // Recovery from illegal state
state <= INITIAL_STATE;
output_signal <= 1'b0;
end

endcase // case(state)
end
end


注意すべき点としては、出力レジスタがある場合と無い場合で出力のタイミングが若干異なるところです。
上記の例で言えば、INITIAL_STATEからSECOND_STATEに移る際のoutput_signalの変化点です。
出力レジスタが無い場合はINITIAL_STATEの最後の1サイクルはoutput_signal=1'b1ですが、出力レジスタ有りの場合は、INITIAL_STATEの最後の1サイクルはoutput_signla=1'b0で、SECOND_STATEの1サイクル目にはじめてoutput_signal=1'b1となります。

また、出力レジスタ無しの場合では出力のハザードにも注意が必要です。
上記の例ではSECOND_STATE,LAST_STATEともに出力output_signal=1'b1なのでコードからはその間のステートの遷移中に出力に変化はないように見えますが、例えば各ステートが次のようにエンコードされていた場合、

INITIAL_STATE=2'b00, SECOND_STATE=2'b01, LAST_STATE=2'b10

実際はSECOND_STATEからLAST_STATEに移る際の信号の変化の過程で

2'b10 -> 2'b00 -> 2'b10

となって、一瞬の間SECOND_STATEでもLAST_STATEでもない状態になり、その瞬間の出力がヒゲのように出てしまいます。

出力レジスタ付きの場合はこのような心配はありません。

今回は出力レジスタ付きの記述でステートマシンを書いてみました。
主要な部分を抜き出すと、次のようになります。

/*
* State Machine
*/
always @(posedge clock or posedge reset) begin
if (reset) begin
state <= ST_DoorClose;
doorControl <= DOOR_STOP;
end
else begin
case(state)

ST_DoorClose: begin
doorControl <= DOOR_STOP;
if (buttonOpen==PUSHED)
state <= ST_Opening;
end

ST_Opening: begin
doorControl <= DOOR_OPEN;
if (buttonClose==PUSHED)
state <= ST_Closing;
else if (doorPosition==DOOR_POS_MAX)
state <= ST_DoorOpen;
end

ST_DoorOpen: begin
doorControl <= DOOR_STOP;
if ((buttonClose==PUSHED)||
(openCounter==TIME_LIMIT))
state <= ST_Closing;
end

ST_Closing: begin
doorControl <= DOOR_CLOSE;
if (buttonOpen==PUSHED)
state <= ST_Opening;
else if (doorPosition==DOOR_POS_MIN)
state <= ST_DoorClose;
end

default: begin
doorControl <= DOOR_STOP;
end

endcase
end
end

StateMachineSample.v←完全なコードはこちら

前回の最後の状態遷移図からこのコードに落とすのはそれほど難しくないですよね。

ちなみにこのコードではクロック入力を1KHzと想定しています。
現在、実際の設計でそんなに遅いクロックを使うことはまず無いと思いますが
練習用として大目に見てください。

次回はこのコード用にテストベンチを書いて、シミュレーションをやってみたいと思います。

0 件のコメント: