ロバストなマイクロコントローラとFPGAのSPIインターフェースの実装:パート1 - FPGAの課題

APDahlen Applications Engineer

この連載記事では、マイクロコントローラ(uC)とフィールドプログラマブルゲートアレイ(FPGA)のシリアルペリフェラルインターフェース(SPI)の関係について説明します。主な目的は、FPGAの制御を容易にすることです。ここでは、Digilent BASYS-3(Xilinx Atrix-7)開発ボードで開発されたVerilog実装と、Arduino Nano Everyで開発された対応するマイクロコントローラコードについて説明します。これらはともに、テスト済みのSPIシリアルクロック16MHzで動作する、16ビットの巡回冗長検査(CRC)を備えたパケット化されたデータ転送を提供します。Verilogの実装は、Xilinx固有の依存関係を避ける必要があるため、最小限の変更で他のプラットフォームに移植可能です。しかし、このクロスプラットフォームという野心的な規定はまだテストされていません。

図1: Digilent BASYS 3とArduino Nano Everyを使用したuCからFPGA SPIインターフェースのベンチテスト。 uCは、Basys 3ボード上のSSDと16個のLEDをマルチプレックスしています。

多くの人は、回路図入力ツールを使用して、ロジックプリミティブを使用した簡単な組み合わせ回路を開発するデジタルロジックの授業の一環としてFPGAを学びます。その後、多くの人は、VerilogまたはVHDLを導入したFPGA専用のクラスで学習を続けます。上級ロジックのクラスに進む人や、キャップストーンプロジェクトでFPGAを実装する人もいます。残念なことに、独立したモジュールを超えて、複数のモジュールをより大きなシステムに統合しようとする人はほとんどいません。これには次のような理由があります。

FPGAは素晴らしい技術ですが、制御が難しいという定評があります。マイクロコントローラ(uC)と比較すると、FPGAの複雑さは桁違いです。ハードウェア構造は、ゼロから構築するか、教科書やインターネットで見つけた例からインスタンス化する必要があります。

本連載は、FPGAを使ったシステム設計の一助となることを目的としています。スピードが要求されるタイムクリティカル、並列処理、決定論的な回路にFPGAの長所を利用することを提案します。そして、マイクロコントローラの長所である柔軟性、比較的容易なプログラミング、無線やクラウド機能を含む通信スタックを活用します。この状況を見る1つの方法は、FPGAを中程度の高速データバスで接続された強力なマイクロコントローラの周辺回路と考えることです。

対象者は、VerilogでFPGAをプログラミングでき、マイクロコントローラをプログラミングできる人またはチームです。チームでのアプローチによりFPGAとuCの同時作業が可能になるため、キャップストーンプロジェクトの学生に適しているかもしれません。

私たちはFPGAに焦点を当てているので、マイクロコントローラの内容を最小限に抑えることが推奨されます。Arduino Nano Everyが選ばれたのは、よく知られた一般的なマイクロコントローラであるためです。この記事の読者のほとんどは、マイクロコントローラとCプログラミングに精通しているでしょう。

単にVerilogやuCのコードを紹介するのではなく、設計プロセスに関連する理由や課題を探ります。その結果、教訓的な連載記事となりましたが、私はこの記事が有益であると信じています。

システム定義の例

システムとは、連携して動作するコンポーネントの集合体です。FPGAアプリケーションの場合、これにはデータ収集、フィルタリング、制御、ユーザーへの情報提示方法、またはより大規模な自動システムへの統合などのモジュールが含まれます。

この用語をより明確に定義するために、FPGAとuCをベースにしたチャレンジングな例を考えてみましょう。三相400Hz波形の有効電力と無効電力を測定するシステムを構築したいとします。設計要件は、実効電圧、実効電流、および信号間の位相差を正確に測定することです。さらに、20kHzまでのライン高調波を測定する必要があると仮定します。

最高の性能を保証するために、電圧と電流の測定が同時に行われると仮定して、6つの独立したアナログデジタルコンバータ(ADC)が必要であるとします。ナイキストのサンプリング定理から、少なくとも毎秒40,000サンプルの速度で測定することが要求されます。合計すると、毎秒24万サンプルが必要となります。これに加えて、システムは実効値と位相角の計算を実行しなければなりません。RMS計算メカニズムには、長期的な安定性を維持しながら過渡現象への高速応答を可能にする、短期および長期の積分を提示するフィルタリングを含める必要があります。加えて、高速フーリエ変換(FFT)により高調波を決定します。

このようなシステムを設計する方法はたくさんあります。ハイエンドのuCや、いくつかの連携したuCがそのタスクを実行できるでしょう。しかし、この記事の焦点はそこではありません。その代わりに、データ収集とフィルタの領域は、エントリーレベルのFPGAの能力の範囲内であることを認識することにしましょう。

