mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-18 22:05:37 +01:00
Merge branch 'rtic-rs:master' into master
This commit is contained in:
commit
77427f962d
24 changed files with 370 additions and 839 deletions
|
|
@ -44,7 +44,7 @@ cortex-m = "0.7.0"
|
||||||
cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.5" }
|
cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.5" }
|
||||||
rtic-monotonic = "0.1.0-alpha.2"
|
rtic-monotonic = "0.1.0-alpha.2"
|
||||||
rtic-core = "0.3.1"
|
rtic-core = "0.3.1"
|
||||||
heapless = "0.7.1"
|
heapless = "0.7.5"
|
||||||
bare-metal = "1.0.0"
|
bare-metal = "1.0.0"
|
||||||
|
|
||||||
[dependencies.dwt-systick-monotonic]
|
[dependencies.dwt-systick-monotonic]
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,16 @@
|
||||||
|
|
||||||
- Приложения должны быть написаны в редакции 2018.
|
- Приложения должны быть написаны в редакции 2018.
|
||||||
|
|
||||||
|
## [Руководство пользователя](https://rtic.rs) - [(Версия в разработке)](https://rtic.rs/dev)
|
||||||
|
|
||||||
## [Документация пользователя](https://rtic.rs)
|
## [Документация пользователя](https://rtic.rs)
|
||||||
|
|
||||||
## [Справочник по API](https://rtic.rs/stable/api/)
|
## [Справочник по API](https://rtic.rs/stable/api/)
|
||||||
|
|
||||||
|
## [Сборник примеров, предоставляемы сообществом][examples]
|
||||||
|
|
||||||
|
[examples]: https://github.com/rtic-rs/rtic-examples
|
||||||
|
|
||||||
## Чат
|
## Чат
|
||||||
|
|
||||||
Присоединяйтесь к нам, чтобы говорить о RTIC [в Matrix-комнате][matrix-room].
|
Присоединяйтесь к нам, чтобы говорить о RTIC [в Matrix-комнате][matrix-room].
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
Внутри модуля `app` атрибут ожидает найти функцию инициализации, помеченную
|
Внутри модуля `app` атрибут ожидает найти функцию инициализации, помеченную
|
||||||
атрибутом `init`. Эта функция должна иметь сигнатуру
|
атрибутом `init`. Эта функция должна иметь сигнатуру
|
||||||
`fn(init::Context) [-> init::LateResources]` (возвращаемый тип нужен не всегда).
|
`fn(init::Context) (-> init::LateResources, init::Monotonics)`.
|
||||||
|
|
||||||
Эта функция инициализации будет первой частью программы, выполняемой при запуске.
|
Эта функция инициализации будет первой частью программы, выполняемой при запуске.
|
||||||
Функция `init` будет запущена *с отключенными прерываниями* и будет иметь эксклюзивный доступ
|
Функция `init` будет запущена *с отключенными прерываниями* и будет иметь эксклюзивный доступ
|
||||||
|
|
@ -54,6 +54,12 @@ $ cargo run --example init
|
||||||
{{#include ../../../../ci/expected/init.run}}
|
{{#include ../../../../ci/expected/init.run}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **ПРИМЕЧАНИЕ**: Не забывайте указывать выбранное вами целевое устройство, передавая параметр target
|
||||||
|
> в cargo (например `cargo run --example init --target thumbv7m-none-eabi`) или
|
||||||
|
> настроив устройство, используемое по умолчанию для сборки примеров в `.cargo/config.toml`.
|
||||||
|
> В нашем случае используется Cortex M3, эмулируемый с помощью QEMU, поэтому пишем `thumbv7m-none-eabi`.
|
||||||
|
> Смотрите [`Создание нового проекта`](./new.md) для большей информации.
|
||||||
|
|
||||||
## `idle`
|
## `idle`
|
||||||
|
|
||||||
Функцию, помеченную атрибутом `idle` может опционально добавить в модуль.
|
Функцию, помеченную атрибутом `idle` может опционально добавить в модуль.
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,26 @@
|
||||||
Фреймворк дает пользователю полный контроль за тем, какой контекст может
|
Фреймворк дает пользователю полный контроль за тем, какой контекст может
|
||||||
получить доступ к какому ресурсу.
|
получить доступ к какому ресурсу.
|
||||||
|
|
||||||
Все ресурсы определены в одной структуре внутри модуля `#[app]`.
|
Все ресурсы определены в *двух* структурах внутри модуля `#[app]`.
|
||||||
Каждое поле структуры соответствует отдельному ресурсу.
|
Каждое поле этих структур соответствует отдельному ресурсу.
|
||||||
`struct`-ура должна быть аннотирована следующим атрибутом: `#[resources]`.
|
Одна `struct`-ура должна быть аннотирована атрибутом `#[local]`.
|
||||||
|
Другая `struct`-ура должна быть аннотирована атрибутом `#[shared]`.
|
||||||
Ресурсам могут быть опционально даны начальные значения с помощью атрибута `#[init]`.
|
Разница между этими двумя множествами ресурсов будет описана познее.
|
||||||
Ресурсы, которым не передано начально значение, называются
|
|
||||||
*поздними* ресурсами, более детально они описаны в одном из разделов на этой странице.
|
|
||||||
|
|
||||||
Каждый контекс (задача-обработчик, `init` или `idle`) должен указать ресурсы, к которым
|
Каждый контекс (задача-обработчик, `init` или `idle`) должен указать ресурсы, к которым
|
||||||
он намерен обращаться, в соответсятвующем ему атрибуте с метаданными, используя
|
он намерен обращаться, в соответсятвующем ему атрибуте с метаданными, используя
|
||||||
аргумент `resources`. Этот аргумент принимает список имен ресурсов в качестве значения.
|
либо аргумент `local`, либо `shared`. Этот аргумент принимает список имен ресурсов в качестве значения.
|
||||||
Перечисленные ресурсы становятся доступны в контексте через поле `resources` структуры `Context`.
|
Перечисленные ресурсы становятся доступны в контексте через поля `local` и `shared` структуры `Context`.
|
||||||
|
|
||||||
Пример программы, показанной ниже содержит два обработчика прерывания, которые разделяют
|
Во время выполнения при выходе из функции `#[init]` все ресурсы инициализированы.
|
||||||
доступ к ресурсу под названием `shared`.
|
Функция `#[init]` должна возвращать начальные значения для всех ресурсов;
|
||||||
|
отсюда следует, что тип возвращаемого ею значения включает типы
|
||||||
|
структур `#[shared]` и `#[local]`.
|
||||||
|
Поскольку ресурсы инициализированы в ходе функции `#[init]`, к ним нельзя
|
||||||
|
получить доступ внетри функции `#[init]`.
|
||||||
|
|
||||||
|
Пример программы, показанной ниже содержит два обработчика прерывания.
|
||||||
|
Каждый обработчик имеет доступ к его собственному `#[local]` ресурсу.
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../examples/resource.rs}}
|
{{#include ../../../../examples/resource.rs}}
|
||||||
|
|
@ -32,15 +37,17 @@ $ cargo run --example resource
|
||||||
{{#include ../../../../ci/expected/resource.run}}
|
{{#include ../../../../ci/expected/resource.run}}
|
||||||
```
|
```
|
||||||
|
|
||||||
Заметьте, что к ресурсу `shared` нельзя получить доступ из `idle`. Попытка сделать это
|
К ресурсу `#[local]` нельзя получить доступ извне задачи к которой он
|
||||||
приведет к ошибке компиляции.
|
привязан атрибутом `#[task]`.
|
||||||
|
Попытка обращения к одному и тому же ресурсу `#[local]` из более чем одной
|
||||||
|
задачи - ошибка компиляции.
|
||||||
|
|
||||||
## `lock`
|
## `lock`
|
||||||
|
|
||||||
Критические секции необходимы для разделения изменяемых данных таким образом,
|
Критические секции необходимы для доступа к ресурсам `#[shared]` таким образом,
|
||||||
чтобы избежать гонок данных.
|
чтобы избежать гонок данных.
|
||||||
|
|
||||||
Поле `resources`, передаваемого `Context` реализует трейт [`Mutex`] для каждого разделяемого
|
Поле `shared`, передаваемого `Context` реализует трейт [`Mutex`] для каждого разделяемого
|
||||||
ресурса, доступного задаче.
|
ресурса, доступного задаче.
|
||||||
|
|
||||||
Единственный метод этого трейта, [`lock`], запускает свой аргумент-замыкание в критической секции.
|
Единственный метод этого трейта, [`lock`], запускает свой аргумент-замыкание в критической секции.
|
||||||
|
|
@ -81,33 +88,7 @@ $ cargo run --example lock
|
||||||
{{#include ../../../../examples/multilock.rs}}
|
{{#include ../../../../examples/multilock.rs}}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Поздние ресурсы
|
## Только разделяемый (`&-`) доступ
|
||||||
|
|
||||||
Поздние ресурсы - такие ресурсы, которым не передано начальное значение во время компиляции
|
|
||||||
с помощью атрибута `#[init]`, но которые вместо этого инициализируются во время выполнения
|
|
||||||
с помощью значений из структуры `init::LateResources`, возвращаемой функцией `init`.
|
|
||||||
|
|
||||||
Поздние ресурсы полезны, например, для *move* (передача владения) периферии,
|
|
||||||
инициализированной в `init`, в задачи.
|
|
||||||
|
|
||||||
Пример ниже использует поздние ресурсы, чтобы установить неблокируемый односторонний канал
|
|
||||||
между обработчиком прерывания `UART0` и задачей `idle`. Для канала использована очередь типа
|
|
||||||
один производитель-один потребитель [`Queue`]. Структура очереди разделяется на потребителя
|
|
||||||
и производителя в `init`, а затем каждая из частей располагается в отдельном ресурсу;
|
|
||||||
`UART0` владеет ресурсом производителя, а `idle` владеет ресурсом потребителя.
|
|
||||||
|
|
||||||
[`Queue`]: ../../../api/heapless/spsc/struct.Queue.html
|
|
||||||
|
|
||||||
``` rust
|
|
||||||
{{#include ../../../../examples/late.rs}}
|
|
||||||
```
|
|
||||||
|
|
||||||
``` console
|
|
||||||
$ cargo run --example late
|
|
||||||
{{#include ../../../../ci/expected/late.run}}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Только разделяемый доступ
|
|
||||||
|
|
||||||
По-умолчанию фреймворк предполагает, что все задачи требуют эксклюзивный доступ (`&mut-`) к ресурсам,
|
По-умолчанию фреймворк предполагает, что все задачи требуют эксклюзивный доступ (`&mut-`) к ресурсам,
|
||||||
но возможно указать, что задаче достаточен разделяемый доступ (`&-`) к ресурсы с помощью синтакисиса
|
но возможно указать, что задаче достаточен разделяемый доступ (`&-`) к ресурсы с помощью синтакисиса
|
||||||
|
|
@ -139,11 +120,21 @@ $ cargo run --example only-shared-access
|
||||||
|
|
||||||
## Неблокируемый доступ к изменяемым ресурсам
|
## Неблокируемый доступ к изменяемым ресурсам
|
||||||
|
|
||||||
Есть две других возможности доступа к ресурсам
|
Критическая секция *не* требуется для доступа к ресурсу `#[shared]`,
|
||||||
|
к которому обращаются только из задач с *одинаковым* приоритетом.
|
||||||
|
В этом случае вы можете избежать `lock` API, добавив атрибут поля `#[lock_free]` при объявдении ресурса (смотреть пример ниже).
|
||||||
|
Заметьте, что это лишь для удобства: даже если вы используете `lock` API,
|
||||||
|
во время выполнения фреймворк *не* создаст критическую секцию.
|
||||||
|
Еще одно ценное замечание: использование `#[lock_free]` на ресурсах,
|
||||||
|
разделяемых задачами, запускаемыми с разными приоритетами
|
||||||
|
приведет к ошибке *компиляции* -- не импользование `lock` API может
|
||||||
|
привести к гонке данных в этом случае.
|
||||||
|
|
||||||
* `#[lock_free]`: могут быть несколько задач с одинаковым приоритетом,
|
``` rust
|
||||||
получающие доступ к ресурсу без критических секций. Так как задачи с
|
{{#include ../../../../examples/lock-free.rs}}
|
||||||
одинаковым приоритетом никогда не могут вытеснить друг друга, это безопасно.
|
```
|
||||||
* `#[task_local]`: в этом случае должна быть только одна задача, использующая
|
|
||||||
этот ресурс, так же как локальный `static mut` ресурс задачи, но (опционально) устанавливаемая с в init.
|
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example lock-free
|
||||||
|
{{#include ../../../../ci/expected/lock-free.run}}
|
||||||
|
```
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
# Советы и хитрости
|
# Советы и хитрости
|
||||||
|
|
||||||
|
Полные примеры для RTIC смотрите в репозитарии [rtic-examples][rtic-examples].
|
||||||
|
|
||||||
|
[rtic-examples]: https://github.com/rtic-rs/rtic-examples
|
||||||
|
|
||||||
## Обобщенное программирование (Generics)
|
## Обобщенное программирование (Generics)
|
||||||
|
|
||||||
Все объекты, предоставляющие ресурысы реализуют трейт `rtic::Mutex`.
|
Все объекты, предоставляющие ресурысы реализуют трейт `rtic::Mutex`.
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,7 @@ mod app {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Важная деталь здесь то, что `interrupt::enable` ведет себя как like a *compiler
|
Важная деталь здесь то, что `interrupt::enable` ведет себя как *барьер компиляции*, который не дает компилятору переставить запись в `X` *после*
|
||||||
fence*, которое не дает компилятору пореставить запись в `X` *после*
|
|
||||||
`interrupt::enable`. Если бы компилятор мог делать такие перестановки появились
|
`interrupt::enable`. Если бы компилятор мог делать такие перестановки появились
|
||||||
бы гонки данных между этой записью и любой операцией `foo`, взаимодействующей с `X`.
|
бы гонки данных между этой записью и любой операцией `foo`, взаимодействующей с `X`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,8 @@ mod app {
|
||||||
}
|
}
|
||||||
|
|
||||||
// очередь готовности диспетчера задач
|
// очередь готовности диспетчера задач
|
||||||
// `U4` - целое число, представляющее собой емкость этой очереди
|
// `5-1=4` - представляет собой емкость этой очереди
|
||||||
static mut RQ1: Queue<Ready<T1>, U4> = Queue::new();
|
static mut RQ1: Queue<Ready<T1>, 5> = Queue::new();
|
||||||
|
|
||||||
// обработчик прерывания, выбранный для диспетчеризации задач с приоритетом `1`
|
// обработчик прерывания, выбранный для диспетчеризации задач с приоритетом `1`
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|
@ -151,9 +151,9 @@ mod app {
|
||||||
const RQ1_CEILING: u8 = 2;
|
const RQ1_CEILING: u8 = 2;
|
||||||
|
|
||||||
// используется, чтобы отследить сколько еще сообщений для `bar` можно поставить в очередь
|
// используется, чтобы отследить сколько еще сообщений для `bar` можно поставить в очередь
|
||||||
// `U2` - емкость задачи `bar`; максимум 2 экземпляра можно добавить в очередь
|
// `3-1=2` - емкость задачи `bar`; максимум 2 экземпляра можно добавить в очередь
|
||||||
// эта очередь заполняется фреймворком до того, как запустится `init`
|
// эта очередь заполняется фреймворком до того, как запустится `init`
|
||||||
static mut bar_FQ: Queue<(), U2> = Queue::new();
|
static mut bar_FQ: Queue<(), 3> = Queue::new();
|
||||||
|
|
||||||
// Поиск максимального приоритета для конечного потребителя `bar_FQ`
|
// Поиск максимального приоритета для конечного потребителя `bar_FQ`
|
||||||
const bar_FQ_CEILING: u8 = 2;
|
const bar_FQ_CEILING: u8 = 2;
|
||||||
|
|
@ -227,7 +227,7 @@ mod app {
|
||||||
|
|
||||||
// список свободной памяти: используется для отслеживания свободных ячеек в массиве `baz_INPUTS`
|
// список свободной памяти: используется для отслеживания свободных ячеек в массиве `baz_INPUTS`
|
||||||
// эта очередь инициализируется значениями `0` и `1` перед запуском `init`
|
// эта очередь инициализируется значениями `0` и `1` перед запуском `init`
|
||||||
static mut baz_FQ: Queue<u8, U2> = Queue::new();
|
static mut baz_FQ: Queue<u8, 3> = Queue::new();
|
||||||
|
|
||||||
// Поиск максимального приоритета для конечного потребителя `baz_FQ`
|
// Поиск максимального приоритета для конечного потребителя `baz_FQ`
|
||||||
const baz_FQ_CEILING: u8 = 2;
|
const baz_FQ_CEILING: u8 = 2;
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,46 @@ mod app {
|
||||||
|
|
||||||
Так как теперь используется обычный модуль Rust, это значит, что можно использовать
|
Так как теперь используется обычный модуль Rust, это значит, что можно использовать
|
||||||
обычный пользовательский код в этом модуле.
|
обычный пользовательский код в этом модуле.
|
||||||
Также жто значит, что `use`-выражения для ресурсов (и т.п.) могут понадобиться.
|
Также это значит, что `use`-выражения для ресурсов, используемые
|
||||||
|
в пользовательском коде должны быть перемещены внутрь `mod app`,
|
||||||
|
либо на них можно сослаться с помощью `super`. Например, измените:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use some_crate::some_func;
|
||||||
|
|
||||||
|
#[rtic::app(/* .. */)]
|
||||||
|
const APP: () = {
|
||||||
|
fn func() {
|
||||||
|
some_crate::some_func();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
на
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[rtic::app(/* .. */)]
|
||||||
|
mod app {
|
||||||
|
use some_crate::some_func;
|
||||||
|
|
||||||
|
fn func() {
|
||||||
|
some_crate::some_func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
или
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use some_crate::some_func;
|
||||||
|
|
||||||
|
#[rtic::app(/* .. */)]
|
||||||
|
mod app {
|
||||||
|
fn func() {
|
||||||
|
super::some_crate::some_func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Перенос диспетчеров из `extern "C"` в аргументы app.
|
## Перенос диспетчеров из `extern "C"` в аргументы app.
|
||||||
|
|
||||||
|
|
@ -63,6 +102,182 @@ mod app {
|
||||||
Это работает и для ОЗУ-функций, см. examples/ramfunc.rs
|
Это работает и для ОЗУ-функций, см. examples/ramfunc.rs
|
||||||
|
|
||||||
|
|
||||||
|
## Структуры ресурсов - `#[shared]`, `#[local]`
|
||||||
|
|
||||||
|
Ранее ресурсы RTIC должны были размещаться в структуре с именем "Resources":
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
struct Resources {
|
||||||
|
// Ресурсы определяются здесь
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Начиная с RTIC v0.6.0 структуры ресурсов аннотируются подобно
|
||||||
|
`#[task]`, `#[init]`, `#[idle]`: аттрибутами `#[shared]` и `#[local]`
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
#[shared]
|
||||||
|
struct MySharedResources {
|
||||||
|
// Разделяемые задачами ресурсы определены здесь
|
||||||
|
}
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct MyLocalResources {
|
||||||
|
// Ресурсы, определенные здесь нельзя передавать между задачами; каждый из них локальный для единственной задачи
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Эти структуры разработчик может называть по своему желанию.
|
||||||
|
|
||||||
|
## `shared` и `local` аргументы в `#[task]`'ах
|
||||||
|
|
||||||
|
В v0.6.0 ресурсы разделены на `shared` ресурсы и `local` ресурсы.
|
||||||
|
`#[task]`, `#[init]` и `#[idle]` больше не имеют аргумента `resources`;
|
||||||
|
они должны использовать аргументы `shared` и `local`.
|
||||||
|
|
||||||
|
В v0.5.x:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
struct Resources {
|
||||||
|
local_to_b: i64,
|
||||||
|
shared_by_a_and_b: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(resources = [shared_by_a_and_b])]
|
||||||
|
fn a(_: a::Context) {}
|
||||||
|
|
||||||
|
#[task(resources = [shared_by_a_and_b, local_to_b])]
|
||||||
|
fn b(_: b::Context) {}
|
||||||
|
```
|
||||||
|
|
||||||
|
В v0.6.0:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
#[shared]
|
||||||
|
struct Shared {
|
||||||
|
shared_by_a_and_b: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {
|
||||||
|
local_to_b: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(shared = [shared_by_a_and_b])]
|
||||||
|
fn a(_: a::Context) {}
|
||||||
|
|
||||||
|
#[task(shared = [shared_by_a_and_b], local = [local_to_b])]
|
||||||
|
fn b(_: b::Context) {}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Симметричные блокировки
|
||||||
|
|
||||||
|
Теперь RTIC использует симметричные блокировки, это значит, что метод `lock` нужно использовать для
|
||||||
|
всех доступов к `shared` ресурсам. Поскольку высокоприоритетные задачи имеют эксклюзивный доступ к ресурсу,
|
||||||
|
в старом коде можно было следующее:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
#[task(priority = 2, resources = [r])]
|
||||||
|
fn foo(cx: foo::Context) {
|
||||||
|
cx.resources.r = /* ... */;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(resources = [r])]
|
||||||
|
fn bar(cx: bar::Context) {
|
||||||
|
cx.resources.r.lock(|r| r = /* ... */);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
С симметричными блокировками нужно вызывать `lock` для обоих задач:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
#[task(priority = 2, shared = [r])]
|
||||||
|
fn foo(cx: foo::Context) {
|
||||||
|
cx.shared.r.lock(|r| r = /* ... */);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(shared = [r])]
|
||||||
|
fn bar(cx: bar::Context) {
|
||||||
|
cx.shared.r.lock(|r| r = /* ... */);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Заметьте, что скорость работы не изменяется благодаря оптимизациям LLVM, которые убирают ненужные блокировки.
|
||||||
|
|
||||||
|
## Неблокирующий доступ к ресурсам
|
||||||
|
|
||||||
|
В RTIC 0.5 к ресурсам разделяемым задачами, запускаемыми с одинаковым
|
||||||
|
приоритетом, можно получить доступ *без* `lock` API.
|
||||||
|
Это все еще возможно в 0.6: ресурс `#[shared]` должен быть аннотирован
|
||||||
|
аттрибутом поля `#[lock_free]`.
|
||||||
|
|
||||||
|
v0.5 код:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
struct Resources {
|
||||||
|
counter: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(resources = [counter])]
|
||||||
|
fn a(cx: a::Context) {
|
||||||
|
*cx.resources.counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(resources = [counter])]
|
||||||
|
fn b(cx: b::Context) {
|
||||||
|
*cx.resources.counter += 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
v0.6 код:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
#[shared]
|
||||||
|
struct Shared {
|
||||||
|
#[lock_free]
|
||||||
|
counter: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(shared = [counter])]
|
||||||
|
fn a(cx: a::Context) {
|
||||||
|
*cx.shared.counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(shared = [counter])]
|
||||||
|
fn b(cx: b::Context) {
|
||||||
|
*cx.shared.counter += 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## нет преобразования `static mut`
|
||||||
|
|
||||||
|
`static mut` переменные больше не преобразуются в безопасные `&'static mut` ссылки.
|
||||||
|
Вместо этого синтаксиса используйте аргумент `local` в `#[init]`.
|
||||||
|
|
||||||
|
v0.5.x code:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
#[init]
|
||||||
|
fn init(_: init::Context) {
|
||||||
|
static mut BUFFER: [u8; 1024] = [0; 1024];
|
||||||
|
let buffer: &'static mut [u8; 1024] = BUFFER;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
v0.6.0 code:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
#[init(local = [
|
||||||
|
buffer: [u8; 1024] = [0; 1024]
|
||||||
|
// type ^^^^^^^^^^^^ ^^^^^^^^^ initial value
|
||||||
|
])]
|
||||||
|
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
|
||||||
|
let buffer: &'static mut [u8; 1024] = cx.local.buffer;
|
||||||
|
|
||||||
|
(Shared {}, Local {}, init::Monotonics())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Init всегда возвращает поздние ресурсы
|
## Init всегда возвращает поздние ресурсы
|
||||||
|
|
||||||
С целью сделать API более симметричным задача #[init] всегда возвращает поздние ресурсы.
|
С целью сделать API более симметричным задача #[init] всегда возвращает поздние ресурсы.
|
||||||
|
|
@ -83,51 +298,27 @@ mod app {
|
||||||
|
|
||||||
на это:
|
на это:
|
||||||
|
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
#[rtic::app(device = lm3s6965)]
|
#[rtic::app(device = lm3s6965)]
|
||||||
mod app {
|
mod app {
|
||||||
|
#[shared]
|
||||||
|
struct MySharedResources {}
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct MyLocalResources {}
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(_: init::Context) -> init::LateResources {
|
fn init(_: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||||
rtic::pend(Interrupt::UART0);
|
rtic::pend(Interrupt::UART0);
|
||||||
|
|
||||||
init::LateResources {}
|
(MySharedResources, MyLocalResources, init::Monotonics())
|
||||||
}
|
}
|
||||||
|
|
||||||
// [еще код]
|
// [more code]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Структура Resources - `#[resources]`
|
|
||||||
|
|
||||||
Ранее ресурсы RTIC должны были располагаться в структуре с именем "Resources":
|
|
||||||
|
|
||||||
``` rust
|
|
||||||
struct Resources {
|
|
||||||
// Ресурсы определены здесь
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
В RTIC v0.6.0 структура ресурсов аннотируется также, как и
|
|
||||||
`#[task]`, `#[init]`, `#[idle]`: атрибутом `#[resources]`
|
|
||||||
|
|
||||||
``` rust
|
|
||||||
#[resources]
|
|
||||||
struct Resources {
|
|
||||||
// Ресурсы определены здесь
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
На самом деле, имя структуры предоставлено на усмотрение разработчика:
|
|
||||||
|
|
||||||
``` rust
|
|
||||||
#[resources]
|
|
||||||
struct Whateveryouwant {
|
|
||||||
// Ресурсы определены здесь
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
будет работать так же хороршо.
|
|
||||||
|
|
||||||
## Вызов/планирование откуда угодно
|
## Вызов/планирование откуда угодно
|
||||||
|
|
||||||
С этой новой возвожностью, старый код, такой как:
|
С этой новой возвожностью, старый код, такой как:
|
||||||
|
|
@ -161,40 +352,6 @@ fn bar(_c: bar::Context) {
|
||||||
|
|
||||||
Заметьте, что атрибуты `spawn` и `schedule` больше не нужны.
|
Заметьте, что атрибуты `spawn` и `schedule` больше не нужны.
|
||||||
|
|
||||||
## Симметричные блокировки
|
|
||||||
|
|
||||||
Теперь RTIC использует симметричные блокировки, это значит, что метод `lock` нужно использовать для
|
|
||||||
всех доступов к ресурсам. Поскольку высокоприоритетные задачи имеют эксклюзивный доступ к ресурсу,
|
|
||||||
в старом коде можно было следующее:
|
|
||||||
|
|
||||||
``` rust
|
|
||||||
#[task(priority = 2, resources = [r])]
|
|
||||||
fn foo(cx: foo::Context) {
|
|
||||||
cx.resources.r = /* ... */;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(resources = [r])]
|
|
||||||
fn bar(cx: bar::Context) {
|
|
||||||
cx.resources.r.lock(|r| r = /* ... */);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
С симметричными блокировками нужно вызывать `lock` для обоих задач:
|
|
||||||
|
|
||||||
``` rust
|
|
||||||
#[task(priority = 2, resources = [r])]
|
|
||||||
fn foo(cx: foo::Context) {
|
|
||||||
cx.resources.r.lock(|r| r = /* ... */);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(resources = [r])]
|
|
||||||
fn bar(cx: bar::Context) {
|
|
||||||
cx.resources.r.lock(|r| r = /* ... */);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Заметьте, что скорость работы не изменяется благодаря оптимизациям LLVM, которые убирают ненужные блокировки.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Дополнительно
|
## Дополнительно
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,6 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
|
||||||
let name = &monotonic.ident;
|
let name = &monotonic.ident;
|
||||||
let name_str = &name.to_string();
|
let name_str = &name.to_string();
|
||||||
let ident = util::monotonic_ident(&name_str);
|
let ident = util::monotonic_ident(&name_str);
|
||||||
let ident = util::mark_internal_ident(&ident);
|
|
||||||
let panic_str = &format!(
|
let panic_str = &format!(
|
||||||
"Use of monotonic '{}' before it was passed to the runtime",
|
"Use of monotonic '{}' before it was passed to the runtime",
|
||||||
name_str
|
name_str
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
||||||
// );
|
// );
|
||||||
let t = util::spawn_t_ident(level);
|
let t = util::spawn_t_ident(level);
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
// #[doc = #doc]
|
// #[doc = #doc]
|
||||||
|
|
@ -44,7 +45,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
||||||
|
|
||||||
let n = util::capacity_literal(channel.capacity as usize + 1);
|
let n = util::capacity_literal(channel.capacity as usize + 1);
|
||||||
let rq = util::rq_ident(level);
|
let rq = util::rq_ident(level);
|
||||||
let rq = util::mark_internal_ident(&rq);
|
|
||||||
let (rq_ty, rq_expr) = {
|
let (rq_ty, rq_expr) = {
|
||||||
(
|
(
|
||||||
quote!(rtic::export::SCRQ<#t, #n>),
|
quote!(rtic::export::SCRQ<#t, #n>),
|
||||||
|
|
@ -59,6 +59,8 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
||||||
// );
|
// );
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr);
|
static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr);
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -69,9 +71,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
||||||
let task = &app.software_tasks[name];
|
let task = &app.software_tasks[name];
|
||||||
let cfgs = &task.cfgs;
|
let cfgs = &task.cfgs;
|
||||||
let fq = util::fq_ident(name);
|
let fq = util::fq_ident(name);
|
||||||
let fq = util::mark_internal_ident(&fq);
|
|
||||||
let inputs = util::inputs_ident(name);
|
let inputs = util::inputs_ident(name);
|
||||||
let inputs = util::mark_internal_ident(&inputs);
|
|
||||||
let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs);
|
let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs);
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ pub fn codegen(
|
||||||
for (name, res) in &app.local_resources {
|
for (name, res) in &app.local_resources {
|
||||||
let cfgs = &res.cfgs;
|
let cfgs = &res.cfgs;
|
||||||
let ty = &res.ty;
|
let ty = &res.ty;
|
||||||
let mangled_name = util::mark_internal_ident(&util::static_local_resource_ident(name));
|
let mangled_name = util::static_local_resource_ident(name);
|
||||||
|
|
||||||
let attrs = &res.attrs;
|
let attrs = &res.attrs;
|
||||||
// late resources in `util::link_section_uninit`
|
// late resources in `util::link_section_uninit`
|
||||||
|
|
@ -33,6 +33,7 @@ pub fn codegen(
|
||||||
// For future use
|
// For future use
|
||||||
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
||||||
mod_app.push(quote!(
|
mod_app.push(quote!(
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
// #[doc = #doc]
|
// #[doc = #doc]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
@ -50,14 +51,12 @@ pub fn codegen(
|
||||||
let expr = &task_local.expr;
|
let expr = &task_local.expr;
|
||||||
let attrs = &task_local.attrs;
|
let attrs = &task_local.attrs;
|
||||||
|
|
||||||
let mangled_name = util::mark_internal_ident(&util::declared_static_local_resource_ident(
|
let mangled_name = util::declared_static_local_resource_ident(resource_name, &task_name);
|
||||||
resource_name,
|
|
||||||
&task_name,
|
|
||||||
));
|
|
||||||
|
|
||||||
// For future use
|
// For future use
|
||||||
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
||||||
mod_app.push(quote!(
|
mod_app.push(quote!(
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
// #[doc = #doc]
|
// #[doc = #doc]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
||||||
|
|
@ -44,11 +44,9 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mangled_name = if matches!(task_local, TaskLocal::External) {
|
let mangled_name = if matches!(task_local, TaskLocal::External) {
|
||||||
util::mark_internal_ident(&util::static_local_resource_ident(name))
|
util::static_local_resource_ident(name)
|
||||||
} else {
|
} else {
|
||||||
util::mark_internal_ident(&util::declared_static_local_resource_ident(
|
util::declared_static_local_resource_ident(name, &task_name)
|
||||||
name, &task_name,
|
|
||||||
))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fields.push(quote!(
|
fields.push(quote!(
|
||||||
|
|
@ -86,9 +84,9 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
|
||||||
|
|
||||||
let doc = format!("Local resources `{}` has access to", ctxt.ident(app));
|
let doc = format!("Local resources `{}` has access to", ctxt.ident(app));
|
||||||
let ident = util::local_resources_ident(ctxt, app);
|
let ident = util::local_resources_ident(ctxt, app);
|
||||||
let ident = util::mark_internal_ident(&ident);
|
|
||||||
let item = quote!(
|
let item = quote!(
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[doc = #doc]
|
#[doc = #doc]
|
||||||
pub struct #ident<#lt> {
|
pub struct #ident<#lt> {
|
||||||
#(#fields,)*
|
#(#fields,)*
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,6 @@ pub fn codegen(
|
||||||
|
|
||||||
if ctxt.has_local_resources(app) {
|
if ctxt.has_local_resources(app) {
|
||||||
let ident = util::local_resources_ident(ctxt, app);
|
let ident = util::local_resources_ident(ctxt, app);
|
||||||
let ident = util::mark_internal_ident(&ident);
|
|
||||||
let lt = if local_resources_tick {
|
let lt = if local_resources_tick {
|
||||||
lt = Some(quote!('a));
|
lt = Some(quote!('a));
|
||||||
Some(quote!('a))
|
Some(quote!('a))
|
||||||
|
|
@ -90,7 +89,6 @@ pub fn codegen(
|
||||||
|
|
||||||
if ctxt.has_shared_resources(app) {
|
if ctxt.has_shared_resources(app) {
|
||||||
let ident = util::shared_resources_ident(ctxt, app);
|
let ident = util::shared_resources_ident(ctxt, app);
|
||||||
let ident = util::mark_internal_ident(&ident);
|
|
||||||
let lt = if shared_resources_tick {
|
let lt = if shared_resources_tick {
|
||||||
lt = Some(quote!('a));
|
lt = Some(quote!('a));
|
||||||
Some(quote!('a))
|
Some(quote!('a))
|
||||||
|
|
@ -131,6 +129,7 @@ pub fn codegen(
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
/// Monotonics used by the system
|
/// Monotonics used by the system
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
pub struct #internal_monotonics_ident(
|
pub struct #internal_monotonics_ident(
|
||||||
#(pub #monotonic_types),*
|
#(pub #monotonic_types),*
|
||||||
);
|
);
|
||||||
|
|
@ -178,6 +177,8 @@ pub fn codegen(
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
/// Execution context
|
/// Execution context
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
pub struct #internal_context_name<#lt> {
|
pub struct #internal_context_name<#lt> {
|
||||||
#(#fields,)*
|
#(#fields,)*
|
||||||
}
|
}
|
||||||
|
|
@ -209,11 +210,8 @@ pub fn codegen(
|
||||||
let args = &args;
|
let args = &args;
|
||||||
let tupled = &tupled;
|
let tupled = &tupled;
|
||||||
let fq = util::fq_ident(name);
|
let fq = util::fq_ident(name);
|
||||||
let fq = util::mark_internal_ident(&fq);
|
|
||||||
let rq = util::rq_ident(priority);
|
let rq = util::rq_ident(priority);
|
||||||
let rq = util::mark_internal_ident(&rq);
|
|
||||||
let inputs = util::inputs_ident(name);
|
let inputs = util::inputs_ident(name);
|
||||||
let inputs = util::mark_internal_ident(&inputs);
|
|
||||||
|
|
||||||
let device = &extra.device;
|
let device = &extra.device;
|
||||||
let enum_ = util::interrupt_ident();
|
let enum_ = util::interrupt_ident();
|
||||||
|
|
@ -263,16 +261,13 @@ pub fn codegen(
|
||||||
// Schedule caller
|
// Schedule caller
|
||||||
for (_, monotonic) in &app.monotonics {
|
for (_, monotonic) in &app.monotonics {
|
||||||
let instants = util::monotonic_instants_ident(name, &monotonic.ident);
|
let instants = util::monotonic_instants_ident(name, &monotonic.ident);
|
||||||
let instants = util::mark_internal_ident(&instants);
|
|
||||||
let monotonic_name = monotonic.ident.to_string();
|
let monotonic_name = monotonic.ident.to_string();
|
||||||
|
|
||||||
let tq = util::tq_ident(&monotonic.ident.to_string());
|
let tq = util::tq_ident(&monotonic.ident.to_string());
|
||||||
let tq = util::mark_internal_ident(&tq);
|
|
||||||
let t = util::schedule_t_ident();
|
let t = util::schedule_t_ident();
|
||||||
let m = &monotonic.ident;
|
let m = &monotonic.ident;
|
||||||
let mono_type = &monotonic.ident;
|
let mono_type = &monotonic.ident;
|
||||||
let m_ident = util::monotonic_ident(&monotonic_name);
|
let m_ident = util::monotonic_ident(&monotonic_name);
|
||||||
let m_ident = util::mark_internal_ident(&m_ident);
|
|
||||||
let m_isr = &monotonic.args.binds;
|
let m_isr = &monotonic.args.binds;
|
||||||
let enum_ = util::interrupt_ident();
|
let enum_ = util::interrupt_ident();
|
||||||
|
|
||||||
|
|
@ -290,7 +285,7 @@ pub fn codegen(
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let tq_marker = util::mark_internal_ident(&util::timer_queue_marker_ident());
|
let tq_marker = &util::timer_queue_marker_ident();
|
||||||
|
|
||||||
// For future use
|
// For future use
|
||||||
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
||||||
|
|
@ -318,6 +313,8 @@ pub fn codegen(
|
||||||
|
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
pub struct #internal_spawn_handle_ident {
|
pub struct #internal_spawn_handle_ident {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
marker: u32,
|
marker: u32,
|
||||||
|
|
@ -327,7 +324,7 @@ pub fn codegen(
|
||||||
impl #internal_spawn_handle_ident {
|
impl #internal_spawn_handle_ident {
|
||||||
pub fn cancel(self) -> Result<#ty, ()> {
|
pub fn cancel(self) -> Result<#ty, ()> {
|
||||||
rtic::export::interrupt::free(|_| unsafe {
|
rtic::export::interrupt::free(|_| unsafe {
|
||||||
let tq = &mut *#tq.get_mut_unchecked().as_mut_ptr();
|
let tq = #tq.get_mut_unchecked();
|
||||||
if let Some((_task, index)) = tq.cancel_marker(self.marker) {
|
if let Some((_task, index)) = tq.cancel_marker(self.marker) {
|
||||||
// Get the message
|
// Get the message
|
||||||
let msg = #inputs
|
let msg = #inputs
|
||||||
|
|
@ -359,7 +356,7 @@ pub fn codegen(
|
||||||
let marker = *#tq_marker.get_mut_unchecked();
|
let marker = *#tq_marker.get_mut_unchecked();
|
||||||
*#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1);
|
*#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1);
|
||||||
|
|
||||||
let tq = &mut *#tq.get_mut_unchecked().as_mut_ptr();
|
let tq = #tq.get_mut_unchecked();
|
||||||
|
|
||||||
tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker })
|
tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker })
|
||||||
})
|
})
|
||||||
|
|
@ -371,6 +368,7 @@ pub fn codegen(
|
||||||
///
|
///
|
||||||
/// This will use the time `Instant::new(0)` as baseline if called in `#[init]`,
|
/// This will use the time `Instant::new(0)` as baseline if called in `#[init]`,
|
||||||
/// so if you use a non-resetable timer use `spawn_at` when in `#[init]`
|
/// so if you use a non-resetable timer use `spawn_at` when in `#[init]`
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub fn #internal_spawn_after_ident<D>(
|
pub fn #internal_spawn_after_ident<D>(
|
||||||
duration: D
|
duration: D
|
||||||
#(,#args)*
|
#(,#args)*
|
||||||
|
|
@ -390,6 +388,7 @@ pub fn codegen(
|
||||||
|
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
/// Spawns the task at a fixed time instant
|
/// Spawns the task at a fixed time instant
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub fn #internal_spawn_at_ident(
|
pub fn #internal_spawn_at_ident(
|
||||||
instant: rtic::time::Instant<#mono_type>
|
instant: rtic::time::Instant<#mono_type>
|
||||||
#(,#args)*
|
#(,#args)*
|
||||||
|
|
@ -420,7 +419,7 @@ pub fn codegen(
|
||||||
|
|
||||||
*#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1);
|
*#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1);
|
||||||
|
|
||||||
let tq = &mut *#tq.get_mut_unchecked().as_mut_ptr();
|
let tq = #tq.get_mut_unchecked();
|
||||||
|
|
||||||
tq.enqueue_unchecked(
|
tq.enqueue_unchecked(
|
||||||
nr,
|
nr,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
||||||
|
|
||||||
// Initialize shared resources
|
// Initialize shared resources
|
||||||
for (name, res) in &app.shared_resources {
|
for (name, res) in &app.shared_resources {
|
||||||
let mangled_name = util::mark_internal_ident(&util::static_shared_resource_ident(name));
|
let mangled_name = util::static_shared_resource_ident(name);
|
||||||
// If it's live
|
// If it's live
|
||||||
let cfgs = res.cfgs.clone();
|
let cfgs = res.cfgs.clone();
|
||||||
if analysis.shared_resource_locations.get(name).is_some() {
|
if analysis.shared_resource_locations.get(name).is_some() {
|
||||||
|
|
@ -29,7 +29,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
||||||
|
|
||||||
// Initialize local resources
|
// Initialize local resources
|
||||||
for (name, res) in &app.local_resources {
|
for (name, res) in &app.local_resources {
|
||||||
let mangled_name = util::mark_internal_ident(&util::static_local_resource_ident(name));
|
let mangled_name = util::static_local_resource_ident(name);
|
||||||
// If it's live
|
// If it's live
|
||||||
let cfgs = res.cfgs.clone();
|
let cfgs = res.cfgs.clone();
|
||||||
if analysis.local_resource_locations.get(name).is_some() {
|
if analysis.local_resource_locations.get(name).is_some() {
|
||||||
|
|
@ -58,7 +58,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
||||||
|
|
||||||
// Store the monotonic
|
// Store the monotonic
|
||||||
let name = util::monotonic_ident(&monotonic.to_string());
|
let name = util::monotonic_ident(&monotonic.to_string());
|
||||||
let name = util::mark_internal_ident(&name);
|
|
||||||
stmts.push(quote!(*#name.get_mut_unchecked() = Some(monotonics.#idx);));
|
stmts.push(quote!(*#name.get_mut_unchecked() = Some(monotonics.#idx);));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
for (name, task) in &app.software_tasks {
|
for (name, task) in &app.software_tasks {
|
||||||
let cap = task.args.capacity;
|
let cap = task.args.capacity;
|
||||||
let fq_ident = util::fq_ident(name);
|
let fq_ident = util::fq_ident(name);
|
||||||
let fq_ident = util::mark_internal_ident(&fq_ident);
|
|
||||||
|
|
||||||
stmts.push(quote!(
|
stmts.push(quote!(
|
||||||
(0..#cap).for_each(|i| #fq_ident.get_mut_unchecked().enqueue_unchecked(i));
|
(0..#cap).for_each(|i| #fq_ident.get_mut_unchecked().enqueue_unchecked(i));
|
||||||
|
|
@ -77,18 +76,10 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
);));
|
);));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize monotonic's interrupts and timer queues
|
// Initialize monotonic's interrupts
|
||||||
for (_, monotonic) in &app.monotonics {
|
for (_, monotonic) in &app.monotonics {
|
||||||
let priority = &monotonic.args.priority;
|
let priority = &monotonic.args.priority;
|
||||||
let binds = &monotonic.args.binds;
|
let binds = &monotonic.args.binds;
|
||||||
let monotonic_name = monotonic.ident.to_string();
|
|
||||||
let tq = util::tq_ident(&monotonic_name);
|
|
||||||
let tq = util::mark_internal_ident(&tq);
|
|
||||||
|
|
||||||
// Initialize timer queues
|
|
||||||
stmts.push(
|
|
||||||
quote!(#tq.get_mut_unchecked().as_mut_ptr().write(rtic::export::TimerQueue::new());),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Compile time assert that this priority is supported by the device
|
// Compile time assert that this priority is supported by the device
|
||||||
stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
|
stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ pub fn codegen(
|
||||||
for (name, res) in &app.shared_resources {
|
for (name, res) in &app.shared_resources {
|
||||||
let cfgs = &res.cfgs;
|
let cfgs = &res.cfgs;
|
||||||
let ty = &res.ty;
|
let ty = &res.ty;
|
||||||
let mangled_name = util::mark_internal_ident(&util::static_shared_resource_ident(&name));
|
let mangled_name = &util::static_shared_resource_ident(&name);
|
||||||
|
|
||||||
// late resources in `util::link_section_uninit`
|
// late resources in `util::link_section_uninit`
|
||||||
let section = util::link_section_uninit();
|
let section = util::link_section_uninit();
|
||||||
|
|
@ -30,6 +30,7 @@ pub fn codegen(
|
||||||
// For future use
|
// For future use
|
||||||
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
||||||
mod_app.push(quote!(
|
mod_app.push(quote!(
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
// #[doc = #doc]
|
// #[doc = #doc]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let ty = &res.ty;
|
let ty = &res.ty;
|
||||||
let mangled_name = util::mark_internal_ident(&util::static_shared_resource_ident(&name));
|
let mangled_name = util::static_shared_resource_ident(&name);
|
||||||
|
|
||||||
if !res.properties.lock_free {
|
if !res.properties.lock_free {
|
||||||
if access.is_shared() {
|
if access.is_shared() {
|
||||||
|
|
@ -102,9 +102,9 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
|
||||||
|
|
||||||
let doc = format!("Shared resources `{}` has access to", ctxt.ident(app));
|
let doc = format!("Shared resources `{}` has access to", ctxt.ident(app));
|
||||||
let ident = util::shared_resources_ident(ctxt, app);
|
let ident = util::shared_resources_ident(ctxt, app);
|
||||||
let ident = util::mark_internal_ident(&ident);
|
|
||||||
let item = quote!(
|
let item = quote!(
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[doc = #doc]
|
#[doc = #doc]
|
||||||
pub struct #ident<#lt> {
|
pub struct #ident<#lt> {
|
||||||
#(#fields,)*
|
#(#fields,)*
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ pub fn codegen(
|
||||||
|
|
||||||
// Create free queues and inputs / instants buffers
|
// Create free queues and inputs / instants buffers
|
||||||
let fq = util::fq_ident(name);
|
let fq = util::fq_ident(name);
|
||||||
let fq = util::mark_internal_ident(&fq);
|
|
||||||
|
|
||||||
let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = {
|
let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = {
|
||||||
(
|
(
|
||||||
|
|
@ -49,6 +48,8 @@ pub fn codegen(
|
||||||
mod_app.push(quote!(
|
mod_app.push(quote!(
|
||||||
// /// Queue version of a free-list that keeps track of empty slots in
|
// /// Queue version of a free-list that keeps track of empty slots in
|
||||||
// /// the following buffers
|
// /// the following buffers
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr);
|
static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr);
|
||||||
));
|
));
|
||||||
|
|
@ -59,7 +60,6 @@ pub fn codegen(
|
||||||
|
|
||||||
for (_, monotonic) in &app.monotonics {
|
for (_, monotonic) in &app.monotonics {
|
||||||
let instants = util::monotonic_instants_ident(name, &monotonic.ident);
|
let instants = util::monotonic_instants_ident(name, &monotonic.ident);
|
||||||
let instants = util::mark_internal_ident(&instants);
|
|
||||||
let mono_type = &monotonic.ty;
|
let mono_type = &monotonic.ty;
|
||||||
|
|
||||||
let uninit = mk_uninit();
|
let uninit = mk_uninit();
|
||||||
|
|
@ -69,6 +69,8 @@ pub fn codegen(
|
||||||
#uninit
|
#uninit
|
||||||
// /// Buffer that holds the instants associated to the inputs of a task
|
// /// Buffer that holds the instants associated to the inputs of a task
|
||||||
// #[doc = #doc]
|
// #[doc = #doc]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
static #instants:
|
static #instants:
|
||||||
rtic::RacyCell<[core::mem::MaybeUninit<rtic::time::Instant<#mono_type>>; #cap_lit]> =
|
rtic::RacyCell<[core::mem::MaybeUninit<rtic::time::Instant<#mono_type>>; #cap_lit]> =
|
||||||
|
|
@ -78,10 +80,11 @@ pub fn codegen(
|
||||||
|
|
||||||
let uninit = mk_uninit();
|
let uninit = mk_uninit();
|
||||||
let inputs_ident = util::inputs_ident(name);
|
let inputs_ident = util::inputs_ident(name);
|
||||||
let inputs_ident = util::mark_internal_ident(&inputs_ident);
|
|
||||||
mod_app.push(quote!(
|
mod_app.push(quote!(
|
||||||
#uninit
|
#uninit
|
||||||
// /// Buffer that holds the inputs of a task
|
// /// Buffer that holds the inputs of a task
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> =
|
static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> =
|
||||||
rtic::RacyCell::new([#(#elems,)*]);
|
rtic::RacyCell::new([#(#elems,)*]);
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,12 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
||||||
|
|
||||||
if !app.monotonics.is_empty() {
|
if !app.monotonics.is_empty() {
|
||||||
// Generate the marker counter used to track for `cancel` and `reschedule`
|
// Generate the marker counter used to track for `cancel` and `reschedule`
|
||||||
let tq_marker = util::mark_internal_ident(&util::timer_queue_marker_ident());
|
let tq_marker = util::timer_queue_marker_ident();
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
// #[doc = #doc]
|
// #[doc = #doc]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
static #tq_marker: rtic::RacyCell<u32> = rtic::RacyCell::new(0);
|
static #tq_marker: rtic::RacyCell<u32> = rtic::RacyCell::new(0);
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -52,40 +53,40 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
||||||
for (_, monotonic) in &app.monotonics {
|
for (_, monotonic) in &app.monotonics {
|
||||||
let monotonic_name = monotonic.ident.to_string();
|
let monotonic_name = monotonic.ident.to_string();
|
||||||
let tq = util::tq_ident(&monotonic_name);
|
let tq = util::tq_ident(&monotonic_name);
|
||||||
let tq = util::mark_internal_ident(&tq);
|
|
||||||
let t = util::schedule_t_ident();
|
let t = util::schedule_t_ident();
|
||||||
let mono_type = &monotonic.ty;
|
let mono_type = &monotonic.ty;
|
||||||
let m_ident = util::monotonic_ident(&monotonic_name);
|
let m_ident = util::monotonic_ident(&monotonic_name);
|
||||||
let m_ident = util::mark_internal_ident(&m_ident);
|
|
||||||
|
|
||||||
// Static variables and resource proxy
|
// Static variables and resource proxy
|
||||||
{
|
{
|
||||||
// For future use
|
// For future use
|
||||||
// let doc = &format!("Timer queue for {}", monotonic_name);
|
// let doc = &format!("Timer queue for {}", monotonic_name);
|
||||||
let cap: u8 = app
|
let cap: usize = app
|
||||||
.software_tasks
|
.software_tasks
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_name, task)| task.args.capacity)
|
.map(|(_name, task)| task.args.capacity as usize)
|
||||||
.sum();
|
.sum();
|
||||||
let n = util::capacity_literal(cap as usize);
|
let n = util::capacity_literal(cap);
|
||||||
let tq_ty =
|
let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n>);
|
||||||
quote!(core::mem::MaybeUninit<rtic::export::TimerQueue<#mono_type, #t, #n>>);
|
|
||||||
|
|
||||||
// For future use
|
// For future use
|
||||||
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
static #tq: rtic::RacyCell<#tq_ty> =
|
static #tq: rtic::RacyCell<#tq_ty> =
|
||||||
rtic::RacyCell::new(core::mem::MaybeUninit::uninit());
|
rtic::RacyCell::new(rtic::export::TimerQueue(rtic::export::SortedLinkedList::new_u16()));
|
||||||
));
|
));
|
||||||
|
|
||||||
let mono = util::monotonic_ident(&monotonic_name);
|
let mono = util::monotonic_ident(&monotonic_name);
|
||||||
let mono = util::mark_internal_ident(&mono);
|
|
||||||
// For future use
|
// For future use
|
||||||
// let doc = &format!("Storage for {}", monotonic_name);
|
// let doc = &format!("Storage for {}", monotonic_name);
|
||||||
|
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
static #mono: rtic::RacyCell<Option<#mono_type>> = rtic::RacyCell::new(None);
|
static #mono: rtic::RacyCell<Option<#mono_type>> = rtic::RacyCell::new(None);
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +103,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
||||||
let cfgs = &task.cfgs;
|
let cfgs = &task.cfgs;
|
||||||
let priority = task.args.priority;
|
let priority = task.args.priority;
|
||||||
let rq = util::rq_ident(priority);
|
let rq = util::rq_ident(priority);
|
||||||
let rq = util::mark_internal_ident(&rq);
|
|
||||||
let rqt = util::spawn_t_ident(priority);
|
let rqt = util::spawn_t_ident(priority);
|
||||||
|
|
||||||
// The interrupt that runs the task dispatcher
|
// The interrupt that runs the task dispatcher
|
||||||
|
|
@ -138,7 +138,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
||||||
unsafe fn #bound_interrupt() {
|
unsafe fn #bound_interrupt() {
|
||||||
while let Some((task, index)) = rtic::export::interrupt::free(|_|
|
while let Some((task, index)) = rtic::export::interrupt::free(|_|
|
||||||
if let Some(mono) = #m_ident.get_mut_unchecked().as_mut() {
|
if let Some(mono) = #m_ident.get_mut_unchecked().as_mut() {
|
||||||
(&mut *#tq.get_mut_unchecked().as_mut_ptr()).dequeue(|| #disable_isr, mono)
|
#tq.get_mut_unchecked().dequeue(|| #disable_isr, mono)
|
||||||
} else {
|
} else {
|
||||||
// We can only use the timer queue if `init` has returned, and it
|
// We can only use the timer queue if `init` has returned, and it
|
||||||
// writes the `Some(monotonic)` we are accessing here.
|
// writes the `Some(monotonic)` we are accessing here.
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ use syn::{Attribute, Ident, LitInt, PatType};
|
||||||
|
|
||||||
use crate::check::Extra;
|
use crate::check::Extra;
|
||||||
|
|
||||||
|
const RTIC_INTERNAL: &str = "__rtic_internal";
|
||||||
|
|
||||||
/// Turns `capacity` into an unsuffixed integer literal
|
/// Turns `capacity` into an unsuffixed integer literal
|
||||||
pub fn capacity_literal(capacity: usize) -> LitInt {
|
pub fn capacity_literal(capacity: usize) -> LitInt {
|
||||||
LitInt::new(&capacity.to_string(), Span::call_site())
|
LitInt::new(&capacity.to_string(), Span::call_site())
|
||||||
|
|
@ -14,7 +16,7 @@ pub fn capacity_literal(capacity: usize) -> LitInt {
|
||||||
|
|
||||||
/// Identifier for the free queue
|
/// Identifier for the free queue
|
||||||
pub fn fq_ident(task: &Ident) -> Ident {
|
pub fn fq_ident(task: &Ident) -> Ident {
|
||||||
Ident::new(&format!("{}_FQ", task.to_string()), Span::call_site())
|
mark_internal_name(&format!("{}_FQ", task.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a `Mutex` implementation
|
/// Generates a `Mutex` implementation
|
||||||
|
|
@ -60,15 +62,12 @@ pub fn impl_mutex(
|
||||||
|
|
||||||
/// Generates an identifier for the `INPUTS` buffer (`spawn` & `schedule` API)
|
/// Generates an identifier for the `INPUTS` buffer (`spawn` & `schedule` API)
|
||||||
pub fn inputs_ident(task: &Ident) -> Ident {
|
pub fn inputs_ident(task: &Ident) -> Ident {
|
||||||
Ident::new(&format!("{}_INPUTS", task), Span::call_site())
|
mark_internal_name(&format!("{}_INPUTS", task))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an identifier for the `INSTANTS` buffer (`schedule` API)
|
/// Generates an identifier for the `INSTANTS` buffer (`schedule` API)
|
||||||
pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident {
|
pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident {
|
||||||
Ident::new(
|
mark_internal_name(&format!("{}_{}_INSTANTS", task, monotonic))
|
||||||
&format!("{}_{}_INSTANTS", task, monotonic),
|
|
||||||
Span::call_site(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interrupt_ident() -> Ident {
|
pub fn interrupt_ident() -> Ident {
|
||||||
|
|
@ -77,8 +76,7 @@ pub fn interrupt_ident() -> Ident {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn timer_queue_marker_ident() -> Ident {
|
pub fn timer_queue_marker_ident() -> Ident {
|
||||||
let span = Span::call_site();
|
mark_internal_name(&"TIMER_QUEUE_MARKER")
|
||||||
Ident::new("TIMER_QUEUE_MARKER", span)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether `name` is an exception with configurable priority
|
/// Whether `name` is an exception with configurable priority
|
||||||
|
|
@ -98,38 +96,24 @@ pub fn is_exception(name: &Ident) -> bool {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark a name as internal
|
||||||
|
pub fn mark_internal_name(name: &str) -> Ident {
|
||||||
|
Ident::new(&format!("{}_{}", RTIC_INTERNAL, name), Span::call_site())
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate an internal identifier for monotonics
|
/// Generate an internal identifier for monotonics
|
||||||
pub fn internal_monotonics_ident(task: &Ident, monotonic: &Ident, ident_name: &str) -> Ident {
|
pub fn internal_monotonics_ident(task: &Ident, monotonic: &Ident, ident_name: &str) -> Ident {
|
||||||
Ident::new(
|
mark_internal_name(&format!(
|
||||||
&format!(
|
"{}_{}_{}",
|
||||||
"__rtic_internal_{}_{}_{}",
|
task.to_string(),
|
||||||
task.to_string(),
|
monotonic.to_string(),
|
||||||
monotonic.to_string(),
|
ident_name,
|
||||||
ident_name,
|
))
|
||||||
),
|
|
||||||
Span::call_site(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate an internal identifier for tasks
|
/// Generate an internal identifier for tasks
|
||||||
pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident {
|
pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident {
|
||||||
Ident::new(
|
mark_internal_name(&format!("{}_{}", task.to_string(), ident_name))
|
||||||
&format!("__rtic_internal_{}_{}", task.to_string(), ident_name,),
|
|
||||||
Span::call_site(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark an ident as internal
|
|
||||||
pub fn mark_internal_ident(ident: &Ident) -> Ident {
|
|
||||||
Ident::new(
|
|
||||||
&format!("__rtic_internal_{}", ident.to_string()),
|
|
||||||
Span::call_site(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark an ident as internal
|
|
||||||
pub fn mark_internal_name(name: &str) -> Ident {
|
|
||||||
Ident::new(&format!("__rtic_internal_{}", name), Span::call_site())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_section_index() -> usize {
|
fn link_section_index() -> usize {
|
||||||
|
|
@ -215,7 +199,7 @@ pub fn shared_resources_ident(ctxt: Context, app: &App) -> Ident {
|
||||||
|
|
||||||
s.push_str("SharedResources");
|
s.push_str("SharedResources");
|
||||||
|
|
||||||
Ident::new(&s, Span::call_site())
|
mark_internal_name(&s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a pre-reexport identifier for the "local resources" struct
|
/// Generates a pre-reexport identifier for the "local resources" struct
|
||||||
|
|
@ -228,7 +212,7 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident {
|
||||||
|
|
||||||
s.push_str("LocalResources");
|
s.push_str("LocalResources");
|
||||||
|
|
||||||
Ident::new(&s, Span::call_site())
|
mark_internal_name(&s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an identifier for a ready queue
|
/// Generates an identifier for a ready queue
|
||||||
|
|
@ -236,7 +220,7 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident {
|
||||||
/// There may be several task dispatchers, one for each priority level.
|
/// There may be several task dispatchers, one for each priority level.
|
||||||
/// The ready queues are SPSC queues
|
/// The ready queues are SPSC queues
|
||||||
pub fn rq_ident(priority: u8) -> Ident {
|
pub fn rq_ident(priority: u8) -> Ident {
|
||||||
Ident::new(&format!("P{}_RQ", priority), Span::call_site())
|
mark_internal_name(&format!("P{}_RQ", priority))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an identifier for the `enum` of `schedule`-able tasks
|
/// Generates an identifier for the `enum` of `schedule`-able tasks
|
||||||
|
|
@ -260,33 +244,28 @@ pub fn suffixed(name: &str) -> Ident {
|
||||||
|
|
||||||
/// Generates an identifier for a timer queue
|
/// Generates an identifier for a timer queue
|
||||||
pub fn tq_ident(name: &str) -> Ident {
|
pub fn tq_ident(name: &str) -> Ident {
|
||||||
Ident::new(&format!("TQ_{}", name), Span::call_site())
|
mark_internal_name(&format!("TQ_{}", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an identifier for monotonic timer storage
|
/// Generates an identifier for monotonic timer storage
|
||||||
pub fn monotonic_ident(name: &str) -> Ident {
|
pub fn monotonic_ident(name: &str) -> Ident {
|
||||||
Ident::new(&format!("MONOTONIC_STORAGE_{}", name), Span::call_site())
|
mark_internal_name(&format!("MONOTONIC_STORAGE_{}", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_shared_resource_ident(name: &Ident) -> Ident {
|
pub fn static_shared_resource_ident(name: &Ident) -> Ident {
|
||||||
Ident::new(
|
mark_internal_name(&format!("shared_resource_{}", name.to_string()))
|
||||||
&format!("shared_resource_{}", name.to_string()),
|
|
||||||
Span::call_site(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_local_resource_ident(name: &Ident) -> Ident {
|
pub fn static_local_resource_ident(name: &Ident) -> Ident {
|
||||||
Ident::new(
|
mark_internal_name(&format!("local_resource_{}", name.to_string()))
|
||||||
&format!("local_resource_{}", name.to_string()),
|
|
||||||
Span::call_site(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declared_static_local_resource_ident(name: &Ident, task_name: &Ident) -> Ident {
|
pub fn declared_static_local_resource_ident(name: &Ident, task_name: &Ident) -> Ident {
|
||||||
Ident::new(
|
mark_internal_name(&format!(
|
||||||
&format!("local_{}_{}", task_name.to_string(), name.to_string()),
|
"local_{}_{}",
|
||||||
Span::call_site(),
|
task_name.to_string(),
|
||||||
)
|
name.to_string()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The name to get better RT flag errors
|
/// The name to get better RT flag errors
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ pub use cortex_m::{
|
||||||
peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC},
|
peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC},
|
||||||
Peripherals,
|
Peripherals,
|
||||||
};
|
};
|
||||||
|
pub use heapless::sorted_linked_list::SortedLinkedList;
|
||||||
pub use heapless::spsc::Queue;
|
pub use heapless::spsc::Queue;
|
||||||
pub use heapless::BinaryHeap;
|
pub use heapless::BinaryHeap;
|
||||||
pub use rtic_monotonic as monotonic;
|
pub use rtic_monotonic as monotonic;
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,6 @@ pub use rtic_monotonic::{self, embedded_time as time, Monotonic};
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod export;
|
pub mod export;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
mod linked_list;
|
|
||||||
#[doc(hidden)]
|
|
||||||
mod tq;
|
mod tq;
|
||||||
|
|
||||||
/// Sets the given `interrupt` as pending
|
/// Sets the given `interrupt` as pending
|
||||||
|
|
|
||||||
|
|
@ -1,597 +0,0 @@
|
||||||
use core::fmt;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::mem::MaybeUninit;
|
|
||||||
use core::ops::{Deref, DerefMut};
|
|
||||||
use core::ptr;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
struct LinkedIndex(u16);
|
|
||||||
|
|
||||||
impl LinkedIndex {
|
|
||||||
#[inline]
|
|
||||||
const unsafe fn new_unchecked(value: u16) -> Self {
|
|
||||||
LinkedIndex(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
const fn none() -> Self {
|
|
||||||
LinkedIndex(u16::MAX)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
const fn option(self) -> Option<u16> {
|
|
||||||
if self.0 == u16::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A node in the linked list.
|
|
||||||
pub struct Node<T> {
|
|
||||||
val: MaybeUninit<T>,
|
|
||||||
next: LinkedIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator for the linked list.
|
|
||||||
pub struct Iter<'a, T, Kind, const N: usize>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
list: &'a LinkedList<T, Kind, N>,
|
|
||||||
index: LinkedIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T, Kind, const N: usize> Iterator for Iter<'a, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
type Item = &'a T;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let index = self.index.option()?;
|
|
||||||
|
|
||||||
let node = self.list.node_at(index as usize);
|
|
||||||
self.index = node.next;
|
|
||||||
|
|
||||||
Some(self.list.read_data_in_node_at(index as usize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Comes from [`LinkedList::find_mut`].
|
|
||||||
pub struct FindMut<'a, T, Kind, const N: usize>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
list: &'a mut LinkedList<T, Kind, N>,
|
|
||||||
is_head: bool,
|
|
||||||
prev_index: LinkedIndex,
|
|
||||||
index: LinkedIndex,
|
|
||||||
maybe_changed: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T, Kind, const N: usize> FindMut<'a, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn pop_internal(&mut self) -> T {
|
|
||||||
if self.is_head {
|
|
||||||
// If it is the head element, we can do a normal pop
|
|
||||||
unsafe { self.list.pop_unchecked() }
|
|
||||||
} else {
|
|
||||||
// Somewhere in the list
|
|
||||||
|
|
||||||
// Re-point the previous index
|
|
||||||
self.list.node_at_mut(self.prev_index.0 as usize).next =
|
|
||||||
self.list.node_at_mut(self.index.0 as usize).next;
|
|
||||||
|
|
||||||
// Release the index into the free queue
|
|
||||||
self.list.node_at_mut(self.index.0 as usize).next = self.list.free;
|
|
||||||
self.list.free = self.index;
|
|
||||||
|
|
||||||
self.list.extract_data_in_node_at(self.index.0 as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This will pop the element from the list.
|
|
||||||
///
|
|
||||||
/// Complexity is O(1).
|
|
||||||
#[inline]
|
|
||||||
pub fn pop(mut self) -> T {
|
|
||||||
self.pop_internal()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This will resort the element into the correct position in the list in needed.
|
|
||||||
/// Same as calling `drop`.
|
|
||||||
///
|
|
||||||
/// Complexity is worst-case O(N).
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
drop(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> Drop for FindMut<'_, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// Only resort the list if the element has changed
|
|
||||||
if self.maybe_changed {
|
|
||||||
let val = self.pop_internal();
|
|
||||||
unsafe { self.list.push_unchecked(val) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> Deref for FindMut<'_, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.list.read_data_in_node_at(self.index.0 as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> DerefMut for FindMut<'_, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
self.maybe_changed = true;
|
|
||||||
self.list.read_mut_data_in_node_at(self.index.0 as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> fmt::Debug for FindMut<'_, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd + core::fmt::Debug,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("FindMut")
|
|
||||||
.field("prev_index", &self.prev_index)
|
|
||||||
.field("index", &self.index)
|
|
||||||
.field(
|
|
||||||
"prev_value",
|
|
||||||
&self
|
|
||||||
.list
|
|
||||||
.read_data_in_node_at(self.prev_index.option().unwrap() as usize),
|
|
||||||
)
|
|
||||||
.field(
|
|
||||||
"value",
|
|
||||||
&self
|
|
||||||
.list
|
|
||||||
.read_data_in_node_at(self.index.option().unwrap() as usize),
|
|
||||||
)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The linked list.
|
|
||||||
pub struct LinkedList<T, Kind, const N: usize>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
list: MaybeUninit<[Node<T>; N]>,
|
|
||||||
head: LinkedIndex,
|
|
||||||
free: LinkedIndex,
|
|
||||||
_kind: PhantomData<Kind>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> LinkedList<T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn node_at(&self, index: usize) -> &Node<T> {
|
|
||||||
// Safety: The entire `self.list` is initialized in `new`, which makes this safe.
|
|
||||||
unsafe { &*(self.list.as_ptr() as *const Node<T>).add(index) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn node_at_mut(&mut self, index: usize) -> &mut Node<T> {
|
|
||||||
// Safety: The entire `self.list` is initialized in `new`, which makes this safe.
|
|
||||||
unsafe { &mut *(self.list.as_mut_ptr() as *mut Node<T>).add(index) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn write_data_in_node_at(&mut self, index: usize, data: T) {
|
|
||||||
unsafe {
|
|
||||||
self.node_at_mut(index).val.as_mut_ptr().write(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn read_data_in_node_at(&self, index: usize) -> &T {
|
|
||||||
unsafe { &*self.node_at(index).val.as_ptr() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn read_mut_data_in_node_at(&mut self, index: usize) -> &mut T {
|
|
||||||
unsafe { &mut *self.node_at_mut(index).val.as_mut_ptr() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn extract_data_in_node_at(&mut self, index: usize) -> T {
|
|
||||||
unsafe { self.node_at(index).val.as_ptr().read() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
/// Safety: This can overwrite existing allocated nodes if used improperly, meaning their
|
|
||||||
/// `Drop` methods won't run.
|
|
||||||
#[inline]
|
|
||||||
unsafe fn write_node_at(&mut self, index: usize, node: Node<T>) {
|
|
||||||
(self.list.as_mut_ptr() as *mut Node<T>)
|
|
||||||
.add(index)
|
|
||||||
.write(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new linked list.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let mut list = LinkedList {
|
|
||||||
list: MaybeUninit::uninit(),
|
|
||||||
head: LinkedIndex::none(),
|
|
||||||
free: unsafe { LinkedIndex::new_unchecked(0) },
|
|
||||||
_kind: PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
let len = N as u16;
|
|
||||||
let mut free = 0;
|
|
||||||
|
|
||||||
if len == 0 {
|
|
||||||
list.free = LinkedIndex::none();
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize indexes
|
|
||||||
while free < len - 1 {
|
|
||||||
unsafe {
|
|
||||||
list.write_node_at(
|
|
||||||
free as usize,
|
|
||||||
Node {
|
|
||||||
val: MaybeUninit::uninit(),
|
|
||||||
next: LinkedIndex::new_unchecked(free + 1),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
free += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize final index
|
|
||||||
unsafe {
|
|
||||||
list.write_node_at(
|
|
||||||
free as usize,
|
|
||||||
Node {
|
|
||||||
val: MaybeUninit::uninit(),
|
|
||||||
next: LinkedIndex::none(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
list
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Push unchecked
|
|
||||||
///
|
|
||||||
/// Complexity is O(N).
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Assumes that the list is not full.
|
|
||||||
pub unsafe fn push_unchecked(&mut self, value: T) {
|
|
||||||
let new = self.free.0;
|
|
||||||
// Store the data and update the next free spot
|
|
||||||
self.write_data_in_node_at(new as usize, value);
|
|
||||||
self.free = self.node_at(new as usize).next;
|
|
||||||
|
|
||||||
if let Some(head) = self.head.option() {
|
|
||||||
// Check if we need to replace head
|
|
||||||
if self
|
|
||||||
.read_data_in_node_at(head as usize)
|
|
||||||
.partial_cmp(self.read_data_in_node_at(new as usize))
|
|
||||||
!= Kind::ordering()
|
|
||||||
{
|
|
||||||
self.node_at_mut(new as usize).next = self.head;
|
|
||||||
self.head = LinkedIndex::new_unchecked(new);
|
|
||||||
} else {
|
|
||||||
// It's not head, search the list for the correct placement
|
|
||||||
let mut current = head;
|
|
||||||
|
|
||||||
while let Some(next) = self.node_at(current as usize).next.option() {
|
|
||||||
if self
|
|
||||||
.read_data_in_node_at(next as usize)
|
|
||||||
.partial_cmp(self.read_data_in_node_at(new as usize))
|
|
||||||
!= Kind::ordering()
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.node_at_mut(new as usize).next = self.node_at(current as usize).next;
|
|
||||||
self.node_at_mut(current as usize).next = LinkedIndex::new_unchecked(new);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.node_at_mut(new as usize).next = self.head;
|
|
||||||
self.head = LinkedIndex::new_unchecked(new);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pushes an element to the linked list and sorts it into place.
|
|
||||||
///
|
|
||||||
/// Complexity is O(N).
|
|
||||||
pub fn push(&mut self, value: T) -> Result<(), T> {
|
|
||||||
if !self.is_full() {
|
|
||||||
Ok(unsafe { self.push_unchecked(value) })
|
|
||||||
} else {
|
|
||||||
Err(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an iterator over the sorted list.
|
|
||||||
pub fn iter(&self) -> Iter<'_, T, Kind, N> {
|
|
||||||
Iter {
|
|
||||||
list: self,
|
|
||||||
index: self.head,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find an element in the list.
|
|
||||||
pub fn find_mut<F>(&mut self, mut f: F) -> Option<FindMut<'_, T, Kind, N>>
|
|
||||||
where
|
|
||||||
F: FnMut(&T) -> bool,
|
|
||||||
{
|
|
||||||
let head = self.head.option()?;
|
|
||||||
|
|
||||||
// Special-case, first element
|
|
||||||
if f(self.read_data_in_node_at(head as usize)) {
|
|
||||||
return Some(FindMut {
|
|
||||||
is_head: true,
|
|
||||||
prev_index: LinkedIndex::none(),
|
|
||||||
index: self.head,
|
|
||||||
list: self,
|
|
||||||
maybe_changed: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut current = head;
|
|
||||||
|
|
||||||
while let Some(next) = self.node_at(current as usize).next.option() {
|
|
||||||
if f(self.read_data_in_node_at(next as usize)) {
|
|
||||||
return Some(FindMut {
|
|
||||||
is_head: false,
|
|
||||||
prev_index: unsafe { LinkedIndex::new_unchecked(current) },
|
|
||||||
index: unsafe { LinkedIndex::new_unchecked(next) },
|
|
||||||
list: self,
|
|
||||||
maybe_changed: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Peek at the first element.
|
|
||||||
pub fn peek(&self) -> Option<&T> {
|
|
||||||
self.head
|
|
||||||
.option()
|
|
||||||
.map(|head| self.read_data_in_node_at(head as usize))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pop unchecked
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Assumes that the list is not empty.
|
|
||||||
pub unsafe fn pop_unchecked(&mut self) -> T {
|
|
||||||
let head = self.head.0;
|
|
||||||
let current = head;
|
|
||||||
self.head = self.node_at(head as usize).next;
|
|
||||||
self.node_at_mut(current as usize).next = self.free;
|
|
||||||
self.free = LinkedIndex::new_unchecked(current);
|
|
||||||
|
|
||||||
self.extract_data_in_node_at(current as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pops the first element in the list.
|
|
||||||
///
|
|
||||||
/// Complexity is O(1).
|
|
||||||
pub fn pop(&mut self) -> Result<T, ()> {
|
|
||||||
if !self.is_empty() {
|
|
||||||
Ok(unsafe { self.pop_unchecked() })
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the linked list is full.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_full(&self) -> bool {
|
|
||||||
self.free.option().is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the linked list is empty.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.head.option().is_none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> Drop for LinkedList<T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let mut index = self.head;
|
|
||||||
|
|
||||||
while let Some(i) = index.option() {
|
|
||||||
let node = self.node_at_mut(i as usize);
|
|
||||||
index = node.next;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
ptr::drop_in_place(node.val.as_mut_ptr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> fmt::Debug for LinkedList<T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd + core::fmt::Debug,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_list().entries(self.iter()).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Min sorted linked list.
|
|
||||||
pub struct Min;
|
|
||||||
|
|
||||||
/// Max sorted linked list.
|
|
||||||
pub struct Max;
|
|
||||||
|
|
||||||
/// Sealed traits and implementations for `linked_list`
|
|
||||||
pub mod kind {
|
|
||||||
use super::{Max, Min};
|
|
||||||
use core::cmp::Ordering;
|
|
||||||
|
|
||||||
/// The linked list kind: min first or max first
|
|
||||||
pub unsafe trait Kind {
|
|
||||||
#[doc(hidden)]
|
|
||||||
fn ordering() -> Option<Ordering>;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Kind for Min {
|
|
||||||
#[inline]
|
|
||||||
fn ordering() -> Option<Ordering> {
|
|
||||||
Some(Ordering::Less)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Kind for Max {
|
|
||||||
#[inline]
|
|
||||||
fn ordering() -> Option<Ordering> {
|
|
||||||
Some(Ordering::Greater)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
// Note this useful idiom: importing names from outer (for mod tests) scope.
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_peek() {
|
|
||||||
let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
|
|
||||||
|
|
||||||
ll.push(1).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1);
|
|
||||||
|
|
||||||
ll.push(2).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &2);
|
|
||||||
|
|
||||||
ll.push(3).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &3);
|
|
||||||
|
|
||||||
let mut ll: LinkedList<u32, Min, 3> = LinkedList::new();
|
|
||||||
|
|
||||||
ll.push(2).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &2);
|
|
||||||
|
|
||||||
ll.push(1).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1);
|
|
||||||
|
|
||||||
ll.push(3).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_full() {
|
|
||||||
let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
|
|
||||||
ll.push(1).unwrap();
|
|
||||||
ll.push(2).unwrap();
|
|
||||||
ll.push(3).unwrap();
|
|
||||||
|
|
||||||
assert!(ll.is_full())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_empty() {
|
|
||||||
let ll: LinkedList<u32, Max, 3> = LinkedList::new();
|
|
||||||
|
|
||||||
assert!(ll.is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_zero_size() {
|
|
||||||
let ll: LinkedList<u32, Max, 0> = LinkedList::new();
|
|
||||||
|
|
||||||
assert!(ll.is_empty());
|
|
||||||
assert!(ll.is_full());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rejected_push() {
|
|
||||||
let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
|
|
||||||
ll.push(1).unwrap();
|
|
||||||
ll.push(2).unwrap();
|
|
||||||
ll.push(3).unwrap();
|
|
||||||
|
|
||||||
// This won't fit
|
|
||||||
let r = ll.push(4);
|
|
||||||
|
|
||||||
assert_eq!(r, Err(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_updating() {
|
|
||||||
let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
|
|
||||||
ll.push(1).unwrap();
|
|
||||||
ll.push(2).unwrap();
|
|
||||||
ll.push(3).unwrap();
|
|
||||||
|
|
||||||
let mut find = ll.find_mut(|v| *v == 2).unwrap();
|
|
||||||
|
|
||||||
*find += 1000;
|
|
||||||
find.finish();
|
|
||||||
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1002);
|
|
||||||
|
|
||||||
let mut find = ll.find_mut(|v| *v == 3).unwrap();
|
|
||||||
|
|
||||||
*find += 1000;
|
|
||||||
find.finish();
|
|
||||||
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1003);
|
|
||||||
|
|
||||||
// Remove largest element
|
|
||||||
ll.find_mut(|v| *v == 1003).unwrap().pop();
|
|
||||||
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1002);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
10
src/tq.rs
10
src/tq.rs
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
linked_list::{LinkedList, Min},
|
|
||||||
time::{Clock, Instant},
|
time::{Clock, Instant},
|
||||||
Monotonic,
|
Monotonic,
|
||||||
};
|
};
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
|
use heapless::sorted_linked_list::{LinkedIndexU16, Min, SortedLinkedList};
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unwrapper<T, E>(val: Result<T, E>) -> T {
|
fn unwrapper<T, E>(val: Result<T, E>) -> T {
|
||||||
|
|
@ -14,7 +14,9 @@ fn unwrapper<T, E>(val: Result<T, E>) -> T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TimerQueue<Mono, Task, const N: usize>(pub LinkedList<NotReady<Mono, Task>, Min, N>)
|
pub struct TimerQueue<Mono, Task, const N: usize>(
|
||||||
|
pub SortedLinkedList<NotReady<Mono, Task>, LinkedIndexU16, Min, N>,
|
||||||
|
)
|
||||||
where
|
where
|
||||||
Mono: Monotonic,
|
Mono: Monotonic,
|
||||||
Task: Copy;
|
Task: Copy;
|
||||||
|
|
@ -24,10 +26,6 @@ where
|
||||||
Mono: Monotonic,
|
Mono: Monotonic,
|
||||||
Task: Copy,
|
Task: Copy,
|
||||||
{
|
{
|
||||||
pub fn new() -> Self {
|
|
||||||
TimerQueue(LinkedList::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Writing to memory with a transmute in order to enable
|
/// Writing to memory with a transmute in order to enable
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue