組込み

【ZynqMP】3. 1. PL EthernetでTCP/IP【調査編前半】

1.記事一覧

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

Zynq7000(armv7)版はこちら

2.PL側Etherが動くようになるまで【前半戦】

前回の「PL EthernetでTCP/IP【動作編】」ではCortex-R5(CR5)+FreeRTOS+LwIP+PL AXI-EthernetでEcho Serverサンプルを動かしてみました。Xilinx-Wiki見る限り、KR260ボードはやり方が乗ってない状況だったので、案の定そのままサンプルをビルドしても動きませんでした。しかし、調べて始めると9割9分は動く状態だったので、予想よりもあっさり動いてくれました。今回はその謎解きの様子を残しておきたいと思います。記事は前半と後半に分割したため長めです。

動くまでの流れ(前半と後半に記事が分かれてます)

調査の前半戦

  • 0.環境とシステム構成
  • 1.Echo Serverサンプルが動かない
  • 2.Wiresharkでパケットを見る。絶望的
  • 3.UARTログからLwIPのソースを追いかける
  • 4.LwIPのデバッグメッセージを有効化
  • 5.CMakeスクリプトを追いかける
  • 6.原因判明、パケットが出る
  • 調査の前半戦まとめ

調査の後半戦

  • 7.DHCPでコケる(延長戦開始)
  • 8.DHCPシーケンスのおさらいとパケットの中身を見る
  • 9.Wiresharkの落とし穴
  • 10.原因判明、DHCPとEcho Server無事動く
  • 調査の後半戦まとめ

2-0. 環境とシステム構成

今回の調査環境とシステムの構成です。

開発PCUbuntu20.04 LTS
開発ツールAMD/Xilinx社 Vivado・Vitis Unified IDE
(再現時=v2024.1,調査当時=v2023.2)
注意:今回は新Vitisを使用します。本記事は再現時のversionです。
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ポートPS GEM0, Phy=SGMII addr=0x04(正面右上)
PS GEM1, Phy=RGMII addr=0x08(正面右下)
PL GEM2, Phy=RGMII addr=0x02(正面左上)←目標はここで動かすこと
PL GEM3, Phy=RGMII addr=0x03(正面左下)
1000BASE-T PhyChip : TI社 DP83867CSRGZ
GEM=Gigabit Ethernet MAC
ターゲットボード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 <-ポートミラーリング機能付きスイッチングハブ

2-1. Echo Serverサンプルが動かない

前回の記事で紹介した環境構築手順で、CMakeスクリプトを2箇所修正しました。その修正がない状態で実行すると、UARTには以下のログが現れます。

Plaintext
Zynq MP First Stage Boot Loader ␊
[01:40:50:443] ␍Release 2024.1   Jun 23 2024  -  16:37:59␍␊
[01:40:50:459] PMU-FW is not running, certain applications may not be supported.␊
[01:40:50:459] ␍␍␊
[01:41:11:978] ␍␊
[01:41:11:994] -----lwIP Socket Mode Echo server Demo Application ------␍␊
[01:41:11:994] Start PHY autonegotiation ␍␊
[01:41:11:994] Waiting for PHY to complete autonegotiation.␍␊
[01:41:17:002] Auto negotiation error ␍␊
[01:41:17:002] Phy setup error ␍␊
[01:41:17:002] Phy setup failure init_emacps ␊
[01:41:17:002] ␍Ethernet Link down␍␊
[01:43:11:990] ERROR: DHCP request timed out␍␊
[01:43:11:990] Configuring default IP of 192.168.1.10␍␊
[01:43:11:990] Board IP: 192.168.1.10␊
[01:43:11:990] ␍Netmask : 255.255.255.0␊
[01:43:12:006] ␍Gateway : 192.168.1.1␊
[01:43:12:006] ␍␍␊
[01:43:12:006]               Server   Port Connect With..␍␊
[01:43:12:006] -------------------- ------ --------------------␍␊
[01:43:12:006]          echo server      7 $ telnet <board_ip> 7␍

“Ethernet Link down”から1分後、DHCPがタイムアウトして終了し、デフォルトのIPアドレスが割り当てられました。試しに同じネットワーク上のPCからtelnet接続を試みます。

Bash
$ telnet 192.168.1.10 7
Trying 192.168.1.10...
telnet: Unable to connect to remote host: No route to host
$

