Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions training-slides/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ Topics about using Rust on ARM Cortex-M Microcontrollers (and similar). Requires
* [The Embedded HAL and its implementations](./embedded-hals.md)
* [Board Support Crates](./board-support.md)
* [Using defmt](./defmt.md)
* [async/await](./embedded-async.md)

## Under development

Expand Down
89 changes: 89 additions & 0 deletions training-slides/src/embedded-async.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# async/await in Embedded Rust

## Non-blocking programming

- General goal: Offload work to the hardware, and use some mechanism to allow
the CPU to do other work while the hardware does the work
- Interrupts are used to signal progress or completion of an operation

## Model of task contexts

In embedded systems, a generic model of how non-blocking programming works
often looks like this:

<figure>
<img src="images/transaction-non-blocking.drawio.svg">
</figure>

Note:

- Technically, yielding is optional and you can do busy-waiting to wait
for an operation to complete. However, allowing the CPU to do other work is
oftentimes the point of non-blocking programming in the first place.

## Mapping to Embedded Rust

- How could this be mapped to Embedded Rust?
- The general model implies an existence of a scheduler / OS. Do we want
a non-blocking ecosystem bound to specific schedulers or operating systems?
- `async` / `await` provides a language-level solution, which can even be
scheduling library independent.

## Async / Await

- Async / Await works by transforming you code into pollable state machines.
- From a users perspective, you can write code like this

```rust,ignore
// Some HAL specific constructor.
let my_async_uart = (...)
let my_data = &[1, 2, 3, 4];
let result = my_async_uart.write_all(my_data).await;

let async_delay = Delay;
async_delay.delay_ms(200).await;
```

- The CPU can do other work while it is waiting for the UART transfer to complete
or the delay to elapse.

Note:

- We assume that the async UART driver was written in a non-blocking way. It would
offload work to the hardware, and then detect the completion condition in an
interrupt.

## `async` executors

- Embedded specific executors are usually static
- Popular executors in the Rust ecosystem: RTICv2, embassy
- Only architecture specific parts that an executor might have: Putting the system to sleep
when there is nothing to do.

## Wakers in embedded `async`

- A waker is registered inside the hardware task is started.
- The task is put to sleep until the waker is called.
- Inside an interrupt handler, the `wake` method on the waker is called to notify the executor
about the completion of an operation.

Note:

- Commonly used waker inside the embedded ecosystem: [`AtomicWaker`](https://docs.rs/futures/latest/futures/task/struct.AtomicWaker.html)
- Usually, a library will have static instances of that waker, tied to a library provided
interrupt handler.

## An `async` UART driver

- We have written an `async` UART driver which can be run with QEMU. An example app using it
can be found [here](https://github.com/ferrous-systems/rust-training/blob/main/example-code/qemu-thumbv7em/src/bin/uart_async.rs)
- The driver can be found [here](https://github.com/ferrous-systems/rust-training/blob/main/example-code/qemu-common/src/cmsdk_uart/asynch.rs)

## Under the hood of `embassy-time`

- `embassy-time` provides a very convenient API. The high-level API for users is also (seemingly)
hardware independent. How does this work?
- We have written an embassy time driver for the simple ARM CMSDK Timer
[here](https://github.com/embassy-rs/embassy/blob/main/embassy-time/src/driver_cmsdk/mod.rs)
- Providing `embassy-time` support boils down to mapping a timekeeper and an scheduling / alarm
mechanism to a hardware timer inside a driver and then creating a global instance of that driver.
152 changes: 152 additions & 0 deletions training-slides/src/images/transaction-non-blocking.drawio
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.6.1 Chrome/142.0.7444.265 Electron/39.8.0 Safari/537.36">
<diagram name="Page-1" id="Ab3wDADH9GVkEaDOiVhm">
<mxGraphModel dx="822" dy="481" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="CTNN7or24MzbXxDFVPSa-1" edge="1" parent="1" source="CTNN7or24MzbXxDFVPSa-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="CTNN7or24MzbXxDFVPSa-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="QwYj2B5eHwuvQTwytDpc-11" edge="1" parent="1" source="CTNN7or24MzbXxDFVPSa-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;dashPattern=8 8;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" target="CTNN7or24MzbXxDFVPSa-15">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="340" y="150" />
<mxPoint x="340" y="150" />
</Array>
<mxPoint x="350" y="140" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-2" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Lucida Console;fillColor=none;" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;Store context for&amp;nbsp;Interrupt&lt;/font&gt;" vertex="1">
<mxGeometry height="70" width="130" x="150" y="120" as="geometry" />
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-3" edge="1" parent="1" source="CTNN7or24MzbXxDFVPSa-5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="CTNN7or24MzbXxDFVPSa-8">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-5" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" value="&lt;font face=&quot;Lucida Console&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Write initial data&lt;/span&gt;&lt;/font&gt;" vertex="1">
<mxGeometry height="50" width="110" x="160" y="220" as="geometry" />
</mxCell>
<mxCell id="QwYj2B5eHwuvQTwytDpc-12" edge="1" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;">
<mxGeometry relative="1" as="geometry">
<mxPoint x="170" y="350" as="sourcePoint" />
<mxPoint x="170" y="470" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-8" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" value="&lt;font face=&quot;Lucida Console&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Yield to OS&lt;/span&gt;&lt;/font&gt;" vertex="1">
<mxGeometry height="50" width="110" x="160" y="300" as="geometry" />
</mxCell>
<mxCell id="QwYj2B5eHwuvQTwytDpc-10" edge="1" parent="1" source="CTNN7or24MzbXxDFVPSa-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.75;entryDx=0;entryDy=0;dashed=1;dashPattern=8 8;" target="CTNN7or24MzbXxDFVPSa-15">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="300" y="395" />
<mxPoint x="300" y="170" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-9" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;dashed=1;dashPattern=8 8;" value="&lt;div&gt;&lt;font face=&quot;Lucida Console&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;data tranfers&lt;br&gt;in interrupts&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;" vertex="1">
<mxGeometry height="50" width="110" x="180" y="370" as="geometry" />
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-14" edge="1" parent="1" source="CTNN7or24MzbXxDFVPSa-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="CTNN7or24MzbXxDFVPSa-18">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-15" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Lucida Console;fillColor=none;" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;Interrupt fires&lt;/font&gt;" vertex="1">
<mxGeometry height="40" width="110" x="409.94000000000005" y="140" as="geometry" />
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-16" edge="1" parent="1" source="CTNN7or24MzbXxDFVPSa-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="CTNN7or24MzbXxDFVPSa-21">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="450" y="310" />
<mxPoint x="420" y="310" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-17" connectable="0" parent="CTNN7or24MzbXxDFVPSa-16" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontFamily=Lucida Console;fontSize=14;labelBackgroundColor=none;" value="Yes" vertex="1">
<mxGeometry relative="1" x="0.2514" y="2" as="geometry">
<mxPoint x="-23" y="-1" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-31" edge="1" parent="1" source="CTNN7or24MzbXxDFVPSa-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.662;exitY=1.003;exitDx=0;exitDy=0;exitPerimeter=0;" target="CTNN7or24MzbXxDFVPSa-38">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="483" y="310" />
<mxPoint x="530" y="310" />
</Array>
<mxPoint x="529.97" y="264.9999999999999" as="sourcePoint" />
<mxPoint x="700" y="280" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-32" connectable="0" parent="CTNN7or24MzbXxDFVPSa-31" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];labelBackgroundColor=none;" value="&lt;font face=&quot;Lucida Console&quot; style=&quot;font-size: 14px;&quot;&gt;No&lt;/font&gt;" vertex="1">
<mxGeometry relative="1" x="-0.2658" y="1" as="geometry">
<mxPoint x="53" y="2" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-18" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" value="&lt;font face=&quot;Lucida Console&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;All Data&lt;br&gt;Written?&lt;/span&gt;&lt;/font&gt;" vertex="1">
<mxGeometry height="50" width="110" x="409.94000000000005" y="230" as="geometry" />
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-35" edge="1" parent="1" source="CTNN7or24MzbXxDFVPSa-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.253;exitY=1.011;exitDx=0;exitDy=0;exitPerimeter=0;dashed=1;dashPattern=8 8;" target="CTNN7or24MzbXxDFVPSa-36">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="400" y="445" />
<mxPoint x="240" y="445" />
</Array>
<mxPoint x="370.24" y="301.0114942528736" as="sourcePoint" />
<mxPoint x="240.0000000000001" y="409.9000000000001" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-21" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" value="&lt;font face=&quot;Lucida Console&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Notify&lt;br&gt;Task&lt;/span&gt;&lt;/font&gt;" vertex="1">
<mxGeometry height="50" width="80" x="380" y="340" as="geometry" />
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-22" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" value="&lt;div&gt;&lt;font style=&quot;font-size: 14px;&quot; face=&quot;Lucida Console&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;font style=&quot;font-size: 14px;&quot; face=&quot;Lucida Console&quot;&gt;Interrupt Done&lt;/font&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;" vertex="1">
<mxGeometry height="40" width="80" x="380" y="460" as="geometry" />
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-28" edge="1" parent="1" style="endArrow=none;html=1;rounded=0;" value="">
<mxGeometry height="50" relative="1" width="50" as="geometry">
<mxPoint x="350" y="510" as="sourcePoint" />
<mxPoint x="350" y="80" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-29" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontStyle=1;fontSize=16;" value="&lt;font face=&quot;Lucida Console&quot;&gt;Task Context&lt;/font&gt;" vertex="1">
<mxGeometry height="30" width="100" x="170" y="80" as="geometry" />
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-30" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontStyle=1;fontSize=16;" value="&lt;font face=&quot;Lucida Console&quot;&gt;Interrupt Context&lt;/font&gt;" vertex="1">
<mxGeometry height="30" width="139.97" x="390" y="80" as="geometry" />
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-38" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" value="&lt;font face=&quot;Lucida Console&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Write next&lt;br&gt;data&lt;/span&gt;&lt;/font&gt;" vertex="1">
<mxGeometry height="50" width="80" x="490" y="340" as="geometry" />
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-40" edge="1" parent="1" source="CTNN7or24MzbXxDFVPSa-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.552;entryY=-0.032;entryDx=0;entryDy=0;entryPerimeter=0;" target="CTNN7or24MzbXxDFVPSa-22">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="424" y="425" />
</Array>
<mxPoint x="425" y="450" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="CTNN7or24MzbXxDFVPSa-36" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" value="&lt;div&gt;&lt;font face=&quot;Lucida Console&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Done&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;" vertex="1">
<mxGeometry height="40" width="110" x="160" y="470" as="geometry" />
</mxCell>
<mxCell id="QwYj2B5eHwuvQTwytDpc-4" edge="1" parent="1" source="CTNN7or24MzbXxDFVPSa-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" target="CTNN7or24MzbXxDFVPSa-15">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="580" y="470" />
<mxPoint x="580" y="160" />
</Array>
<mxPoint x="570" y="435" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="QwYj2B5eHwuvQTwytDpc-9" connectable="0" parent="QwYj2B5eHwuvQTwytDpc-4" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];labelBackgroundColor=none;" value="&lt;font style=&quot;font-size: 14px;&quot; face=&quot;Lucida Console&quot;&gt;Not Finished&lt;/font&gt;" vertex="1">
<mxGeometry relative="1" x="-0.6918" y="1" as="geometry">
<mxPoint x="-3" y="13" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="QwYj2B5eHwuvQTwytDpc-8" edge="1" parent="1" source="CTNN7or24MzbXxDFVPSa-38" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="CTNN7or24MzbXxDFVPSa-22">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="530" y="420" />
<mxPoint x="450" y="420" />
</Array>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading