4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Simulinkから生成されたCコードをRustから呼び出す

Last updated at Posted at 2022-05-02

これはなに?

完全に車輪の再発明と思いますが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 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としました。

Snag_3f233a9f.png

3. コード生成

次にコード生成のために必要な設定をしていきましょう。

  • モデル設定を押してソルバータイプを固定ステップに変更し、ソルバーを離散(連続状態なし)とする
    Snag_3f27e11c.png

  • コード生成の設定から ターゲットの選択でシステムターゲットファイルをert.tlcに変更
    image.png

  • アプリタブからEmbedded CoderをクリックしてCコードタブを開く(ここでEmbedded CoderのライセンスをMATALBが掴みます)
    image.png

  • Cコードタブの”コードインタフェース”->”規定のコードマッピング"を選んでモデル画面下にコードマッピングウィンドウを表示させます。
    Snag_3f2dad3e.png

  • functionsタブ を開き、下の図の通り操作して、コード生成後の関数の引数、戻り値を指定しておきます。

  • ここらへんのインタフェースの変え方、たとえば引数でなくてグローバル変数にしたいとか。色々変えれるみたいなのですがワイは実はEmbedded Coderの詳細機能についてはよくわからんのです・・・

  • ここらへん知りたい方はぜひMATALB Answersに投稿してみてください。僕より詳しい人が教えてくれます。
    Snag_3f3371ae.png

  • コード生成ボタンでCコードを生成します。

  • counter_model_stepというところにSimulinkモデル記述と等価なCコードがでているのがわかります。
    Snag_3f35a25a.png

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

ちゃんとできとります。
image.png

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ホントにわかってないんで使い方間違ってたらコメントください!
いつもありがとうございます!

参考文献

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?