趣味でRustを使ってarm64向けのBaremetal開発を行っています。 今日はその環境でdefmtを使おうとして躓いたのでメモを残しておきます。
critical-section
defmt_rttはcritical-sectionというcrateに依存しています。
critical-sectionはRustでatomicな処理をエミュレーションするためなどに使うcrateです。 defmtではログの出力部にでも使われているのでしょう。
https://crates.io/crates/critical-section
critical-sectionをビルドするには一部メソッドの実装が必要です。 ただし、これらはcortex-mなどのbaremetal開発で使う主要なcrateには実装済みなので、自分で実装が必要になることはほぼないと思います。
cortex-m/cortex-m/src/critical_section.rs at master · rust-embedded/cortex-m · GitHub
今回Baremetal Arm64用の実装は見つけられなかったので、自分で実装することにしました。
ちなみに、critical-sectionの一部メソッドを実装せずにビルドすると、リンカが以下のようなエラーを出してビルドに失敗します。
function `critical_section::release': /home/tnishinaga/.cargo/registry/src/index.crates.io-6f17d22bba15001f/critical-section-1.1.2/src/lib.rs:200: undefined reference to `_critical_section_1_0_release'
SingleCoreCriticalSection
自分でcritical-section用の処理を実装する方法は、critical-sectionのREADMEにかかれています。
# critical-section v1.1.2, READMEより引用 # https://github.com/rust-embedded/critical-section/tree/7ac14a65144bea29eed06947f47b9aef51512cf4 # #[cfg(not(feature = "std"))] // needed for `cargo test --features std` # mod no_std { // This is a type alias for the enabled `restore-state-*` feature. // For example, it is `bool` if you enable `restore-state-bool`. use critical_section::RawRestoreState; struct MyCriticalSection; critical_section::set_impl!(MyCriticalSection); unsafe impl critical_section::Impl for MyCriticalSection { unsafe fn acquire() -> RawRestoreState { // TODO } unsafe fn release(token: RawRestoreState) { // TODO } } # }
single coreでの実装例は以下です。 acquireで割り込みを禁止し、releaseで割り込みを再度有効化しています。 シングルコアで割り込み禁止して、atomicなメモリアクセスをエミュレーションしているようです。
// https://github.com/rust-embedded/cortex-m/blob/master/cortex-m/src/critical_section.rs より引用 use critical_section::{set_impl, Impl, RawRestoreState}; use crate::interrupt; use crate::register::primask; struct SingleCoreCriticalSection; set_impl!(SingleCoreCriticalSection); unsafe impl Impl for SingleCoreCriticalSection { unsafe fn acquire() -> RawRestoreState { let was_active = primask::read().is_active(); interrupt::disable(); was_active } unsafe fn release(was_active: RawRestoreState) { // Only re-enable interrupts if they were enabled before the critical section. if was_active { interrupt::enable() } } }
ところで、私の作っているコードはまだ割り込み周りの実装を行っていないので割り込みは常に無効状態で、シングルコアでしか動かしていません。 ということは、acquireとreleaseの両方で何もしなくても大丈夫なはずです。 よって、ほぼサンプルそのままのコードを実装してcritical-sectionの実装は完了ということにしました。
以上です。