組込み

【ZynqMP】3. PL EthernetでTCP/IP【動作編】

1.記事一覧

記事は複数回に分けて投稿する予定です。AMD/XilinxのARM SoC(ZynqMP Kria K26 SOM)で動作する、AMD/Xilinx公式認定Ubuntuやベアメタル開発を勉強していく覚書きです。

Zynq7000(armv7)版はこちら

2.PL側EtherをCR5+RTOS+LwIPで使ってみる

前回までで、Cortex-R5(CR5)でFreeRTOSを動かすことが出来ました。次はもう少し難しいEthernetをやってみます。KR260スタータキットには4つのGigabit Ethernet(GEther)がついていて、内2つはPS側、もう2つはPL側の接続となっています。基本的にPS側はLinuxが使用するため、PL側を使おうと思います。(最近話題のTSNはやりません)

正直CR5でPL Etherを使うユースケースはあまりない気がしますが、これは遊びなので気にしません。使う理由はそこにEtherが余っていたから。みなさんもきっとそうでしょう?(笑)。

2-1. PL側のEthernetは簡単には使えなさそう

いざやろうとは言っても、簡単に扱えるかはわかりません。なぜならば、Xilinx-Wikiでもざっと見た限り、具体的なやり方が載っていません。いくつかサンプルがありますが、KR260ボードでサポートされていません。比較的新しいボードである事と、想定された使い方ではないので当然といえば当然です。逆にあまりやっている方がいないようであれば、コンテンツとしては価値が高いのでやりがいはありそうです。もちろん失敗して終わる可能性が高めですが…

WikiやDocは以下を見ています。

VitisのサンプルプロジェクトではLwIPを使ったEcho ServerやIperf測定のサンプルが用意されているようです。ただし、ZCU102評価ボードを前提としています。

LwIP(Lightweight IP stack)とはオープンソースのTCP/IPスタックです。C言語で実装されています。

https://savannah.nongnu.org/projects/lwip
https://github.com/lwip-tcpip/lwip

Echo Serverサンプルがお手軽そうなので、まずはこれを動かしてみます。PCからTelnet接続した後、適当な文字列を送るとそのまま文字列が返ってきます。

Echo Serverサンプルのソフト・ハード構成は以下のようになりそうです。

ソフトウェア構成

ハードウェア構成

概要見た限りですが、不安点は

  • LwIPとEthernet/DMAドライバ間のポーティング部分がわからん
  • 割込みまわりがわからん
  • Ethernet DriverのPHY対応がわからん

と言ったところでしょうか。

LwIPのWikiによると以下に対応されているようです。

  • Support for MII, GMII, RGMII, SGMII, and 1000BASE-X PHY interfaces
  • Support for Check sum offloading.
  • Supports AXI DMA + 1G Ethernet Configuration on KC705/KCU105Zynq/ZynqMP Platforms
  • Supports AXI FIFO + 1G Ethernet Configuration.

回路図をみるとPL側GEtherPHY(GEM2,GEM3)は2つとも、RGMII接続になっていますのでI/F対応はしていそうです。ただし、PHYの制御は不安があります。

<参考 https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842366/Standalone+LWIP+library>

echo serverのサンプルではRGMIIの対応は無さそうです。SGMIIと1000Base-Xはギガビットトランシーバ接続(のはず)なので互換性は無さそうですね。KC705はKintexボードでMicroblazeを使用する前提と思われますが、RGMII接続に対応しているようなので、もしかしたらPHYドライバをこちらから移植出来るかもしれません。

2-2. 先に結論:FreeRTOS+LwIPでPL Ethernetは使える

先に結果を書くと無事動きました。さすがにそのままでは動きませんでしたが、最小限の修正で動作しました。修正の方法は以下の2つの方法があります。

  • 方法1.VivadoでPS側のGEM0、GEM1を無効化する。(新Vitis可。旧Vitisもいけるかも)
  • 方法2.Vitis Unified IDE(新Vitis)のCMakeスクリプトを修正する。(旧Vitis不可)

そもそもの原因はnetif(PS-GEM0、PS-GEM1、PL-GEM2)の選択をVitisが間違ってビルドを進めてしまうことです。VivadoのKR260ボードプリセットではPS側のGEM0とGEM1が有効になるため、このままではnetifが3つになり、最初のGEM0が選択されます。

方法1ではPS側のnetifを無効化してPL側GEM2が選択されるようにします。

方法2ではCMakeスクリプトで強制的にGEM2を選択するように修正します。

この後の説明では方法2のやり方を説明していきます。また、次回の記事ではこの解決方法に至った【調査編】を出す予定です。

3.環境

今回の環境です。Tri-Mode Ethernet MAC IPの評価版ライセンス(Hardware Evaluation)を取得している点に注意です。そのままではコンパイルできません。Evaluationライセンスの取得方法は別の記事「Xilinxの評価版IPライセンスを取得する」で紹介しています。

また、このCMakeを修正する方法はVitisは新しい方のVitis Unified IDE のみの対応になります。旧版(Vitis classic)は筆者自身が解決方法がわからないので出来ません。理由は新VitisのみがCMakeビルドシステムを採用しているためです。

開発PC Ubuntu20.04 LTS
開発ツールAMD/Xilinx社 Vivado・Vitis Unified IDE v2024.1
注意:今回は新Vitisを使用します。旧Vitisでは方法がわかりません。
Xilinx IPVivadoにて以下を主に使用
・AXI 1G/2.5G Ethernet SubSystem
 →このMAC IPは以下のIPを含みます
  Tri-Mode Ethernet MAC (TEMAC, 評価版を使用。そのままでは使用不可)
  評価版IPは1時間以上の稼働で機能停止する制限があります。
・AXI DMA
BSPFreeRTOS V10.6.1
xiltimer v2_0
LwIP v2.2.0
サンプルFreeRTOS LwIP Echo Server
使用EtherポートPL GEM2ポート(正面左上)のみ
ターゲットボードXilinx社製 Kria KR260 ロボティクススターターキット(SK-KR260-G)
SOM : Xilinx社 Kria K26 SOM (Zynq UltraScale+ MPSoCベース)
   APU : Arm CA53x4 1333MHz arm64
   RPU : Arm CR5x2 533MHz armv7-R
SDRAM : DDR4 4GB
その他ツール・WireShark
・パケットキャプチャ用PC
・NETGEAR GS105E <-ポートミラーリング機能付きスイッチングハブ

4.VivadoでAXI Etherを構築

4-1. ZynqMP IPの配置

IP INTEGRATORでZynqMP IPを以下の条件で配置します。

  • PL Clock 0を75MHzにする
  • AXI HPM0 LPD Master I/Fを有効にする。FPDでも可
  • AXI HPx FPD Slave I/Fを有効にする。今回はHP0

4-2. AXI-Ethernet IP群の配置

ボードプリセットで自動配線します。

BoardタブでPL GEM2 RGMII ETHERNETをダブルクリックしてOKを押します。

ちなみにGEM2_LEDの配線は不要です(PHYが制御します)。

PHYへ自動配線されます。”Run Block Automation”と”Run Conection Automation”の2つを実施します。前者はそのままの設定でOK、後者はすべての信号にチェックをいれOKします。後者は2回実施する場合があります。

AXI 1G/2.5G Ethernet SubSystem IPとAXI DMA IP、その他リセットIPやClock wizard、Inter connectが追加されます。

4-3. 割込み線の配線

割込み線が以下4本あります。Concatで束ねてZynqMP Pl-PS-irqに接続します。

  • AXI DMA : mm2s_introut(Memory Mapped To Stream intrrupt out)
  • AXI DMA : s2mm_introut(Stream To Memory Mapped intrrupt out)
  • AXI 1G/2.5G Ethernet Sybsystem : mac_irq
  • AXI 1G/2.5G Ethernet Sybsystem : interrupt

Concat IPを追加します。in:out=4:1に設定します。Concatの入力端子の接続先順は特に気にしなくて良いと思います(割込み端子のひも付けはSDTで管理されているはず)。

以上で終了です。

4-4. 全体

潰れて見えないですが、一応全体図です。GithubにプロジェクトのTCLを上げておく予定です。ビットストリームを生成してVitis向けにExportします。

5.VitisでLwIP EchoServerサンプルを構築

5-1. Platformコンポーネントの作成

Platformを作成します。

File>New Component>Platformを選択します。

Platform名と.xsaファイルを選択します。

続いて次のように設定します。

  • OS : freeRTOS
  • Processor : psu_cortexr5_0
  • Generate Boot artifacts : Yes
  • Target Processor to create FSBL : psu_cortexa53_0

注意:上記画像はFSBL実行がCR5になっていますが、たまにバグる(DDRコントローラ初期化に失敗したりする)のでCA53のほうが良いかもしれません。

残りは最後までそのまま進めてプロジェクト生成をします。

5-2. BSPの設定

vitis-comp.jsonを開き、Board Support Packageの項目を開きます。lwip220にチェックをいれます。

lwipの項目を開きます。以下を設定します。FreeRTOSはRAW_APIが使えません。

  • lwip220_api_mode : SOCKET_API
  • lwip220_dhcp : true

最後にビルドします。おや、エラーが出てしまいました。(1つ前のVitis2023.2では出なかったのですが)

以下のソースコードを修正します。path:pf_kr260_rpu_axiether/psu_cortexr5_0/freertos_psu_cortexr5_0/bsp/libsrc/lwip220/src/lwip-2.2.0/contrib/ports/xilinx/sys_arch.c

-L:178 LWIP_DEBUGF(NETIF_DEBUGF, (“Queue is full\r\n”));
+L:178 LWIP_DEBUGF(NETIF_DEBUG, (“Queue is full\r\n”));

タイポですね…おそらく置換ミスかな…

5-3. Platformコンポーネントの修正

以下のPathにある、Cmakeスクリプトを修正します。L.198に#enable-axieth-beginから#enable-axieth-endの間を追加します。

Path=<Platrom Name>/psu_cortexr5_0/freertos_psu_cortexr5_0/bsp/libsrc/lwip220/src/lwip220.cmake

この1行はGEM2に接続されているAXI-Ethernet IPに、netifを強制的に切り替えます。

## Checksum handling should be based on the MAC_INSTANCE variable
list(LENGTH MAC_INSTANCES _len)
if (${_len} GREATER 1)
    list(GET MAC_INSTANCES 0 MAC_INSTANCES)
endif()

#enable-axieth-begin
set(MAC_INSTANCES "axi_ethernet_0")
#enable-axieth-end

if (${CONFIG_AXIETHERNET})
    if (MAC_INSTANCES IN_LIST AXIETHERNET_NUM_DRIVER_INSTANCES)
        if (${lwip220_temac_tcp_ip_tx_checksum_offload})
            if (${axieth_txcsum} EQUAL 0x2)
                set(CHECKSUM_GEN_TCP " ")
                set(CHECKSUM_GEN_UDP " ")
                set(CHECKSUM_GEN_IP " ")
                set(LWIP_FULL_CSUM_OFFLOAD_TX 1)
            else()
                message(FATAL_ERROR "Wrong Tx checksum options. The selected Tx checksum does not match with the HW supported Tx csum offload option")
            endif()
        endif()

5-4. Applicationコンポーネントの作成

File>New Component>From Examplesを選択します。画面左側にサンプルテンプレート一覧が表示されます。

“Embedded Software Examples>FreeRTOS lwIP Echo Server”を選択します。”Create Application Component from Template”を押します。

platform選択は先程作成したものを選択します。

その他に注意点はなく、最後まで進めて完了します。

5-5. Applicationコンポーネントの修正

以下のPathにある、Cmakeスクリプトを修正します。L.18に#enable-axieth-beginから#enable-axieth-endの間を追加します。理由はあとで説明します。

Path=<Application Name>/src/freertos_lwip_echo_server.cmake

この1行はGEM2に接続されているAXI-Ethernet IPに、netifを強制的に切り替えます。

# Copyright (C) 2023 Advanced Micro Devices, Inc.  All rights reserved.
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.3)

list(APPEND TOTAL_MAC_INSTANCES ${EMACPS_NUM_DRIVER_INSTANCES})
list(APPEND TOTAL_MAC_INSTANCES ${AXIETHERNET_NUM_DRIVER_INSTANCES})
list(APPEND TOTAL_MAC_INSTANCES ${EMACLITE_NUM_DRIVER_INSTANCES})
SET(MAC_INSTANCES "${TOTAL_MAC_INSTANCES}" CACHE STRING "MAC Instances")
SET_PROPERTY(CACHE MAC_INSTANCES PROPERTY STRINGS "${TOTAL_MAC_INSTANCES}")
list(LENGTH TOTAL_MAC_INSTANCES _len)

if (${_len} GREATER 1)
    list(GET MAC_INSTANCES 0 MAC_INSTANCES)
