ぼくの技術日誌

日誌って銘打っていますが、更新頻度が…

Zynq PSのUART0を有効化してPetaLinuxで使う

はじめに

ZynqのPSにはUARTが2つ入っています。
Linuxを使用する際にはこのUART1をブートログとコンソールの入出力に使用してるのですが、残り1つを使っていないのは勿体ないですね。
というわけで、今回PSに残るUART0を使用してみました(PLにUARTを作るのはやったことない…)。


この記事の実行環境は下記のとおりです。

VivadoでUART0を有効化

Zybo/ZedboardにはMIO Pmodという端子にPSのI/O(MIO)が出ています。
ここへUART0を配線し、HDFを作成します。

TODO:後でVivadoの設定がわかる図を張る!!

PetaLinuxのビルド

作成したHDFを元にPetaLinuxプロジェクトを作成、ビルドしたうえ、BOOT.BINとimage.ubを作ってPetaLinuxを起動します(前回手順参照)。
これだけでUARTが使えるんじゃないかと思っていたのですが、ダメでした。
シリアルログが途中で途切れてしまいました…

U-Boot 2015.07 (Feb 15 2016 - 11:35:46 +0900)

DRAM:  ECC disabled 512 MiB
MMC:   zynq_sdhci: 0
SF: Detected S25FL256S_64K with page size 256 Bytes, erase size 64 KiB, total 32 MiB
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   Gem.e000b000
U-BOOT for mio_pmod

Gem.e000b000 Waiting for PHY auto negotiation to complete.... done
BOOTP broadcast 1
DHCP client bound to address 192.168.0.5 (3 ms)
Hit any key to stop autoboot:  0
U-Boot-PetaLinux> printenv
autoload=no
baudrate=115200
boot_img=BOOT.BIN
bootcmd=run default_bootcmd
bootdelay=4
bootenvsize=0x20000
bootenvstart=0x500000
clobstart=0x01000000
console=console=ttyPS0,115200
cp_kernel2ram=mmcinfo && fatload mmc 0 ${netstart} ${kernel_img}
default_bootcmd=run cp_kernel2ram && bootm ${netstart}
dnsip=192.168.0.1
dtb_img=system.dtb
dtbnetstart=0x02800000
eraseenv=sf probe 0 && sf erase ${bootenvstart} ${bootenvsize}
ethact=Gem.e000b000
ethaddr=00:0a:35:00:1e:53
fault=echo ${img} image size is greater than allocated place - partition ${img} is NOT UPDATED
gatewayip=192.168.0.1
hostname=mio_pmod
install_boot=mmcinfo && fatwrite mmc 0 ${clobstart} ${boot_img} ${filesize}
install_jffs2=sf probe 0 && sf erase ${jffs2start} ${jffs2size} && sf write ${clobstart} ${jffs2start} ${filesize}
install_kernel=mmcinfo && fatwrite mmc 0 ${clobstart} ${kernel_img} ${filesize}
ipaddr=192.168.0.5
jffs2_img=rootfs.jffs2
kernel_img=image.ub
load_boot=tftpboot ${clobstart} ${boot_img}
load_dtb=tftpboot ${clobstart} ${dtb_img}
load_jffs2=tftpboot ${clobstart} ${jffs2_img}
load_kernel=tftpboot ${clobstart} ${kernel_img}
loadaddr=0x01000000
nc=setenv stdout nc;setenv stdin nc;
netboot=tftpboot ${netstart} ${kernel_img} && bootm
netmask=255.255.255.0
netstart=0x01000000
psserial0=setenv stdout ttyPS0;setenv stdin ttyPS0
sd_update_dtb=echo Updating dtb from SD; mmcinfo && fatload mmc 0:1 ${clobstart} ${dtb_img} && run install_dtb
sd_update_jffs2=echo Updating jffs2 from SD; mmcinfo && fatload mmc 0:1 ${clobstart} ${jffs2_img} && run install_jffs2
sdboot=echo boot Petalinux; mmcinfo && fatload mmc 0 ${netstart} ${kernel_img} && bootm
serial=setenv stdout serial;setenv stdin serial
serverip=192.168.0.9
test_crc=if imi ${clobstart}; then run test_img; else echo ${img} Bad CRC - ${img} is NOT UPDATED; fi
test_img=setenv var "if test ${filesize} -gt ${psize}; then run fault; else run ${installcmd}; fi"; run var; setenv var
update_boot=setenv img boot; setenv psize ${bootsize}; setenv installcmd "install_boot"; run load_boot ${installcmd}; setenv img; setenv psize; setenv installcmd
update_dtb=setenv img dtb; setenv psize ${dtbsize}; setenv installcmd "install_dtb"; run load_dtb test_img; setenv img; setenv psize; setenv installcmd
update_jffs2=setenv img jffs2; setenv psize ${jffs2size}; setenv installcmd "install_jffs2"; run load_jffs2 test_img; setenv img; setenv psize; setenv installcmd
update_kernel=setenv img kernel; setenv psize ${kernelsize}; setenv installcmd "install_kernel"; run load_kernel ${installcmd}; setenv img; setenv psize; setenv installcmd

Environment size: 2671/131068 bytes
U-Boot-PetaLinux> set console console=ttyPS1,115200
U-Boot-PetaLinux> bootcmd
Unknown command 'bootcmd' - try 'help'
U-Boot-PetaLinux> bootm
## Loading kernel from FIT Image at 01000000 ...
   Using 'conf@1' configuration
   Verifying Hash Integrity ... OK
   Trying 'kernel@1' kernel subimage
     Description:  PetaLinux Kernel
     Type:         Kernel Image
     Compression:  gzip compressed
     Data Start:   0x010000f0
     Data Size:    6378185 Bytes = 6.1 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: 0x00008000
     Entry Point:  0x00008000
     Hash algo:    crc32
     Hash value:   29f18b43
   Verifying Hash Integrity ... crc32+ OK
## Loading fdt from FIT Image at 01000000 ...
   Using 'conf@1' configuration
   Trying 'fdt@1' fdt subimage
     Description:  Flattened Device Tree blob
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0x016154a0
     Data Size:    13352 Bytes = 13 KiB
     Architecture: ARM
     Hash algo:    crc32
     Hash value:   8a9d6513
   Verifying Hash Integrity ... crc32+ OK
   Booting using the fdt blob at 0x16154a0
   Uncompressing Kernel Image ... OK
   Loading Device Tree to 07ff9000, end 07fff427 ... OK

Starting kernel ...

Booting Linux on physical CPU 0x0
Linux version 4.0.0-xilinx (root@ssd-vm) (gcc version 4.9.2 (Sourcery CodeBench Lite 2015.05-17) ) #4 SMP PREEMPT Mon Feb 15 11:36:08 JST 2016
CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=18c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
Machine model: mio_pmod
bootconsole [earlycon0] enabled
cma: Reserved 16 MiB at 0x1f000000
Memory policy: Data cache writealloc
PERCPU: Embedded 11 pages/cpu @debcf000 s12672 r8192 d24192 u45056
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 130048
Kernel command line: console=ttyPS0,115200 earlyprintk
PID hash table entries: 2048 (order: 1, 8192 bytes)
Dentry cache hash table entries: 65536 (order: 6, 262144 bytes)
Inode-cache hash table entries: 32768 (order: 5, 131072 bytes)
Memory: 493252K/524288K available (4759K kernel code, 223K rwdata, 1708K rodata, 3028K init, 208K bss, 14652K reserved, 16384K cma-reserved, 0K highmem)
Virtual kernel memory layout:
    vector  : 0xffff0000 - 0xffff1000   (   4 kB)
    fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
    vmalloc : 0xe0800000 - 0xff000000   ( 488 MB)
    lowmem  : 0xc0000000 - 0xe0000000   ( 512 MB)
    pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
    modules : 0xbf000000 - 0xbfe00000   (  14 MB)
      .text : 0xc0008000 - 0xc0658efc   (6468 kB)
      .init : 0xc0659000 - 0xc094e000   (3028 kB)
      .data : 0xc094e000 - 0xc0985de0   ( 224 kB)
       .bss : 0xc0985de0 - 0xc09ba0f4   ( 209 kB)
Preemptible hierarchical RCU implementation.
        Additional per-CPU info printed with stalls.
        RCU restricting CPUs from NR_CPUS=4 to nr_cpu_ids=2.
