RTICメモ
RTICメモ
Rustの組み込み向けライブラリ RTIC
をつかってみたときのメモ
RTIC概要
Real-Time Interrupt-drien Concurrency の略。
Cortex-MのNVICの機能を使ってうまいことマルチタスク的なことをするライブラリ。
使ってみたコード
Rust向けのCMSIS-DAP実装 rust-dap
ドキュメントを読んで分からなかったところ
大体の使い方は ドキュメント に書いてあるが、実際に使うにあたってサンプルを読んだりして分かった部分についてメモしておく
#[local]
や #[shared]
を付けた構造体のメンバーには Send
が必要
RTICのマクロ内部で #[local]
や #[shared]
のメンバーに対して Send
トレイトが実装されているか確認している模様。
#[init]
関数内部で #[local]
や #[shared]
のコンテキストは初期化されて別の割り込みコンテキスト等に共有されるので、確かに Send
が必要である。
rust-dap ではUARTのRead/Writeを個別に使いたいので split
を呼び出して Reader
Writer
に分離しているが、rp-hal
の Reader
Writer
は Send
を実装していないようで、コンパイル時にエラーが発生する。
WARN rustc_metadata::locator no metadata found: incompatible metadata version found: '/home/kenta/repos/rust-dap/boards/xiao_rp2040/target/debug/deps/libpaste-1d007800db85bb25.so'
error[E0277]: `UnsafeCell<u32>` cannot be shared between threads safely
--> src/main.rs:24:1
|
24 | #[rtic::app(device = rp_pico::hal::pac, peripherals = true)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<u32>` cannot be shared between threads safely
|
= help: within `rp_pico::rp2040_pac::uart0::RegisterBlock`, the trait `Sync` is not implemented for `UnsafeCell<u32>`
= note: required because it appears within the type `vcell::VolatileCell<u32>`
= note: required because it appears within the type `Reg<UARTDR_SPEC>`
= note: required because it appears within the type `rp_pico::rp2040_pac::uart0::RegisterBlock`
= note: required because of the requirements on the impl of `Send` for `&'static rp_pico::rp2040_pac::uart0::RegisterBlock`
= note: required because it appears within the type `Writer<UART0, (rp_pico::rp2040_hal::gpio::Pin<Gpio0, rp_pico::rp2040_hal::gpio::Function<rp_pico::rp2040_hal::gpio::Uart>>, rp_pico::rp2040_hal::gpio::Pin<Gpio1, rp_pico::rp2040_hal::gpio::Function<rp_pico::rp2040_hal::gpio::Uart>>)>`
note: required because it appears within the type `app::UartWriter`
--> src/main.rs:77:16
|
77 | pub struct UartWriter(hal::uart::Writer<pac::UART0, UartPins>);
| ^^^^^^^^^^
= note: required because it appears within the type `Option<app::UartWriter>`
note: required by a bound in `assert_send`
--> /home/kenta/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rtic-1.0.0/src/export.rs:107:8
|
107 | T: Send,
| ^^^^ required by this bound in `assert_send`
= note: this error originates in the attribute macro `rtic::app` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0277`.
error: could not compile `rust-dap-xiao-rp2040` due to previous error
rp-hal
の Reader
Writer
は内部でレジスタブロックへの参照をもっているが、これが Send
を実装していないためエラーとなっている。
内容としてはレジスタブロックのアドレスはシステムで固定なので、実際には構築したコンテキスト以外で参照しても問題ない。
よって外側で Send
を実装して Reader
Writer
を #[shared]
で使えるようにしている。 (Reader
Writer
Sync
はどれも対象のコードがあるクレートで宣言された物ではないので、Orphan Rule対策でNew Type Patternで新しい型を作っている。)
type Uart = hal::uart::UartPeripheral<hal::uart::Enabled, pac::UART0, UartPins>;
pub struct UartReader(hal::uart::Reader<pac::UART0, UartPins>);
pub struct UartWriter(hal::uart::Writer<pac::UART0, UartPins>);
unsafe impl Sync for UartReader {}
unsafe impl Sync for UartWriter {}
unsafe impl Send for UartReader {}
unsafe impl Send for UartWriter {}
#[shared]
struct Shared {
uart_reader: Option<UartReader>,
uart_writer: Option<UartWriter>,
usb_serial: SerialPort<'static, UsbBus>,
uart_rx_consumer: heapless::spsc::Consumer<'static, u8, UART_RX_QUEUE_SIZE>,
uart_tx_producer: heapless::spsc::Producer<'static, u8, UART_TX_QUEUE_SIZE>,
uart_tx_consumer: heapless::spsc::Consumer<'static, u8, UART_TX_QUEUE_SIZE>,
}
複数の共有変数の同時ロック
#[shared]
のメンバーはタスク関数の属性の shared
で使用を宣言して、内部で c.shared.(メンバー名).lock(|m| ...)
を呼び出してロックを取得して使用する。
複数の共有メンバーを同時に使いたい場合は、 (&mut c.shared.member1, &mut c.shared.member2, ...).lock(|m1, m2, ...| )
として同時にロックを取得できる。
このときタプルの中の共有メンバーに対して &mut
をつけ忘れるとムーブされてしまうので注意が必要。
#[task(shared = [usb_serial, uart_tx_producer])]
fn task() {
(&mut c.shared.usb_serial, &mut c.shared.uart_tx_producer).lock(|usb_serial, uart_tx_producer| {
//usb_serial, uart_tx_producer共有メンバーを使う処理
});
}
staticなものの初期化
各タスクでは local = [...]
の中で #[local]
構造体に無いそのタスク固有のstaticな変数を定義できる。
これは #[init]
でも使用可能なので、初期化処理が必要なものや、 #[shared]
で共有するもののうち、staticな領域が必要なものは、 #[init]
の local
を使って宣言する。
#[shared]
struct Shared {
uart_tx_producer: heapless::spsc::Producer<'static, u8, UART_TX_QUEUE_SIZE>,
uart_tx_consumer: heapless::spsc::Consumer<'static, u8, UART_TX_QUEUE_SIZE>,
}
#[init(local = [
uart_rx_queue: heapless::spsc::Queue<u8, UART_RX_QUEUE_SIZE> = heapless::spsc::Queue::new(),
uart_tx_queue: heapless::spsc::Queue<u8, UART_TX_QUEUE_SIZE> = heapless::spsc::Queue::new(),
USB_ALLOCATOR: Option<UsbBusAllocator<UsbBus>> = None,
])]
fn init(c: init::Context) -> (Shared, Local, init::Monotonics) {
// ...
let (uart_rx_producer, uart_rx_consumer) = c.local.uart_rx_queue.split();
let (uart_tx_producer, uart_tx_consumer) = c.local.uart_tx_queue.split();
// ...
(Shared {uart_tx_producer, uart_tx_consumer}, Local { ... }, init::Monotonics())
}
Discussion