このサンプルプログラムは,いくつかのLEDを環状に配置し,それらをスイッチで止めるものです.外観を下に示します.14個のLEDが並んでおり,スイッチを押すとひとつだけLEDが点灯し,そのLEDが矢印方向に進んでいきます.その後,スイッチを離すとLEDがゆっくりと進むようになり,およそ3秒後に完全に停止する前に3回ほど点滅します.
このように,内部の状態を遷移するような回路の場合,状態マシン図などを使うと分かりやすくなります.下の図では状態変化の様子を示しています.
回路でこれを実現するにはregとして状態を管理することになります.今回は状態が下表のようになりますので全部で4種類となります.従いまして2ビットのregがあればよいわけです.この状態をMODEという名前でVerilogHDLでは取り扱うこととします.
状態 | MODE |
停止状態 | 2'b00 |
高速移動状態 | 2'b01 |
低速移動状態 | 2'b10 |
点滅状態 | 2'b11 |
ルーレットを動作させるためのコードを下に示します.MODEが変化していく様子を確認してください.
`timescale 1ns / 1ps module roulette(M_CLK, SW, LED); input M_CLK; // マスタークロック(4[MHz]) input SW; // スイッチ output [13:0] LED; // ルーレット用LED wire DIVIDED_CLK; // 分周されたクロック(およそ30[Hz]) wire CLK_FOR_STOPPING; // 停止用クロック(およそ2[Hz] wire CLK_FOR_BLINKING; // 点滅用カウンタ(およそ7.5[Hz]) wire [3:0]LED_LOCATE; // LEDの位置 wire CLK; // ルーレット回路を動かすためのクロック reg [2:0]COUNT_FOR_STOPPING = 3'b111; // 停止用カウンタ reg [2:0]COUNT_FOR_BLINKING = 3'b101; // 点滅用カウンタ reg STOP = 1'b1; // LEDの移動を停止するとき1,移動しているとき0 reg [1:0]MODE = 2'b00; //停止状態 Divider17 d(.SRC_CLK(M_CLK), .DEST_CLK(DIVIDED_CLK)); // 2^17分周器 Divider4 d2(.SRC_CLK(DIVIDED_CLK), .DEST_CLK(CLK_FOR_STOPPING)); // 2^4分周器 Divider2 d3(.SRC_CLK(DIVIDED_CLK), .DEST_CLK(CLK_FOR_BLINKING)); // 2^2分周器 always @(posedge CLK) // クロックが立ち上がるとき begin if(SW == 2'b0) // スイッチが押されている begin MODE <= 3'b001;// 高速移動状態へ STOP <= 1'b0;// LEDを移動させる end else if(SW == 2'b1) // スイッチが離されている begin case(MODE) 2'b01:// 高速移動状態のとき begin COUNT_FOR_STOPPING <= 3'b111; //低速移動カウンタを初期化する MODE <= 2'b10; // 低速移動状態へ end 2'b10: // 低速移動状態のとき begin if(COUNT_FOR_STOPPING == 3'b000) //低速移動カウンタがアンダーフローしたら begin COUNT_FOR_STOPPING <= 3'b111; // 停止用カウンタを初期化する STOP <= 1'b1; // LEDを停止させる MODE <= 2'b11; // 点滅状態へ end else begin /* 停止用カウンタをデクリメントする */ COUNT_FOR_STOPPING <= COUNT_FOR_STOPPING-3'b001; end end 2'b11: // 点滅状態のとき begin if(COUNT_FOR_BLINKING == 3'b000) // 点滅用カウンタがアンダーフローしたら begin COUNT_FOR_BLINKING <= 3'b101; // 点滅用カウンタを初期化する MODE <= 2'b00; // 停止状態へ end else begin /* 点滅用カウンタをデクリメントする */ COUNT_FOR_BLINKING <= COUNT_FOR_BLINKING-3'b001; end end endcase end end /* クロックのセレクタ */ function ClockSelector; input [1:0]MODE; begin /* 状態により変える */ case(MODE) 2'b10:ClockSelector=CLK_FOR_STOPPING;//低速移動状態のとき,停止用クロックを選択する 2'b11:ClockSelector=CLK_FOR_BLINKING;//点滅状態のとき,点滅用クロックを選択する default:ClockSelector=DIVIDED_CLK; // 上記以外のとき,分周クロックを選択する endcase end endfunction assign CLK = ClockSelector(MODE); // クロックセレクタにより,クロックを得る /* カウンタを得る */ counter COUNT(.CLK(CLK), .STOP(STOP), .BLINKING(COUNT_FOR_BLINKING[0]), .LED_LOCATE(LED_LOCATE)); /* デコードする */ decoder DECODER(.LED_LOCATE(LED_LOCATE), .LED(LED)); endmodule