ciniml / rust-dap

CMSIS-DAP Rust implementation
Apache License 2.0
88 stars 10 forks source link

RP2040 の critical_section の初期化についての潜在的問題 #36

Closed elfmimi closed 2 years ago

elfmimi commented 2 years ago

非常にややこしい話なのです。 実際には cortex-m-rtic の問題のような気もします。 あるいは、ブートコードが面倒を見てくれてもいい気がします。

RP2040 の場合、 #[cortex_m_rt::entry] ではなく #[rp2040_hal::entry] あるいは 同じ意味ですが #[rp_pico::hal::entry] を使うことが提案されています。

何が違うかというと、 critical_section のための SPINLOCK の初期化(=強制的な開放) を行うようになっていることです。

cortex-m-rtic を使うと、 entry マクロの代わりに app マクロを使うことになるので、SPINLOCK の初期化が行われません。

現在の rust-dap の実装では、 critical_section を利用していないように思いますが、 cortex-m-rtic は、デフォルトで cs: bare_metal::CriticalSection というメンバを init::Context に含んでいるようです。

何故これを気にしているかというと、 defmt-rtt が critical_section を利用しているためです。

デバッグに便利なので defmt-rtt を rust-dap に組み込んで開発に使っているのですが、 稀に何も出力されずにスタックしてしまう事があります。その原因が SPINLOCK の未初期化であることが分かっています。

RP2040 は、SYSRESETREQ をしても SPINLOCK の部分は、ハードウェア的にリセットがかからないということも分かっています。

対処療法としては、 rust-dap の init() の先頭に、以下の部分のコピペを挿入する方法があります。 https://github.com/rp-rs/rp-hal/blob/83b990ee30ec970adf0de68eff0cde4cae69db6c/rp2040-hal-macros/src/lib.rs#L19-L24

    unsafe {
        const SIO_BASE: u32 = 0xd0000000;
        const SPINLOCK0_PTR: *mut u32 = (SIO_BASE + 0x100) as *mut u32;
        const SPINLOCK_COUNT: usize = 32;
        for i in 0..SPINLOCK_COUNT {
            SPINLOCK0_PTR.wrapping_add(i).write_volatile(1);
        }
    }

※ SPINLOCK31 が critical_section 用として使われている様子です。

elfmimi commented 2 years ago

追記:

SWD の RESCUE_DP によってリセットをかけると SIO および SPINLOCK の部分もリセットされます。 あるいは、AP 経由で SPINLOCK にアクセスして強制的に開放することでもスタックを回避できます。

ciniml commented 2 years ago

とりあえず対処療法いれてしまうことにしましょうか。 rust-dap-rp2040 のutilあたりに実装しておいて、各ボードの init の先頭で呼ぶのでいいかな。

elfmimi commented 2 years ago

プルリクを用意しますね。

ciniml commented 2 years ago

よろしくお願いします!