ZYBO用U-Boot で MAC アドレスを SPI ROM から読む

# はじめに


筆者は、次の記事で示すように https://www.denx.de/wiki/U-Boot が提供しているu-boot をカスタマイズして使っています。

* [「ZYBO用U-Bootを、ブート時にuEnv.txtを読むようにして、かつBOOT.binから起動できるようにする」 @Qiita](https://qiita.com/ikwzm/items/1734676d787e2693df47)
* [「ZYBO用U-Bootでブート時にメニューを表示させる」 @Qiita](https://qiita.com/ikwzm/items/c7932ebd1a7b40919241)


https://www.denx.de/wiki/U-Boot が提供している ZYNQ 用の u-boot では、MAC アドレスは I2C ROM から読むか、環境変数 ethaddr に設定しておくかしか出来ません。もし設定されていなければ、乱数を使って MAC アドレスを設定します。

しかし、ZYBO-Z7 では SPI ROM (S25FL128S 4-bit Quad-SPI serial NOR flash) の OTP(One-Time-Programmable) 領域に MAC アドレスに格納されています。この記事では https://www.denx.de/wiki/U-Boot が提供している ZYNQ 用の u-boot を SPI ROM から MAC アドレスを読んで設定するようにソースコードを修正する方法を説明します。

なお、この修正を施した ZYBO-Z7 用の U-Boot のイメージを次の URL で公開しています。

* https://github.com/ikwzm/FPGA-SoC-U-Boot-ZYBO-Z7

# U-Boot のビルドの手順

## U-Boot のダウンロード

```console
shell$ git clone git://git.denx.de/u-boot.git 
shell$ cd u-boot
```

## U-Boot v2017.11 をチェックアウトして作業用のブランチを作る

この記事では v2017.11 を元に変更します。ここでは ZYBO-Z7 用の U-Boot を作る場合の例を示します。


```console
shell$ git checkout -b u-boot-2017.11-zynq-zybo-z7 refs/tags/v2017.11
```

## board/xilinx/zynq/board.c を修正する

board/xilinx/zynq/board.c に spi 関連のヘッダファイルをインクルードするようにします。

```C:board/xilinx/zynq/board.c
#include <fdtdec.h>
#include <fpga.h>
#include <mmc.h>
#include <spi.h>                 // 追加
#include <spi_flash.h>           // 追加
#include <zynqpl.h>
#include <asm/arch/hardware.h>
#include <asm/arch/sys_proto.h>
```

zynq_board_read_rom_ethaddr() に SPI から読むコードを追加します。具体的には、次の ```#if defined(CONFIG_ZYNQ_QSPI) && defined(CONFIG_ZYNQ_GEM_SPI_MAC_OFFSET)``` から ```#endif``` までのコードを追加します。

```C:board/xilinx/zynq/board.c
int zynq_board_read_rom_ethaddr(unsigned char *ethaddr)
{
#if defined(CONFIG_ZYNQ_GEM_EEPROM_ADDR) && \
    defined(CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET)
	if (eeprom_read(CONFIG_ZYNQ_GEM_EEPROM_ADDR,
			CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET,
			ethaddr, 6))
		printf("I2C EEPROM MAC address read failed\n");
#endif

#if defined(CONFIG_ZYNQ_QSPI) &&                \
    defined(CONFIG_ZYNQ_GEM_SPI_MAC_OFFSET)
#define CMD_OTPREAD_ARRAY_FAST		0x4b
	struct spi_flash *flash;
	flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS,
				CONFIG_SF_DEFAULT_CS,
				CONFIG_SF_DEFAULT_SPEED,
				CONFIG_SF_DEFAULT_MODE);
	if (!flash) {
		printf("SPI(bus:%u cs:%u) probe failed\n",
			CONFIG_SF_DEFAULT_BUS,
			CONFIG_SF_DEFAULT_CS);
		return 0;
	}
	/* set the cmd to otp read */
	flash->read_cmd = CMD_OTPREAD_ARRAY_FAST;
	if (spi_flash_read(flash, CONFIG_ZYNQ_GEM_SPI_MAC_OFFSET, 6, ethaddr))
		printf("SPI MAC address read failed\n");

	if (flash)
		spi_flash_free(flash);
#endif
	return 0;
}
```

## include/configs/zynq_zybo_z7.h を修正する

ボード依存の設定用ヘッダファイルに CONFIG_ZYNQ_GEM_SPI_MAC_OFFSET を設定します。

ZYBO-Z7 のリファレンスマニュアル( https://reference.digilentinc.com/reference/programmable-logic/zybo-z7/reference-manual )によれば、

>On an Ethernet network each node needs a unique MAC address. To this end, the one-time-programmable (OTP) region of the Quad-SPI flash has been programmed at the factory with a 48-bit globally unique EUI-48/64™ compatible identifier. The OTP address range [0x20;0x25] contains the identifier with the first byte in transmission byte order being at the lowest address.

ということで、CONFIG_ZYNQ_GEM_SPI_MAC_OFFSET に 0x20 を設定します。

```C:include/configs/zynq_zybo_z7.h
/*
 * (C) Copyright 2012 Xilinx
 * (C) Copyright 2014 Digilent Inc.
 *
 * Configuration for Zynq Development Board - ZYBO
 * See zynq-common.h for Zynq common configs
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#ifndef __CONFIG_ZYNQ_ZYBO_Z7_H
#define __CONFIG_ZYNQ_ZYBO_Z7_H

#define CONFIG_ZYNQ_I2C0
#define CONFIG_ZYNQ_I2C1
#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN	1
#define CONFIG_DISPLAY
#define CONFIG_I2C_EDID

#define CONFIG_ZYNQ_GEM_SPI_MAC_OFFSET 0x20

#include <configs/zynq-common.h>

#endif /* __CONFIG_ZYNQ_ZYBO_Z7_H */
```

## scripts/config_whitelist.txt に CONFIG_ZYNQ_GEM_SPI_MAC_OFFSET を追加する

v2017.11 では Kconfig に定義されていない CONFIG_XXX 定数(XXX は Kconfig の config 文で定義した名前) を使うと、make 時の最後の CFGCHK フェーズでエラーが出てビルドに失敗します。このエラーを回避するために scripts/config_whitelist.txt に CONFIG_ZYNQ_GEM_SPI_MAC_OFFSET を追加しておきます。

```text:scripts/config_whitelist.txt
   :
 (中略)
   :
CONFIG_ZYNQ_EEPROM_BUS
CONFIG_ZYNQ_GEM_EEPROM_ADDR
CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET
CONFIG_ZYNQ_GEM_SPI_MAC_OFFSET
CONFIG_ZYNQ_HISPD_BROKEN
CONFIG_ZYNQ_I2C0
CONFIG_ZYNQ_I2C1
CONFIG_ZYNQ_SDHCI0
CONFIG_ZYNQ_SDHCI1
CONFIG_ZYNQ_SDHCI_MAX_FREQ
CONFIG_ZYNQ_SDHCI_MIN_FREQ
CONFIG_ZYNQ_SERIAL
CONFIG_eTSEC_MDIO_BUS
```

## U-Boot をビルドする


次のように U-Boot をビルドします。

```console
shell$ export ARCH=arm
shell$ export CROSS_COMPILE=arm-linux-gnueabihf-
shell$ make zynq_zybo_z7_defconfig
shell$ make 
```

# U-Boot の実行

ZYBO-Z7 で U-Boot を起動して次のようにメッセージが出れば成功です。

```console
U-Boot SPL 2017.11 (Apr 19 2018 - 07:44:57)
mmc boot
Trying to boot from MMC1
spl_load_image_fat_os: error reading image system.dtb, err - -2
reading u-boot.img
reading u-boot.img


U-Boot 2017.11 (Apr 19 2018 - 07:44:57 +0900)

Model: Zynq ZYBO Z7 Development Board
Board: Xilinx Zynq
I2C:   ready
DRAM:  ECC disabled 1 GiB
MMC:   sdhci@e0100000: 0
SF: Detected s25fl128s_64k with page size 256 Bytes, erase size 64 KiB, total 16 MiB
In:    serial@e0001000
Out:   serial@e0001000
Err:   serial@e0001000
Model: Zynq ZYBO Z7 Development Board
Board: Xilinx Zynq
Net:   ZYNQ GEM: e000b000, phyaddr 0, interface rgmii-id
SF: Detected s25fl128s_64k with page size 256 Bytes, erase size 64 KiB, total 16 MiB

Warning: ethernet@e000b000 using MAC address from ROM
eth0: ethernet@e000b000
reading uEnv.txt
510 bytes read in 10 ms (49.8 KiB/s)
Importing environment from mmc ...
```

MAC アドレスが設定されると 環境変数 ethaddr が設定されます。U-Boot console から 確認してみましょう。

```console
Zynq> env print ethaddr
ethaddr=aa:bb:cc:dd:ee:ff
```

上の例では MAC アドレスは ```aa:bb:cc:dd:ee:ff``` になっていますが、これはあくまでも参考で、実際はボードに設定されている MAC アドレスが設定されているはずです。ボードに設定されている MAC アドレスは、ボードに貼られているバーコード付きのシールで確認できます。シールは黒いヘッドホンジャックの上あたりにあります。

なお、u-boot で設定した MAC アドレスがどのようにして Linux に渡されるかは次のブログが参考になります。

* [「ZYNQ LinuxのGigabitEtherのMACアドレスはどのように設定されるのか(前編)」@なひたふJTAG日記](http://nahitafu.cocolog-nifty.com/nahitafu/2018/01/zynq.html)
* [「ZYNQ LinuxのGigabitEtherのMACアドレスはどのように設定されるのか(後編)」@なひたふJTAG日記](http://nahitafu.cocolog-nifty.com/nahitafu/2018/01/zynq-linuxgigab.html)

# 参考

* https://www.denx.de/wiki/U-Boot/WebHome
* https://reference.digilentinc.com/reference/programmable-logic/zybo-z7/reference-manual 
* https://github.com/ikwzm/FPGA-SoC-U-Boot-ZYBO-Z7
* [「ZYBO用U-Bootを、ブート時にuEnv.txtを読むようにして、かつBOOT.binから起動できるようにする」 @Qiita](https://qiita.com/ikwzm/items/1734676d787e2693df47)
* [「ZYBO用U-Bootでブート時にメニューを表示させる」 @Qiita](https://qiita.com/ikwzm/items/c7932ebd1a7b40919241)
* [「ZYNQ LinuxのGigabitEtherのMACアドレスはどのように設定されるのか(前編)」@なひたふJTAG日記](http://nahitafu.cocolog-nifty.com/nahitafu/2018/01/zynq.html)
* [「ZYNQ LinuxのGigabitEtherのMACアドレスはどのように設定されるのか(後編)」@なひたふJTAG日記](http://nahitafu.cocolog-nifty.com/nahitafu/2018/01/zynq-linuxgigab.html)