Теперь давайте рассмотрим непосредственно критическую секцию. В этом примере мы должны увеличить динамический приоритет минимум до 2, чтобы избежать гонки данных. В архитектуре Cortex-M динамический приоритет можно изменить записью в регистр BASEPRI.
Семантика регистра BASEPRI такова:
• Запись 0 в BASEPRI отключает его функциональность.
• Запись ненулевого значения в BASEPRI изменяет уровень приоритета, требуемого для вытеснения прерывания. Однако, это имеет эффект, только когда записываемое значение
Таким образом, динамический приоритет в любой момент времени может быть рассчитан как
#![allow(unused)]
fn main() {
dynamic_priority = max(hw2logical(BASEPRI), hw2logical(static_priority))
}
Где static_priority - приоритет, запрограммированный в NVIC для текущего прерывания, или логический 0, когда текущий контекств - это idle.
В этом конкретном примере мы можем реализовать критическую секцию так:
ПРИМЕЧАНИЕ: это упрощенная реализация
#![allow(unused)]
fn main() {
impl rtic::Mutex for resources::x {
type T = u64;
fn lock
where
F: FnOnce(&mut u64) -> R,
{
unsafe {
asm!("msr BASEPRI, 192" : : : "memory" : "volatile");
let r = f(&mut x);
asm!("msr BASEPRI, 0" : : : "memory" : "volatile");
r
}
}
}
}
В данном случае важно указать "memory" в блоке asm!. Это не даст компилятору менять местами операции вокруг него. Это важно, поскольку доступ к переменной x вне критической секции привело бы к гонке данных.
Важно отметить, что сигнатура метода lock препятствет его вложенным вызовам. Это необходимо для безопасности памяти, так как вложенные вызовы привели бы к созданию множественных уникальных ссылок (&mut-) на x, ломая правила заимствования Rust. Смотреть ниже:
#![allow(unused)]
fn main() {
#[interrupt(binds = UART0, priority = 1, resources = [x])]
fn foo(c: foo::Context) {
let mut res: resources::x = c.resources.x;
res.lock(|x: &mut u64| {
res.lock(|alias: &mut u64| {
});
});
}
}
Вложенные вызовы lock на
Рассмотрим такую программу:
#![allow(unused)]
fn main() {
#[rtic::app(device = ..)]
mod app {
struct Resources {
#[init(0)]
x: u64,
#[init(0)]
y: u64,
}
#[init]
fn init() {
rtic::pend(Interrupt::UART0);
}
#[interrupt(binds = UART0, priority = 1, resources = [x, y])]
fn foo(c: foo::Context) {
let mut x = c.resources.x;
let mut y = c.resources.y;
y.lock(|y| {
*y += 1;
*x.lock(|x| {
x += 1;
});
*y += 1;
});
x.lock(|x| {
*x += 1;
y.lock(|y| {
*y += 1;
});
*x += 1;
})
}
#[interrupt(binds = UART1, priority = 2, resources = [x])]