このサンプルプログラムは,いくつかのLEDを環状に配置し,それらをスイッチで止めるものです.外観を下に示します.14個のLEDが並んでおり,スイッチを押すとひとつだけLEDが点灯し,そのLEDが矢印方向に進んでいきます.その後,スイッチを離すとLEDがゆっくりと進むようになり,およそ3秒後に完全に停止する前に3回ほど点滅します.

roulette2

 

このように,内部の状態を遷移するような回路の場合,状態マシン図などを使うと分かりやすくなります.下の図では状態変化の様子を示しています.

RouletteStateMachineDiagram

回路でこれを実現するには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