FPGAでLED点滅を作る:ステップバイステップチュートリアル
ステップバイステップガイド:初めてのFPGA LED点滅設計
パート1:VHDLまたはVerilogでの設計
このチュートリアルでは、ユーザー定義の周波数でLEDを駆動するVHDLおよびVerilogコードの作り方を学びます。ワークフローに合った言語を選んでください。
HDLを書く際は、設計が意図通りに振る舞うか検証する必要があります。間違いは避けられないため、シミュレーションは不可欠です。本チュートリアルは以下の2つの重要なフェーズで構成されます:
- HDL設計
- HDLシミュレーション
シミュレーションを省くと、実機デバッグで多大なコストがかかります。シミュレーションを必須のチェックポイントとして扱いましょう。
プロジェクト要件
100Hz、50Hz、10Hz、1Hzのいずれかで、デューティサイクル50%でLEDを点滅させるHDLを作成します。2つのスイッチで周波数を選択し、追加のLED_ENスイッチがHighのときのみLEDを有効化します。FPGAは25MHzの発振器で動作します。
周波数セレクタの真理値表:
| Enable | Switch 1 | Switch 2 | LED駆動周波数 |
|---|---|---|---|
| 0 | – | – | 無効 |
| 1 | 0 | 0 | 100 Hz |
| 1 | 0 | 1 | 50 Hz |
| 1 | 1 | 0 | 10 Hz |
| 1 | 1 | 1 | 1 Hz |
信号まとめ:
| 信号名 | 方向 | 説明 |
|---|---|---|
| i_clock | Input | 25 MHzクロック |
| i_enable | Input | イネーブルスイッチ(ロジック0=LEDオフ) |
| i_switch_1 | Input | 周波数選択スイッチ1 |
| i_switch_2 | Input | 周波数選択スイッチ2 |
| o_led_drive | Output | LED駆動信号 |
4つの並行カウンタプロセスが25MHzクロックを監視し、各目標周波数用のトグル信号を生成します。特定の周波数が選択されていないときも、そのカウンタは動き続けます。これはハードウェア並行性の核心原理です。
スイッチが多重化器(マルチプレクサ)を形成し、選択されたトグルをLED出力にルーティングします。マルチプレクサは純粋な組み合わせ論理なので、クロックなしで動作します。
以下はアーキテクチャを示すブロック図です:

VHDL実装
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tutorial_led_blink is
port (
i_clock : in std_logic;
i_enable : in std_logic;
i_switch_1 : in std_logic;
i_switch_2 : in std_logic;
o_led_drive : out std_logic
);
end tutorial_led_blink;
architecture rtl of tutorial_led_blink is
-- 周波数生成用定数
-- 式: (25 MHz / 目標Hz * 50% duty)
-- 100 Hz: 25,000,000 / 100 * 0.5 = 125,000
constant c_CNT_100HZ : natural := 125000;
constant c_CNT_50HZ : natural := 250000;
constant c_CNT_10HZ : natural := 1250000;
constant c_CNT_1HZ : natural := 12500000;
-- カウンタ信号
signal r_CNT_100HZ : natural range 0 to c_CNT_100HZ;
signal r_CNT_50HZ : natural range 0 to c_CNT_50HZ;
signal r_CNT_10HZ : natural range 0 to c_CNT_10HZ;
signal r_CNT_1HZ : natural range 0 to c_CNT_1HZ;
-- トグル信号
signal r_TOGGLE_100HZ : std_logic := '0';
signal r_TOGGLE_50HZ : std_logic := '0';
signal r_TOGGLE_10HZ : std_logic := '0';
signal r_TOGGLE_1HZ : std_logic := '0';
-- 1ビット選択線
signal w_LED_SELECT : std_logic;
begin
-- 各プロセスが異なる周波数で特定信号をトグル
-- スイッチがその出力を選んでいなくても常に動作
p_100_HZ : process (i_clock) is
begin
if rising_edge(i_clock) then
if r_CNT_100HZ = c_CNT_100HZ-1 then -- -1はカウンタが0から始まるため
r_TOGGLE_100HZ <= not r_TOGGLE_100HZ;
r_CNT_100HZ <= 0;
else
r_CNT_100HZ <= r_CNT_100HZ + 1;
end if;
end if;
end process p_100_HZ;
p_50_HZ : process (i_clock) is
begin
if rising_edge(i_clock) then
if r_CNT_50HZ = c_CNT_50HZ-1 then
r_TOGGLE_50HZ <= not r_TOGGLE_50HZ;
r_CNT_50HZ <= 0;
else
r_CNT_50HZ <= r_CNT_50HZ + 1;
end if;
end if;
end process p_50_HZ;
p_10_HZ : process (i_clock) is
begin
if rising_edge(i_clock) then
if r_CNT_10HZ = c_CNT_10HZ-1 then
r_TOGGLE_10HZ <= not r_TOGGLE_10HZ;
r_CNT_10HZ <= 0;
else
r_CNT_10HZ <= r_CNT_10HZ + 1;
end if;
end if;
end process p_10_HZ;
p_1_HZ : process (i_clock) is
begin
if rising_edge(i_clock) then
if r_CNT_1HZ = c_CNT_1HZ-1 then
r_TOGGLE_1HZ <= not r_TOGGLE_1HZ;
r_CNT_1HZ <= 0;
else
r_CNT_1HZ <= r_CNT_1HZ + 1;
end if;
end if;
end process p_1_HZ;
-- スイッチ入力に基づく多重化器
w_LED_SELECT <= r_TOGGLE_100HZ when (i_switch_1 = '0' and i_switch_2 = '0') else
r_TOGGLE_50HZ when (i_switch_1 = '0' and i_switch_2 = '1') else
r_TOGGLE_10HZ when (i_switch_1 = '1' and i_switch_2 = '0') else
r_TOGGLE_1HZ;
-- i_enableがHighのときのみo_led_driveを駆動(ANDゲート)
o_led_drive <= w_LED_SELECT and i_enable;
end rtl;
Verilog実装
module tutorial_led_blink (
i_clock,
i_enable,
i_switch_1,
i_switch_2,
o_led_drive
);
input i_clock;
input i_enable;
input i_switch_1;
input i_switch_2;
output o_led_drive;
// 定数(パラメータ)で周波数生成
// 入力クロックは25 kHz(任意に選択)
// 式: (25 kHz / 100 Hz * 50% duty)
// 100 Hz: 25,000 / 100 * 0.5 = 125
parameter c_CNT_100HZ = 125;
parameter c_CNT_50HZ = 250;
parameter c_CNT_10HZ = 1250;
parameter c_CNT_1HZ = 12500;
// カウンタ信号
reg [31:0] r_CNT_100HZ = 0;
reg [31:0] r_CNT_50HZ = 0;
reg [31:0] r_CNT_10HZ = 0;
reg [31:0] r_CNT_1HZ = 0;
// トグル信号
reg r_TOGGLE_100HZ = 1'b0;
reg r_TOGGLE_50HZ = 1'b0;
reg r_TOGGLE_10HZ = 1'b0;
reg r_TOGGLE_1HZ = 1'b0;
// 1ビット選択
reg r_LED_SELECT;
wire w_LED_SELECT;
begin
// 各alwaysブロックが異なる周波数で特定信号をトグル
// スイッチがその出力を選んでいなくても常に動作
always @ (posedge i_clock) begin
if (r_CNT_100HZ == c_CNT_100HZ-1) // -1はカウンタが0から始まるため
begin
r_TOGGLE_100HZ <= !r_TOGGLE_100HZ;
r_CNT_100HZ <= 0;
end else
r_CNT_100HZ <= r_CNT_100HZ + 1;
end
always @ (posedge i_clock) begin
if (r_CNT_50HZ == c_CNT_50HZ-1)
begin
r_TOGGLE_50HZ <= !r_TOGGLE_50HZ;
r_CNT_50HZ <= 0;
end else
r_CNT_50HZ <= r_CNT_50HZ + 1;
end
always @ (posedge i_clock) begin
if (r_CNT_10HZ == c_CNT_10HZ-1)
begin
r_TOGGLE_10HZ <= !r_TOGGLE_10HZ;
r_CNT_10HZ <= 0;
end else
r_CNT_10HZ <= r_CNT_10HZ + 1;
end
always @ (posedge i_clock) begin
if (r_CNT_1HZ == c_CNT_1HZ-1)
begin
r_TOGGLE_1HZ <= !r_TOGGLE_1HZ;
r_CNT_1HZ <= 0;
end else
r_CNT_1HZ <= r_CNT_1HZ + 1;
end
// スイッチ入力に基づく多重化器
always @ (*) begin
case ({i_switch_1, i_switch_2}) // 連結演算子
2'b11 : r_LED_SELECT <= r_TOGGLE_1HZ;
2'b10 : r_LED_SELECT <= r_TOGGLE_10HZ;
2'b01 : r_LED_SELECT <= r_TOGGLE_50HZ;
2'b00 : r_LED_SELECT <= r_TOGGLE_100HZ;
endcase
end
assign o_led_drive = r_LED_SELECT & i_enable;
// 代替の多重化器設計(上記と同等):
// よりコンパクトだが、Verilog初心者には読みにくい
// assign w_LED_SELECT = i_switch_1 ? (i_switch_2 ? r_TOGGLE_1HZ : r_TOGGLE_10HZ) :
// (i_switch_2 ? r_TOGGLE_50HZ : r_TOGGLE_100HZ);
// assign o_led_drive = w_LED_SELECT & i_enable;
end
endmodule
次のステップ:この設計をVHDLまたはVerilogでシミュレーションし、配備前に正しい振る舞いを確認してください。
VHDL
- VHDLの条件分岐をマスター:If-Then-Elsif-Else の完全解説
- VHDLで乱数生成: 一様分布からOSVVMまで
- 上級VHDLクイズ パート2:HDL専門知識をテスト
- OSVVMで制約付きランダム検証をマスターする
- Modelsim入門:FPGA・ASICシミュレーションのための初心者ガイド
- VUnit 101:自動化VHDL検証の実践ガイド
- VHDLテストベンチでTEXTIOを使ってBMPビットマップ画像を読み込む
- Tclベースのインタラクティブテストベンチでハードウェア検証を強化
- プロセス内プロシージャでVHDLステートマシンをクリーンに設計
- 上級VHDL習熟度クイズ パート3
- VHDLのWait-For文で実行を遅延させる