これはなに?
完全に車輪の再発明と思いますがSimulinkから生成されたCコードをRustから呼び出すことをためしてみました。
今後、もしかするとベアメタル組み込み開発の足回りがRustで開発される未来がくるかもな~と妄想したので試してみた次第です。
環境
OS: Windows 10
VS Code: 1.66.2
Rust
> cargo --version
cargo 1.51.0 (43b129a20 2021-03-16)
> rustc -V
rustc 1.51.0 (2fd73fabe 2021-03-23)
> rustup -V
rustup 1.23.1 (3df2264a9 2020-11-30)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active `rustc` version is `rustc 1.51.0 (2fd73fabe 2021-03-23)`
MATLAB
>> ver
---------------------------------------------------------------------------------------------------
MATLAB バージョン: 9.12.0.1884302 (R2022a)
MATLAB ライセンス番号: ======ひみつ=====
オペレーティング システム: Microsoft Windows 10 Enterprise Version 10.0 (Build 19044)
Java バージョン: Java 1.8.0_202-b08 with Oracle Corporation Java HotSpot(TM) 64-Bit Server VM mixed mode
やってみたこと
1. cargo new でプロジェクト作成と.tomlの編集
slcode_call_from_rustって名前で作ってみます。
えっCargoって何? Rustってどうやって環境整えるの?って人はこの辺を御覧ください。
-
- Cargoについて学ぶ: https://qiita.com/yoshii0110/items/6d70323f01fefcf09682
Rustコーディング環境構築@VSCode: https://zenn.dev/23prime/articles/74cda5a096a3b3
> cargo new slcode_call_from_rust
Created binary (application) `slcode_call_from_rust` package
そうすると勝手にフォルダとファイルができますがそのうちのCargo.tomlファイルを編集しましょう。
これをやっておくと後々やるcargo run とかcargo buildしたタイミングで指定したcソースをライブラリとしてコンパイルする作業もまとめてやってくれるようになります。
Cargo.tomlファイルはパッケージマネージャ及びRustのビルドシステムであるCargoの設定を記述できます。
Cargo.toml
[package]
name = "slcode_call_from_rust"
version = "0.1.0"
authors = ["griffin921"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ここで下記のように追加しました。
-
- links は依存ライブラリを記述するもの – http://doc.crates.io/build-script.html#the-links-manifest-key
build はパッケージビルドパスを指定します。ここで指定したbuild.rsにビルドを実行する際の動作をRustで記述します。
[dependencies]に追加したlibc は char や int などの C API の型が定義されているクレートで、こいつを依存関係に含めることでC言語の型を扱えます
[build-dependencies]に追加したccはC/C++/アセンブリをRustライブラリ/アプリケーションにコンパイルするための機能を提供するクレートでビルドスクリプトに利用するのでここに追加しています。
[package]
name = "slcode_call_from_rust"
version = "0.1.0"
authors = ["griffin921"]
edition = "2018"
links = "counter_model"
build = "build.rs"
[dependencies]
libc = "0.2.0"
[build-dependencies]
cc = "1.0.72"
build.rs
外部クレート ccを使ってビルド定義を行います。
ビルド対象となるCソースは.fileに、
インクルードパスは.includeに
cをコンパイルしてライブラリファイルとして出力する際のファイル名を.compileに記述しました。
.fileに記述したPassは今はまだないですが、src/simulinkフォルダを後ほど作り、
counter_model.slxというモデルをコード生成した際に
counter_model_ert_rtwというフォルダがSimulinkが自動生成して、その下にコードを生成してくれるので
それを見越してこのPassにしています。
こちらは生成されるコードのディレクトリ名が異なればその都度変更が必要なので注意しましょう。
extern crate cc;
fn main(){
cc::Build::new()
.file("src/simulink/counter_model_ert_rtw/counter_model.c")
.include("src")
.compile("libsl.a");
}
2. Simulinkモデルの作成
Cargoが自動生成してくれたslcode_call_from_rust直下のsrcフォルダに”simulink”フォルダを作成しましょう。
\slcode_call_from_rust> mkdir src/simulink
そのフォルダに下記のようなモデルを作ってみました。モデル名はbuild.rsの定義に合わせて
“counter_model”にしました。
非常に簡単なモデルですね。入力信号に対して加算をしていく処理になっています。
データ型はint32としました。
3. コード生成
次にコード生成のために必要な設定をしていきましょう。
5. .hファイルから.rsファイルを生成
bindgenを使ってEmbedded Coderが吐き出してくれたヘッダ”counter_model.h”からcounter_model.rsを作りましょう。
あ、事前にcargo install bindgenでインストールをしておいてくださいね。
main.rsから見える位置にcounter_model.rsを作りたいのでこのようにします。
slcode_call_from_rust\src\simulink\counter_model_ert_rtw> bindgen .\counter_model.h > ../../counter_model.rs
6. main.rsを書く
bindgenから生成されたcounte_model.rsをモジュールとして指定し、
useでcounter_model.rs内のcounter_model_step関数を使えるようにします。
生成したコード、counter_model_stepに引数1を与えてfor文で4回実行してみましょう。
Rustでは外部のソースで記述された関数の安全性までは担保できないので、
unsafeスコープ内で実行します。
mod counter_model;
pub use counter_model::counter_model_step;
fn main() {
println!("Hello, world!");
for n in 1..=4{
unsafe{
println!("Step:{}",counter_model_step(1));
}
}
}
7. cargo runする
slcode_call_from_rust> cargo run
こうするとなんだか大量にワーニングが出ますが、ほとんどがSimulinkから生成されたコードに対して
キャメル型ではなくスネーク型で変数名を宣言せよ!等というアドバイスなので無視します。
でも逆にビルドシステムがこういうの言ってくれるのはスゴイですよね。
うざったい人もいるかもしれないけれど私はこういうところは好きです。
そして実行結果はこうなります。
Hello, world!
Step:1
Step:2
Step:3
Step:4
おおおおおお!!
ちゃんとステップごとに加算していますね!
同じ引数1を与えて同じ関数を実行していますが、ちゃんとSimulinkでつくったUnitDelayに相当するコードが
前回値を覚えてくれているので加算していっています。
まとめ
普通に下記参考文献でかいてあったことをまとめただけなような気がしますが・・・
ご参考になれば幸いです。
あ、あとRustホントにわかってないんで使い方間違ってたらコメントください!
いつもありがとうございます!
参考文献
-
- rustでCで書いた関数を呼ぶ / Cからrustで書いた関数を呼ぶ: https://mmi.hatenablog.com/entry/2017/02/28/213656
Rust公式ドキュメント FFI: Foreign Function Interface:https://doc.rust-lang.org/nomicon/ffi.html
rust-bindgen で手軽に共有ライブラリを動的読み込み https://tech-blog.optim.co.jp/entry/2021/05/27/100000