RCU: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=2
NR_IRQS:16 nr_irqs:16 16
L2C: platform modifies aux control register: 0x72360000 -> 0x72760000
L2C: DT/platform modifies aux control register: 0x72360000 -> 0x72760000
L2C-310 erratum 769419 enabled
L2C-310 enabling early BRESP for Cortex-A9
L2C-310 full line of zeros enabled for Cortex-A9
L2C-310 ID prefetch enabled, offset 1 lines
L2C-310 dynamic clock gating enabled, standby mode enabled
L2C-310 cache controller enabled, 8 ways, 512 kB
L2C-310: CACHE_ID 0x410000c8, AUX_CTRL 0x76760001
slcr mapped to e0804000
zynq_clock_init: clkc starts at e0804100
Zynq clock init
sched_clock: 64 bits at 333MHz, resolution 3ns, wraps every 3298534883328ns
timer #0 at e0808000, irq=17
Console: colour dummy device 80x30
Calibrating delay loop... 1332.01 BogoMIPS (lpj=6660096)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
CPU: Testing write buffer coherency: ok
CPU0: thread -1, cpu 0, socket 0, mpidr 80000000
Setting up static identity map for 0x481788 - 0x4817e0
CPU1: thread -1, cpu 1, socket 0, mpidr 80000001
Brought up 2 CPUs
SMP: Total of 2 processors activated (2664.03 BogoMIPS).
CPU: All CPU(s) started in SVC mode.
devtmpfs: initialized
VFP support v0.3: implementor 41 architecture 3 part 30 variant 9 rev 4
pinctrl core: initialized pinctrl subsystem
NET: Registered protocol family 16
DMA: preallocated 256 KiB pool for atomic coherent allocations
cpuidle: using governor ladder
cpuidle: using governor menu
hw-breakpoint: found 5 (+1 reserved) breakpoint and 1 watchpoint registers.
hw-breakpoint: maximum watchpoint size is 4 bytes.
zynq-ocm f800c000.ocmc: ZYNQ OCM pool: 256 KiB @ 0xe0880000
vgaarb: loaded
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
media: Linux media interface: v0.10
Linux video capture interface: v2.00
pps_core: LinuxPPS API ver. 1 registered
pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
PTP clock support registered
EDAC MC: Ver: 3.0.0
Advanced Linux Sound Architecture Driver Initialized.
Switched to clocksource arm_global_timer
NET: Registered protocol family 2
TCP established hash table entries: 4096 (order: 2, 16384 bytes)
TCP bind hash table entries: 4096 (order: 3, 32768 bytes)
TCP: Hash tables configured (established 4096 bind 4096)
TCP: reno registered
UDP hash table entries: 256 (order: 1, 8192 bytes)
UDP-Lite hash table entries: 256 (order: 1, 8192 bytes)
NET: Registered protocol family 1
RPC: Registered named UNIX socket transport module.
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
hw perfevents: enabled with armv7_cortex_a9 PMU driver, 7 counters available
futex hash table entries: 512 (order: 3, 32768 bytes)
jffs2: version 2.2. (NAND) (SUMMARY)  c 2001-2006 Red Hat, Inc.
io scheduler noop registered
io scheduler deadline registered
io scheduler cfq registered (default)
zynq-pinctrl 700.pinctrl: zynq pinctrl initialized
dma-pl330 f8003000.dmac: Loaded driver for PL330 DMAC-241330
dma-pl330 f8003000.dmac:        DBUFF-128x8bytes Num_Chans-8 Num_Peri-4 Num_Events-16
e0000000.serial: ttyPS0 at MMIO 0xe0000000 (irq = 144, base_baud = 3125000) is a xuartps
console [ttyPS0] enabled
bootconsole [earlycon0] disabled

<この後何も表示されない>

これでは、そもそもLinuxが死んでいるのか生きているのかさえもよくわかりません。

DeviceTreeの変更

この現象をネットで調べると、Xilinxのフォーラムにデバイスツリーを修正する書き込みを見つけました。
そして、試してみるとログインまでたどり着くことが出来ました!
詳細な手順は以下の通りです。

