xapp1078をzyboで動かすまで(Xilinx v2017.3ツールチェイン)

ソースは
https://github.com/shohei/xapp1078_2014.4_zybo
を使う

1. xapp1078のビットストリームをVivadoで合成する。

design/work/project_2/project_2.xprを起動する。
IPは自動アップグレードする。

Generate bitstreamでビットストリームを生成する。
Export Hardwareでbitstreamを含んだHardwareをエクスポートする。
Launch SDKSDKを起動する

2. BSPおよびapp_cpu1プロジェクトの作成

GitHubレポジトリのdesign/work/project_2/project_2.sdk/以下にあるコードは
どこかに退避させるかすべて削除する。(これらはあとで使うので削除した場合は再度クローンしてくる)
app_cpu1アプリケーションプロジェクトに、退避させた以下のコードを入れてRefreshすることでプロジェクトに反映する。

  • app_cpu1.c
  • app_cpu1.h
  • my_utils.c
  • my_utils.h
  • scu_sleep.c
  • scu_sleep.h
  • lscript.ld

アプリケーションプロジェクトは特に変更の必要はない。
ここで重要なのはリンカースクリプトlscript.ld。
ベアメタルがアクセスできるDDR3のベースアドレスが変更されている。

次にBSPを修正する。
Board support package settingsから、drivers->ps7_cortexa1でコンパイラの設定を行う。
extra_compiler_flagsに、-g -DUSE_AMP=1を追加する。
(※standalne->stdin/stdoutをnoneにすると、後述のsoftUart.elfが実行できなくなるのでいじらない)
ブート時のメモリ読み込みに関する設定?をする。
(※1. これをしないとCPU0がCPU1を呼び出したときにハングする)
(※2. libsrcの下にあるファイルはBSPの設定を変更すると上書きされる?BSPを設定し直す度に修正する必要があるのかも )
app_cpu1_bsp/ps7_cortexa9_1/libsrc/standalone_v6_4/src/boot.S

〜(中略)〜
#ifdef SHAREABLE_DDR
	/* Mark the entire DDR memory as shareable */
	ldr	r3, =0x3ff			/* 1024 entries to cover 1G DDR */
	ldr	r0, =TblBase			/* MMU Table address in memory */
	ldr	r2, =0x15de6			/* S=b1 TEX=b101 AP=b11, Domain=b1111, C=b0, B=b1 */
shareable_loop:
	str	r2, [r0]			/* write the entry to MMU table */
	add	r0, r0, #0x4			/* next entry in the table */
	add	r2, r2, #0x100000		/* next section */
	subs	r3, r3, #1
	bge	shareable_loop			/* loop till 1G is covered */
#endif

########### ここから ##################

	/* In case of AMP, mark address 0x00000000 - 0x17ffffff DDR as unassigned/reserved */
	/* and address 0x30000000 - 0x3fffffff DDR as inner cached only */
#if USE_AMP==1
	ldr	r3, =0x17f			  /* 384 entries to cover 384MB DDR updated by Hu Xiaolei */
	ldr	r0, =TblBase			/* MMU Table address in memory */
	ldr	r2, =0x0000			  /* S=b0 TEX=b000 AP=b00, Domain=b0, C=b0, B=b0 */
mmu_loop:
	str	r2, [r0]			    /* write the entry to MMU table */
	add	r0, r0, #0x4			/* next entry in the table */
	add	r2, r2, #0x100000	/* next section */
	subs	r3, r3, #1
	bge	mmu_loop			    /* loop till 384MB is covered */

	ldr	r3, =0x07f			  /* 128 entries to cover 128MB DDR */
	movw r2, #0x4de6		  /* S=b0 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b1 */
	movt r2, #0x1800      /* S=b0, Section start for address 0x30000000 */
mmu_loop1:
	str	r2, [r0]			    /* write the entry to MMU table */
	add	r0, r0, #0x4			/* next entry in the table */
	add	r2, r2, #0x100000	/* next section */
	subs	r3, r3, #1
	bge	mmu_loop1			    /* loop till 128MB is covered */
#endif

########### ここまでを追加する ##################

	mrs	r0, cpsr			/* get the current PSR */
	mvn	r1, #0x1f			/* set up the irq stack pointer */
	and	r2, r1, r0
	orr	r2, r2, #0x12			/* IRQ mode */
	msr	cpsr, r2
	ldr	r13,=IRQ_stack			/* IRQ stack pointer */

これでapp_cpu1.elfの準備ができた。
LinuxアプリケーションsoftUartの準備もしておく。
New ApplicationからOSにLinuxを選び、Empty Applicationを作成する。
以下のファイルをアプリケーションにImportしてビルドし、softUart.elfを作成する。
/design/src/apps/softUart/softUart.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <byteswap.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <sys/mman.h>

#define PAGE_SIZE ((size_t)getpagesize())
#define PAGE_MASK ((uint64_t)(long)~(PAGE_SIZE - 1))

#define COMM_BASE       0xFFFF9000
#define COMM_TX_FLAG_OFFSET    0x00
#define COMM_TX_DATA_OFFSET    0x04
#define COMM_RX_FLAG_OFFSET    0x08
#define COMM_RX_DATA_OFFSET    0x0C

#define MAX_STR 256

int main()
{
    int fd;
    uint32_t value=0;
    uint32_t flag=0;
    volatile uint8_t *mm;
    uint8_t str[MAX_STR];
    int rcnt;

    fd = open("/dev/mem", O_RDWR|O_SYNC);
    if (fd < 0) {
        fprintf(stderr, "open(/dev/mem) failed (%d)\n", errno);
        return 1;
    }

    mm = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, COMM_BASE);
    if (mm == MAP_FAILED) {
        fprintf(stderr, "mmap64(0x%x@0x%x) failed (%d)\n",
                PAGE_SIZE, (uint32_t)(COMM_BASE), errno);
        return 1;
    }

    rcnt = 0;
    while(1) {

    	//read
    	if( (flag = *(volatile uint32_t *)(mm + COMM_TX_FLAG_OFFSET)) ) {
        	value = *(volatile uint32_t *)(mm + COMM_TX_DATA_OFFSET);

    		//process non-string type data
        	if(flag > 1) {
        		printf("CPU1: 0x%08x = 0x%08x\n", (uint32_t)(mm + COMM_TX_DATA_OFFSET), value);

        	//process string type data
        	} else {

        		if(rcnt < MAX_STR) {
        			str[rcnt++] = value;
        		}
        		if(value == '\n') {
        			if(rcnt != 0) {
        				str[rcnt-1] = '\0';
        			} else {
        				str[0] = '\0';
        			}
        			printf("CPU1: %s\n", str);
        			rcnt = 0;
        		}
        	}
    		*(volatile uint32_t *)(mm + COMM_TX_FLAG_OFFSET) = 0;
    	}

    }

    munmap((void *)mm, PAGE_SIZE);
    close(fd);

    return 0;
}

3. petalinuxでLinuxシステムを作成する

Vivadoでエクスポートしたsystem_wrapper.hdfを使ってhw-descriptionを設定する。

$ petalinux-create -t project -n plnx-project --template zynq
$ petalinux-config --get-hw-description=<path to system_wrapper.hdf directory>
-> Image Packing configuration
 -> Root file system type
  -> SD card
  -> /dev/mmcblk0p12 (Device node of SD device)

$ petalinux-config -c u-boot
-> Boot images
  -> Support flattened image treeのチェックを外す(image.ubではなくuImageを見に行くようにする)
-> Boot media
  -> Support for booting from SD/EMMCにチェックを入れる

$ petalinux-config -c kernel
-> 特に設定は必要ない

$ petalinux-config -c rootfs
-> peek-poke appを有効にする

$ petalinux-build

次にBIFファイルを以下のように用意する
bootimage.bif

the_ROM_image:
{

  [bootloader] zynq_fsbl.elf
               system_wrapper.bit
	       u-boot.elf
	       app_cpu1.elf

}

次のコマンドでBOOT.BINを生成する。なお、app_cpu1.elfはカレントディレクトリ(plnx-project/images/linux)に移しておく

$ bootgen -image bootimage.bif -o i BOOT.BIN -w on

次にデバイスツリーを修正する
system.dtbからsystem.dtsを作成する

dtc -O dts -I dtb -o system.dts system.dtb

system.dtsを開いて以下を修正する

  • bootargsにmem=384M maxcpus=1を追加
  • memoryのregをreg = <0x0 0x18000000>に修正
〜(中略)〜

        chosen {
		bootargs = "console=ttyPS0,115200 earlyprintk root=/dev/mmcblk0p2 rw rootwait mem=384M maxcpus=1";
		stdout-path = "serial0:115200n8";
	};

	aliases {
		ethernet0 = "/amba/ethernet@e000b000";
		serial0 = "/amba/serial@e0001000";
		spi0 = "/amba/spi@e000d000";
	};

	memory {
		device_type = "memory";
		reg = <0x0 0x18000000>;
	};
〜(中略)〜

修正したsystem.dtsからsystem.dtbを再度作成する。

$ dtc -I dts -O dtb -o system.dtb system.dts

u-bootの設定ファイルは以下のように用意する。
https://gist.github.com/shohei/93cf5d59b561ab2d65388133f9c5a5e1
uEnv.txt

kernel_image=uImage
devicetree_image=system.dtb
netstart=0x10000000
dtbnetstart=0x11800000
bootargs=console=ttyPS0,115200 rw rootwait earlyprintk root=/dev/mmcblk0p2 rootfstype=ext4 maxcpus=1 mem=384M
uenvcmd=mmcinfo && echo bootargs ${bootargs} && fatload mmc 0 ${netstart} ${kernel_image} && fatload mmc 0 ${dtbnetstart} ${devicetree_image} && bootm ${netstart} - ${dtbnetstart}

カーネルイメージuImageを作成する

$ petalinux-package --image -c kernel --format uImage

4. 作成したファイル群をSDカードに書き込む

$ sudo mkdir /mnt/boot
$ sudo mount /dev/sdc1 /mnt/boot
$ sudo cp BOOT.BIN /mnt/boot
$ sudo cp uImage /mnt/boot
$ sudo cp uEnv.txt /mnt/boot
$ sudo cp system.dtb /mnt/boot
$ sudo umount /mnt/boot
$ sudo mkdir /mnt/rootfs
$ sudo mount /dev/sdc2 /mnt/rootfs
$ sudo tar -xf rootfs.tar.gz -C /mnt/rootfs
$ sudo umount /mnt/rootfs

5. ZyboをSDカードから起動する

gtktermでzyboに接続する

$ sudo gtkterm &

アカウントroot/rootでPetalinuxに入る

# mount /dev/mmcblk0p1 /mnt
# cd /mnt
# ls
BOOT.BIN softUart.elf system.dtb uEnv.txt uImage
# softUart.elf &
[1] 1078 //ソフトシリアルを起動する

# peek 0xffff8000 
0x00000000
# peek 0xfffffff0 0x18000000 //CPU1を起動する→サンプルでは ./rwmem 0xfffffff0 0x30000000となっていた
# peek 0xffff8000 
0x00000001 //1秒毎にカウントアップされる
# poke 0x78600000 1
CPU1: IRQ clr0 //IPコア irq_genが叩かれ、CPU1のPPIに割込みをかける