Petalinux2018.3でPLとDevice Treeを動的に変更する のバックアップ差分(No.5)

更新


  • 追加された行はこの色です。
  • 削除された行はこの色です。
[[公開メモ]]

* 再起動せずにプログラマブルロジック及び Device Tree を更新する [#m94ce4dd]

Device Tree Overlay を使うと、再起動なしに Device Tree を更新できる
- https://okchan08.hateblo.jp/entry/2019/02/05/000000

FPGA Region という機能を使うと再起動なしにプログラマブルロジックを更新できる
- https://okchan08.hateblo.jp/entry/zynq-fpga_region

作業手順としては、
- [[電気回路/zynq/Petalinux2018.3環境を整える]]
- [[電気回路/zynq/Petalinux2018.3によるzynq-7000ブート用SDカード作成]]

の続き。

** 目次 [#p702fcfb]

#contents


* Device Tree Overlay [#v5d9c94b]

[[電気回路/zynq/Device Tree Overlay]] ではまだできなかった、
configfs を用いた方法が使えるようになっているみたいだ。

** カーネル設定 [#j404620e]

[[電気回路/zynq/Petalinux2018.3によるzynq-7000ブート用SDカード作成#t9b088e3]] の続きから、

 LANG:console
 $ cd ~/petalinux/zturn-v2018.3/kernel-source
 $ make ARCH=arm menuconfig
  Device Tree and Open Firmware support  ---> 
      [*]   Device Tree overlays
      [*]   Device Tree Overlay ConfigFS interface

Petalinux からカーネルソースを取ってきた場合には
これらは元々チェックが付いていた。

** やりかた [#j424db65]

ブート直後、すでに /sys/kernel/config が使えるようになっていた。

 LANG:console
 $ mount | grep configfs
  configfs on /sys/kernel/config type configfs (rw,relatime)

あとは、
Device Tree Overlay の手順は:
- 追加したい部分を記述した some-name.dts を作成する
- dtc でコンパイルして .dtb ファイルを作る
- /sys/kernel/config/device-tree/overlays/some_name といったフォルダを作る
- .dtb ファイルを /sys/kernel/config/device-tree/overlays/some_name/dtbo へ書き込む
- 元に戻すには /sys/kernel/config/device-tree/overlays/some_name フォルダを rmdir する

https://okchan08.hateblo.jp/entry/zynq-device_tree_overlay#ConfigFS%E3%81%AE%E6%BA%96%E5%82%99
 LANG:console
 $ cat << EOT > some-name.dts # dts fragment を記述したソースファイル
 > /dts-v1/;
 > /plugin/;
 > / {
 >   fragment@0 {
 >     target-path = "/brabra";
 >     #address-cells = <1>;
 >     #size-cells = <1>;
 >     __overlay__ {
 >       #address-cells = <1>;
 >       #size-cells = <1>;
 >       property1;
 >       property2 = "value";
 >       property3 = <1 2 3>;
 >       some : some@1234 {
 >         property1;
 >         property2 = "value";
 >         property3 = <1 2 3>;
 >       }
 >     };
 >   };
 > };
 > EOT
 $ dtc -O dtb some-name.dts -o some-name.dtbo # .dts を .dtbo に変換
 $ sudo mkdir -p /sys/kernel/config/device-tree/overlays/some_name # 書き込み先を作る
 $ sudo cp some-name.dtbo /sys/kernel/config/device-tree/overlays/some_name/dtbo # これで反映される
 $ # 使う
 $ sudo rmdir /sys/kernel/config/device-tree/overlays/some_name # 取り消す

などを参考にすればいいはず。
** Device Tree Ovelay 用スクリプト [#b87fd39f]

下記の fpga region も device tree overlay 
を用いた設定方法なので、以下では fpga region を試す。
dts ファイルを直接登録できるスクリプト

file-name.dtso を folder-name とうフォルダに登録するための使い方は以下の通り。

 LANG:console
 $ dto                             # 使い方を表示
  USAGE: sudo dto folder-name [file-name.dts]
 $ dto folder-name file-name.dtso  # 登録
 $ dto folder-name                 # 解除

dts ファイルを dtb ファイルに変換しておく必要がないため、
いろいろ試すのにはとても便利。

/usr/local/dto

#gist(osamutake/cf9ec69d9cc5e8d552a57fe4646816ee)

* FPGA Region によるプログラマブルロジック書き換え [#qc05dbec]

** カーネル設定 [#f82da98e]

[[電気回路/zynq/Petalinux2018.3によるzynq-7000ブート用SDカード作成#t9b088e3]] の続きから、

 LANG:console
 $ cd ~/petalinux/zturn-v2018.3/kernel-source
 $ make ARCH=arm menuconfig
  Device Drivers  ---> 
      FPGA Configuration Framework  --->    
          <*> FPGA Region
          <*> Xilinx Zynq FPGA
          <*> FPGA Bridge Framework
          <*> Xilinx LogiCORE PR Decoupler

Petalinux からカーネルソースを取ってきた場合には
これらは元々チェックが付いているようだ。



** .bit ファイルを .bin ファイルに変換する [#rd04c9cc]
** .bin ファイルを生成する [#rd04c9cc]

*** fpga-bit-to-bin.py で .bit ファイルを .bin ファイルに変換する [#s2cc5137]

https://github.com/topic-embedded-products/meta-topic/blob/master/recipes-bsp/fpga/fpga-bit-to-bin/fpga-bit-to-bin.py

を使うと、bit ファイルを bin ファイルに変換できる。

 LANG:console
 $ cd
 $ sudo curl https://raw.githubusercontent.com/topic-embedded-products/meta-topic/master/recipes-bsp/fpga/fpga-bit-to-bin/fpga-bit-to-bin.py -o /usr/local/bin/fpga-bit-to-bin.py
 $ sudo chown root:root /usr/local/bin/fpga-bit-to-bin.py
 $ sudo chmod a+x /usr/local/bin/fpga-bit-to-bin.py
 $ sudo mkdir -p /lib/firmware/bit.bin
 $ sudo fpga-bit-to-bin.py --flip /boot/system.bit /lib/firmware/bit.bin/system.bit.bin

ただ、この変換にはかなり時間がかかるみたい。

fpga へ書き込むための bin ファイルはビッグエンディアンになっていなければならなくて、
リトルエンディアンの bit ファイルからエンディアンの変換を行うのが python 
とても時間がかかる。

*** vivado で .bin ファイルを作る [#o589f9e5]

vivado の [Flow Navigator] の [Generate Bitstream] の上で右クリックして、
[Bitstream Setting...] を選ぶと、bit ファイルと同時に bin ファイルも生成してくれるようになる。

&ref(bin_file-creation.png,,50%);

ところが、こうしてできる .bin ファイルはリトルエンディアン形式なので、
書き込む前にはやはりビッグエンディアンに直さなければならない。

 LANG:console
 $ od -tx1 ~/design_1_wrapper.bin | head -5
  0000000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
  *
  0000040 00 00 00 bb 11 22 00 44 ff ff ff ff ff ff ff ff
  0000060 aa 99 55 66 20 00 00 00 30 02 20 01 00 00 00 00
  0000100 30 02 00 01 00 00 00 00 30 00 80 01 00 00 00 00
 $ od -tx1 /lib/firmware/bit.bin/system.bit.bin | head -5
  0000000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
  *
  0000040 bb 00 00 00 44 00 22 11 ff ff ff ff ff ff ff ff
  0000060 66 55 99 aa 00 00 00 20 01 20 02 30 00 00 00 00
  0000100 01 00 02 30 00 00 00 00 01 80 00 30 00 00 00 00

*** エンディアンネス変更プログラム [#zb2c4ddb]

 LANG:console
 $ flip32 design_1_wrapper.bin_LE > design_1_wrapper.bin_BE

のようにして使えるプログラムを C で書いた。

これなら短時間でバイトオーダーをひっくり返せる。

/usr/local/bin/flip32
 LANG:c
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 int main(int argc, const char *argv[])
 {
     if (argc != 2) {
         fprintf(stderr, "USAGE: flip32 input_file\n");
         return -1;
     }
    
     int fd;
     FILE *fp;
     struct stat stbuf;
     long file_size, i;
     u_int32_t *buffer;
     
     fd = open(argv[1], O_RDONLY);
     if (fd == -1) {
         fprintf(stderr, "Input file not found.\n");
         return -1;
      }
    
     if (fstat(fd, &stbuf) == -1) {
         fprintf(stderr, "fstat failed.\n");
         return -1;
     }
 
     file_size = stbuf.st_size;
     buffer = (u_int32_t*)malloc(file_size);
     if (buffer == NULL) {
         fprintf(stderr, "malloc failed.\n");
         return -1;
     }
    
     if (read(fd, buffer, file_size)!=file_size) {
         fprintf(stderr, "read failed.\n");
         return -1;
     }
    
     close(fd);
    
     for(i=0; i<file_size/4; i++) {
         buffer[i] = __builtin_bswap32(buffer[i]);
     }
    
     fwrite(buffer, 1, file_size, stdout);
 }


** fpga region デバイス [#qd5ca1ee]

 LANG:console
 $ dtc -I dtb -O dts /boot/devicetree.dtb
  /dts-v1/;
  
  / {
          #address-cells = <0x1>;
          #size-cells = <0x1>;
          compatible = "xlnx,zynq-7000";
  
          cpus {
                ...
          };
  
          fpga-full {
                  compatible = "fpga-region";
                  fpga-mgr = <0x3>;
                  #address-cells = <0x1>;
                  #size-cells = <0x1>;
                  ranges;
          };
          ...

のように /fpga-full として定義されている。

** ロジックの書き込み [#mc0ccfe8]

- vivado で作成された design_1_wrap.bin を flip32 に通す
- 結果を /lib/firmware の下に保存する
- device tree の /fpga-full の firmware-name というプロパティに、
/lib/firmware からの相対パスで bin のありかを指定する


 LANG:console
 $ mkdir fpga-region
 $ cd fpga-region/
 $ cat << EOT > fpga-region.dts
 > // Device Tree File for FPGA Region:   fpga_reg.dts
 $ flip32 design_1_wrap.bin > /lib/firmware/fpga-full/big-endian.bin # /lib/firmware 以下にコピーする
 $ cat << EOT > fpga-region.dtso
 > /dts-v1/;
 > /plugin/;
 > / {
 >     fragment@1 {
 >     fragment@0 {
 >         target-path = "/fpga-full";
 >         #address-cells = <1>;
 >         #size-cells = <1>;
 >         __overlay__ {
 >             #address-cells = <1>;
 >             #size-cells = <1>;
 >             firmware-name = "bit.bin/system.bit.bin";
 >             firmware-name = "fpga-full/big-endian.bin";
 >         };
 >     };
 > };
 > EOT
 $ dtc -I dts -O dtb fpga-region.dts -o fpga-region.dtbo
 $ sudo mkdir -p /sys/kernel/config/device-tree/overlays/fpga-region
 $ cat /sys/kernel/config/device-tree/overlays/fpga-region/status
  unapplied
 $ sudo cp fpga-region.dtbo /sys/kernel/config/device-tree/overlays/fpga-region/dtbo
 $ cat /sys/kernel/config/device-tree/overlays/fpga-region/status
  applied
 $ dto fpga-region fpga-region.dtso

これでできた。

標準で /fpga-full というラベルで fpga region のデバイスが宣言されているので、
そこをめがけて device tree ovelay すればいいらしい。

status に 1 を書き込む必要はなく、dtbo への書き込みだけでいい。

ただ、もう一度 dtbo へ書き込んでも DONE の LED に変化がないので、
内容を更新できているのかどうかよく分からない。
** ロジックの取り消し [#z61fe09b]

device tree overlay の基本としては、作ったディレクトリを rmdir してしまえば取り消せる。

 LANG:console
 $ sudo rmdir /sys/kernel/config/device-tree/overlays/fpga-region

** PL 領域書き換え用のスクリプト [#gb07ac62]

/usr/local/bin/fpga-program

 LANG:sh
 #!/bin/sh
 fpga-bit-to-bin.py --flip $1 /lib/firmware/bit.bin/system.bit.bin && \
 (rmdir /sys/kernel/config/device-tree/overlays/fpga-region || true) && \
 mkdir -p /sys/kernel/config/device-tree/overlays/fpga-region && \
 dtc -I dts -O dtb << EOT > /sys/kernel/config/device-tree/overlays/fpga-region/dtbo
 /dts-v1/;
 /plugin/;
 / {
   fragment@0 {
     target-path = "/fpga-full";
     #address-cells = <1>;
     #size-cells = <1>;
     __overlay__ {
       #address-cells = <1>;
       #size-cells = <1>;
       firmware-name = "bit.bin/system.bit.bin";
     };
   };
 };
 EOT

 LANG:console
 $ sudo fpga-program design_1_wrapper.bit

のようにして使えるのだが・・・
fpga-bit-to-bin.py の Flipping data... ですごく時間がかかる。

こういう処理は python で書いちゃダメな気がするなあ・・・

 LANG:python
 def flip32(data):
         sl = struct.Struct('<I')
         sb = struct.Struct('>I')
         try:
                 b = buffer(data)
         except NameError:
                 # Python 3 does not have 'buffer'
                 b = data
         d = bytearray(len(data))
         for offset in range(0, len(data), 4):
                  sb.pack_into(d, offset, sl.unpack_from(b, offset)[0])
         return d


/usr/local/bin/fpga-program の2行目を書き換えて、

 LANG:sh
 #!/bin/sh
 flip32 $1 /lib/firmware/bit.bin/system.bit.bin && \
 (rmdir /sys/kernel/config/device-tree/overlays/fpga-region || true) && \
 mkdir -p /sys/kernel/config/device-tree/overlays/fpga-region && \
 dtc -I dts -O dtb << EOT > /sys/kernel/config/device-tree/overlays/fpga-region/dtbo
 /dts-v1/;
 /plugin/;
 / {
   fragment@0 {
     target-path = "/fpga-full";
     #address-cells = <1>;
     #size-cells = <1>;
     __overlay__ {
       #address-cells = <1>;
       #size-cells = <1>;
       firmware-name = "bit.bin/system.bit.bin";
     };
   };
 };
 EOT

とすれば、今度は Windows 版 vivado で作成したままの design_1_wrapper.bin に対して、

 LANG:console
 $ fpga-program design_1_wrapper.bin

でサクッと書き込める。

あとはもう少しまともなシェルスクリプトを書けば・・・と思ったのだけれど、
実は上記の fpga-program スクリプトの冒頭を /bin/bash 
にしただけでエラーになっちゃうことが分かって愕然としている。

 LANG:console
 $ sudo fpga-load design_1_wrapper.bin
  Unable to handle kernel NULL pointer dereference at virtual address 00000008
  pgd = c0004000
  [00000008] *pgd=00000000
  Internal error: Oops - BUG: 5 [#1] PREEMPT SMP ARM
  Modules linked in:
  CPU: 1 PID: 1707 Comm: dtc Not tainted 4.14.0-xilinx-v2018.3 #1
  Hardware name: Xilinx Zynq Platform
  task: ee56d900 task.stack: ee9ea000
  PC is at set_root+0x30/0xb0
  LR is at path_init+0x12c/0x2c4
  pc : [<c01dbd2c>]    lr : [<c01dc1cc>]    psr: 20010013
  sp : ee9ebbc8  ip : 00000014  fp : ee42b000
  r10: ef3c1c60  r9 : ee41f4c4  r8 : c0a55b54
  r7 : 00000001  r6 : 00000041  r5 : 00000000  r4 : ee9ebc68
  r3 : ee9ebbc8  r2 : ee9ebc80  r1 : 00000041  r0 : ee9ebc68
  Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
  Control: 18c5387d  Table: 2e5e004a  DAC: 00000051
  Process dtc (pid: 1707, stack limit = 0xee9ea210)
  Stack: (0xee9ebbc8 to 0xee9ec000)
  bbc0:                   ee9ebc68 ef157010 00000041 c01dc1cc ee9ebc68 ee9ebd04
  ...
  [<c01dbd2c>] (set_root) from [<c01dc1cc>] (path_init+0x12c/0x2c4)
  [<c01dc1cc>] (path_init) from [<c01e046c>] (path_openat+0x1ac/0xc94)
  [<c01e046c>] (path_openat) from [<c01e0f84>] (do_filp_open+0x30/0x78)
  [<c01e0f84>] (do_filp_open) from [<c01d1948>] (file_open_name+0xcc/0xf8)
  [<c01d1948>] (file_open_name) from [<c01d199c>] (filp_open+0x28/0x40)
  [<c01d199c>] (filp_open) from [<c01d8a74>] (kernel_read_file_from_path+0x2c/0x78)
  [<c01d8a74>] (kernel_read_file_from_path) from [<c03bee00>] (_request_firmware+0x2bc/0x55c)
  [<c03bee00>] (_request_firmware) from [<c03bf0dc>] (request_firmware+0x3c/0x54)
  [<c03bf0dc>] (request_firmware) from [<c0511104>] (fpga_mgr_firmware_load+0x3c/0x98)
  [<c0511104>] (fpga_mgr_firmware_load) from [<c05127e4>] (of_fpga_region_notify+0x2fc/0x4d8)
  [<c05127e4>] (of_fpga_region_notify) from [<c01344ec>] (notifier_call_chain+0x40/0x64)
  [<c01344ec>] (notifier_call_chain) from [<c01348b0>] (__blocking_notifier_call_chain+0x40/0x58)
  [<c01348b0>] (__blocking_notifier_call_chain) from [<c01348dc>] (blocking_notifier_call_chain+0x14/0x1c)
  [<c01348dc>] (blocking_notifier_call_chain) from [<c0501a30>] (of_overlay_notify+0x48/0x78)
  [<c0501a30>] (of_overlay_notify) from [<c0501e94>] (of_overlay_create+0x220/0x4dc)
  [<c0501e94>] (of_overlay_create) from [<c04fc728>] (create_overlay+0x8c/0xd0)
  [<c04fc728>] (create_overlay) from [<c04fc7c4>] (cfs_overlay_item_dtbo_write+0x58/0x88)
  [<c04fc7c4>] (cfs_overlay_item_dtbo_write) from [<c022c414>] (configfs_release_bin_file+0x50/0x84)
  [<c022c414>] (configfs_release_bin_file) from [<c01d4b68>] (__fput+0xd8/0x1ac)
  [<c01d4b68>] (__fput) from [<c0132124>] (task_work_run+0x9c/0xac)
  [<c0132124>] (task_work_run) from [<c011d36c>] (do_exit+0x3f8/0x8b4)
  [<c011d36c>] (do_exit) from [<c011e3f8>] (do_group_exit+0x58/0xc0)
  [<c011e3f8>] (do_group_exit) from [<c011e470>] (__wake_up_parent+0x0/0x18)
  Code: e3120040 0a000015 e2802018 e285c014 (e595e008)
  ---[ end trace 296e080d10983c60 ]---
  Fixing recursive fault but reboot is needed!
  ...

エラーはエラーでも Kernel Panic 的な。

システムの再起動が必要になっちゃう。

えー?!

生の /bin/sh ではまともなシェルスクリプトは書けない気がするので
とりあえずはこのままで行こう。。。
* system.dtb にシンボルを埋め込む [#fc20918e]

https://qiita.com/ikwzm/items/03d518b7c46d1ee49943#device-tree-compilerdtc-%E3%82%92%E7%9B%B4%E6%8E%A5%E4%BD%BF%E3%81%86%E5%A0%B4%E5%90%88

を参考にすると、dtb にシンボルを埋め込むには dtc 1.4.2 以上でなければならないらしい。
ところが petalinux のツールチェインに含まれる dtc は 1.4.2 なのでシンボルに対応していない。
ちなみに母艦の Ubuntu 16.04LTS の dtc は 14.0 だった。

そこで、zynq 側に入れた Ubuntu 18.04LTS の dtc 1.4.5 を使ってコンパイルする。

system.dtb のソースファイルは
components/plnx_workspace/device-tree/device-tree/system-top.dts
にあるのだけれど、これは C のプリプロセッサを通し #include 
を処理しないとそのままでは dtc でコンパイルできない。

そのため、母艦の gcc のプリプロセッサを通して dts を1つにまとめてから
zynq に持っていく。ただ、dtc でのコンパイル時に
components/plnx_workspace/device-tree/device-tree/system-conf.dtsi
を必要とするので、こちらも一緒に zynq に移す必要がある。

https://qiita.com/ikwzm/items/b07af1a861d6f1c0fde2

を参考に、

 LANG:console
 $ which dtc
  /opt/petalinux_2018.3/tools/linux-i386/petalinux/bin/dtc
 $ dtc -v
  Version: DTC 1.4.1
 $ sudo apt-get install -y device-tree-compiler
 $ /usr/bin/dtc -v
  Version: DTC 1.4.0
 $ gcc -E -P -x assembler-with-cpp -I project-spec/meta-user/recipes-bsp/device-tree/files/ components/plnx_workspace/device-tree/device-tree/system-top.dts > images/linux/system.dts
 $ sudo mount /mnt/sdcard1/
 $ sudo cp images/linux/system.dts /mnt/sdcard1/
 $ sudo cp components/plnx_workspace/device-tree/device-tree/system-conf.dtsi /mnt/sdcard1/
 $ sudo umount /mnt/sdcard1/

としておいて、zynq 側で

 LANG:console
 $ dtc -v
  Version: DTC 1.4.5
 $ sudo dtc -I dts -O dtb -@ -i /boot /boot/system.dts -o /boot/devicetree.dtb
  /boot/devicetree.dtb: Warning (unit_address_vs_reg): Node /memory has a reg or ranges property, but no unit name
  /boot/devicetree.dtb: Warning (unit_address_format): Node /amba/spi@e000d000/flash@0/partition@0x00000000 unit name should not have leading "0x"
  /boot/devicetree.dtb: Warning (unit_address_format): Node /amba/spi@e000d000/flash@0/partition@0x00000000 unit name should not have leading 0s
  /boot/devicetree.dtb: Warning (unit_address_format): Node /amba/spi@e000d000/flash@0/partition@0x00500000 unit name should not have leading "0x"
  /boot/devicetree.dtb: Warning (unit_address_format): Node /amba/spi@e000d000/flash@0/partition@0x00500000 unit name should not have leading 0s
  /boot/devicetree.dtb: Warning (unit_address_format): Node /amba/spi@e000d000/flash@0/partition@0x00520000 unit name should not have leading "0x"
  /boot/devicetree.dtb: Warning (unit_address_format): Node /amba/spi@e000d000/flash@0/partition@0x00520000 unit name should not have leading 0s
  /boot/devicetree.dtb: Warning (unit_address_format): Node /amba/spi@e000d000/flash@0/partition@0x00fa0000 unit name should not have leading "0x"
  /boot/devicetree.dtb: Warning (unit_address_format): Node /amba/spi@e000d000/flash@0/partition@0x00fa0000 unit name should not have leading 0s
 $ dtc -O dts /boot/devicetree.dtb
  ...
         __symbols__ {
                  cpu0 = "/cpus/cpu@0";
                  cpu1 = "/cpus/cpu@1";
                  fpga_full = "/fpga-full";
                  regulator_vccpint = "/fixedregulator";
                  amba = "/amba";
                  adc = "/amba/adc@f8007100";
                  ...
  ...

警告は出るけれど何とかコンパイルできた。
起動も問題ないみたい。


Counter: 7747 (from 2010/06/03), today: 5, yesterday: 0