elseif(${_len} EQUAL 0)
    message(FATAL_ERROR "This application requires an Ethernet MAC IP instance in the hardware.")
endif()

#enable-axieth-begin
set(MAC_INSTANCES "axi_ethernet_0")
#enable-axieth-end

set(index 0)
if (MAC_INSTANCES IN_LIST AXIETHERNET_NUM_DRIVER_INSTANCES)
    LIST_INDEX(${index} ${MAC_INSTANCES} "${AXIETHERNET_NUM_DRIVER_INSTANCES}")
    list(GET TOTAL_AXIETHERNET_PROP_LIST ${index} prop_list)
endif()

6.EchoServerサンプルの実行

サンプルを以下の手順で実行します。

  • 1.Platromコンポーネント→Applicationコンポーネントの順にビルド
  • 2.USBケーブルを接続(JTAG and UART)
  • 3.シリアルターミナルを開く(115200 8-N-1,本環境の場合/dev/ttyUSB1)
  • 4.VitisでJTAG起動(フリーRun)

シリアルターミナルはUbuntuPCの場合、screenコマンドでも良いのですが、改行文字の設定で困ったので、代わりにCuteComソフトを使用しました。

JTAG起動後、シリアルに以下の様子で出力され、EchoServerが待機します。

DHCP有効にしたので、ルータからIP=10.42.0.101を割り当てられました。Port=7で待機中です。Port7はECHOプロトコルのWell Knownポートです。

Plaintext
Zynq MP First Stage Boot Loader ␊
[15:45:08:386] ␍Release 2024.1   Jun 20 2024  -  06:37:16␍␊
[15:45:08:386] PMU-FW is not running, certain applications may not be supported.␊
[15:45:08:386] ␍␍␊
[15:45:26:354] ␍␊
[15:45:26:354] -----lwIP Socket Mode Echo server Demo Application ------␍␊
[15:45:27:362] Start PHY autonegotiation ␍␊
[15:45:27:362] Start PHY autonegotiation ␍␊
[15:45:27:378] Waiting for PHY to complete autonegotiation.␍␊
[15:45:30:754] autonegotiation complete ␍␊
[15:45:30:754] auto-negotiated link speed: 1000␍␊
[15:45:30:754] Ethernet Link down␍␊
[15:45:31:762] Start PHY autonegotiation ␍␊
[15:45:31:762] Start PHY autonegotiation ␍␊
[15:45:31:762] Waiting for PHY to complete autonegotiation.␍␊
[15:45:35:185] autonegotiation complete ␍␊
[15:45:35:185] auto-negotiated link speed: 1000␍␊
[15:45:35:185] Ethernet Link up␍␊
[15:45:36:353] DHCP request success␍␊
[15:45:36:353] Board IP: 10.42.0.101␊
[15:45:36:353] ␍Netmask : 255.255.255.0␊
[15:45:36:353] ␍Gateway : 10.42.0.1␊
[15:45:36:353] ␍         echo server      7 $ telnet <board_ip> 7␍␊

理由は不明ですが、PHYオートネゴシエーションを2回行っていました。認識してしっかり1000BASE-TでLink upしているので大丈夫そうではあります。

この状態でPCからTelnet接続します。Ctrl+’]’と”quit”で終了できます。”hello”と”hogehoge”と送信すると同じ文字列が返ってきました。

$ telnet 10.42.0.101 7
Trying 10.42.0.101...
Connected to 10.42.0.101.
Escape character is '^]'.
hello
hello
hogehoge
hogehoge
^]
telnet> quit
Connection closed.
$

WiresharkでキャプチャするとDHCP、ARP、ECHOまで一応動いているように見えます。

7.最後に

今回、Cortex-R5 + FreeRTOS + LwIP + PL AXI-Ethernetの組み合わせでサンプルのEcho Serverを動かしてみました。2つの方法のどちらかの修正を行わないと、PHYがLink upせず、1パケットも出てきません。初めは絶望的でしたが、想定よりもあっさり動いてくれました。WikiではPhyに対応していないような記述でしたが、バッチリ実装されて動いてました。実装を勉強するにはやはり動くものがあると理解がはかどりますね。

次回はEcho Serverを動かせるようになるまでの道のりを解説した、【調査編】の記事を出す予定です。

ABOUT ME
sh-goto
低レイヤで遊んでいます
関連記事