チュートリアル:初めての FPGA プログラム:LED ブリンカー
パート 1:VHDL または Verilog の設計
このチュートリアルでは、指定された周波数で LED を点滅させる VHDL および Verilog コードの構造を示します。 VHDL と Verilog の両方が表示され、どちらを先に学習するかを選択できます。 FPGA 設計者は、設計コードが記述されるたびに、それが意図したとおりに機能することを確認する必要があります。最善を尽くしても、最初の設計には必ず間違いがあります。これらの間違いを見つける最善の方法は、シミュレーション環境で行うことです。このチュートリアルは 2 つの段階に分かれています:
<オール>これらのステップは両方とも、FPGA 開発を成功させるために不可欠です。時間に追われている FPGA 設計者は、コードのシミュレーションであるステップ 2 を省略しようとすることがあります。ただし、これは非常に重要なステップです。適切なシミュレーションがなければ、コードをハードウェア上でデバッグせざるを得なくなり、非常に困難で時間のかかる作業になる可能性があります。
プロジェクトの要件:
100 Hz、50 Hz、10 Hz、または 1 Hz の指定された周波数で LED を点滅させる HDL コードを設計します。点滅周波数ごとに、LED は 50% のデューティ サイクルに設定されます (半分の時間点灯します)。 LED 周波数は、FPGA への入力である 2 つのスイッチを介して選択されます。 LED をオンにするために「1」にする必要がある LED_EN と呼ばれる追加のスイッチがあります。 FPGA は 25 MHz 発振器によって駆動されます。
まず、周波数セレクターの真理値表を作成しましょう:
これが正しく機能するためには、4 つの入力と 1 つの出力があります。信号は次のようになります:
この設計では、同時に実行される 4 つのカウンター プロセスがあります。これは、すべてがまったく同時に実行されていることを意味します。彼らの仕事は、異なる周波数ごとに見られるクロック パルスの数を追跡することです。スイッチが特定の周波数を選択していなくても、カウンターは実行されています。これは、ハードウェア設計と並行性の美しさです。すべてが常に実行されます!これを最初に理解するのは難しいかもしれませんが、マスターする必要があるコアコンセプトです。
スイッチは、使用する出力を選択するためだけに機能します。それらは、マルチプレクサと呼ばれるものを作成します。マルチプレクサまたは略してマルチプレクサは、多数の入力の 1 つを選択して伝播または出力に渡すセレクタです。これはロジックの組み合わせであり、動作にクロックを必要としません。以下は、設計のブロック図です。この設計をどのように実装するかについて、時間をかけて考えてください。自分でコードを書いてみてください。私が選択した方法を以下に示します。
ブロック図 - LED 点滅プログラムデザインの VHDL コード、tutorial_led_blink.vhd:
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 -- Constants to create the frequencies needed: -- Formula is: (25 MHz / 100 Hz * 50% duty cycle) -- So for 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; -- These signals will be the counters: 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; -- These signals will toggle at the frequencies needed: 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'; -- One bit select wire. signal w_LED_SELECT : std_logic; begin -- All processes toggle a specific signal at a different frequency. -- They all run continuously even if the switches are -- not selecting their particular output. p_100_HZ : process (i_clock) is begin if rising_edge(i_clock) then if r_CNT_100HZ = c_CNT_100HZ-1 then -- -1, since counter starts at 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 -- -1, since counter starts at 0 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 -- -1, since counter starts at 0 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 -- -1, since counter starts at 0 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; -- Create a multiplexor based on switch inputs 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; -- Only allow o_led_drive to drive when i_enable is high (and gate). o_led_drive <= w_LED_SELECT and i_enable; end rtl;
デザインの Verilog コード、tutorial_led_blink.v:
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; // Constants (parameters) to create the frequencies needed: // Input clock is 25 kHz, chosen arbitrarily. // Formula is: (25 kHz / 100 Hz * 50% duty cycle) // So for 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; // These signals will be the counters: 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; // These signals will toggle at the frequencies needed: reg r_TOGGLE_100HZ = 1'b0; reg r_TOGGLE_50HZ = 1'b0; reg r_TOGGLE_10HZ = 1'b0; reg r_TOGGLE_1HZ = 1'b0; // One bit select reg r_LED_SELECT; wire w_LED_SELECT; begin // All always blocks toggle a specific signal at a different frequency. // They all run continuously even if the switches are // not selecting their particular output. always @ (posedge i_clock) begin if (r_CNT_100HZ == c_CNT_100HZ-1) // -1, since counter starts at 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) // -1, since counter starts at 0 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) // -1, since counter starts at 0 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) // -1, since counter starts at 0 begin r_TOGGLE_1HZ <= !r_TOGGLE_1HZ; r_CNT_1HZ <= 0; end else r_CNT_1HZ <= r_CNT_1HZ + 1; end // Create a multiplexer based on switch inputs always @ (*) begin case ({i_switch_1, i_switch_2}) // Concatenation Operator { } 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; // Alternative way to design multiplexer (same as above): // More compact, but harder to read, especially to those new to 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