接続失敗しました。ただし、リンクアップLEDはPHYセットアップ中に点灯しました。どうやら電源ONとリセットが無効かつケーブル接続済みであれば点灯するようで、プログラムとは関係無さそうです。おそらくPHYのAuto negotiationが働いているためと思われます。

結果はほぼ全滅です。前回の記事でLwIPのEcho ServerサンプルはSGMIIと1000Base-Xのみの対応だったので、だいたい予想通りです。

2-2. Wiresharkでパケットを見る。絶望的

全滅したと言ってもこれでは原因の的が絞れないため、まずはEtherからパケットが出ているか、外側から調べていきます。

結果ですが、1パケットも出てきません。PCに直結して以下の表示フィルタをかけます(KR260->PC方向のパケットを可視化)。PC->KR260方向の通信は大量にあったのですが、KR260からは0パケでした。

表示フィルタ:(eth.addr == 00:0a:35:00:01:02 || eth.dst == ff:ff:ff:ff:ff:ff || eth.dst == <キャプチャPCのMACアドレス>) && eth.src != <キャプチャPCのMACアドレス>

フィルタの説明(PC:パケットキャプチャ用PC)

eth.addr == 00:0a:35:00:01:02 :KR260のサンプルコード内のMACアドレス設定値

eth.dst == ff:ff:ff:ff:ff:ff:ブロードキャストMACアドレス(PC受信)。KR260のDHCPの最初のDISCOVERパケットを拾う

eth.dst == <キャプチャPCのMACアドレス>:KR260がサンプルコード内のMACアドレスを正しく設定できない場合に拾う

eth.src != <キャプチャPCのMACアドレス>:PC→ブロードキャスト宛のパケットを非表示にする。

少なくともハード側に原因は1つ以上ありそうです。的はあまり絞れてないですね。ソフト側にも原因はありそうなので、この時点では絶望的です。

2-3. UARTログからLwIPのソースを追いかける

もう一つの手がかりとなる、UARTログを確認します。L.9~L.11のエラーを出している箇所のソースを確認します。

Plaintext
Zynq MP First Stage Boot Loader ␊
[01:40:50:443] ␍Release 2024.1   Jun 23 2024  -  16:37:59␍␊
01:40:50:459] PMU-FW is not running, certain applications may not be supported.␊
[01:40:50:459] ␍␍␊
[01:41:11:978] ␍␊
[01:41:11:994] -----lwIP Socket Mode Echo server Demo Application ------␍␊
[01:41:11:994] Start PHY autonegotiation ␍␊
[01:41:11:994] Waiting for PHY to complete autonegotiation.␍␊
[01:41:17:002] Auto negotiation error ␍␊
[01:41:17:002] Phy setup error ␍␊
[01:41:17:002] Phy setup failure init_emacps ␊
[01:41:17:002] ␍Ethernet Link down␍␊
[01:43:11:990] ERROR: DHCP request timed out␍␊
[01:43:11:990] Configuring default IP of 192.168.1.10␍␊
[01:43:11:990] Board IP: 192.168.1.10␊
[01:43:11:990] ␍Netmask : 255.255.255.0␊
[01:43:12:006] ␍Gateway : 192.168.1.1␊
[01:43:12:006] ␍␍␊
[01:43:12:006]               Server   Port Connect With..␍␊
[01:43:12:006] -------------------- ------ --------------------␍␊
[01:43:12:006]          echo server      7 $ telnet <board_ip> 7␍

L.9はxemacpsif_physpeed.cのL.510にありました。

“<Platrom Dir>/psu_cortexr5_0/freertos_psu_cortexr5_0/bsp/libsrc/lwip220/src/lwip-2.2.0/contrib/ports/xilinx/netif/xemacpsif_physpeed.c”

u32_t get_TI_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)関数内の処理です。オートネゴシエーションを行い、リンクスピードを設定するところで失敗したようです。

C
	XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
	XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);

	xil_printf("Waiting for PHY to complete autonegotiation.\r\n");

	while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) {<
  	sleep(1);
		timeout_counter++;
		if (timeout_counter == 5) {
			xil_printf("Auto negotiation error \r\n");<
			return XST_FAILURE;
	}
		XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
	}
	xil_printf("autonegotiation complete \r\n");

	XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_STS, &status_speed);
	if ((status_speed & 0xC000) == 0x8000) {

ここで気づいたのですが、調べると、”EmacPS”はPS側GEMのMACドライバを指していました。今回使用するのはAXI-EthernetMACドライバのはずなので、初期化しようとしているデバイスがそもそも違うということになります。出口の光が少し見えてきましたね。

ちなみにPS側のGEMにケーブルを挿し直しても動きませんでした。

2-4. LwIPのデバッグメッセージを有効化

ソースを読んでいたら、便利そうな記述を見つけました。

C
LWIP_DEBUGF(NETIF_DEBUG, (" addr "));

LwIPが出力するシステムメッセージでxil_printf()へのマクロとなっていました。

以下2ファイルに定義されています。

  • Path1=”<Platform dir>/psu_cortexr5_0/freertos_psu_cortexr5_0/bsp/include/lwip/debug.h”
  • Path2=”<Platform dir>/psu_cortexr5_0/freertos_psu_cortexr5_0/bsp/include/arch/cc.h”

このメッセージを有効にするには以下の操作が必要です。

  • LWIP_DEBUGマクロを有効にする
  • 出力したい各LwIPコンポーネントのDEBUGマクロを有効にする(Vitisで操作可)
    • IP_DEBUG
    • TCP_DEBUG
    • TCP_INPUT_DEBUG
    • TCP_OUTPUT_DEBUG
    • TCPIP_DEBUG
    • UDP_DEBUG
    • ICMP_DEBUG
    • IGMP_DEBUG
    • NETIF_DEBUG:(Network Interface)
    • SYS_DEBUG
    • API_MSG_DEBUG
    • PBUF_DEBUG:(Packet Buffer)
    • DHCP_DEBUF
    • ACD_DEBUG:(Address Conflict Detection)
    • MEMP_DEBUG

debugメッセージが出るように再ビルドして、再度UARTログを出してみます。Vitis Unified IDEでBSPのLwIP設定で以下を有効にするとマクロが有効になります。

  • lwip220_debug=true
  • lwip220_dhcp_debug=true
  • lwip220_icmp_debug=true
  • lwip220_netif_debug=true
  • (lwip220_pbuf_debug)=false <-大量のメッセージが出るため必要なとき以外はOFF推奨
  • lwip220_sys_debug=true

再ビルドして、もう一度UARTログを見てみます。

Plaintext
[17:24:54:590] Zynq MP First Stage Boot Loader ␊
[17:24:54:590] ␍Release 2024.1   Jun 24 2024  -  08:18:08␍␊
[17:24:54:590] PMU-FW is not running, certain applications may not be supported.␊
[17:24:54:606] ␍␍␊
[17:25:12:285] ␍␊
[17:25:12:285] -----lwIP Socket Mode Echo server Demo Application ------␍␊
[17:25:12:301] Start PHY autonegotiation ␍␊
[17:25:12:301] Waiting for PHY to complete autonegotiation.␍␊
[17:25:17:293] Auto negotiation error ␍␊
[17:25:17:293] Phy setup error ␍␊
[17:25:17:309] Phy setup failure init_emacps ␊
[17:25:17:309] ␍rxringptr: 0x00200174␍␊
17:25:17:309] txringptr: 0x00200130␍␊
17:25:17:309] rx_bdspace: 600000 ␍␊
[17:25:17:309] tx_bdspace: 610000 ␍␊
[17:25:17:309] netif: added interface te IP addr 0.0.0.0 netmask 0.0.0.0 gw 0.0.0.0␊
[17:25:17:325] netif: setting default interface te␊
17:25:17:325] dhcp_start(netif=200040) te0␊
[17:25:17:325] dhcp_start(): mallocing new DHCP client␊
[17:25:17:325] dhcp_start(): allocated dhcp␊
[17:25:17:325] dhcp_start(): starting DHCP configuration␊
[17:25:17:341] dhcp_discover()␊
[17:25:17:341] transaction id xid(4BB5F646)␊
[17:25:17:341] dhcp_discover: making request␊
[17:25:17:341] dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER)␊

(...略)

