OpenOCD + ST-LinkでFirmware書き込み

OpenOCDを使ってSTM32F401 Nucleo-64にST-Link経由でFirmwareを書き込む。

OpenOCDとST-Linkドライバのインストール

Arch Linuxなら以下のコマンドでインストールする。

$ pacman -S openocd stlink

Firmwareを書き込む

OpenOCDで指定できるinterface, target, boardの設定ファイルは/usr/share/openocd/scriptsに配置されている。

OpenOCDで接続する

STM32F401 Nucleo-64 boardのUSB接続の場合

f:id:ryochack:20171103201550j:plain:w480

$ openocd -f board/st_nucleo_f4.cfg

もしくは、

$ openocd -f interface/stlink-v2-1.cfg -f target/stm32f4x.cfg

STM32F401 Chip + 中華ST-Linkの場合

f:id:ryochack:20171103201242j:plain:w480

Nucleoボードを使っているのなら中華ST-Linkを使う必要はないが、試した結果を書き残しておく。

$ openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg

なお、Nucleoボード上のSWDコネクタ(CN4)は以下のピンアサインとなっている。

Pin CN4
1 VDD_TARGET
2 SWCLK
3 GND
4 SWDIO
5 NRST
6 SWO

※中華ST-Linkでは、一度ST-LinkのFirmwareをアップデートした後に書き込みに成功するようになった。 (書き込みに成功している中華ST-LinkのFW versionはV2J28S7)

telnetで書き込む

上記手順でOpenOCDで接続後、別のterminalからtelnetを起動する。

$ telnet localhost 4444

telnet上で以下のコマンドを実行することでFirmwareを書き込む。

> reset halt
> flash write_image erase ${elf_file_absolute_path}

telnet無しでFirmwareを書き込む

上記の方法だと、OpenOCDとは別にtelnetを起動する必要があり、若干煩わしい。 そこで、Firmwareを書き込む手順を記載したOpenOCDのconfigファイルを用意し、OpenOCDだけで書き込みを行えるようにする。

任意の名前で以下の内容のconfigファイルを作成する。 (今回はopenocd.cfgという名前にした)

telnet_port 4444
gdb_port 3333

source [find board/st_nucleo_f4.cfg]

init

proc flash_elf {elf_file} {
    reset
    halt
    flash write_image erase $elf_file
    verify_image $elf_file
    echo "flash write_image ($elf_file) complete"
    reset
    exit
}

configファイルを作成したら、以下のコマンドで書き込みを実行する。

$ openocd -f openocd.cfg -c "flash_elf ${elf_file_relative_path}"

※OpenOCDのprocの名前はOpenOCDの既存コマンド名にあるものを使うとエラーになるようだ。
(最初、proc flash {elf_file}としていたところ、以下のエラーが出て小一時間ほどハマってしまった)

Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
none separate
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : clock speed 1800 kHz
Info : STLINK v2 JTAG v28 API v2 SWIM v18 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 3.256353
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
flash
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
adapter speed: 1800 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x61000000 pc: 0x1fff1048 msp: 0x20002e40
openocd.cfg:13: Error: wrong # args: should be "flash elf_file"
in procedure 'flash'
at file "openocd.cfg", line 13

ST-LinkのFirmwareアップデート

こちらで公開されているツールを使ってアップデートする。

参考

Rustでクロスコンパイル

x86_64-unknown-linux-gnuのホスト環境上で、CHIP向けのバイナリをRustでクロスコンパイルすることを目的とする。

概要

  1. rustup target add ${target_triple} でターゲットのツールチェインを追加
  2. ターゲットのリンカをインストール
  3. Cargoのconfigファイルにターゲット用のリンカ設定を記述
  4. cargo build --target ${target_triple} でクロスコンパイル実行

※Rustのツールチェイン管理を行ってくれるツールのrustupこちらを参考にインストール済みとする。

環境

ホストのシステム情報。

