Creating Xilinx Linux SD boot image for Zybo Z7-20 on Windows10 (Ubuntu16.04 WSL)

# Environment

- Ubuntu 16.04 is installed on WSL.
- Vivado 2018.3 WebPack is installed both on Windows and WSL Ubuntu 16.04 to default paths.

Maybe it can be done only with WSL Ubuntu 16.04 . (But my vcXsrv often freezes with GUI applications.)

# Working path
- Windows 10
```d:\xlnx```

- WSL Ubuntu 16.04
```/mnt/d/xlnx```

# Install Digilent board files into Vivado
```
$ sudo apt install git
$ git clone https://github.com/Digilent/vivado-boards
```
Then copy ```./vivado-boards/new/board_files/*``` to ```C:\Xilinx\Vivado\2018.3\data\boards\board_files``` (See ['Digilent board file installation guide'][1])

[1]:https://reference.digilentinc.com/reference/software/vivado/board-files?redirect=1

# Create a CPU design for Zybo Z7-20 (Vivado)
Invoke Vivado on Windows 10.

1. Select `File->Project->New`
1. Click "Next" and "Next", setting:
 - Project Name : `zybo_z7_20_linux`
 - Project Path : `d:/xlnx`
 - Board : `Zybo Z7-20`
1. Create Block Design
 - Design Name: `cpu`
1. Click `+` in diaglam window
1. Select `ZYNQ7 Processing System`
1. Run `Block Automation` and click `OK`
1. Connect `FCLK_CLK0` to `M_AXI_GP0_ACLK`
1. Create HDL wrapper (A pop-up menu appears with right click on `cpu` in Sources window.), click `OK`.
1. Click `Generate Bitstream`
1. Select `File->Export->Hardware`, check `Include bitstream`, click `OK`

# Setting environmet variables 
Create following `env.sh`

```env.sh
#!/bin/sh

export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm
export KBUILD_DIR=/mnt/d/xlnx/linux-xlnx-xilinx-v2018.3
export PATH=$PATH:/mnt/d/xlnx/u-boot-xlnx/tools
source /tools/Xilinx/Vivado/2018.3/settings64.sh
```
Apply it.

```
$ source env.sh
```

# Build Xilinx u-boot

Because [u-boot-xlnx v2018.3][2] does not have Zybo Z7-20 configuration, need to clone snapshot
[2]:https://github.com/Xilinx/u-boot-xlnx/archive/xilinx-v2018.3.tar.gz

```
$ git clone https://github.com/Xilinx/u-boot-xlnx
```
The hash of the commit was,

```
$ git rev-parse HEAD
195c620e348891ca2d90c759781413f2adb3f748
```

Append the following line to `u-boot-xlnx/configs/zynq_zybo_z7_defconfig `

```
CONFIG_OF_EMBED=y
CONFIG_CMD_NET=n
```
`CONFIG_OF_EMBED=y` embeds device tree for the board into binary. `CONFIG_CMD_NET=n` prevents  `BOOTP` tries few times before `bootm` runs.
 If the system does not have flex and bison,

```
$ sudo apt install flex bison
```

Build u-boot.

```
$ cd u-boot-xlnx
$ make zynq_zybo_z7_defconfig
$ make
$ cd ..
```

# Build Xilinx Linux kernel

```
$ wget https://github.com/Xilinx/linux-xlnx/archive/xilinx-v2018.3.tar.gz
$ tar zxvf xilinx-v2018.3.tar.gz
$ rm xilinx-v2018.3.tar.gz
$ cd linux-xilinx-v2018.3
$ make ARCH=arm xilinx_zynq_defconfig
$ make ARCH=arm UIMAGE_LOADADDR=0x8000 uImage
$ cd ..
```

# Build DTC
```
$ git clone https://git.kernel.org/pub/scm/utils/dtc/dtc.git
$ cd dtc
$ make
$ cp dtc ../u-boot-xlnx/tools
$ cd ..
```

# Create RAM disk image (see [Xilinx wiki][3])
Download `arm_ramdisk.image.gz` to /`mnt/d/xlnx` (`d:/xlnx`) from [here][3]. Then create  RAM disk image.

```
mkimage -A arm -T ramdisk -C gzip -d arm_ramdisk.image.gz uramdisk.image.gz
```
Because WSL Ubuntu does not have loop device, we cannot mount the root file system as in [Xilinx wiki][3]. To modify the image, other Linux computer should be prepared on VMs or PCs. 
## My own modification on rootfs
1. __Eliminating two lines of `fstab` mounting SD card to `/mnt`__ : I do not know the exact reason why the two lines do not work properly. (`/dev/mmcblk0p1` may not be prepared when the auto-mount is invoked.)
2. __Inserting '`mount /dev/mmcblk0p1 /mnt`' into `/etc/init.d/rcS` just before executing `/mnt/init.sh`__.: `init.sh` placed at root of the SD card is automatically executed at the end of `rcS`.  So `init.sh` is a convenient mean for us to insert start-up operation without modifying rootfs.