[17:27:12:279] ERROR: DHCP request timed out␍␊[17:27:12:296] Configuring default IP of 192.168.1.10␍␊
[17:27:12:296] Board IP: 192.168.1.10␊
[17:27:12:296] ␍Netmask : 255.255.255.0␊
[17:27:12:296] ␍Gateway : 192.168.1.1␊
[17:27:12:296] ␍␍␊
[17:27:12:296]               Server   Port Connect With..␍␊
[17:27:12:311] -------------------- ------ --------------------␍␊<
[17:27:12:311]          echo server      7 $ telnet <board_ip> 7␍␊

PHYまわりの新規メッセージは増えてないですが、その後のバッファ初期化(NETIF_DEBUG)とDHCPのDISCOVER(DHCP_DEBUG)リトライの様子が、詳細に出てきました。

ついでにPS側のGEM0にケーブルを挿し直して見てみます。macpsのドライバが有効になっているので何かしら挙動が変わればラッキーです。

Plaintext
[17:32:51:178] Zynq MP First Stage Boot Loader ␊
[17:32:51:178] ␍Release 2024.1   Jun 24 2024  -  08:18:08␍␊
[17:32:51:195] PMU-FW is not running, certain applications may not be supported.␊
[17:32:51:195] ␍␍␊
[17:33:18:616] ␍␊
[17:33:18:616] -----lwIP Socket Mode Echo server Demo Application ------␍␊
[17:33:18:632] Start PHY autonegotiation ␍␊
[17:33:18:632] Waiting for PHY to complete autonegotiation.␍␊
[17:33:23:640] Auto negotiation error ␍␊
[17:33:23:640] Phy setup error ␍␊
[17:33:23:640] Phy setup failure init_emacps ␊
[17:33:23:640] ␍rxringptr: 0x00200174␍␊
[17:33:23:640] txringptr: 0x00200130␍␊
[17:33:23:640] rx_bdspace: 600000 ␍␊
[17:33:23:656] tx_bdspace: 610000 ␍␊
[17:33:23:656] netif: added interface te IP addr 0.0.0.0 netmask 0.0.0.0 gw 0.0.0.0␊
[17:33:23:656] netif: setting default interface te␊
[17:33:23:656] dhcp_start(netif=200040) te0␊
[17:33:23:656] dhcp_start(): mallocing new DHCP client␊
[17:33:23:672] dhcp_start(): allocated dhcp␊
[17:33:23:672] dhcp_start(): starting DHCP configuration␊
[17:33:23:672] dhcp_discover()␊
[17:33:23:672] transaction id xid(4BB5F646)␊
[17:33:23:672] dhcp_discover: making request␊
[17:33:23:672] dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER)␊

(...略)

[17:35:18:627] ERROR: DHCP request timed out␍
[17:35:18:627] Configuring default IP of 192.168.1.10␍␊
[17:35:18:627] Board IP: 192.168.1.10␊
[17:35:18:627] ␍Netmask : 255.255.255.0␊
[17:35:18:643] ␍Gateway : 192.168.1.1␊
[17:35:18:643] ␍␍␊
[17:35:18:643]               Server   Port Connect With..␍␊
[17:35:18:643] -------------------- ------ --------------------␍␊
[17:35:18:643]          echo server      7 $ telnet <board_ip> 7␍

残念ながら変わりませんでした。当時はわかりませんでしたが、今の予想としては原因はSGMIIのPHY初期化あたりに原因があると思います。GEM0のみSGMIIの仕様になっています。

ちなみにPS側のGEM1も結果は同じでした。

2-5. CMakeスクリプトを追いかける

現時点でPL側のAXI-EtherMACではなく、PS側のEtherMACのドライバが動いていることが判明しました。なぜこちらが動作しているのかを調べてみましょう。

調べる対象のソースコードがApplicationとPlatformコンポーネントの2つあるので、行ったり来たりするのが面倒ですが、ポイントとしては、LwIPの初期化処理は2段階あります。

  • lwip_init() or tcpip_init()で、TCP/IPスタックを初期化
  • netif_add()で、ネットワークインターフェースの初期化と追加

この内、EtherMACの初期化を行うのはnetif_add()の内部になります。

Echo Serverサンプル(Application側)では以下のxemac_add()が呼ばれていて、その中でnetif_add()が呼ばれています。

C
//main.c 
void network_thread(void *p)
(...略)
    /* Add network interface to the netif_list, and set it as default */
    if (!xemac_add(netif, NULL, NULL, NULL, mac_ethernet_address, PLATFORM_EMAC_BASEADDR)) {
	xil_printf("Error adding N/W interface\r\n");
	return;
    }
