LoginSignup
7
1

More than 3 years have passed since last update.

SoCFPGAのハードマクロをFPGA側I/Oから使用する場合の注意点

Last updated at Posted at 2020-12-13

概要

 SoCFPGAのI/Oは,HPS側I/OとFPGA側I/Oに二分されます。SoCFPGAにはEMAC,I2C,QSPI等を制御する種々のハードマクロが搭載されており,これらはHPS側I/Oを介して接続することができます。一方,HPS側I/Oの数は限られており,ハードマクロすべてを有効にしてHPS側I/Oから接続することはできません。
 本制約に対処可能な手段として,ハードマクロの信号線をFPGA側にルーティングし,FPGA側のI/Oを利用する方法があります。
Screenshot from 2020-09-12 22-43-08.png
 【CycloneV HPS Technical Reference Manual 抜粋】
 EMACについてはHPS側I/OまたはFPGA側I/Oいずれかが利用可能である旨,記載があります。

 このようなモジュールについてFPGA側のI/Oを使用する場合,当然FPGA側でタイミング制約を記述し,Setup/Holdが要件を満たすように合成する必要があります。単にI/Oをそのまま出力するだけのように感じますが,うまくタイミングが収束しないパターンがあります。
 本記事ではその一例を挙げたいと思います。

検証環境

  • Linux ubuntu20 5.4.0-42-generic
  • Quartus Prime Lite Edition 20.1.0.711

検証用プロジェクトの用意 

 今回はCycloneV SoC(5CSTFD6D5F31I7)を使用します。5CSTFD6D5F31I7を選択してプロジェクトを作成します。
Screenshot from 2020-09-12 20-00-38.png

 続いて,Platform Designerを用いてHPSモジュールを作成します。この際,EMAC0をFPGA側に出力するよう設定します。Screenshot from 2020-09-12 20-03-11.png
 作成したモジュールの名前を変更します(hps_0 => hps)。必要な信号をダブルクリックでExportし,Generateボタンを押下します。CUTScreenshot from 2020-09-12 20-11-37.png
 
 次にトップモジュール(gmiitest.v)を作成します。内容は先ほどPlatform DesignerでGenerateしたIPをインスタンス化し,各入出力信号をそのまま外部入出力として接続するものです。

gmiitest.v
module gmiitest
  (
   output wire [7:0]  hps_0_emac0_phy_txd_o,
   output wire        hps_0_emac0_phy_txen_o,
   output wire        hps_0_emac0_phy_txer_o,
   input wire         hps_0_emac0_phy_rxdv_i,
   input wire         hps_0_emac0_phy_rxer_i,
   input wire [7:0]   hps_0_emac0_phy_rxd_i,
   input wire         hps_0_emac0_phy_col_i, 
   input wire         hps_0_emac0_phy_crs_i, 
   output wire        hps_0_emac0_gmii_mdo_o,
   output wire        hps_0_emac0_gmii_mdo_o_e,
   input wire         hps_0_emac0_gmii_mdi_i,
   output wire        hps_0_emac0_ptp_pps_o, 
   input wire         hps_0_emac0_ptp_aux_ts_trig_i,
   output wire        hps_0_emac0_gtx_clk_clk,
   output wire        hps_0_emac0_md_clk_clk, 
   input wire         hps_0_emac0_rx_clk_in_clk,
   input wire         hps_0_emac0_tx_clk_in_clk,
   input wire         hps_0_emac_ptp_ref_clock_clk,
   input wire         hps_f2h_axi_clock_clk,
   input wire         hps_f2h_sdram0_clock_clk,
   input wire         hps_h2f_axi_clock_clk,
   input wire         hps_h2f_lw_axi_clock_clk,
   output wire [12:0] memory_mem_a,
   output wire [2:0]  memory_mem_ba,
   output wire        memory_mem_ck,
   output wire        memory_mem_ck_n,
   output wire        memory_mem_cke,
   output wire        memory_mem_cs_n,
   output wire        memory_mem_ras_n,
   output wire        memory_mem_cas_n,
   output wire        memory_mem_we_n,
   output wire        memory_mem_reset_n,
   inout wire [7:0]   memory_mem_dq,
   inout wire         memory_mem_dqs,
   inout wire         memory_mem_dqs_n,
   output wire        memory_mem_odt,
   output wire        memory_mem_dm,
   input wire         memory_oct_rzqin
   );

    hps u0 (
        .hps_0_emac0_phy_txd_o         (hps_0_emac0_phy_txd_o),
        .hps_0_emac0_phy_txen_o        (hps_0_emac0_phy_txen_o),
        .hps_0_emac0_phy_txer_o        (hps_0_emac0_phy_txer_o),
        .hps_0_emac0_phy_rxdv_i        (hps_0_emac0_phy_rxdv_i),
        .hps_0_emac0_phy_rxer_i        (hps_0_emac0_phy_rxer_i),
        .hps_0_emac0_phy_rxd_i         (hps_0_emac0_phy_rxd_i),
        .hps_0_emac0_phy_col_i         (hps_0_emac0_phy_col_i),
        .hps_0_emac0_phy_crs_i         (hps_0_emac0_phy_crs_i),
        .hps_0_emac0_gmii_mdo_o        (hps_0_emac0_gmii_mdo_o),
        .hps_0_emac0_gmii_mdo_o_e      (hps_0_emac0_gmii_mdo_o_e),
        .hps_0_emac0_gmii_mdi_i        (hps_0_emac0_gmii_mdi_i),
        .hps_0_emac0_ptp_pps_o         (hps_0_emac0_ptp_pps_o),
        .hps_0_emac0_ptp_aux_ts_trig_i (hps_0_emac0_ptp_aux_ts_trig_i),
        .hps_0_emac0_gtx_clk_clk       (hps_0_emac0_gtx_clk_clk),
        .hps_0_emac0_md_clk_clk        (hps_0_emac0_md_clk_clk),
        .hps_0_emac0_rx_clk_in_clk     (hps_0_emac0_rx_clk_in_clk),
        .hps_0_emac0_tx_clk_in_clk     (hps_0_emac0_tx_clk_in_clk),
        .hps_0_emac_ptp_ref_clock_clk  (hps_0_emac_ptp_ref_clock_clk),
        .hps_f2h_axi_clock_clk         (hps_f2h_axi_clock_clk),
        .hps_f2h_sdram0_clock_clk      (hps_f2h_sdram0_clock_clk),
        .hps_h2f_axi_clock_clk         (hps_h2f_axi_clock_clk),
        .hps_h2f_lw_axi_clock_clk      (hps_h2f_lw_axi_clock_clk),
        .memory_mem_a                  (memory_mem_a),
        .memory_mem_ba                 (memory_mem_ba),
        .memory_mem_ck                 (memory_mem_ck),
        .memory_mem_ck_n               (memory_mem_ck_n),
        .memory_mem_cke                (memory_mem_cke),
        .memory_mem_cs_n               (memory_mem_cs_n),
        .memory_mem_ras_n              (memory_mem_ras_n),
        .memory_mem_cas_n              (memory_mem_cas_n),
        .memory_mem_we_n               (memory_mem_we_n),
        .memory_mem_reset_n            (memory_mem_reset_n),
        .memory_mem_dq                 (memory_mem_dq),
        .memory_mem_dqs                (memory_mem_dqs),
        .memory_mem_dqs_n              (memory_mem_dqs_n),
        .memory_mem_odt                (memory_mem_odt),
        .memory_mem_dm                 (memory_mem_dm),
        .memory_oct_rzqin              (memory_oct_rzqin)
    );
endmodule

 Platform Designerで作成したqipファイルとトップモジュールファイルをプロジェクトに追加します。続いてFitterでエラーが出ないよう,Assignment EditorからDDR3 I/Fの設定を適当に行います(本記事末尾の付録参照)。なお,このときFPGA側I/Oの位置は特に指定しません。指定しない場合はFitterがタイミング制約などを元に位置を決定します。設定後,一通りコンパイル(論理合成〜タイミング解析)を行い,エラーが発生しないことを確認します。

 続いてタイミング制約を記述します。その前に,Quartusの設定を変更し,Timing AnalyzerからEMACの内部信号が見えるようにする必要があります(そうしないとTX系のタイミング解析ができない)。
 以下ファイルをプロジェクトファイル(.qpf)を配置しているディレクトリに配置し,Quartusを再起動します。

quartus.ini
b2t_enable_hps_emac_internal_clock_arcs=on

 タイミング制約は以下のように記述します。今回は接続するEthernet PHYとしてKSZ9021GQを想定し,簡単のため基板上の配線遅延,クロックジッタ・スキュー等は無視します。

gmiitest.sdc
# GTX Clock
create_generated_clock -name GTXCLK -master_clock emac0_tx_clk \
-source [get_keepers {u0|hps|fpga_interfaces|peripheral_emac0~internal_clock}] \
[get_ports {hps_0_emac0_gtx_clk_clk}]

# Setup/Hold
set_output_delay -clock GTXCLK -max 2.0 \
[get_ports {hps_0_emac0_phy_txd_o[*] hps_0_emac0_phy_txen_o hps_0_emac0_phy_txer_o}]
set_output_delay -clock GTXCLK -min 0.0 \
[get_ports {hps_0_emac0_phy_txd_o[*] hps_0_emac0_phy_txen_o hps_0_emac0_phy_txer_o}]

# RX Clock
create_clock -name RXCLK -period 8.0 [get_ports {hps_0_emac0_rx_clk_in_clk}]
# Setup/Hold
set_input_delay -clock RXCLK -max 5.5 \
[get_ports {hps_0_emac0_phy_rxdv_i hps_0_emac0_phy_rxer_i hps_0_emac0_phy_rxd_i[*]}]
set_input_delay -clock RXCLK -min 0.5 \
[get_ports {hps_0_emac0_phy_rxdv_i hps_0_emac0_phy_rxer_i hps_0_emac0_phy_rxd_i[*]}]

derive_clock_uncertainty
# 注意 #####################################################################
# ここで使用したemac0_tx_clkは,Platform Designerから出力されたsdcに記述されている
# @ hps/synthesis/submodules/hps_hps_fpga_interfaces.sdc
# create_clock -name emac0_tx_clk -period 8.0 \
# [get_keepers {*|fpga_interfaces|peripheral_emac0~internal_clock}] -add

Let's 論理合成

 合成結果がこちらです。TX系は問題ありませんが,RX系でタイミングエラーが出ています。単にハードマクロの出力をFPGA側のI/Oに配線するだけですが,このようにタイミングが収束しないことがあります。
Screenshot from 2020-09-13 01-08-29.png

 パスの詳細をTiming Analyzerで見てみましょう。
Screenshot from 2020-09-19 17-32-49.png
 最初の5.5nsの遅延はEthernet PHY出力の仕様であり,タイミング制約で記載した内容です。このパスの遅延で支配的なのはオレンジ色で示した行であり,入力バッファからFPGA interfaceまでの内部配線(IC:InterConnect)が要因であることがわかります。
 

実験その1

 前述のパスにFFを挿入することで内部配線を短くして再度合成をしてみましょう。RX側の信号へFFを挿入するにあたり,当該FFのリセット回路を実装する必要があります。Platform Designerから操作を行い,新たにHPSからEMACのリセット信号をExportし,本信号を使用して実装します。
Screenshot from 2020-09-19 17-39-21.png

 コードの追記・変更内容は以下です。

gmiitest.v
  //--- register declaration
  reg                 hps_0_emac0_phy_rxdv_i_r;
  reg                 hps_0_emac0_phy_rxer_i_r;
  reg [7:0]           hps_0_emac0_phy_rxd_i_r;

  //--- wire declaration
  wire                reset_rx_n;
  wire                reset_tx_n;

  //--- FF for timing closure
  always @ (posedge hps_0_emac0_rx_clk_in_clk or negedge reset_rx_n) begin
    if (~reset_rx_n) begin
      hps_0_emac0_phy_rxdv_i_r <= 1'b0;
      hps_0_emac0_phy_rxer_i_r <= 1'b0;
      hps_0_emac0_phy_rxd_i_r  <= 8'b0;
    end else begin
      hps_0_emac0_phy_rxdv_i_r <= hps_0_emac0_phy_rxdv_i;
      hps_0_emac0_phy_rxer_i_r <= hps_0_emac0_phy_rxer_i;
      hps_0_emac0_phy_rxd_i_r  <= hps_0_emac0_phy_rxd_i;
    end
  end

  hps u0 (
          .hps_0_emac0_phy_txd_o         (hps_0_emac0_phy_txd_o),
          //... 省略
          .hps_0_emac0_phy_rxdv_i        (hps_0_emac0_phy_rxdv_i_r),
          .hps_0_emac0_phy_rxer_i        (hps_0_emac0_phy_rxer_i_r),
          .hps_0_emac0_phy_rxd_i         (hps_0_emac0_phy_rxd_i_r),
          //... 省略
          .hps_emac0_tx_reset_reset_n    (reset_tx_n),
          .hps_emac0_rx_reset_reset_n    (reset_rx_n)  // reset for FF
          );

 結果がこちらです。
Screenshot from 2020-09-19 18-05-09.png
 狙い通りRX系のSetupは改善しましたが,Holdが悪化し,Setup/Hold双方でエラーが出ています。加えてTX系の信号についてもSetup/Holdでエラーが出ています。そう簡単にはいかないようです。
 Timing Analyzerによる確認をしてみましょう。
Screenshot from 2020-09-19 18-13-50.png
 FF〜FPGA interfaceまでのSetupパスは問題ないようです。省略しますが,Holdパスも問題ありません。一方,入力バッファ〜FFまでのSetupパスはうまく行っていないようです。
 後者のパスをChip PlannerにLocateしてみましょう。
 Screenshot from 2020-09-19 18-22-17.png
 青紫の矢印がデータのパスであり,赤紫のパスがクロックのパスです。FFは入力バッファから離れた位置に配置されており,データパスは短くする余地があることがわかります。

実験その2

 FF1つではデータパスが長く,タイミング収束しませんでした。そこで,FFの数を2つに増やし,さらなるパスの分割を試みます。追記内容は「実験その1」とほぼ同じであるため,コードは省略します。結果がこちらです。
Screenshot from 2020-09-19 18-31-46.png
 ほとんど改善は見られていません。理由は簡単で,1つ目のFF〜2つ目のFFまでの距離がとても短く,入力バッファ〜1つ目のFFまでのパスを短くする役割を果たしていないためでした。確認のため,1つ目のFF〜2つ目のFFまでのパスをChip PlannterにLocateした様子をお見せします。
Screenshot from 2020-09-19 18-40-45.png
 見づらいですが,画面中央部に赤紫の矢印でデータパスが描かれています。以下が拡大図です。
Screenshot from 2020-09-19 18-41-02.png
 Logic Lockを使用して,2つのFFの位置をよい塩梅に指定することで解決可能かもしれません。しかしながら,本合成環境はQuartusのLite Editionなので当該機能を使用できません。この記事では本機能を使用せずに解決していきます。
 
 脱線しますが,図らずもTX系のタイミングが収束しています。使用するI/Oの位置によって収束可否が決まるようです。逆説的に考えると,I/Oの位置が先に決まっているプロジェクトなどではTX系のタイミング収束作業が必要となる可能性があります。