PetaLinuxプロジェクトの./subsystem/linux/configs/device-treeにあるsystem-conf.dtsiに1行追加します。
#「このファイル変更するべからず。」というコメントは気になりますが進めます。

/*
 * CAUTION: This file is automatically generated by PetaLinux SDK.
 * DO NOT modify this file
 */

/include/ "skeleton.dtsi"
/include/ "zynq-7000.dtsi"
/include/ "pcw.dtsi"

/ {
	model = "mio_pmod";
	aliases {
		serial0 = &uart1;
		serial1 = &uart0;//<=追加する
		ethernet0 = &gem0;
		spi0 = &qspi;
	};
	chosen {
		bootargs = "console=ttyPS0,115200 earlyprintk";
	};
	memory {
		device_type = "memory";
		reg = <0x0 0x20000000>;
	};
};

<略>

serialとUARTの番号を逆に付けていることに注意してください。
初期設定でブートログとコンソールの入出力に使っているのはUART1ですが、PetaLinux上ではttyPS0だと認識しています。
つまり、UART1をttyPS0として使用し、新しく追加するUART0をttyPS1として使用するというわけですね。
#この逆転の修正もできそうですが、このまま進めます。

動作確認

再ビルド・起動するとttyPS0を使ったままなのでログイン画面まで表示されますので、ログインします。
/devを確認すると、ttyPS0とttyPS1が存在します。

Built with PetaLinux v2015.4 (Yocto 1.8) mio_pmod /dev/ttyPS0
mio_pmod login: root
Password:
login[870]: root login on 'ttyPS0'
root@mio_pmod:~# ls /dev
console             ram10               tty21               tty50
cpu_dma_latency     ram11               tty22               tty51
flash               ram12               tty23               tty52
full                ram13               tty24               tty53
i2c-0               ram14               tty25               tty54
iio:device0         ram15               tty26               tty55
initctl             ram2                tty27               tty56
input               ram3                tty28               tty57
kmsg                ram4                tty29               tty58
loop-control        ram5                tty3                tty59
loop0               ram6                tty30               tty6
loop1               ram7                tty31               tty60
loop2               ram8                tty32               tty61
loop3               ram9                tty33               tty62
loop4               random              tty34               tty63
loop5               shm                 tty35               tty7
loop6               snd                 tty36               tty8
loop7               tty                 tty37               tty9
mem                 tty0                tty38               ttyPS0
memory_bandwidth    tty1                tty39               ttyPS1
mmcblk0             tty10               tty4                urandom
mmcblk0p1           tty11               tty40               vcs
mtab                tty12               tty41               vcs1
network_latency     tty13               tty42               vcsa
network_throughput  tty14               tty43               vcsa1
null                tty15               tty44               vga_arbiter
port                tty16               tty45               watchdog
psaux               tty17               tty46               watchdog0
ptmx                tty18               tty47               xdevcfg
pts                 tty19               tty48               zero
ram0                tty2                tty49
ram1                tty20               tty5
root@mio_pmod:~#

動作確認

では、本当にttyPS1を使用することができるようになったのか確認します。
確認にあたり、今回はechoコマンドを使いました。
PetaLinuxにminicomのようなシリアルコンソールは入っていませんし、テストアプリを作成するのは少し面倒です。

MIO Pmodに出したUART0のGNDとRX、TX端子とUSBシリアル変換基板と接続し、シリアル変換基板をPCに接続します。

TODO:後でシリアル接続のわかる写真を張る!!

PCでシリアルターミナルを開き、PetaLinuxのコンソールで以下を実行します。

root@mio_pmod:~# stty -F /dev/ttyPS1 115200 clocal cread cs8 -cstopb -parenb
root@mio_pmod:~# echo 'hoge' > /dev/ttyPS1


すると…

TODO:後で証拠写真を張る!!

PC側のシリアルターミナルに"hoge"と出ました。ちゃんと送信できています!!
また、PC側から文字入力をするとPC側シリアルターミナルに入力が反映されることからZynq側で受信もできていることがわかります(Zynq側で表示が無いような…)。

さいごに

今回はUART0をZyboのMIO Pmodに配線し、PetaLinuxから使用できることを確認しました。
ttyPS*とUART*の番号が逆なのが少し気になりますが、使うことができたのでとりあえずは良しとします。