(...略)
}

xemac_add()はXilinx実装で、Platform側のxadapter.cに実装されています。

C
struct netif *
xemac_add(struct netif *netif,
	ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_t *gw,
	unsigned char *mac_ethernet_address,
	UINTPTR mac_baseaddr)
{
	int i;
	struct netif * nif = NULL;

	/* set mac address */
	netif->hwaddr_len = 6;
	for (i = 0; i < 6; i++)
		netif->hwaddr[i] = mac_ethernet_address[i];

	/* initialize based on MAC type */
		switch (find_mac_type(mac_baseaddr)) {
			case xemac_type_xps_emaclite:
#ifdef XLWIP_CONFIG_INCLUDE_EMACLITE
				nif = netif_add(netif, ipaddr, netmask, gw,
					(void*)(UINTPTR)mac_baseaddr,
					xemacliteif_init,
#if NO_SYS
					ethernet_input
#else
					tcpip_input
#endif
					);
#else
				nif = NULL;
#endif
				break;

			case xemac_type_axi_ethernet:
#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET
					nif = netif_add(netif, ipaddr, netmask, gw,
					(void*)(UINTPTR)mac_baseaddr,
					xaxiemacif_init,
#if NO_SYS
					ethernet_input
#else
					tcpip_input
#endif
					);
#else
				nif = NULL;
#endif
				break;

#if defined (__arm__) || defined (__aarch64__)
			case xemac_type_emacps:
#ifdef XLWIP_CONFIG_INCLUDE_GEM
                nif = netif_add(netif, ipaddr, netmask, gw,
						(void*)(UINTPTR)mac_baseaddr,
						xemacpsif_init,
#if NO_SYS
						ethernet_input
#else
						tcpip_input
#endif

						);
#endif
				break;
#endif
			default:
				xil_printf("unable to determine type of EMAC with baseaddress 0x%08lx\r\n",
						(UINTPTR)mac_baseaddr);
	}

	#ifdef OS_IS_FREERTOS
		/* Start thread to detect link periodically for Hot Plug autodetect */
		sys_thread_new("link_detect_thread", link_detect_thread, netif,
				THREAD_STACKSIZE, tskIDLE_PRIORITY);
	#endif

	return nif;
}

引数のmac_baseaddrでMAC種別ごとにswitchしているのがわかります。おそらく、

case xemac_type_axi_ethernet:ではなく、
case xemac_type_emacps:に分岐したのだと予想できます。

mac_baseaddrの値を調べます。Application側でPLATFORM_EMAC_BASEADDRとしてマクロ定義されています。platform_config.h内に定義されていました。

C
//platform_config.h
#ifndef __PLATFORM_CONFIG_H_
#define __PLATFORM_CONFIG_H_

#define PLATFORM_EMAC_BASEADDR 0xff0b0000

#endif

UG1087によると、物理ベースアドレス0xff0b0000はPS側のGEM0になります。これまでのなぜかPS側GEMのMACドライバが動作している原因はこれのようです。

なぜ、GEM0が選択されているのでしょうか、似たファイル名でplatform_config.h.inがありました。

C
//platform_config.h.in
#ifndef __PLATFORM_CONFIG_H_
#define __PLATFORM_CONFIG_H_

#cmakedefine PLATFORM_EMAC_BASEADDR @PLATFORM_EMAC_BASEADDR@

#endif

この文法はCMakeコマンドconfigure_file()で使用する、ソースファイル生成のための入力テンプレートです。@PLATFORM_EMAC_BASEADDR@という記述は同名のCMake変数の内容を文字列として置換し、別のソースファイルとして出力できます。つまるところ、platform_config.hファイルはCMakeスクリプトで自動生成されたファイルになります。

ここで注意点ですが、Vitis Unified IDE(新Vitis)のみCMakeビルドシステムが導入されています。旧Vitisはどうなっているかわかりません。

configure_file()を実装しているCMakeスクリプトは以下のfreertos_lwip_echo_server.cmakeになります。

CMake
# 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()

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()

if (MAC_INSTANCES IN_LIST EMACLITE_NUM_DRIVER_INSTANCES)
    LIST_INDEX(${index} ${MAC_INSTANCES} "${EMACLITE_NUM_DRIVER_INSTANCES}")
    list(GET TOTAL_EMACLITE_PROP_LIST ${index} prop_list)