実験その3

 一般に,入力段のタイミングでエラーが出ている場合,入力バッファのすぐそばに配置されているFFを使用してデータを受けることで改善することがあります。Screenshot from 2020-09-19 19-02-53.png
 【CycloneV Device Handbook 第5章抜粋:図中,下方に記載がある「Input Register」】
 当該FFを使用するには,Assignment Editorから「Fast Input Register:on」を設定します。
Screenshot from 2020-09-19 18-58-41.png
 結果です。
Screenshot from 2020-09-19 18-57-36.png
 無事すべてのパスでタイミングが収束しました。

結論

 以上のように,ハードマクロをFPGA側I/Oから使用する場合,考えている以上に煩雑なタイミング収束作業が発生するケースがあります。今回はFPGA側I/Oの設定をデフォルト(2.5V, 12mA, Slew Rate: Fast)かつ位置の制約なし,という条件の下で作業を行いましたが,条件次第で収束作業の難易度が変わります。
 
 作業を可能な限り簡単にするには,以下に気をつけると良いと思います。

  • FPGA側I/Oを使用する場合,早期にI/O規格を決定し,タイミング収束性を検証すること
  • 収束した結果を用いてI/Oピンの位置を検討すること
  • 必要に応じてタイミング収束を実現する配置配線結果をLogic Lock等で固定し,開発を進めること

付録:Assignment Editor設定

 "To"フィールドの[*]は適宜展開してください。

To Assignment Name Value
memory_mem_a[*] I/O Standard SSTL-15 Class I
memory_mem_ba[*] I/O Standard SSTL-15 Class I
memory_mem_cas_n I/O Standard SSTL-15 Class I
memory_mem_odt I/O Standard SSTL-15 Class I
memory_mem_ras_n I/O Standard SSTL-15 Class I
memory_mem_reset_n I/O Standard SSTL-15 Class I
memory_mem_we_n I/O Standard SSTL-15 Class I
memory_oct_rzqin I/O Standard SSTL-15 Class I
memory_mem_cke I/O Standard SSTL-15 Class I
memory_mem_cs_n I/O Standard SSTL-15 Class I
memory_mem_dm I/O Standard SSTL-15 Class I
memory_mem_dm Output Termination Series 50 Ohm with Calibration
memory_mem_dq[*] I/O Standard SSTL-15 Class I
memory_mem_dq[*] Input Termination Parallel 50 Ohm with Calibration
memory_mem_dq[*] Output Termination Series 50 Ohm with Calibration
memory_mem_dqs I/O Standard SSTL-15 Class I
memory_mem_dqs Input Termination Parallel 50 Ohm with Calibration
memory_mem_dqs Output Termination Series 50 Ohm with Calibration
memory_mem_dqs_n I/O Standard SSTL-15 Class I
memory_mem_dqs_n Input Termination Parallel 50 Ohm with Calibration
memory_mem_dqs_n Output Termination Series 50 Ohm with Calibration
memory_mem_dqs I/O Standard Differential 1.5-V SSTL Class I
memory_mem_dqs Input Termination Parallel 50 Ohm with Calibration
memory_mem_dqs Output Termination Series 50 Ohm with Calibration
memory_mem_dqs_n I/O Standard Differential 1.5-V SSTL Class I
memory_mem_dqs_n Input Termination Parallel 50 Ohm with Calibration
memory_mem_dqs_n Output Termination Series 50 Ohm with Calibration
memory_mem_ck I/O Standard Differential 1.5-V SSTL Class I
memory_mem_ck Output Termination Series 50 Ohm without Calibration
memory_mem_ck_n I/O Standard Differential 1.5-V SSTL Class I
memory_mem_ck_n Output Termination Series 50 Ohm without Calibration
7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1