1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! 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 direction
//! - [`client`] - RPC client for sending commands and receiving responses, typically used
//! on the host
//! - [`io`] - 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**:
//! 1. Reserve SRAM region(s) e.g. 1KB each for each channel
//! 2. Create a [`channel::RamChannelIo`] instance for each required channel
//! 3. Create a [`channel::RamChannel`] instance for each channel
//! 4. Poll [`channel::RamChannel::data_available()`] for your consumer channel, in your
//! main loop or dedicated task
//! 5. When data arrives, process it, and optionally send responses on alternate channel
//! 6. Data format is application-specific and currently either bytes or u32s
//!
//! **Host setup**:
//! 1. Configure channel locations, or dynamically read from the target using well-known
//! locations for pointers to the channel locations/sizes
//! 2. Create a [`channel::ReaderWriterChannelIo`] instance with your debug interface
//! reader/writer implementation (see `airfrog::airfrog_bin::firmware` for an SWD
//! implementation)
//! 3. Create a [`channel::ReaderWriterChannel`] instance for your channel.
//! 4. Send data with [`channel::ReaderWriterChannel::publish_bytes()`].
//! 5. 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 (requires `alloc`), 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.
// Copyright (C) 2025 Piers Finlayson <piers@piers.rocks>
//
// MIT License
extern crate alloc;
/// RPC errors
/// Type to represent the result of an RPC operation
pub type Result<T> = Result;