endif()

if (MAC_INSTANCES IN_LIST EMACPS_NUM_DRIVER_INSTANCES)
    LIST_INDEX(${index} ${MAC_INSTANCES} "${EMACPS_NUM_DRIVER_INSTANCES}")
    list(GET TOTAL_EMACPS_PROP_LIST ${index} prop_list)
endif()

set(y ${${prop_list}})
list(GET y 0 reg)
set(PLATFORM_EMAC_BASEADDR "${reg}")
configure_file(${CMAKE_SOURCE_DIR}/platform_config.h.in ${CMAKE_BINARY_DIR}/include/platform_config.h)

CMakeスクリプトの内容

L.5-7では、TOTAL_MAC_INSTANCES変数にAXI-Ethernet、AXI-EMACLite、EMACPS(PS GEM)の3種の有効なMACのリストを作成しています。有効なMACはSDT(System Device Tree)から取得される模様です。SDTは新Vitisで導入されたハードウェア情報管理の仕組みです。

L.9ではキャッシュ変数MAC_INSTANCESにTOTAL_MAC_INSTANCESを代入しています。

L.13ではMAC_INSTANCES内のMACのリストの内、0番目の要素を同変数に再代入していますMAC_INSTANCES内のMACがここで1つに選択されます

L.18-32では、MAC_INSTANCESのMACを、AXI-Ethernet、AXI-EMACLite、EMACPS(PS GEM)の3種のMAC別にベースアドレスに変換しています。

L.36-37でPLATFORM_EMAC_BASEADDR変数に物理ベースアドレスを代入し、configure_file()コマンドを実行しています。

試しにTOTAL_MAC_INSTANCESとMAC_INSTANCES変数の中身を見てみましょう。print文的なもので出力するには以下2つがお手軽そうです。

  • message()コマンド:コンソールに表示する
  • file()コマンド:ファイルを作成やその他の操作をする
CMake
#例
message(STATUS "TOTAL_MAC_INSTANCES = ${TOTAL_MAC_INSTANCES}\n")
file(WRITE switch_mactype_cmake.txt "read TOTAL_MAC_INSTANCES = ${TOTAL_MAC_INSTANCES}\n")

Vitis IDEのビルドコンソールでメッセージを見るのは辛いので、ファイルに出力するようにします。file()コマンドをfreertos_lwip_echo_server.cmake L.11とL.17に追加します。すると、CMakeファイルと同階層に以下の出力ファイルが得られました。

CMake
#以下をL.11とL.17に追加
file(WRITE switch_mactype_cmake.txt "TOTAL_MAC_INSTANCES = ${TOTAL_MAC_INSTANCES}\n")
file(APPEND switch_mactype_cmake.txt "MAC_INSTANCES = ${MAC_INSTANCES}\n")

Plaintext
TOTAL_MAC_INSTANCES = psu_ethernet_0;psu_ethernet_1;axi_ethernet_0
MAC_INSTANCES = psu_ethernet_0

TOTAL_MAC_INSTANCESの0要素目”psu_ethernet_0″を選択したということになります。psuはPSを指すのでGEM0のMACということになります。”axi_ethernet_0″が今回動かしたいAXI-EtherMACになります。

2-6. 原因判明、パケットが出る

CMakeでPS GEM0のMACドライバが選択される理由が判明しました。VivadoのプリセットでPS GEM0 GEM1が有効になっているために、そちらが選択されてしまったということになります。すぐ思いつく応急対策は次の2つです。

  • VivadoでPS GEM0,GEM1を無効化する
  • CMakeのMAC_INSTANCES変数を”axi_ethernet_0″で上書きする

今回は後者でやってみます。

freertos_lwip_echo_server.cmake L.17に以下の記述を追加します。

CMake
set(MAC_INSTANCES "axi_ethernet_0")

再ビルドしてUARTログを確認します。

