Чтобы проиллюстрировать, рассмотрим следующий пример:
#![allow(unused)]
fn main() {
#[rtic::app(device = ..)]
mod app {
#[task(priority = 3, spawn = [baz])]
fn foo(c: foo::Context) {
// ..
}
#[task(priority = 2, schedule = [foo, baz])]
fn bar(c: bar::Context) {
// ..
}
#[task(priority = 1)]
fn baz(c: baz::Context) {
// ..
}
}
}
Анализ приоритетов происходил бы вот так:
• foo (prio = 3) и baz (prio = 1) планируемые задачи, поэтому SysTick должен работать на максимальном из этих двух приоритетов, т.е. 3.
• foo::Spawn (prio = 3) и bar::Schedule (prio = 2) соперничают за конечный потребитель baz_FQ; это приводит к максимальному приоритету 3.
• bar::Schedule (prio = 2) имеет экслюзивный доступ к конечному потребителю foo_FQ; поэтому максимальный приоритет foo_FQ фактически 2.
• SysTick (prio = 3) и bar::Schedule (prio = 2) соперничают за очередь таймера TQ; это приводит к максимальному приоритету 3.
• SysTick (prio = 3) и foo::Spawn (prio = 3) оба имеют неблокируемый доступ к очереди готовности RQ3, что хранит записи foo; поэтому максимальный приоритет RQ3 фактически 3.
• SysTick имеет эксклюзивный доступ к очереди готовности RQ1, которая хранит записи baz; поэтому максимальный приоритет RQ1 фактически 3.
Когда интерфейс schedule используется, реализация spawn немного изменяется, чтобы отслеживать baseline задач. Как можете видеть в реализации schedule есть буферы INSTANTS, используемые, чтобы хранить время, в которое задача была запланирована навыполнение; этот Instant читается диспетчером задач и передается в пользовательский код, как часть контекста задачи.
#![allow(unused)]
fn main() {
mod app {
// ..
#[no_mangle]
unsafe UART1() {
const PRIORITY: u8 = 1;
let snapshot = basepri::read();
while let Some(ready) = RQ1.split().1.dequeue() {
match ready.task {
Task::baz => {
let input = baz_INPUTS[ready.index as usize].read();
// ADDED
let instant = baz_INSTANTS[ready.index as usize].read();
baz_FQ.split().0.enqueue_unchecked(ready.index);
let priority = Celclass="underline" :new(PRIORITY);
// ИЗМЕНЕНО instant передан как часть контекста задачи
baz(baz::Context::new(&priority, instant), input)
}
Task::bar => {
// выглядит также как ветка для `baz`
}
}
}
// инвариант BASEPRI
basepri::write(snapshot);
}
}
}
И наоборот, реализации spawn нужно писать значение в буфер INSTANTS. Записанное значение располагается в структуре Spawn и это либо время start аппаратной задачи, либо время scheduled программной задачи.
#![allow(unused)]
fn main() {
mod foo {
// ..
pub struct Spawn<'a> {
priority: &'a Cell<u8>,
// ADDED
instant: Instant,
}
impl<'a> Spawn<'a> {
pub unsafe fn priority(&self) -> &Cell<u8> {
&self.priority
}
// ADDED
pub unsafe fn instant(&self) -> Instant {
self.instant
}
}
}
mod app {
impl<'a> foo::Spawn<'a> {
/// Spawns the `baz` task
pub fn baz(&self, message: u64) -> Result<(), u64> {
unsafe {
match lock(self.priority(), baz_FQ_CEILING, || {
baz_FQ.split().1.dequeue()
}) {
Some(index) => {
baz_INPUTS[index as usize].write(message);
// ADDED
baz_INSTANTS[index as usize].write(self.instant());
lock(self.priority(), RQ1_CEILING, || {
RQ1.split().1.enqueue_unchecked(Ready {
task: Task::foo,
index,
});
});
rtic::pend(Interrupt::UART0);
}
None => {
// достигнута максимальная вместительность; неудачный вызов