RustのRTFM (Real Time For the Masses)を試してみる①

はじめに

組込みRust界の伝説japaric氏が実装しているReal Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollersを試してみます。

github.com

RTFM自体は、Real-time for the masses, step 1: Programming API and static priority SRP kernel primitives.という論文が大本のようです。

ドキュメントもしっかり整備されています。見習わないといけないですね。

japaric.github.io

RTFM brief

ドキュメントの機能一覧を抜粋します。

  • タスク管理
  • タスク間メッセージ
  • タイマキュー
  • プリエンプティブマルチタスキング
  • 効率的でデータ競合のないメモリ共有
  • デッドロックしない実行
  • 最小限のスケジューリングオーバーヘッド
  • 極めて効率的なメモリ使用

最小限のリアルタイムコアシステム、といった印象を受けます。

initサンプル

qemu-system-armがあれば、動かせるサンプルが多く用意されています。 QEMUをターゲットに、少しサンプルを動かしてみましょう。

$ git clone https://github.com/japaric/cortex-m-rtfm.git
$ cd cortex-m-rtfm
$ cargo run --example init
    Finished dev [unoptimized + debuginfo] target(s) in 0.07s
     Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel target/thumbv7m-none-eabi/debug/examples/init`
init # サンプルで出力した文字列

単純に文字を出力するだけのサンプルのようです。 アプリケーションのソースコードを見てみましょう。

ソースコード

#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]

extern crate panic_semihosting;

use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;

#[app(device = lm3s6965)]
const APP: () = {
    #[init]
    fn init() {
        static mut X: u32 = 0;

        // Cortex-M peripherals
        let _core: rtfm::Peripherals = core;

        // Device specific peripherals
        let _device: lm3s6965::Peripherals = device;

        // Safe access to local `static mut` variable
        let _x: &'static mut u32 = X;

        hprintln!("init").unwrap();

        debug::exit(debug::EXIT_SUCCESS);
    }
};

まず、良いなと思ったのは、最初に#![deny(unsafe_code)]しています。 組込みRust普及活動をする上で、unsafeだらけのコードになるのでは?という質問を良く受けますが、アプリケーションレベルでは、unsafeを許さないこのアトリビュートを設定できます。 後は、それだけこのRTFMフレームワークが上手にunsafeを閉じ込めた実装になっている、というのもこの1行からわかります。

見慣れないのは、#[app(device = lm3s6965)]アトリビュートと、#[init]アトリビュートです。

#[app(..)]アトリビュートは、device引数を使って、svd2rustで生成されたperipheral access crate (PAC)を指すパスを指定します。 #[app(..)]アトリビュートは、const itemに適用しなければなりません。これは、nightlyを使わないようにするための制限です。

appアトリビュートは、initアトリビュートでマーキングされた初期化関数があることを想定します。 initアトリビュートは、[unsafe] fn()シグネチャに持つ関数につけることができます。

init関数は、アプリケーションとして実行される最初の関数です。 この関数は、割り込み禁止状態で実行します。

coredeviceという変数があり、この変数を通して、Cortex-Mとペリフェラルにアクセスできます。

        // Cortex-M peripherals
        let _core: rtfm::Peripherals = core;

        // Device specific peripherals
        let _device: lm3s6965::Peripherals = device;

appinitアトリビュートあたりがマクロ展開されて定義されるのでしょう。

mdbook-epubお試し

はじめに

同人誌を書く環境を模索しています。 Rustが好きなので、Rust製のドキュメントビルダーmdbookも試してみます。

mdbook自体には、PDF出力機能がないため、3rd party pluginであるdbook-epubを試してみます。

インストール

Rust環境はあらかじめ構築しておいて下さい。

cargo install mdbook
cargo install mdbook-epub

mdbookプロジェクトのbook.tomlに次の一行を追加します。

[output.epub]

通常通り、mdbookを呼び出します。

$ mdbook build
2019-05-02 20:51:53 [INFO] (mdbook::book): Book building has started
2019-05-02 20:51:53 [INFO] (mdbook::book): Running the epub backend
2019-05-02 20:51:53 [INFO] (mdbook::renderer): Invoking the "epub" renderer
$ ls book
'The Embedonomicon.epub'

htmlではなく、epubが出力されています。

読んでみる

calibreというepubリーダー / 編集ソフトを使って、少しCSSをいじりながら、調節してみます。

sudo apt install calibre

f:id:tomo-wait-for-it-yuki:20190503060606p:plain
mdbook-epub

意外と悪くない…?

組込みRustドキュメントを和訳したお話

はじめに

組込みRustの(勝手に主要と思っている)ドキュメント3つの和訳が、一通り完了しました。 今後は、upstream変更に対するメンテンナンスをやっていきます。

節目なので、整理しておこうと思います。

和訳も、大本になる文章があるからできるわけで、素晴らしいドキュメントを作成しているRust Embeddedチームに対して、尊敬の念に堪えません。

Rustは簡単な言語ではないので、どうしてもドキュメントが必要です。 ありがたいことに、Rustではドキュメントを書く文化が色濃いです。 今後も、Rust好きな一人として、ドキュメントの和訳や、自身の理解を解説するドキュメントを書いて行こうと思います。 (当面、大きな和訳はやらないと思いますが)

偉大な本家

和訳

和訳レポジトリは下記です。和訳に関するフィードバックはこちらにお願いします。

各ドキュメントの紹介

The embedded Rust book

マイクロコントローラのような「ベアメタル」の組込みシステムでRustを使うための入門書です。

少し注意が必要なのは、Rustの入門書でも組込み開発の入門書でもない、ということです。 Rustの基本文法とがわかった上で、マイコンでRustを使う方法を学ぶためのものです。 手を動かす要素は少なめです。

すでにRustをある程度学んでいて、組込み経験もあるのであれば、本書から入るのがおすすめです。

内容は多岐に渡ります。

  • 環境構築
  • ペリフェラル制御
  • コンパイラを使った静的検証
  • 移植性
  • 並行性
  • コレクション
  • 組込みC開発者へのヒント
  • 相互運用性(FFI
  • 最適化

Discovery

Rustを使った組込みシステム初心者向けのドキュメントです。 多少Rustを知っていれば、組込みシステムの前提知識はあまり必要ありません。 逆に、組込みシステムを知っていれば、Rustの知識は少しだけで困らないと思います。

STMF3Discoveryボードを使って、ひたすら手を動かすため、The embedded Rust bookより、こちらを先に読むと良いかもしれません。

UARTでpinrt!マクロを作る部分は、組込み以外の方も一度見てみる価値があるかと思います。

uprintln! -

Rust、組込み、両方とも中級者だと、物足りない内容かと思います。

Embedonomicon

ひたすらリンカのセクションをいじくり回すドキュメントです。 こちらは、組込みに限らず、ベアメタルでRustを使う方々にとって有用な内容になっています。 特に、メモリレイアウト、mainインタフェースの作り方、はOS自作勢にとって必須科目ですね。

Rustでのセクションやシンボルの扱い方が解説されています。 行数としては大した量を書かないですが、細かくクレートを切ることで、うまくRustプログラミングする方法の参考にもなります。

よもやま話

ポエムです。

和訳を始めたきっかけ

2018年の年末あたり、Rustを仕事にしたいと考えていました。 Rustの勉強ができて、売名できて、普及用のリソースもできる、というやらない理由が見つからない状況でした。

ちょうど年末年始に時間があったので、見切り発車で和訳を開始しました。

tomo-wait-for-it-yuki.hatenablog.com

和訳の進め方

文章が長かったり、構造がわかりにくい時だけ、一度google翻訳に突っ込んで、文脈の理解に役立てました。 驚くほど良い文章が出て来ることがありますが、基本的には、自分で作った訳の方が自然な日本語になります。

たまに出てくるジョークや、洒落た言い回しが厄介でした。 技術的に難しい部分はありましたが、おおよそ、平易な英語で書かれており、それほど読むのに困らない文章でした。

YoctoでRust②〜cargo-bitbake〜

はじめに

組込みLinuxでRustする場合、普通にホストマシンで開発して、普通にホストマシンのRustコンパイラでクロスビルドしたものをターゲットに持って行っています。 これで事足りているのですが、前々から興味があったため、YoctoでRustアプリケーションをビルドします。

今回は、CargoプロジェクトからYoctoのレシピを生成してくれるcargo-bitbakeを試してみます。

github.com

cargo-bitbakeのインストール

libssl-devが必要なのでインストールします。

$ sudo apt install libssl-dev
$ cargo install cargo-bitbake

ripgrepでお試し

github.com

ripgrepをYoctoでビルドしてみます。 構築している環境のRustのバージョンが1.24.0なので、そのコンパイラでビルド可能なbranchを引っ張ってきます。

meta-rustはmaster branchを使うのが良いらしいです。確かに、1.33まで対応されていました。

$ git clone https://github.com/BurntSushi/ripgrep.git -b 0.9.0
$ ripgrep
$ cargo bitbake
$ ls *.bb
ripgrep_0.9.0.bb

レシピができました。

$ cat ripgrep_0.9.0.bb 
# Auto-Generated by cargo-bitbake 0.3.10
#
inherit cargo

# If this is git based prefer versioned ones if they exist
# DEFAULT_PREFERENCE = "-1"

# how to get ripgrep could be as easy as but default to a git checkout:
# SRC_URI += "crate://crates.io/ripgrep/0.9.0"
SRC_URI += "git://github.com/BurntSushi/ripgrep.git;protocol=https"
SRCREV = "6799dcfc0ee63d741cd721c3311852a1b01449d8"
S = "${WORKDIR}/git"
CARGO_SRC_DIR=""


# please note if you have entries that do not begin with crate://
# you must change them to how that package can be fetched
SRC_URI += " \
crate://crates.io/aho-corasick/0.6.6 \
crate://crates.io/ansi_term/0.11.0 \
crate://crates.io/atty/0.2.11 \
crate://crates.io/bitflags/1.0.3 \
crate://crates.io/bytecount/0.3.1 \
crate://crates.io/cfg-if/0.1.4 \
crate://crates.io/clap/2.32.0 \
crate://crates.io/crossbeam/0.3.2 \
crate://crates.io/encoding_rs/0.8.4 \
crate://crates.io/encoding_rs_io/0.1.1 \
crate://crates.io/fnv/1.0.6 \
crate://crates.io/fuchsia-zircon-sys/0.3.3 \
crate://crates.io/fuchsia-zircon/0.3.3 \
crate://crates.io/glob/0.2.11 \
crate://crates.io/lazy_static/1.0.2 \
crate://crates.io/libc/0.2.42 \
crate://crates.io/log/0.4.3 \
crate://crates.io/memchr/2.0.1 \
crate://crates.io/memmap/0.6.2 \
crate://crates.io/num_cpus/1.8.0 \
crate://crates.io/rand/0.4.2 \
crate://crates.io/redox_syscall/0.1.40 \
crate://crates.io/redox_termios/0.1.1 \
crate://crates.io/regex-syntax/0.6.2 \
crate://crates.io/regex/1.0.2 \
crate://crates.io/remove_dir_all/0.5.1 \
crate://crates.io/same-file/1.0.2 \
crate://crates.io/simd/0.2.2 \
crate://crates.io/strsim/0.7.0 \
crate://crates.io/tempdir/0.3.7 \
crate://crates.io/termcolor/1.0.1 \
crate://crates.io/termion/1.5.1 \
crate://crates.io/textwrap/0.10.0 \
crate://crates.io/thread_local/0.3.5 \
crate://crates.io/ucd-util/0.1.1 \
crate://crates.io/unicode-width/0.1.5 \
crate://crates.io/unreachable/1.0.0 \
crate://crates.io/utf8-ranges/1.0.0 \
crate://crates.io/void/1.0.2 \
crate://crates.io/walkdir/2.1.4 \
crate://crates.io/winapi-i686-pc-windows-gnu/0.4.0 \
crate://crates.io/winapi-x86_64-pc-windows-gnu/0.4.0 \
crate://crates.io/winapi/0.3.5 \
crate://crates.io/wincolor/1.0.0 \
"



# FIXME: update generateme with the real MD5 of the license file
LIC_FILES_CHKSUM=" \
file://Unlicense OR MIT;md5=generateme \
"

SUMMARY = "ripgrep is a line-oriented search tool that recursively searches your current
directory for a regex pattern while respecting your gitignore rules. ripgrep
has first class support on Windows, macOS and Linux"
HOMEPAGE = "https://github.com/BurntSushi/ripgrep"
LICENSE = "Unlicense OR MIT"

# includes this file if it exists but does not fail
# this is useful for anything you may want to override from
# what cargo-bitbake generates.
include ripgrep-${PV}.inc
include ripgrep.inc

なんかライセンス関連のところが変、というかFIXMEと書いてありますね。 ということで、ちょろっと直しておきます。

LIC_FILES_CHKSUM=" \
file://COPYING;md5=034e2d49ef70c35b64be514bef39415a \
"

ビルドしてみる

面倒くさいので、meta-rustのrecipes-exampleに生成されたレシピを突っ込みます。

meta-rust/recipes-example/ripgrep$ ls
ripgrep_0.9.0.bb
$ bitbake ripgrep
ERROR: ParseError at /home/tomoyuki/others/01.rust/yocto/layers/meta-rust/recipes-example/ripgrep/ripgrep_0.9.0.bb:103: unparsed line: 'SUMMARY = "ripgrep is a line-oriented search tool that recursively searches your current'

サマリの説明部分でエラーが出ます。レシピを確認してみます。

SUMMARY = "ripgrep is a line-oriented search tool that recursively searches your current
directory for a regex pattern while respecting your gitignore rules. ripgrep
has first class support on Windows, macOS and Linux"

改行入ってます。

SUMMARY = "ripgrep is a line-oriented search tool that recursively searches your current directory for a regex pattern while respecting your gitignore rules. ripgrep has first class support on Windows, macOS and Linux"

改行をなくします。

$ bitbake ripgrep
WARNING: Host distribution "ubuntu-18.04" has not been validated with this version of the build system; you may possibly experience unexpected failures. It is recommended that you use a tested distribution.
Loading cache: 100% |############################################| Time: 0:00:00
Loaded 2785 entries from dependency cache.
NOTE: There are 1 recipes to be removed from sysroot cortexa7hf-neon-vfpv4, removing...
NOTE: Resolving any missing task queue dependencies

Build Configuration:
BB_VERSION           = "1.36.0"
BUILD_SYS            = "x86_64-linux"
NATIVELSBSTRING      = "universal"
TARGET_SYS           = "arm-poky-linux-gnueabi"
MACHINE              = "raspberrypi3"
DISTRO               = "poky"
DISTRO_VERSION       = "2.4.4"
TUNE_FEATURES        = "arm armv7ve vfp thumb neon vfpv4 callconvention-hard cortexa7"
TARGET_FPU           = "hard"
meta                 
meta-poky            
meta-yocto-bsp       = "rocko:5f660914cd7eec8117efccdf1eb29c466b4e74f7"
meta-oe              
meta-python          
meta-networking      = "rocko:eae996301d9c097bcbeb8046f08041dc82bb62f8"
meta-raspberrypi     = "rocko:8e4c537d84fdde8e3b4642d0dda2c0f4af76d52f"
meta-rust            = "rocko:a4797129e2ab7f11671f817653b11ed876c2b43c"

Initialising tasks: 100% |#######################################| Time: 0:00:00
NOTE: Executing SetScene Tasks
NOTE: Executing RunQueue Tasks
WARNING: ripgrep-0.9.0-r0 do_populate_lic: ripgrep: No generic license file exists for: Unlicense in any provider
NOTE: Tasks Summary: Attempted 564 tasks of which 549 didn't need to be rerun and all succeeded.

Summary: There were 2 WARNING messages shown.

はい、OKです。

YoctoでRust①〜meta-rustのhello-worldビルド〜

はじめに

組込みLinuxでRustする場合、普通にホストマシンで開発して、普通にホストマシンのRustコンパイラでクロスビルドしたものをターゲットに持って行っています。 これで事足りているのですが、前々から興味があったため、YoctoでRustアプリケーションをビルドします。

やってみてわかったことは、アプリケーションを複数個、ディストリビューションとして一括管理したい場合でなければ、RustアプリケーションをYoctoでビルドするうまみはないように思えました。 環境だけYoctoで構築し、sysrootをCargoの設定ファイルで読み込んで開発するのが良さそうに思えます。

ターゲット

ハードはラズパイ3 & Yoctoのバージョンはrockoです。

bitbake

前準備

sudo apt install gawk wget git-core diffstat unzip texinfo gcc-multilib \
     build-essential chrpath socat cpio python python3 python3-pip python3-pexpect \
     xz-utils debianutils iputils-ping libsdl1.2-dev xterm

ソースダウンロード

git clone -b rocko git://git.yoctoproject.org/poky.git
git clone -b rocko git://git.yoctoproject.org/meta-raspberrypi
git clone -b rocko git://git.openembedded.org/meta-openembedded
git clone -b rocko https://github.com/meta-rust/meta-rust.git

NOTE: sumoではビルドが失敗します。rockoではビルドが成功することがわかっています。

環境設定

source layers/poky/oe-init-build-env build

レイヤ追加

bitbake-layers add-layer ../layers/meta-openembedded/meta-oe
bitbake-layers add-layer ../layers/meta-openembedded/meta-python
bitbake-layers add-layer ../layers/meta-openembedded/meta-networking
bitbake-layers add-layer ../layers/meta-raspberrypi
bitbake-layers add-layer ../layers/meta-rust

local.conf修正

MACHINEをラズパイ3にします。

MACHINE = "raspberrypi3"

rust-hello-worldのビルド

Rustのサンプルプロジェクトであるrust-hello-worldがビルドできるかどうか、試してみます。

bitbake rust-hello-world

i7-8565Uで2時間ほどかかりました。

rustコンパイラのバージョンを確認してみます。 ビルド生成物のnative用ツールチェインにRustコンパイラがあります。

$ build/tmp/work/x86_64-linux/rust-cross-arm$ ls
1.24.1-r0

1.24.1…。 古いですねぇ…。

Rustのツールチェイン関係では、RustコンパイラLLVMがビルドされています。

$ ls rust*
rust-cross-arm:
1.24.1-r0

rust-llvm-native:
1.24.1-r0

rust-native:
1.24.1-r0

そして、LLVMが27GB、Rustコンパイラが13GBと、ものすごい容量を占めていました。 ビルド時間も、LLVMとRustコンパイラをビルドしてる時間が長く、その割に、ツールチェインが古いので、割に合わない感じです。 Rustはデフォルトでクロスコンパイルできるので、どうしてもYoctoでビルドしてdistributionとして配布したい、という状況でなければ、わざわざYoctoでビルドするうまみはなさそうです。

muslでビルドして、完全に独立したバイナリとして作る方が、移植性が高くて良いのではないですかね?

参考

pagefault.blog

注意

どうもsumoでのビルドが失敗します。

github.com

続きはないかもしれません。

実践Rust入門

はじめに

著者の皆様から本書をいただきました。本当にありがとうございます。

実践Rust入門[言語仕様から開発手法まで]

実践Rust入門[言語仕様から開発手法まで]

本の大まかな構成は次の通りです。

  • 第1部基礎編:Rustの特徴や文法
  • 第2部実践編:パーサパッケージWebアプリケーションFFIの実践的な使い方の例と解説

全体を通して、動くサンプルコードで学べるようになっている点が、本書の大きな特徴だと思います。 println!かテスト(assert!, assert_eq!マクロ)ですぐに動作を確認できるコードが多いです。

また、Rustに入門するにあたり、エラーとの格闘はつきものですが、 本書では、こう書くと、このようなエラーが出て、その理由はこうで、直し方は〜、というエラーの解説まであり、入門者に親切な作りになっています。

Rust入門のための1冊として、自信を持って推奨することができます!

以下、個人的に特筆したいと思った2点について書きます。

  • なぜRustなのか?
  • FFI

なぜRustなのか?

第1章では、Rustの特徴が記載されています。 その中には、なぜRustなのか?という節があります。

  • 1-4 なぜRustなのか?
    • 1-4-1 トップクラスのパフォーマンス
    • 1-4-2 安全なシステムプログラミング言語
    • 1-4-3 生産性を高めるモダンな機能
    • 1-4-4 シングルバイナリ、クロスコンパイル
    • 1-4-5 他言語との連携が容易

Rustを導入していく際に、説得材料にできるような内容がまとめられています。 私個人でも、なぜRustが良いのか、という部分は資料を集めに時間をかけたため、Rust普及活動したい方がそこにかける時間が減らせる本書が出たことは、非常に意義深いです。

FFI

C言語との連携について書かれた章です。 現状、同様の日本語ドキュメントはないと思います。

私が主に活動している組込みに限らず、C言語の資産を活用することは避けて通れない道です。 C言語との連携の話をすると、多くの場合「unsafe地獄にならないですか?」という質問を頂きます。

章の冒頭に書かれている通り、RustとC言語の橋渡しを作ることは、Rust単体やC言語単体のプログラミングよりも困難なことが多々あります。 動的確保したメモリを、RustとC、どちらで解放するか、を意識しながらラッパーを書く部分など、慎重な設計が必要で、デバッグも難しいです。

ただ、1度unsafeを閉じ込めたAPIが作れると、Rustらしい安全で生産性の高いコードを記述することが可能になります。 FFIの章では、Rust風のラッパAPIを作っていく手順が丁寧に説明されており、多くのRustユーザーにとって有用な内容になっています。 本書で書かれているレベルのことができるようになるまで、自分で色々調べたり、試行錯誤が必要だった状況が大幅に改善されると思います。

他の書評など

私は興味のある部分がニッチですので、ここだけでは判断できない!という方々は、ぜひ他の方の書評を読んで頂ければ、幸いです。

  • 著者の一人であるκeenさんの記事

keens.github.io

  • Rust勉強会でよくLTされているyukiさんの記事

yuk1tyd.hatenablog.com

  • Rust LTなどを主催されているdorayakikunさんの記事

medium.com

KiCadで基板を作ってみよう①〜基板を描くまで〜

はじめに

技術書典で入手した基板を作ってみよう 技術書典6特装版 -KiCad 5による基板作成-を参考に基板を作ってみます。 書籍内で詳細が記述されていない部分 (インストール方法など)のみ、メモとして記載します。

techbookfest.org

私は、電子回路(LSI)設計は経験がありますが、基板設計は未経験で、電気回路は素人です。

KiCadインストール

ダウンロードページからUbuntuのリリースビルドバイナリを入手します。 aptによるインストール方法が書いてあるため、手順に従います。

sudo add-apt-repository --yes ppa:js-reynaud/kicad-5.1
sudo apt update
sudo apt install --install-suggests kicad
sudo apt install kicad

中々回線が細くて、時間がかかります。

起動

適当にディレクトリを作成し、KiCadを起動します。

# working directory
kicad

f:id:tomo-wait-for-it-yuki:20190427154650p:plain
kicad

circuit-testerというプロジェクトを作成し、作業を進めて行きます。

回路図

Eeschemaを起動して、回路図を書いていきます。

f:id:tomo-wait-for-it-yuki:20190427195256p:plain
schematic

操作に慣れないながらも、なんとか回路図を作っていきます。 回路の意味を完全に理解できているわけではないのですが、とにかくやってみるの精神です。

フットプリントと対応付け

おぉ、これは大変です…。 ちょっと自分で経験積まないと、実装側で対応、とかそういった応用ができなさそうです。

基板を描こう

トランジスタの物理レイアウト設計したいた頃を思い出します。 こういう配線は、パズル要素があって面白いですね。

今回は、書籍の配線を真似して、サクっと仕上げて終了です。

f:id:tomo-wait-for-it-yuki:20190427212009p:plain
pcb

区切りが良いので、発注は別途やります。