Expand description
Remote Procedure Call crate for co-processing with ARM targets over SWD and other debug protocols.
This crate enables reliable, bidirectional communication between a debug host and target microcontroller using memory-mapped channels. Unlike traditional debugging tools, this allows the host and target act as co-processors alongisde each other, with one being controlled by the other, handling commands and returning results.
no_std. Requires alloc for async traits, typically used on the Host side..
Includes sample host and target implementations.
§Architecture
Assumes a Host (debug controller) and Target (microcontroller) architecture.
Communication typically takes place using two unidirectional channels in the target’s SRAM:
- Command channel: Host writes commands, target reads
- Response channel: Target writes responses, host reads
Channels can be used in either direction, and for different purposes as required. All that is required is a dedicated memory region in the target’s SRAM for each channel. CCM RAM may also be used on STM32F4 MCUs if available.
Each channel contains a control block (sequence numbers, flags, data size) followed by a data area, with the data area used to transmit information on the channel. The amount of memory provided for each channel is used for both the control block and data area. The control block is small (tens of byes), so the majority of the reserved memory is available for data.
Only a single producer and single consumer are supported per channel, and only one data chunk can be written and “transferred” by the producer to the consumer at a time.
The channel protocol ensures reliable delivery using producer/consumer sequence numbers and atomic word operations using the target’s memory ordering guarantees.
Data can be writte in any format, using bytes or 32-bit little endian words.
Currently this crate only supports little-endian Hosts and Targets.
§Modules
channel- Channel objects for unidirectional communication in either directionclient- RPC client for sending commands and receiving responses, typically used on the hostio- Async I/O traits for debug interface access, typically used for host access to target RAM/flash peripherals
§Supported Targets
Works on ARM Cortex-M microcontrollers with strong memory ordering, with SWD accessing the SRAM via the AHB bus matrix, and no data caches:
- Cortex-M0/M0+/M3/M4/M23/M33 (STM32, RP2040/2350, nRF52, etc.)
The target implementation, channel::RamChannel can be used as is. The host
implementation needs io::Reader and io::Writer implementations.
airfrog::airfrog_bin::firmare contains an SWD implementation of io::Reader and
io::Writer for ESP32-C3, and uses channel::ReaderWriterChannel, allowing it
to communicate using these channels with an SWD target.
§Getting Started
The target must reserved dedicated SRAM regions for each channel.
The host (debug controller) must know the addresses of these regions and reads/writes them over the debug interface.
If you wish the host to dynamically learn the addresses/sizes of these regions you must implement this separately - for example, include pointers at well know SRAM or flash locations on the target pointeing to these regions. Alternatively, you could arrange for the locations and sizes to be fixed by the target’s linker script.
Each communication channel consists of a control block, and data area. The control block is used by the protocol to ensure reliable message passing. The data area is where the actual command or response payload is stored.
Target setup:
- Reserve SRAM region(s) e.g. 1KB each for each channel
- Create a
channel::RamChannelIoinstance for each required channel - Create a
channel::RamChannelinstance for each channel - Poll
channel::RamChannel::data_available()for your consumer channel, in your main loop or dedicated task - When data arrives, process it, and optionally send responses on alternate channel
- Data format is application-specific and currently either bytes or u32s
Host setup:
- Configure channel locations, or dynamically read from the target using well-known locations for pointers to the channel locations/sizes
- Create a
channel::ReaderWriterChannelIoinstance with your debug interface reader/writer implementation (seeairfrog::airfrog_bin::firmwarefor an SWD implementation) - Create a
channel::ReaderWriterChannelinstance for your channel. - Send data with
channel::ReaderWriterChannel::publish_bytes(). - Before using a different channel you will likely need to ensure the previous channel is dropped, to free up the Io instance to be mutably borrowed by your new channel.
As implied a Channel is intended to be short-lived - create, use, drop. This allows
temporary ownership of the Reader/Writer, which may be a shared hardware resource on
the host. If this becomes tedious, create a wrapper to abstract away this creation/
destruction or use client::AsyncRpcClient which abstracts this lifecycle away,
and provides a higher-level request() API, using a pair of channels (one command,
the other response).
The RPC layer handles reliable delivery, but your application defines the actual command/response protocol and data formats.
While the above documentation describes the Host controlling the Target, it is possible to use the channel(s) in the reverse direction.
See individual module and struct documentation for usage examples.
§Features
Default features:
async- Enable async channel implementations and traits (requiresalloc), which is generally required by the Host, but not by the Target.
Compile with --no-default-features to disable unnecessary async support for a Target.
Modules§
- channel
- Channel for RPC communication between Host and Target over SWD and similar protocols
- client
- Client for RPC communication between Host and Target using two channels:
- io
- Async I/O traits for accessing RAM, flash, files, etc.
Enums§
- Error
- RPC errors
Type Aliases§
- Result
- Type to represent the result of an RPC operation