$ uname -a
Linux hostname 4.13.7-1-ARCH #1 SMP PREEMPT Sat Oct 14 20:13:26 CEST 2017 x86_64 GNU/Linux

$ rustup show
Default host: x86_64-unknown-linux-gnu

1.21.0-x86_64-unknown-linux-gnu (default)
rustc 1.21.0 (3b72af97e 2017-10-09)

ターゲットであるCHIPのシステム情報。

chip@chip:~$ uname -a
Linux chip 4.4.13-ntc-mlc #1 SMP Thu Nov 3 01:28:54 UTC 2016 armv7l GNU/Linux

ターゲットのツールチェインを追加

rustup target listで、Rustがサポートするターゲットのリストを確認する。

$ rustup target list | pr -w 90 --columns=2

aarch64-apple-ios                            mips-unknown-linux-gnu
aarch64-linux-android                        mips-unknown-linux-musl
aarch64-unknown-fuchsia                      mips64-unknown-linux-gnuabi64
aarch64-unknown-linux-gnu                    mips64el-unknown-linux-gnuabi64
arm-linux-androideabi                        mipsel-unknown-linux-gnu
arm-unknown-linux-gnueabi                    mipsel-unknown-linux-musl
arm-unknown-linux-gnueabihf                  powerpc-unknown-linux-gnu
arm-unknown-linux-musleabi                   powerpc64-unknown-linux-gnu
arm-unknown-linux-musleabihf                 powerpc64le-unknown-linux-gnu
armv7-apple-ios                              s390x-unknown-linux-gnu
armv7-linux-androideabi                      sparc64-unknown-linux-gnu
armv7-unknown-linux-gnueabihf                wasm32-unknown-emscripten
armv7-unknown-linux-musleabihf               x86_64-apple-darwin
armv7s-apple-ios                             x86_64-apple-ios
asmjs-unknown-emscripten                     x86_64-linux-android
i386-apple-ios                               x86_64-pc-windows-gnu
i586-pc-windows-msvc                         x86_64-pc-windows-msvc
i586-unknown-linux-gnu                       x86_64-rumprun-netbsd
i686-apple-darwin                            x86_64-unknown-freebsd
i686-linux-android                           x86_64-unknown-fuchsia
i686-pc-windows-gnu                          x86_64-unknown-linux-gnu (default)
i686-pc-windows-msvc                         x86_64-unknown-linux-musl
i686-unknown-freebsd                         x86_64-unknown-netbsd
i686-unknown-linux-gnu                       x86_64-unknown-redox
i686-unknown-linux-musl

armv7-unknown-linux-gnueabihfが見つかり、RustはCHIPのターゲットをサポートしていることを確認できたため、以下のコマンドでCHIP用のツールチェインを追加する。

$ rustup target add armv7-unknown-linux-gnueabihf

rustup target add後にrustup showrustup target listを実行してRustのツールチェインの状態を確認すると、armv7-unknown-linux-gnueabihf(installed)になっているのがわかる。

$ rustup show
Default host: x86_64-unknown-linux-gnu

installed targets for active toolchain
--------------------------------------

armv7-unknown-linux-gnueabihf
x86_64-unknown-linux-gnu

active toolchain
----------------

1.21.0-x86_64-unknown-linux-gnu (default)
rustc 1.21.0 (3b72af97e 2017-10-09)

$ rustup target list | pr -w 90 --columns=2