並列構造を備えたFPGAベースのシステムでは、タスクを簡単に実行できます。個々のモジュールの設計はそれほど難しくありません。 実際、多くの人がすでに1つのADCをFPGAに組み込んでいます。本当の課題は、すべてのモジュールを結合して、必要なときに必要な場所にデータを移動させることです。

FPGAグルー

個人的に、私が遭遇したFPGA学習の最も困難な課題の1つは、uCプログラミングテクニックをなかなか頭から振り払うことができなかったことです。VerilogとVHDLはハードウェア記述言語ですが、C言語などはハードウェアの依存関係を排除するための抽象化を特徴とする手続き型言語です。FPGAのハードウェア記述に内在する並列性(すべてを一度に処理)を理解するには、必要以上に時間がかかりました。

冒頭の例では、6つのADCを表示する必要があります。それらを接続するために使用される様々な制御線とデータ線も表示する必要があります。続いて、中間結果を保持するレジスタ、乗算器、加算器がRMS計算の2乗と和演算を実行し、その他多数のステートマシンのハードウェアが各処理を調整します。

このFPGAベースのハードウェアをつなぎ合わせるものが、レジスタ転送レベル(RTL)設計手法です。レジスタ転送(RT)という用語は、多くの場合、レジスタ間に挿入された一連の組み合わせロジックを使用して、あるレジスタから別のレジスタにデータを転送するために使用される制御メカニズムを意味します。これは、マイクロプロセッサで使用されるパイプライン処理に相当します。たとえば、最初のクロックサイクルで、データが加算器に与えられます。2番目のクロックサイクルで加算器が演算を実行します。3番目のクロックサイクルで、データがメモリに転送されます。この例のコントローラは、RTプロセスの開始を担当するステートマシンです。ここでは、すべてのレジスタが単一のクロックドメイン内にあると仮定します。

重要なのでもう一度言います。

私たちのRTL設計では、ステートマシンまたはステートマシンの集合がレジスタからレジスタへのデータ転送を制御および調整を行います。すべての動作は同じクロックドメイン内で行われると想定されます。 この機会に、synchronizers to cross clock domainsの使用に関する関連記事をご覧ください。

各レジスタがメモリであることを思い出してください。この用語は、単一のDタイプフリップフロップからFPGAの大規模ブロックメモリの実装まで、あらゆるものに適用されます。単純な8ビットレジスタを使って、この概念を探ってみましょう。

以下に示す例には、タイミング規定を備えたすべてのRTL構造が含まれています。Q出力はレジスタです。@(posedge clk)ステートメントとVerilogのノンブロック<=演算子の使用から明らかなように、レジスタの更新はクロックの立ち上がりエッジと同期します。

module reg_8bit(
    input clk,
    input load,
    input wire [7:0] D, 
    output reg [7:0] Q 
);
always @(posedge clk) begin
    if (load) 
        Q <= D;      
end
endmodule

ロード信号の性質を考慮して下さい。ロード信号はクロックの立ち上がりエッジより前に安定していなければなりません。すべての信号が同じクロックドメインにあり、ロード信号自体がステートマシンの登録された出力によって駆動されると仮定すると、合成ツールはこの重要なタイミングの安定性が満たされるように最善の動作をします。

「ロード」コマンドラインはクロックの立ち上がりエッジでアサートされることに注意してください。Dに存在するデータは、クロックの次の立ち上がりエッジでQになります。これは新人プログラマにとっては罠であり、予期しない1クロック遅延を引き起こす可能性があります。このようなRTLアクションを追跡するには、状態と次の状態の観点から考えることが重要です。 最後に、ロード信号の幅 (周期) に注意してください。

同期RTLシステムでは、信号のオン時間はクロックの周期(立ち上がりエッジから立ち上がりエッジまで)以上になることがあります。これは、クロックの立ち上がりエッジでの関連するステートマシンの更新を理解することで説明されます。

プログラミングのヒント: この記事で説明したRTL設計の制約により、FPGAのパフォーマンスが制限され、FGPAファブリックが不必要に使用される可能性があります。ただし、すべての信号を@(posedge clk)規定に登録すると、一般にシステムの安定性が向上します。これは、後でニーズに合わせて変更できる優れた出発点です。

ストローブ付きレジスタ転送

前の例では、「ロード」信号のオン時間(幅)が変化する可能性があることを指摘しました。これは同期システムであるため、幅は常にクロックの関数です。最小オン時間はシステムクロックの1周期です。この短い信号には、ストローブ、ティック、パルスなど、いくつかの異なる名前があります。この一連の記事では、ストローブという用語を使用します。

先ほど、RTLを一連のレジスタを特徴とする設計方法論として定義しました。データはレジスタ間で転送され、すべてのレジスタは同じクロックドメイン内にあると想定されます。すべての論理演算がドメインのクロック周期内で安定する必要があることを理解した上で、データを変更するためにレジスタ間に組み合わせロジックが配置されます。たとえば、100MHzクロックの場合、次のクロックイベントの準備ができるように、すべてのFPGA信号が10nsで安定する必要があります。

RTLプロセスには、レジスタを制御するためのコントローラまたは連携したコントローラの集まりが必要です。レジスタを制御する1つの方法は、各コントローラがストローブ信号を生成して対象のレジスタを進めることです。

1つの単純なストローブベースのコントローラを以下に示します。このRTLは、100MHzクロックの場合、20kHzレートでストローブを生成します。それはmod-5000カウンタです。 これは、ストローブを使用して、ADCなどの1秒間に 20,000 回繰り返すプロセスを開始できるという意味でコントローラです。

module pulse_20k (                          // mod 5000 for a 100 MHz clock
    input clk,
    output reg zero_strobe,
    output reg [12:0] count                 // 13 bits to hold numbers from 0 to 4999
);
always @(posedge clk) begin
    zero_strobe <= 1'b0; 			        // default
    count <= count + 1;
    if (count >= 4999) begin                // Count starts at 0
        count <= 13'd0;  
        zero_strobe <= 1'b1;
    end 
end
endmodule

ストローブとゼロカウントが同じクロックサイクルで発生することに注目してください。これは、ステート/次のステートの視点でステートマシンを見ない限り、直感的に理解しにくいかもしれません。 (カウント >= 4999)の条件は、カウンタが状態4999にあるときに真になります。この例では、カウント4999がmod-5000カウンタの最大カウントです。クロックの次の立ち上がりエッジで、カウントは0に戻り、同時にzero_strobeをアサートします。これは、最大分数が60である時計のようなものです。

この時点で、より複雑なコントローラの設計を開始できるでしょう。この記事の後半までいけば必ずできるようになります。今のところ、単純な時間ベースのコントローラはその目的を果たし、クロックドメイン内で同期したストローブが生成されることがわかっています。そのストローブは、より大きなRTLシステムの一部として使用できます。

ダブルバッファ

ここまでで、単一クロックドメイン内で同期レジスタ転送を維持することに注意しながら、RTL動作について簡単に説明しました。次に、レジスタの幅が異なる場合のRTL操作をみてみましょう。これは、特にSPIなどの通信プロトコルを使用する場合によく発生します。この場合、SPIは通常、連続したバイトを操作することによってデータを処理しますが、関連するFPGAハードウェアの幅は2~4バイトになる場合があります。

例として、10-bit Pulse Width Modulator(PWM)を考えてみましょう。reg_B1とreg_B0が与えられると、次の操作を実行できます。

assign reg_PWM = {reg_B1, reg_B0}[9:0];

それは合理的な連結です。しかし、物事がうまくいかない可能性は大いにあります。 問題はreg_B1とreg_B0の更新時間です。更新の間に遅延がある場合、reg_PWMは誤った値になる可能性があります。

reg_B1とreg_B0がSPIインターフェースから派生したと仮定します。この場合、レジスタは異なる時間に更新されます。最悪のシナリオとして、PWMコマンドが255から256に増加しているとします。ある時点で、PWMは25%のデューティサイクルで動作しています(10ビットPWMに対して255の設定値)。ここで、reg_B0が残っている間にreg_B1がSPIによって更新されると仮定します。デューティサイクルは50%にジャンプします(10ビットPWMに対して511の設定値)。reg_B0がSPIによって更新されるまで、この誤った値が維持されます。コマンドの短い期間であっても、PWMのこのジャンプはシステムの安定性に望ましくない影響を与える可能性があります。PWMが閉ループシステム内のモータを制御している場合に、これによって引き起こされる不安定性を考慮してみてください。

解決策は、図2に示すように、ダブルバッファとして知られる中間レジスタを追加することです。これにより、reg_B1からreg_B0が自動的に更新されます。その後、両方のレジスタが更新されることがわかると、コントローラはその内容をダブルバッファとして知られる2バイトレジスタに転送することができます。これにより、PWMなどのデバイスは、中間値ではなく、既知の完全なレジスタで更新されます。

Block diagram representation of the double buffer RTL.

図2: ダブルバッファRTLのブロック図

第1部のクロージング

今回は、FPGA設計に関するシステムレベルの考慮事項をいくつか検討しました。これは確かに完全なリストではありませんが、重要なRTL手法、単一クロック境界による同期設計、およびストローブの使用は頭の中に明確にあるはずです。この情報は、次回で紹介するSPIモジュールを理解するのに役立ちます。この記事は、SPIモジュールの出力ストローブを使用して、uCマスターからFPGAへのデータフローを制御する方法に関するヒントを提供します。

パート2が投稿されました。

ご意見やご提案をお待ちしております。 高レベルのRTLシステム設計方法論に関するさらなる議論は特に歓迎します。

幸運をお祈りしています。

APDahlen




オリジナル・ソース(English)