電気回路/z-turn/基本事項 のバックアップソース(No.16)

更新

[[公開メモ]]

#contents

* 概要 [#tff00b8e]

z-turn に独自設計のロジックを組み込み、
Linux 上のソフトウェアから制御するために必要な基本事項を確認しました。

このページはあくまで最小手順で行うことを目標としており、
z-turn 付属の SD カードとできるかぎり同じ構成で、
独自ロジックを格納した .bin ファイルおよび必要不可欠な設定項目 .dtb のみを
書き換えるだけで動作させることを考えています。

SD カード内容をがっつり書き換えて、
Linux カーネルやディストリビューションまでいじる話はページの最後にリンクを置きました。

* z-turn board とは [#v625fc22]

http://www.myirtech.com/list.asp?id=502

zynq-7000 を載せた MYIR の SoC ボードです

ユーザーI/O が豊富なので、周辺機器をたくさん繋ぎたい場合には他になかなか選択肢がないです

ただ、あまりメジャーなボードじゃないので情報が少なくて大変です。

とりあえずこのページの内容が判明したおかげで、ようやく開発に移れます。

** 搭載 CPU について [#n82a058d]

https://japan.xilinx.com/products/silicon-devices/soc/zynq-7000.html ~
https://japan.xilinx.com/support/documentation/user_guides/j_ug585-Zynq-7000-TRM.pdf ~
によると、

XC7Z020 は
- アプリケーションプロセッシングユニット (APU) を2つ含む
-- CoreSight 搭載のデュアルコア ARM Cortex-A9 MPCore (ARMv7-A アーキテクチャ) 667MHz (-1)
-- 各プロセッサに NEON™ および単精度/倍精度浮動小数点ユニッ ト
--- 各コアで最大 2.0MFLOPS/MHz
--- NEON メディア処理エンジンで SIMD をサポート
-- スヌープ制御ユニット (SCU) で L1 キャッシュのコヒーレンシを維持します
-- 各 Cortex-A9 CPU は 1 サイクルあたり 2 つの命令を発行でき、これらをアウトオブオーダー実行できます
- DMA チャネル8 (4 つがプログラマブルロジック用) 
- 16 個の割り込み
- Artix-7 ベースの FPGA 
- アクセラレータコヒーレンシポート (ACP) インターフェイスによって、PL から CPU メモリ空間への整合が取れたアクセスが可能
- デュアルポートのオンチップ RAM (256KB) 
-- CPU およびプログラマブルロジック (PL) からアクセス可能
-- CPU からのアクセスが低レイテンシ
-- アドレスマ ッ プの先頭または末尾の 256KB に配置
--- ARM の例外ベクターの Low モードと High モードに柔軟に対応
- 割り込みおよびタイマー
-- グローバル割り込みコントローラー (GIC)
-- 3 つのウォッチドックタイマー (WDT) (各 CPU に 1 つずつ、 システム用に 1 つ)
-- 2 つのトリプルタイマー/カウンター (TTC)
- CoreSight による Cortex-A9 のデバッグおよびトレースをサポート
-- 命令およびトレース用のプログラムトレースマクロセル (PTM)
-- クロストリガーインターフェイス (CTI) によって、ハードウェアブレークポイントおよびトリガーが可能

** CPU を初期化する一般的な手順 [#c63b6464]

「3.9 CPU の初期化シーケンス」より:

+ ベクターベースアドレスレジスタを設定します。
+ L1 キャッシュ、TLB、分岐予測アレイを無効にします (「L1 キャッシュの初期化」参照)。
+ L2 キャッシュを無効にします。
+ ページテーブルを用意し、プログラムを物理メモリにロードします (ページテーブルの詳細は、「変換テーブルベースレジスタ 0 および 1」参照)。
+ スタックをセットアップします。
+ ページテーブルベースアドレスを変換テーブルベースレジスタにロードします (「変換テーブルベースレジスタ 0 および 1」参照)。
+ システム制御レジスタの MMU イネーブルビットをセットします。
+ L2 キャッシュを初期化して有効にします ( 「3.4.10 プログラミングモデル」の「L2 キャッシュ」参照)。
+ システム制御レジスタへの書き込みにより L1 キャッシュを有効にします。
+ アプリケーションのエントリへジャンプします。

** メモリマップ [#r4d5943a]

メモリマップ
|開始アドレス   |サイズ(MB)|説明|
|0x0000_0000|1,024|DDR DRAM およびオンチップメモリ (OCM)|
|0x4000_0000|1,024|PL AXI スレーブポート #0|
|0x8000_0000|1,024|PL AXI スレーブポート #1|
|0xE000_0000|256|IOP デバイス|
|0xF000_0000|128|予約|
|0xF800_0000|32|AMBA APB バス経由のプログラム可能なレジスタアクセス|
|0xFA00_0000|32|予約|
|0xFC00_0000|64MB ~ 256KB|クワッド SPI リニアアドレスのベースドレス (OCM の最高位 256KB を除く)、64MB 予約領域、 現時点では 32MB のみサポート|
|0xFFFC_0000|256KB|高位アドレス空間へマップされた場合の OCM|

「4.1 アドレス マップ」により詳しい内容
* 通常の起動 [#z0e4b676]

+ 付属の SD カードを Micro_SD ポートに挿す
+ USB キーボード&マウスを USB_OTG ポートに挿す
+ HDMI ケーブルでディスプレイと繋ぐ
+ 電源コードを挿す

これで Linux が起動します

ただし、HDMI の IP が試用版なので、30分するとディスプレイの表示が消えます orz

* USB ケーブルで起動 [#z04bbcd5]

- 付属の SD カードを Micro_SD ポートに挿す
- USB_UART ポートへ PC からの USB ケーブルを挿す
- PC に新しい COM ポートが増える
-- Silicon Labs CP210x USB to UART Bridge (COMx)
- この COM ポートをターミナルソフトで開く
-- ボーレート 115,200 baud
-- データ 8bit
-- パリティ none
-- ストップ 1bit
-- フロー制御 none
-- → [[電気回路/z-turn/tera term の設定方法]]
- リセットを押す

これで、ターミナルソフトから Linux のコンソールにアクセスできます

* 起動用 SD カードの中身を確認する [#m2f6eee7]

自作のロジックとプログラムを保存した SD カードを作るため、
新しい SD カードにブートローダや Linux イメージをインストールします。

どういったものを作る必要があるかを確認するため
z-turn 上で Linux が立ち上がった状態で SD カードの状況を見ると、

 LANG:console
 $ df
  Filesystem     1K-blocks    Used Available Use% Mounted on
  /dev/root        3546736 1466584   1880272  44% /
  none               77172     560     76612   1% /run
  none                5120       0      5120   0% /run/lock
  none              385856       8    385848   1% /run/shm
  /dev/mmcblk0p1    197676   28001    169676  15% /media/boot
 $ mount
  /dev/root on / type ext4 (rw)
  none on /proc type proc (rw,noexec,nosuid,nodev)
  none on /sys type sysfs (rw,noexec,nosuid,nodev)
  devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620)
  none on /run type tmpfs (rw,noexec,nosuid,size=10%,mode=0755)
  none on /run/lock type tmpfs (rw,noexec,nosuid,nodev,size=5242880)
  none on /run/shm type tmpfs (rw,nosuid,nodev)
  /dev/mmcblk0p1 on /media/boot type vfat (rw,nosuid,nodev,uid=0,gid=0,shortname=mixed,dmask=0077,utf8=1,showexec,flush,uhelper=udisks)
 $ # /dev/root と /dev/mmcblk0p1 とが SD カード
 $ ls /
  bin   dev  home  lost+found  mnt  proc  run   selinux  sys  usr
  boot  etc  lib   media       opt  root  sbin  srv      tmp  var
 $ ls /media/boot
  7z010-hdmi.bit  BOOT.bin                   uEnv-ramdisk.txt
  7z010-lcd.bit   System Volume Information  uEnv-ubuntu.txt
  7z010.bit       devicetree-7ic.dtb         uEnv.txt
  7z020-hdmi.bit  devicetree-hdmi-1080p.dtb  uImage
  7z020-lcd.bit   devicetree-hdmi-720p.dtb   uramdisk.image.gz
  7z020.bit       devicetree.dtb

のようになっていて、
- /media/boot にマウントされた /dev/mmcblk0p1
-- 起動用ファイルが入っており、FAT32 なので Windows からでも読める
-- 32MB から 64MB 程度あれば十分みたい
- / にマウントされた /dev/root
-- Linux 用ファイルが入っており、ext4 なので Windows からは読めない
-- Linux 側から使うメインのファイルシステムになるので、残り容量のすべてをここに充てる

の2つが SD カードの2つのパーティションです。

* 起動用 SD カードを新規作成する [#j9ee085d]

新しい SD カードにこの2つのパーティションを用意するのは 
Windows からだと難しいので、VirtualBox に Linux をインストールして作業しました。

参考 → [[電気回路/z-turn/VirtualBox に Debia8 を入れる]]

VirtualBox に Linux が入っていると、その他にも色々役立ちます。

作ったパーティションをフォーマットして、
z-turn 付属の DVD から起動用 SD カードのイメージをコピーすれば完成です。

http://marsee101.blog19.fc2.com/blog-entry-2818.html を参考に、

+ 新しい SD カードを PC の SD カードリーダに挿す
+ VirtualBox 上の Linux コンソールを開く
+ VirtualBox のメニュー [デバイス]-[USB]-[Card Reader] にチェックを入れる
-- 最近の VirtualBox では USB2 や USB3 のカードリーダーを認識するには Extention を入れないとだめみたい -> [[電気回路/z-turn/VirtualBox に Debia8 を入れる#g8dc93aa]]
+ lsblk で SD カードに対応するデバイスを見つける
+ fdisk で2つパーティションを作る
+ mkfs でパーティションをフォーマットする
-- mkfs.msdos がなければ "sudo apt-get install dosfstools"
+ mount でマウントする
+ z-turn 付属の DVD を PC に挿入
+ lsblk で DVD を見つける
+ mount でマウントする
+ /media/cdrom0/02-Images/Ubuntu/sd_partitions/ の中身を SD カードへ書き込む
+ umount でアンマウントする

 LANG:console
 $ lsblk
  NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
  sda      8:0    0   10G  0 disk
  ├─sda1   8:1    0  9.6G  0 part /
  ├─sda2   8:2    0    1K  0 part
  └─sda5   8:5    0  466M  0 part [SWAP]
  sdf      8:80   1  7.4G  0 disk
  sr0     11:0    1  2.3G  0 rom
 $ sudo fdisk /dev/sdf
    p (print)
    d (delete) 1  # 必要に応じて
    d (delete) 2  # 必要に応じて
    n (new) p (primary) 1 ret (start=default) +64M (size)
    n (new) p (primary) 2 retrn (start=default) return (size=default)
    t (type) 1 b (W95 FAT32)
    a (bootable) 1
    p (print)
  Device     Boot  Start      End  Sectors  Size Id Type
  /dev/sdf1  *      2048   133119   131072   64M  b W95 FAT32
  /dev/sdf2       133120 15552511 15419392  7.4G 83 Linux
 $ lsblk
  NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
  sda      8:0    0   10G  0 disk
  ├─sda1   8:1    0  9.6G  0 part /
  ├─sda2   8:2    0    1K  0 part
  └─sda5   8:5    0  466M  0 part [SWAP]
  sdf      8:80   1  7.4G  0 disk
  ├─sdf1   8:81   1   64M  0 part
  └─sdf2   8:82   1  7.4G  0 part
  sr0     11:0    1  2.3G  0 rom
 $ sudo mkfs.msdos -n BOOT /dev/sdf1
 $ sudo mkfs.ext4 -L ROOT /dev/sdf2
 $ sudo mkdir /mnt/sdcard1
 $ sudo mount /dev/sdf1 /mnt/sdcard1
 $ sudo mkdir /mnt/sdcard2
 $ sudo mount /dev/sdf2 /mnt/sdcard2
 $ mount /dev/sr0
 $ sudo tar fxz /media/cdrom0/02-Images/Ubuntu/sd_partitions/part0.tar.gz -C /mnt/sdcard1 --strip-components 1
 $ sudo tar fxz /media/cdrom0/02-Images/Ubuntu/sd_partitions/part1.tar.gz -C /mnt/sdcard2 --strip-components 1
 $ sudo umount /dev/sdf1
 $ sudo umount /dev/sdf2

* Zynq のブートローダを理解する [#v1a82ae1]

上で作った SD カードから起動するだけでは、標準のロジックがロードされるだけなので
自分で作ったロジックがロードされるように変更したいところです。

Zynq がどのように SD カードからロジックを PL 領域 (Programmable Logic) に読み込んで、
Linux を起動するか、そのプロセスを理解するために z-turn 付属の SD カードから起動した場合の
起動メッセージを読んでみました。

この知識は、途中で止まっちゃったりのエラーの際にも役に立つはず。

始めに表示されるのは、

 LANG:console
 U-Boot 2013.10-svn7 (Apr 20 2015 - 20:49:24)
 
 Memory: ECC disabled
 DRAM:  1 GiB
 myir_board_init
 MMC:   zynq_sdhci: 0
 SF: Detected W25Q128BV with page size 256 Bytes, erase size 4 KiB, total 16 MiB
 *** Warning - bad CRC, using default environment
 
 In:    serial
 Out:   serial
 Err:   serial
 Net:   Gem.e000b000
 Hit any key to stop autoboot:  3

- U-Boot というプログラムが走っている
- 最後の行で Enter キーを押すと起動オプションを入力するためのコンソールが現れる
- print と打つと、環境変数の一覧を表示できる (http://marsee101.blog19.fc2.com/blog-entry-2927.html)

特に選択しない場合、

 LANG:console
 Device: zynq_sdhci
 Manufacturer ID: 3
 OEM: 5344
 Name: SU04G
 Tran Speed: 50000000
 Rd Block Len: 512
 SD version 3.0
 High Capacity: Yes
 Capacity: 3.7 GiB
 Bus Width: 4-bit
 reading uEnv.txt
 395 bytes read in 20 ms (18.6 KiB/s)
 Loaded environment from uEnv.txt
 Importing environment from SD ...

と続き、ここで uEnv.txt というファイルが読み込まれる。

uEnv.txt の中身は
 bootargs=console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk rootfstype=ext4 rootwait devtmpfs.mount=0
 load_image=fatload mmc 0 ${kernel_load_address} ${kernel_image} && fatload mmc 0 ${devicetree_load_address} ${devicetree_image}
 uenvcmd=run mmc_loadbit_fat && echo Copying Linux from SD to RAM... && mmcinfo &&  run load_image && bootm ${kernel_load_address} - ${devicetree_load_address}

ここに出てくる環境変数の値を調べると

 kernel_image=uImage
 kernel_load_address=0x2080000
 devicetree_image=devicetree.dtb
 devicetree_load_address=0x2000000

となっている。この uImage というのが Linux カーネルイメージ。

これらを書き換えれば好きな内容をメモリにロードできるはず。

で、起動メッセージの続きは、

 LANG:console
 Running uenvcmd ...
 Loading bitstream from SD/MMC/eMMC to RAM..
 ...
 reading 7z020.bit
 4045675 bytes read in 250 ms (15.4 MiB/s)
   design filename = "mys_xc7z020_trd;UserID=0XFFFFFFFF;Version=2014.4"
   part number = "7z020clg400"
   date = "2014/10/10"
   time = "14:37:26"
   bytes in bitstream = 4045564

となって、7z020.bit がこの段階で読み込まれる。

ということは、これを書き換えれば好きなロジックをロードできるはず。

さらにその後、

 LANG:console
 zynq_load: Align buffer at 10006f to 100000(swap 1)
 Copying Linux from SD to RAM...
 ...
 reading uImage
 3886152 bytes read in 253 ms (14.6 MiB/s)
 reading devicetree.dtb
 17334 bytes read in 22 ms (768.6 KiB/s)
 ## Booting kernel from Legacy Image at 02080000 ...
    Image Name:   Linux-3.15.0-xilinx
    Image Type:   ARM Linux Kernel Image (uncompressed)
    Data Size:    3886088 Bytes = 3.7 MiB
    Load Address: 00008000
    Entry Point:  00008000
    Verifying Checksum ... OK
 ## Flattened Device Tree blob at 02000000
    Booting using the fdt blob at 0x2000000
    Loading Kernel Image ... OK
    Loading Device Tree to 1fff8000, end 1ffff3b5 ... OK
 
 Starting kernel ...
 
 Booting Linux on physical CPU 0x0
 Linux version 3.15.0-xilinx (tom@dev-server) (gcc version 4.6.1 (Sourcery CodeBench Lite 2011.09-50) ) #9 SMP PREEMPT Tue May 26 17:26:14 CST 2015

となる。

一番最初の u-boot がどこから来たかというと、(DVD)/02-Images/BOOT/boot.bif が

 the_ROM_image:
 {
 	[bootloader]D:\BOOT\fsbl.elf
 	D:\BOOT\u-boot.elf
 }

となっていて、BOOT.bin に fsbl.elf と u-boot.elf が入っていたことが分かる。

通常の FSBL (First Stage Boot Loader) を用いた起動では、
+ Processing System 内部にハードコードされた BootROM が SD カードの BOOT.bin 
から [bootloader] のついた領域を PS 内 RAM へロードして実行する
+ BOOT.bin には FSBL (First Stage Boot Loader) と呼ばれるプログラムに [bootloader] を付けて保存しておく。
+ FSBL が同じく BOOT.bin に保存された *.bit ファイルを読み込むことで PS および PL 領域の初期化を行う。
+ FSBL はさらに BOOT.bin に保存されたソフトウェアを読み込んで起動する

となっていることをイメージしつつ、ここまでの起動順を整理すると、

+ PS (Processing System) 内のブートローダ
+ BOOT.bin 内の
-- FSBL.elf (First Stage Boot Loader) 内部で PL 領域の最小限の初期化を行う?
-- u-boot.elf (Second stage boot loader)
-- 環境変数の設定
+ uEnv.txt
+ 7z020.bit (User Logic)
+ uImage (Linux-3.15.0-xilinx)
+ devicetree.dtb

となってそう。

ロジック部分は uEnv.txt の後で読み込んでる。

恐らく、下記の記事に近い構成になっているのでは、と思われる???~
http://qiita.com/ikwzm/items/1734676d787e2693df47



* 7z020.bin を生成してみる [#qd72eb9b]

ユーザーロジックを 7z020.bin という名前で保存しておけば、
起動時に読み込んでくれることが分かった。

ここには自作のIPなどを含んだ PL 領域の設定に加えて、
PS 領域を Linux を動かせるように初期化するための設定も必要になる。
** PS の設定を z-turn DVD から取ってくる [#made65de]

PS 領域の設定を自分でするのは大変なので、

(DVD)/05-ProgrammableLogic_Source/7Z020/mys-xc7z020-trd.rar

を使えばいいような気がする。

このサンプルプロジェクトは以下のように説明されている。

 1. mys_xc7z020_trd.zip
   vivado project without display controller support, it's free to use.

上記 .rar ファイルの中身を C:\z-turn\mys-xc7z020-trd に展開する。

** プロジェクトおよびIPのアップグレード [#l5b06c58]

展開したファイルの中から mys-xc7z020-trd.xpr をダブルクリックして vivado を起動~
&ref(open-project.png,,66%);

プロジェクトファイルが古いと言われるので automatically upgrade する。~
&ref(automatic-upgrade.png,,66%);

Xilinx 製 IP もいくつかアップグレードすべきと言われるので Report IP Status する。~
&ref(upgrade-ip.png,,66%);

画面下の IP Status で Upgrade Selected する。~
&ref(upgrade-selected-ip.png,,66%);

design_1.bd を作成し直すか聞かれるけれど、このまま Generate を押しても以下のエラーが出てしまうので、
ここではスキップする。~
&ref(skip-generate-output-products.png,,66%);

** ダミーの割り込み信号線を繋ぐ [#y9a11043]

上記ダイアログで何もせずに Generate すると次のエラーが出る
> [BD 41-759] The input pins (listed below) are either not connected or do not have a source port, and they don't have a tie-off specified. These pins are tied-off to all 0's to avoid error in Implementation flow.
Please check your design and connect them as needed: 
>>/xlconcat_0/In1

曰く、xlconcat_0 の In1 ピンが未接続ですよ、と。~
&ref(unconnected-xlconcat_0-In1.png,,50%);

もともとここには有償のディスプレイ IP である xylon:logicbricks:logicvc:3.02.a 
からの割り込み線が繋がっていたところ、この有償 IP を削除したために未接続となったもの。

ダミーを繋げばいい。

Diagram 上の何もないところで右クリックから、[Add IP..] を選び、[Constant] を追加する。~
&ref(constant-for-aux_intn-added.png,,66%);

追加された xlconstant_0 をダブルクリックして、Width = 1, Value = 0 に設定。~
&ref(constant-for-aux_int-config.png,,66%);

xlconstant_0/dout[0:0] から xlconcat_0/In1[0:0] までドラッグすると線で繋がる。~
IRQ_F2P ポートは1になると割り込みが掛かる。0を入れておけば実質的に何もしない。~
自分で追加した IP の割り込みを監視したくなったら Constant を削除して、繋ぎ直せばいい。~
&ref(constant-for-aux_intn-connected.png,,66%);



** Block Design の生成 [#k1f853dc]

IP Integrator の Generate Block Design ボタンを押す。~
&ref(generate-block-design-button.png,,66%);

Synthesis Option は Global のままで OK

** Bitstream の生成 [#x1c8a098]

&ref(generate-bitstream-button.png,,66%);

いきなり Generate Bitstream を押せば、Synthesis, Implementation などの必要な処理を自動でやってくれて、

C:\z-turn\mys-xc7z020-trd\mys-xc7z020-trd.runs\impl_1\design_1_wrapper.bit

にファイルができる。

#ほぼまっさらなこの状態で bitstream 生成にこれだけ時間がかかるとげんなりしますね・・・

** SD カードにコピー [#mdf52ca7]

上記の bit ファイルの名前を 7z020.bit に変えて SD カードへコピーする。

** Vivado にファイルコピー用のボタンを用意する [#i3d781e1]

- [Tools]-[Custom Commands]-[Customize Commands...] を選択
- 緑の矢印を押して "Copy Bitstream to SD card" というコマンドを作る
- Run command: に下記コマンドを記入

 file copy -force "C:/z-turn/mys-xc7z020-trd/mys-xc7z020-trd.runs/impl_1/design_1_wrapper.bit" "N:/7z020.bit"

&ref(customize-commands-copy-bitstream.png,,66%);

ツールバーにボタンができる

&ref(copy-bitstream-button.png,,66%);

これを押すと、ファイルがコピーされる。



&ref(bitstream-copy-command-line.png,,66%);

* デバイスツリーから hdmi 関連を削除 [#ybe699ce]

これで起動しようとすると、起動時に以下のメッセージが出た後で止まってしまう。

 LANG:console
 ...
 mmc0: new high speed SDHC card at address 0007
 mmcblk0: mmc0:0007 SD8GB 7.42 GiB
  mmcblk0: p1 p2

標準の SD カードで起動すると、ここは、

 LANG:console
 ...
 mmc0: new high speed SDHC card at address 0007
 mmcblk0: mmc0:0007 SD8GB 7.42 GiB
  mmcblk0: p1 p2
 xylonfb video mode: 1280x720-32@60
 Console: switching to colour frame buffer device 160x45
 xylonfb 1 registered
 xylonfb 0 registered

のように、hdmi ドライバの設定が続く部分。

つまり、Linux 側のデバイスツリー設定に、今は存在しない xylonfb video が含まれているのが問題。

AXI-4 バスは、相手が存在しないアドレスを読みに行ったり書きに行ったりするとデッドロックするので、それで止まっちゃっているのだと思う。

** デバイスツリーファイル [#lbe55fc1]

devicetree.dtb などというのがデバイスツリーファイルだそうで、
これは devicetree.dts などというソースファイルをコンパイルしてバイナリ形式にしたものらしい。

dts : デバイスツリーソース~
dtb : デバイスツリーブロブ

** z-turn 付属の dtb を書き換える [#p42f1d7d]

http://qiita.com/spicemanjp/items/5054e536442248aad87c によると、
既存の dtb を dts に直すことができるらしい。

つまり、z-turn 付属の dtb を dts に直し、手直しをしてから dtb に書き戻せばよさそう。

やってみよう。

 LANG:console
 $ sudo apt-get install device-tree-compiler
 $ dtc --version
  Version: DTC 1.4.2
 $ dtc -I dtb -O dts -o devicetree-hdmi-1080p.dts devicetree-hdmi-1080p.dtb
  Warning (unit_address_vs_reg): Node /memory has a reg or ranges property, but no unit name
  Warning (unit_address_vs_reg): Node /pmu has a reg or ranges property, but no unit name
  Warning (unit_address_vs_reg): Node /fixedregulator@0 has a unit name, but no reg property
  Warning (unit_address_vs_reg): Node /amba/cache-controller has a reg or ranges property, but no unit name
 $ dtc -I dts -O dtb -o devicetree-hdmi-1080p-rebuild.dtb devicetree-hdmi-1080p.dts
  Warning (unit_address_vs_reg): Node /memory has a reg or ranges property, but no unit name
  Warning (unit_address_vs_reg): Node /pmu has a reg or ranges property, but no unit name
  Warning (unit_address_vs_reg): Node /fixedregulator@0 has a unit name, but no reg property
  Warning (unit_address_vs_reg): Node /amba/cache-controller has a reg or ranges property, but no unit name
 $ diff devicetree-hdmi-1080p.dtb devicetree-hdmi-1080p-rebuild.dtb
 $

何か警告が出るけれど、確かに dts ファイルが生成されるし、
それをコンパイルし直すと完全に元と一致する .dtb に戻ることが確認できた。

logiclk, logicvc の部分を削り、led_? の 

 linux,default-trigger = "heartbeat";

を

 linux,default-trigger = "none";

に書き換えたものを devicetree-wo-display.dts として保存した。

 LANG:console
 $ dtc -I dts -O dtb -o devicetree-wo-display.dtb devicetree-wo-display.dts

として dtb に戻して、devicetree.dtb として SD カードへ書き込んだ。

* 起動した! [#s872bcdb]

上記手順で、自作の *.bin を読み込んだうえでちゃんと Linux が起動するようになった。

led はちかちかしない代わりに付きっぱなしだ。
default-state = "off" とすべきだった。

自作 IP を Linux から制御するにはこの dts に自作 IP へのアクセスができるよう項目を書き加え、
dtb に直して保存すればいいはず。

* Xilinx SDK を立ち上げる [#s56e212f]

あとは Linux 上でも開発できるのだけれど、
FSBL を作り直すなんて用途には Xilinx SDK が必要になる。

mys-xc7z020-trd プロジェクトから [File]-[Launch SDK] すると、
ワークスペースファイルが古いので新しくしていいか聞かれるので OK する。

&ref(update-sdk-workspace.png,,66%);

SDK が立ち上がると、ハードウェアプラットフォームに加えて、
fsbl と fsbl_bsp のプロジェクトができている。

fsbl は First Stage Boot Loader, BSP は Board Support Package 

とりあえず開くことを確認して今はここまで。

* Example を試してみる [#yd39f572]

ロジック側で定義したレジスタに Linux アプリケーションからアクセスする標準的な方法を知るために
z-turn の DVD に含まれていた Example を試してみます。


** led-test [#ff141338]

(DVD)/04-Linux_Source/Examples/leds から、
- led-test.c
- Makefile

を ~/z-turn/led-test/ へコピー

 LANG:console
 $ make

これだけで led-test という実行ファイルができる。

root 以外の一般ユーザーで実行しようとするとエラーになるので、
sudo するか、あるいは setuid しなければならない。

 LANG:console
 $ ./led-test
  [usr_led1] Get trigger: 'none'
  Open /sys/class/leds/usr_led1/trigger failed!
 $ sudo ./led-test
  [usr_led1] Get trigger: 'none'
  [usr_led1] Set trigger to 'none'
  [usr_led2] Get trigger: 'none'
  [usr_led2] Set trigger to 'none'
  [   led_r] Get trigger: 'none'
  [   led_r] Set trigger to 'none'
  [   led_g] Get trigger: 'none'
  [   led_g] Set trigger to 'none'
  [   led_b] Get trigger: 'none'
  [   led_b] Set trigger to 'none'
 ^C[usr_led1] Set trigger to 'none'
  [usr_led2] Set trigger to 'none'
  [   led_r] Set trigger to 'none'
  [   led_g] Set trigger to 'none'
  [   led_b] Set trigger to 'none'
 $ sudo chown root:root led-test
 $ sudo chmod u+s led-test
 $ sudo chmod o+x led-test
 $ ./led-test
  [usr_led1] Get trigger: 'none'
  [usr_led1] Set trigger to 'none'
  [usr_led2] Get trigger: 'none'
  [usr_led2] Set trigger to 'none'
  [   led_r] Get trigger: 'none'
  [   led_r] Set trigger to 'none'
  [   led_g] Get trigger: 'none'
  [   led_g] Set trigger to 'none'
  [   led_b] Get trigger: 'none'
  [   led_b] Set trigger to 'none'
  ^C[usr_led1] Set trigger to 'none'
  [usr_led2] Set trigger to 'none'
  [   led_r] Set trigger to 'none'
  [   led_g] Set trigger to 'none'
  [   led_b] Set trigger to 'none'

確かに LED が順に点灯する。

*** ソースコードを読む [#v63363ac]

led は物理的には次のように繋がれている:

- USER_LED1 = MIO0 = E6
- USER_LED2 = MIO9 = B5
- LED_R     = LN6  = R14
- LED_G     = LP7  = Y16
- LED_B     = LN7  = Y17

このうち MIO ポートはピン番号が事前に決まっている。

RGB の方は EMIO で、これは単に design_1 モジュールから外部に接続される信号線なので、
wrapper モジュールで適当に処理してトップレベルモジュールの外へ出さなければならない。

wrapper モジュールに

 LANG:verilog
     output    [2:0]  LEDS;

というポートが用意されていて、

 LANG:verilog
    assign LEDS[0]  =     PS_iRESETn   ?  ledr : arm_io_led_r;
    assign LEDS[1]  =     PS_iRESETn   ?  ledg : arm_io_led_g;
    assign LEDS[2]  =     PS_iRESETn   ?  ledb : arm_io_led_b;
 
    wire ledr      =     LED1_reg && SW[0];
    wire ledg      =     SW[1] ;
    wire ledb      =     SW[2] ;
 
    assign  gpio_0_tri_o[60] = arm_io_led_r;
    assign  gpio_0_tri_o[61] = arm_io_led_g;
    assign  gpio_0_tri_o[62] = arm_io_led_b;

のように design_1 の出力ポート gpio_0_tri_o と繋がっている。

ピン番号は制約ファイル system.xdc で次のように定義されている。
 LANG:xdc
 set_property PACKAGE_PIN Y16 [get_ports {LEDS[0]}]
 set_property PACKAGE_PIN Y17 [get_ports {LEDS[1]}]
 set_property PACKAGE_PIN R14 [get_ports {LEDS[2]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {LEDS[2]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {LEDS[1]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {LEDS[0]}]

PS 設定の ZYNQ PS REGISTER SUMMARY VIEWER で発見した、関連しそうなレジスタ

- MIO_PIN_00 : Address=0xF8000700 Width=32 Type=rw ResetValue=0x00000000
-- bit 0 : TRI_ENABLE
-- bit 1 : L0_SEL
-- bit 2 : L1_SEL
-- bit 4-3 : L2_SEL
-- bit 7-5 : L3_SEL
-- bit 8 : Speed
-- bit 11-9 : IO_Type
-- bit 12 : PULLUP
-- bit 13 : DisableRcvr
- MIO_PIN_09 : Address=0xF8000724 Width=32 Type=rw ResetValue=0x00000000
- MASK_DATA_0_LSW[15:0] : Address=0XE000A000
- DIRM_0 : Address=0XE000A204
- OEN_0 : Address = Address=0XE000A208

EMIO 内の位置:

- LED_R     = GPIO_60
- LED_G     = GPIO_61
- LED_B     = GPIO_62

led は dts で次のように定義されている。

 /dts-v1/;
 
 / {
   #address-cells = <0x1>;
   #size-cells = <0x1>;
   compatible = "myir,zynq-zturn", "xlnx,zynq-7000";
   model = "MYIR Z-turn Development Board";
 
   amba {
     compatible = "simple-bus";
     #address-cells = <0x1>;
     #size-cells = <0x1>;
     interrupt-parent = <0x3>;
     ranges;
 
     gpio@e000a000 {
       compatible = "xlnx,zynq-gpio-1.0";
       #gpio-cells = <0x2>;
       clocks = <0x1 0x2a>;
       gpio-controller;
       interrupt-parent = <0x3>;
       interrupts = <0x0 0x14 0x4>;
       reg = <0xe000a000 0x1000>;
       emio-gpio-width = <0x40>;
       gpio-mask-high = <0x0>;
       gpio-mask-low = <0x5600>;
       xlnx,emio-gpio-width = <0x40>;
       xlnx,mio-gpio-mask = <0x5600>;
       linux,phandle = <0x5>;
       phandle = <0x5>;
     };
 
     gpio-leds {
       compatible = "gpio-leds";
 
       led_r {
         label = "led_r";
         gpios = <0x5 0x72 0x1>;
         default-state = "on";
         linux,default-trigger = "none";
       };
 
       led_g {
         label = "led_g";
         gpios = <0x5 0x73 0x1>;
         default-state = "on";
         linux,default-trigger = "none";
       };
 
       led_b {
         label = "led_b";
         gpios = <0x5 0x74 0x1>;
         default-state = "on";
         linux,default-trigger = "none";
       };
 
       usr_led1 {
         label = "usr_led1";
         gpios = <0x5 0x0 0x1>;
         default-state = "off";
         linux,default-trigger = "none";
       };
       
       usr_led2 {
         label = "usr_led2";
         gpios = <0x5 0x9 0x1>;
         default-state = "off";
         linux,default-trigger = "none";
       };
     };
   };
 }; 

emio-gpio-width の 0x40 は 64 だ。~
LED_RGB の 0x72-0x74 はそのままだと 114-116 なのでそのままだとピン番号と合わない。

mio の幅が 54 ピンなので、54 を引いてやると 60-62 となって、ぴったり一致する。

54 は 0x36 だけど、この値は dts には書かれていない。

0xe000a0?? の、~
0x00-0x35 が mio の 0-53 ピンに相当して、~
0x36-0x76 が emio の 0-63 ピンに相当するっぽい。

ソフト的には、例えば

 LANG:c
   int fd = open("/sys/class/leds/usr_led1/trigger", O_RDONLY);
   ret = read(fd, tmp, sizeof(tmp));
   close(fd);
 
   int fd = open("/sys/class/leds/usr_led1/trigger", O_WRONLY);
   ret = write(fd, "none", strlen("none"));
   close(fd);

のようにして trigger を読み書きできる。これはコマンドラインから、

 LANG:console
 $ ls -l /sys/class/leds/usr_led1/trigger
  -rw-r--r-- 1 root root 4096 Jan 27 06:03 /sys/class/leds/usr_led1/trigger
 $ cat /sys/class/leds/usr_led1/trigger
  [none] nand-disk mmc0 timer oneshot heartbeat backlight gpio cpu0 cpu1 default-on transient flash torch

などとしても確認できる。どうやら、このファイル?に書き込むのに root 権限が必要らしい。

trigger の内容から読み取れることには、この led のデバイスドライバは
z-turn board 用に、あるいは少なくとも zynq 用に作られたもので、
恐らくカーネルに組み込まれている???

 LANG:console
 $ ls -l /sys/class/leds/usr_led1/*
  -rw-r--r-- 1 root root 4096 Jan 27 07:44 /sys/class/leds/usr_led1/brightness
  lrwxrwxrwx 1 root root    0 Jan 27 07:45 /sys/class/leds/usr_led1/device -> ../../../gpio-leds.2
  -r--r--r-- 1 root root 4096 Jan 27 07:45 /sys/class/leds/usr_led1/max_brightness
  lrwxrwxrwx 1 root root    0 Jan 27 07:45 /sys/class/leds/usr_led1/subsystem -> ../../../../../class/leds
  -rw-r--r-- 1 root root 4096 Jan 27 07:40 /sys/class/leds/usr_led1/trigger
  -rw-r--r-- 1 root root 4096 Jan 27 07:45 /sys/class/leds/usr_led1/uevent
  
  /sys/class/leds/usr_led1/power:
  total 0
  -rw-r--r-- 1 root root 4096 Jan 27 07:45 autosuspend_delay_ms
  -rw-r--r-- 1 root root 4096 Jan 27 07:45 control
  -r--r--r-- 1 root root 4096 Jan 27 07:45 runtime_active_time
  -r--r--r-- 1 root root 4096 Jan 27 07:45 runtime_status
  -r--r--r-- 1 root root 4096 Jan 27 07:45 runtime_suspended_time

brightness に 0 以外を書き込むと LED が点灯する。~
max_brightness は 255 になっているけれど、1 より大きくしても明るさは変化しない。

** デバイスドライバが必要っぽい? [#y605bb8b]

上記を見る限り、デバイスドライバを書いてファイルシステムに載せてやると簡単にアクセスできるということみたい?

自作の IP にそこまで高機能なデバイスドライバは必要ないなあ・・・

* 汎用デバイスドライバを使う [#yc6e9418]

Zynq デバイスドライバとデバイスツリーを作る~
http://yuki-sato.com/wordpress/2015/01/12/zynq14/

デバイスドライバ udmabuf を使用する1~
http://marsee101.blog19.fc2.com/blog-entry-3373.html

ZynqのPLを操作するLinuxドライバ (DTS設定編)~
https://formalism.github.io/blog/posts/2014/05/zynqpllinux-dts/

ZynqのPLを操作するLinuxドライバ (ユーザプログラム編)~
https://formalism.github.io/blog/posts/2014/05/zynqpllinux/

最後の2つの、generic-uio を利用する方法が使えれば簡単そう。

 LANG:console
 $ ls /sys/class/
  bdi       hwmon        misc          pps           sound        vtconsole
  block     i2c-adapter  mmc_host      ptp           spi_master   watchdog
  dma       i2c-dev      mtd           regulator     thermal      xdevcfg
  drm       ieee80211    myir-stlm75x  rtc           tty
  fclk      input        net           scsi_device   udc
  firmware  leds         pci_bus       scsi_disk     uio
  gpio      mdio_bus     power_supply  scsi_generic  vc
  graphics  mem          ppp           scsi_host     video4linux

どうやらカーネルで UIO は有効になっているので使えそう。

DMA を有効に使いたい場合には udmabuf を使うことになるみたい。

FPGA+SoC+LinuxでDevice Tree Overlayを試してみた~
http://qiita.com/ikwzm/items/ec514e955c16076327ce

Device Tree Overlay を使えば動的にデバイスツリーにデバイスを追加できるらしい?

** やってみる [#ba2f369b]

gpio のレジスタをいじるために .dts ファイルにて以下のようにしたところ、

  amba {
    compatible = "simple-bus";
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    interrupt-parent = <0x3>;
    ranges;
 
    foo_bar_device: foo-bar-device@e000a000 {
      compatible = "generic-uio";
      reg = <0xe000a000 0x1000>;
 //     interrupts = < 0 29 4 >;
 //     interrupt-parent = <&ps7_scugic_0>;
    };

/var/log/dmesg
 LANG:console
 ...
 PPP generic driver version 2.4.2
 PPP BSD Compression module registered
 PPP Deflate Compression module registered
 usbcore: registered new interface driver rtl8192cu
 uio_pdrv_genirq e000a000.foo-bar-device: failed to get IRQ
 uio_pdrv_genirq: probe of e000a000.foo-bar-device failed with error -22
 ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
 ehci-pci: EHCI PCI platform driver
 ULPI transceiver vendor/product ID 0x0424/0x0007
 ...

として、"failed to get IRQ", "probe of e000a000.foo-bar-device failed with error -22" 
が報告され、デバイスが追加されなかった。

仕方がないので adc の設定をパクって、
   amba {
     compatible = "simple-bus";
     #address-cells = <0x1>;
     #size-cells = <0x1>;
     interrupt-parent = <0x3>;
     ranges;
 
     foo_bar_device: foo-bar-device@e000a000 {
       compatible = "generic-uio";
       reg = <0xe000a000 0x1000>;
       interrupts = < 0 7 4 >;
       interrupt-parent= <3>;
     };
 
 //    adc@f8007100 {
 //      compatible = "xlnx,zynq-xadc-1.00.a";
 //      reg = <0xf8007100 0x20>;
 //      interrupts = <0x0 0x7 0x4>;
 //      interrupt-parent = <0x3>;
 //      clocks = <0x1 0xc>;
 //    };

としたらエラーメッセージは消え、

 LANG:console
 $ ls -l /sys/class/uio
  total 0
  lrwxrwxrwx 1 root root 0 Jan  1 00:00 uio0 -> ../../devices/amba.1/e000a000.foo-bar-device/uio/uio0
 $ ls -l /sys/devices/amba.1/e000a000.foo-bar-device/uio/uio0
  total 0
  -r--r--r-- 1 root root 4096 Jan 31 05:37 dev
  lrwxrwxrwx 1 root root    0 Jan 31 05:37 device -> ../../../e000a000.foo-bar-device
  -r--r--r-- 1 root root 4096 Jan 31 05:37 event
  drwxr-xr-x 3 root root    0 Jan 31 05:37 maps
  -r--r--r-- 1 root root 4096 Jan 31 05:37 name
  drwxr-xr-x 2 root root    0 Jan 31 05:37 power
  lrwxrwxrwx 1 root root    0 Jan 31 05:37 subsystem -> ../../../../../class/uio
  -rw-r--r-- 1 root root 4096 Jan  1  1970 uevent
  -r--r--r-- 1 root root 4096 Jan 31 05:37 version

として uio デバイスが追加されたみたい。

ただし、/dev/uio0 というデバイスは存在していない。~
/sys/class/uio/uio0/dev はあるけれど・・・

リンク先のようにダミーの irq を指定するのが筋みたい~
http://fpga.org/2013/05/28/how-to-design-and-access-a-memory-mapped-device-part-two/


* まとめ [#oe77ca2c]

z-turn ボードに独自仕様の PL を組み込みつつ、USB 経由の UART 端末から制御可能な Linux 
を立ち上げられる SD カードを作成する最短手順をまとめる:

+ 新しい SD カードにパーティションを切る
-- 先頭に bootable な FAT32 で 32MB くらい
-- 残りを Linux ext4
+ z-turn DVD にある SD カードイメージからファイルをコピーする
+ FAT32 側に入れるファイル
-- BOOT.bin : DVD のまま
-- uEnv.txt : DVD のまま
-- 7z020.bit : DVD の mys-xc7z020-trd というプロジェクトをひな形にした PS + 独自 PL
-- devicetree.dtb : DVD のものを一旦 .dts に直し、ディスプレイ関係を除いた .dtb を作る
--- 必要に応じて自作 IP 用のエントリーを追加する必要あり
--- uio デバイスとして使う限り、頻繁に書き換える必要は無いはず
-- uImage : DVD のまま
-- uramdisk.image.gz : DVD のまま
+ ext4 側に入れるファイル
-- まずはすべて DVD のまま
-- 必要に応じて z-turn コンソール上からインストール&設定する

あとは独自 PL を組み込んだ 7z020.bit を書き換えつつ、~
それに応じた devicetree に沿ってアプリケーションを開発すれば、~
開発を進められるはず。

独自 PL を追加したり、それに対応して devicetree を書き換えたりしても、
FAT32 側の書き換えだけで済むのが良い感じ。

それよりも、z-turn 側に samba なりをインストールして、Windows 側からマウントすれば、
ネットワーク経由で SD カードへ書き込んで、reboot するだけで良くなるはず。
SD カードの抜き差しすら必要なくなる。

ブート関連ファイルを誰でも書き込み可能にしておかなければならないけれど、
ファイアーウォール内ならまあ許されるかな・・・

* Linux カーネルとディストリビューションを最新版に差し替える? [#y9679b47]

付属 DVD の SD カードイメージに含まれる Linux カーネルやディストリビューションはかなり古いので、

- [[電気回路/z-turn/linux kernel のビルド]] ← 動作が怪しいです
- [[電気回路/z-turn/Linuxの設定]] ← こちらはバッチリな気がします

の手順で、

- Linux Kernel を 4.6.0 に
- ディストリビューションを Debian 8 (jessie) に

それぞれ更新しました。

が、ここでビルドしたカーネルではうまく動作しない部分も多いようで、
まだ調査が必要そうです。一緒に u-boot も新しくすれば動くとか、
あるんでしょうか???

* z-turn 起動用 SD カードの作成 (最新版) [#x68dcffd]

さしあたり z-turn 付属のカーネルは jessie と一緒に使う分には十分新しいので、

+ 2つのパーティションを準備
+ パーティション1に
-- BOOT.bin : z-turn 付属のものをそのまま使用
-- 7z020.bit : vivado で独自ロジックを生成
-- uEnv.txt : z-turn 付属のものをそのまま使用
-- uImage : z-turn 付属のものをそのまま使用 (3.15.0-xilinx)
-- devicetree.dtb : z-turn 付属のものを編集して使用
+ パーティション2に
-- debootstrap で作ったイメージを~
cp -a ~/z-turn/rootfs/* /mnt/sdcard2 ~
でコピーする (Debian 8 = jessie)

ロジックを書き換えた場合にも、7z020.bit のみの書き換えで済むことを期待しています。

* その他メモ [#q6caf2fd]

* コメント・質問 [#zab8b5ae]

#article_kcaptcha

Counter: 14242 (from 2010/06/03), today: 4, yesterday: 0