Plaintext
[17:12:19:234] Zynq MP First Stage Boot Loader ␊
[17:12:19:234] ␍Release 2024.1   Jun 26 2024  -  08:11:27␍␊
[17:12:19:234] PMU-FW is not running, certain applications may not be supported.␊
[17:12:19:234] ␍␍␊
[17:12:40:113] ␍␊
[17:12:40:113] -----lwIP Socket Mode Echo server Demo Application ------␍␊
[17:12:40:113] rx_bdspace: 0x00300000␍␊
[17:12:40:113] tx_bdspace: 0x00310000␍␊
[17:12:40:113] rxringptr: 0x00200180␍␊
[17:12:40:130] txringptr: 0x00200114␍␊
[17:12:41:121] XAxiEthernet detect_phy: PHY detected at address 2.␍␊
[17:12:41:137] XAxiEthernet detect_phy: PHY detected.␍␊
[17:12:41:137] Start PHY autonegotiation ␍␊
[17:12:41:137] XAxiEthernet detect_phy: PHY detected at address 2.␍␊
[17:12:41:137] XAxiEthernet detect_phy: PHY detected.␍␊
[17:12:41:154] Start PHY autonegotiation ␍␊
[17:12:41:154] Waiting for PHY to complete autonegotiation.␍␊
[17:12:44:225] autonegotiation complete ␍␊
[17:12:44:225] auto-negotiated link speed: 1000␍␊
[17:12:44:241] netif: added interface te IP addr 0.0.0.0 netmask 0.0.0.0 gw 0.0.0.0␊
[17:12:44:241] netif: setting default interface te␊
[17:12:44:241] dhcp_start(netif=200040) te0␊
[17:12:44:241] dhcp_start(): mallocing new DHCP client␊
[17:12:44:257] dhcp_start(): allocated dhcp␊
[17:12:44:257] dhcp_start(): starting DHCP configuration␊
[17:12:44:257] dhcp_discover()␊
[17:12:44:257] transaction id xid(4BB5F646)␊
[17:12:44:257] dhcp_discover: making request␊
[17:12:44:257] dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER)␊
(...略)

AXI-EtherMACドライバが動作してPHYオートネゴシエーションも成功しました。Phyアドレス=0x02も自動で見つけたようです。何故か2回ネゴシエーションが行われていますが、正常に1000Mbpsでリンクアップしているので、とりあえず良しとします。

ケーブルを直結したPCでキャプチャします。

DHCP DISCOVERパケットが出てきました。LwIPとAXI-Ethernet MACとAXI-DMAまわりは問題なく動いていそうということがわかり、ほっとしました。このあたり動かなかったらかなりの大仕事間違いなしです。ちなみにDHCP DISCOVERがリトライされているのはPCのLANポートでDHCPサーバを動かしていないためです。

ルータ配下のネットワークにつないでDHCPでIP取得とEcho Serverサンプルの動作を試みます。以下のネットワーク構成で行いました。

ポートミラーHubとはポートミラーリング機能付きのスイッチングHubを指します。KR260の接続ポートのパケットをキャプチャPCのポートに転送します。今回はNETGEAR製のGS105Eを使用しています。

Wiresharkの表示フィルタをeth.addr == 00:0a:35:00:01:02に設定し直します。KR260のMACアドレスが正常に設定されたのを確認したためです。

実際に動かしてみると、あれ、、、DHCP DISCOVERのリトライを繰り返して、DHCPが完了しません。残念ながらまだ動かないようです。

3.調査前半戦まとめ

CR5でPL Ethernetを使うことに挑戦した際の調査の過程を書きました。だらだらと長くなってしまったので、ここまで読んでいただけた方は本当に尊敬いたします。

毎度のことですが、公式が手順書を公開していない、いわゆる簡単に動かせなさそうなテーマをあえて選んでいるため、それなりの苦労はありますが、得られる経験は非常に価値があると感じています。

前半戦ではAXI-EtherMACとPHYを起動して、実際にパケットを出すところまで出来ました。後半戦はMACの仕様とWiresharkのちょっとした落とし穴にはまりますが、無事通信できるようになります。

前半戦の事の運びがうまく進んだのは新VitisのCMake採用が大きいと感じています。printデバッグが出来るのが強みでした。旧Vitisだとどう調べればよいかそもそもわかりません。Eclipseベースから方向転換するのはAMDさんにとって並大抵の判断ではなかったと思いますが、個人的には応援したいと思います。

CMakeあまり慣れていなかったのですが、公式ドキュメントの読込みやデバッグもしてみて、そこそこスキル上がった気がします。オープンソースのライブラリもCMake対応しているものが多いので、そのうちなにか動かしてみたいですね。

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