aarch64-apple-ios                            i686-unknown-linux-musl
aarch64-linux-android                        mips-unknown-linux-gnu
aarch64-unknown-fuchsia                      mips-unknown-linux-musl
aarch64-unknown-linux-gnu                    mips64-unknown-linux-gnuabi64
arm-linux-androideabi                        mips64el-unknown-linux-gnuabi64
arm-unknown-linux-gnueabi                    mipsel-unknown-linux-gnu
arm-unknown-linux-gnueabihf                  mipsel-unknown-linux-musl
arm-unknown-linux-musleabi                   powerpc-unknown-linux-gnu
arm-unknown-linux-musleabihf                 powerpc64-unknown-linux-gnu
armv7-apple-ios                              powerpc64le-unknown-linux-gnu
armv7-linux-androideabi                      s390x-unknown-linux-gnu
armv7-unknown-linux-gnueabihf (installed)    sparc64-unknown-linux-gnu
armv7-unknown-linux-musleabihf               wasm32-unknown-emscripten
armv7s-apple-ios                             x86_64-apple-darwin
asmjs-unknown-emscripten                     x86_64-apple-ios
i386-apple-ios                               x86_64-linux-android
i586-pc-windows-msvc                         x86_64-pc-windows-gnu
i586-unknown-linux-gnu                       x86_64-pc-windows-msvc
i686-apple-darwin                            x86_64-rumprun-netbsd
i686-linux-android                           x86_64-unknown-freebsd
i686-pc-windows-gnu                          x86_64-unknown-fuchsia
i686-pc-windows-msvc                         x86_64-unknown-linux-gnu (default)
i686-unknown-freebsd                         x86_64-unknown-linux-musl
i686-unknown-linux-gnu                       x86_64-unknown-netbsd

ターゲットのリンカをインストール

Rustのツールチェインではターゲットのリンカはサポートしていないため、別途用意する。

$ apt-get install gcc-arm-linux-gnueabihf

Cargoのconfigにターゲットのリンカ設定を記述

ロスコンパイルする対象とするプロジェクトを生成する。

$ cargo new --bin hello

Cargoが生成したスケルトンコードのままで実行すると、Hello, world!が標準出力に出力される。

$ cargo run
   Compiling hello v0.1.0 (file:///host_shared/hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.44 secs
     Running `target/debug/hello`
Hello, world!

これをそのままターゲット(CHIP)上で動作するようにするために、プロジェクトのconfigファイルを作成し、ターゲットのリンカの設定を記述する。

$ mkdir .cargo
$ cat > .cargo/config << EOF
> [target.armv7-unknown-linux-gnueabihf]
> linker = "arm-linux-gnueabihf-gcc"
> EOF

ロスコンパイル実行

$ cargo build --target armv7-unknown-linux-gnueabihf

ビルドされたバイナリの情報を確認すると、ターゲット(CHIP)向けのバイナリになっていることがわかる。

$ file target/armv7-unknown-linux-gnueabihf/debug/hello
target/armv7-unknown-linux-gnueabihf/debug/hello: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=70dfb01aa67d797fc64005ac8cff1dba94a48a34, not stripped

$ readelf -A target/armv7-unknown-linux-gnueabihf/debug/hello
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "7-A"
  Tag_CPU_arch: v7
  Tag_CPU_arch_profile: Application
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-2
  Tag_FP_arch: VFPv3-D16
  Tag_ABI_PCS_GOT_use: GOT-indirect
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_rounding: Needed
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_enum_size: int
  Tag_ABI_VFP_args: VFP registers
  Tag_CPU_unaligned_access: v6
  Tag_ABI_FP_16bit_format: IEEE 754

ロスコンパイルしたバイナリをターゲット上で実行

chip@chip:~$ ./hello
Hello, world!

ターゲット(CHIP)上で実行できることが確認できた。

参考

Rustでのクロスコンパイルに必要な情報が、以下のページに非常にわかりやすく説明されている。
japaric/rust-cross: Everything you need to know about cross compiling Rust programs!

環境構築の検証用にDockerを使う

Rustのクロスコンパイル環境構築の検証にDockerを使い始めた。

開発環境を構築する際、最初は試行錯誤しながら行なうため、さぁ環境が構築ができた、となった時に行なってきた手順のどれが最低限必要なものだったのかがわからなくなることが多い。 必要だと思ってインストールしたものが実は必要なかったり、必要ないと思ったものに実は依存していたりするため、手順に再現性があるのかどうかを検証したくなる。

このようなケースにおいて、いつでもクリーンな環境から手順の確認ができるDockerが非常に便利だったので、以下にRustの開発環境をDockerで立ち上げるまでにやったことをメモとして残しておく。

$ docker --version
Docker version 17.10.0-ce, build f4ffd2511c

Docker HubでRustがインストールされたDockerイメージが公開されているのでpullする。

$ docker pull rust

Rustのイメージが取得できたことを確認する。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
rust                latest              9578919665db        7 days ago          1.33GB

RustのDockerイメージからコンテナを起動する。

$ docker run -i -t -v ${host_dir_path}:${container_dir_path} --rm rust

docker runのオプションの意味は以下を参照。

  • -i : 標準入力を有効にする
  • -t : 疑似ターミナルを割り当てる
  • -v ${host_dir_path}:${container_dir_path} : ホストとコンテナの共有ディレクトリを指定する
  • --rm : コンテナからExitした時に自動的にコンテナを削除する

共有ディレクトリを指定しているのは、ホストで書いたコードをコンテナ内でビルドし、コンテナでビルドしたファイルをホストから参照するのを容易にするため。

Target Triple

Rustのコンパイラソースコードの中で使用されていた"Triple"という言葉の意味がわからなかったので調べた。

Target Triplet - OSDev Wiki

Target Triplets describe a platform on which code runs and are a core concept in the GNU build system. They contain three fields: the name of the CPU family/model, the vendor, and the operating system name.

サポートするターゲットを表す表現方法を Target Triple と呼んでいる。 ターゲットの指定が以下の3つのフィールドから成っていることが、Tripleと呼ぶ理由らしい。

  • CPU family/model
  • Vendor
  • OS

具体例としては、以下のようなターゲット指定が挙げられる。(よく見るやつ)

なお、clangのドキュメントでは以下のようにフィールド指定が定義されている。
Cross-compilation using Clang

The triple has the general format <arch><sub>-<vendor>-<sys>-<abi>, where:

Arch Linux上にRe:VIEWをインストールする

書籍執筆支援システムRe:VIEWの環境構築のメモ。

依存パッケージのインストール

Re:VIEWを使うために必要なパッケージをインストールする。

$ sudo pacman -S ruby ruby-rubyzip texlive-most texlive-langjapanese poppler-data

dvipdfmxのフォントのエラー対策

Re:VIEWからpdfを生成する際(後述のreview-pdfmaker実行時)にデータに日本語を含んでいると、以下のエラーが出る場合がある。

kpathsea: Running mktexpk --mfmode / --bdpi 600 --mag 2+180/600 --dpi 1380 uphgothr-h
mktexpk: don't know how to create bitmap font for uphgothr-h.
mktexpk: perhaps uphgothr-h is missing from the map file.
kpathsea: Appending font creation commands to missfont.log.

dvipdfmx:warning: Could not locate a virtual/physical font for TFM "uphgothr-h".
dvipdfmx:warning: >> There are no valid font mapping entry for this font.
dvipdfmx:warning: >> Font file name "uphgothr-h" was assumed but failed to locate that font.
dvipdfmx:fatal: Cannot proceed without .vf or "physical" font for PDF output...

対策として、/etc/texmf/dvipdfmx/dvipdfmx.cfgを編集する。

%% Put additional fontmap files here (usually for Type0 fonts)
f  cid-x.map

上記変更を反映するために以下を実行する。

$ sudo updmap-sys
$ sudo texconfig rehash
$ updmap
$ texconfig rehash

Re:VIEWのインストール

AURにあるRe:VIEWパッケージは若干古いようなので、gitからインストールする。

$ git clone https://github.com/kmuto/review.git

clone後、展開されたreview/bin/にPATHを通す。

始め方

$ review-init ${document_name}

${document_name}/以下に${document_name}.reが生成されているので、これを編集する。

各フォーマットで出力する

  • PDF出力 $ review-pdfmaker config.yml
  • EPUB出力 $ review-epubmaker config.yml
  • HTML出力 $ review-epubmaker config.yml

参考