[3]: https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842473/Build+and+Modify+a+Rootfs

# Create FSBL and Device Tree Blob (XSDK on WSL Ubuntu 16.04)
First, download device tree files.

```
$ wget https://github.com/Xilinx/device-tree-xlnx/archive/xilinx-v2018.3.tar.gz
$ tar zxvf xilinx-v2018.3.tar.gz
$ rm xilinx-v2018.3.tar.gz
```
Invoke XSDK.

```
$ xsdk &
```

## Create hardware platform specification

1. Set workspace directory as `/mnt/d/xlnx`
1. Select File->New->Project, select `Xilinx->'Hardware Platform Specification'`, click `Next`
1. Set `project name` to `zybo_z7_20_linux_hw`, set `Target Hardware Specification` to `/mnt/d/xlnx/zybo_z7_20_linux/zybo_z7_20_linux.sdk/cpu_wrapper.hdf`, click `Finish`

## Create FSBL
1. Select `File->New->Application Project`, set `project name` to `zybo_z7_20_linux_fsbl`
2. Confirm `zybo_z7_20_linux_hw` is selected as `Hardware Platform`
3. Click `Next`, Select `Zynq FSBL`, click `Finish`

## Create devicetree
1. Select `Xilinx->Repositories`, select `/mnt/d/xlnx/device-tree-xlnx-xilinx-v2018.3` in `Global Repositories`, click `Rescan Repositories`, `Apply`, then `OK`
2. Click `File > New > Board Support Package`, set `Project name` to `zybo_z7_20_linux_dt_0`, select 'device_tree' in 'Board Support Package OS', click 'Finish', then 'OK'

## Modify and build device tree
At `/mnt/d/xlnx/zybo_z7_20_linux_dt_0` (WSL Ubuntu 16.04), revise following lines in `system-top.dts`

```system-top.dts
09 #include "zynq-7000.dtsi"
10 #include "pcw.dtsi"
21         memory {
```

``` system-top.dts(revised)
09 /include/ "zynq-7000.dtsi"
10 /include/ "pcw.dtsi"
21         memory@0 {
```

Insert the description of Ethernet PHY  into `&gem0` node in `system.dts`

```system.dts
&gem0 {
	local-mac-address = [00 0a 35 00 00 00];
	phy-handle = <&ethernet_phy>;
	ethernet_phy: ethernet-phy@0 {
		      reg = <0>;
	};
};

```

Compile device tree as,

```
dtc -I dts -O dtb -o devicetree.dtb system-top.dts
```


# Create boot image
At /mnt/d/xlnx (WSL Ubuntu 16.04),

```
$ mkdir zybo_z7_20_linux_boot
$ cd zybo_z7_20_linux_boot
$ cp ../zybo_z7_20_linux_hw/cpu_wrapper.bit fpga.bit
$ cp ../zybo_z7_20_linux_fsbl/Debug/zybo_z7_20_linux_fsbl.elf fsbl.elf
$ cp ../u-boot-xlnx/u-boot u-boot.elf
$ cp ../linux-xlnx-xilinx-v2018.3/arch/arm/boot/uImage uImage
$ cp ../uramdisk.image.gz uramdisk.image.gz
$ cp ../zybo_z7_20_linux_dt_0/devicetree.dtb devicetree.dtb
```

Create `boot.bif` as

```boot.bif
image : {
      [bootloader]fsbl.elf
      fpga.bit
      u-boot.elf
}
```
Run bootgen

```
bootgen -image boot.bif -o i boot.bin
```


# Boot
Move follwing files to a SD card.
- boot.bin
- devicetree.dtb
- uramdisk.image.gz
- uImage

1. Insert SD card to the slot.
1. Confirm 'JP5' is in SD boot configuration.
1. Connect USB cable to PROG/UART port
1. Slide power switch and open the serial terminal with 115200 bps.
![IMAG0406.jpg](https://qiita-image-store.s3.amazonaws.com/0/373727/29972ce6-503d-178b-3d88-5607e7a59561.jpeg)
<img width="494" alt="zybo-boot.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/373727/e26b528b-0bd3-297e-b6e5-33e2dd00b36b.png">

#Appendix
## Kernel configuration
First, install ncurses-dev if the system does not have.
```
$ sudo apt install ncurses-dev
```
Then, execute following at the root of the kernel source.
```
$ make ARCH=arm menuconfig
```