avr_progmem/
lib.rs

1// We don't need anything from std, and on AVR there is no std anyway.
2#![no_std]
3//
4// We need inline assembly for the `lpm` instruction.
5// As of now (mid 2022), inline assembly for AVR is still unstable.
6#![feature(asm_experimental_arch)]
7//
8// We to access the length of a slice pointer (for unsized `ProgMem`s)
9#![feature(slice_ptr_len)]
10//
11// Allow to implement `CoerceUnsized` on `ProgMem`
12#![cfg_attr(feature = "unsize", feature(coerce_unsized))]
13//
14// Needed for implementing `CoerceUnsized` on `ProgMem`
15#![cfg_attr(feature = "unsize", feature(unsize))]
16//
17// Allows to document required crate features on items
18#![cfg_attr(doc, feature(doc_auto_cfg))]
19//
20// Allow `unsafe` in `unsafe fn`s, and make `unsafe` blocks everywhere a
21// necessity.
22#![forbid(unsafe_op_in_unsafe_fn)]
23
24//!
25//! Progmem utilities for the AVR architectures.
26//!
27//! This crate provides unsafe utilities for working with data stored in
28//! the program memory of an AVR micro-controller. Additionally, it defines a
29//! 'best-effort' safe wrapper struct [`ProgMem`](crate::wrapper::ProgMem)
30//! to simplify working with it,
31//! as well as a [`PmString`](crate::string::PmString) wrapper for string
32//! handling.
33//!
34//! This crate is implemented only in Rust and some short assembly, it does NOT
35//! depend on the [`avr-libc`] or any other C-library. However, due to the use
36//! of inline assembly, this crate may only be compiled using a **nightly Rust**
37//! compiler (as of mid 2022, inline assembly for AVR is still 'experimental').
38//!
39//! ## MSRV
40//!
41//! This crate works with a Rust `nightly-2023-08-08` compiler.
42//! All versions `0.4.x` will adhere to work with `nightly-2023-08-08`.
43//! Other Rust compiler version (particularly newer ones) might also work,
44//! but due to the use of experimental compiler features it is possible that
45//! some future Rust compiler version will fail to work.
46//!
47//! Future versions such as `0.5.x` might required a newer Rust compiler
48//! version.
49//!
50//!
51//! # AVR Memory
52//!
53//! This crate is specifically for [AVR-base micro-controllers][avr] such as
54//! the Arduino Uno (and some other Arduino boards, but not all), which have a
55//! modified Harvard architecture which implies the strict separation of program
56//! code and data while having special instructions to read and write to
57//! program memory.
58//!
59//! While, of course, all ordinary data is stored in the data domain where it is
60//! perfectly usable, the harsh constraints of most AVR processors make it very
61//! appealing to use the program memory (also referred to as _progmem_) for
62//! storing constant values. However, due to the Harvard design, those values
63//! are not usable with normal instructions (i.e. those emitted from normal
64//! Rust code). Instead, special instructions are required to load data from
65//! the program code domain, such as the `lpm` (load \[from\] program memory)
66//! instruction. And because there is no way to emit it from Rust code, this
67//! crate uses inline assembly to emit that instruction.
68//!
69//! However, since a pointer into program code cannot be differentiated from a
70//! normal data pointer, it is entirely up to the programmer to ensure that
71//! these different 'pointer-types' are not accidentally mixed. In other words,
72//! this is `unsafe` in the context of Rust.
73//!
74//!
75//! # Loading Data from Program Memory
76//!
77//! The first part of this crate simply provides a few functions (e.g.
78//! [`read_byte`](crate::raw::read_byte)) to load constant data (i.e. a Rust `static` that is
79//! immutable) from the program memory into the data domain, so that
80//! sub-sequentially it is normal usable data, i.e. as owned data on the stack.
81//!
82//! Because, as aforementioned, a simple `*const u8` in Rust does not specify
83//! whether is lives in the program code domain or the data domain, all
84//! functions which simply load a given pointer from the program memory are
85//! inherently `unsafe`.
86//!
87//! Notice that using references (e.g. `&u8`) to the program code domain should
88//! generally be avoided because references in Rust should be dereferencable,
89//! which the program code domain is not.
90//!
91//! Additionally references can be easily dereferenced by safe code,
92//! which would be **undefined behavior** if that reference points into the
93//! program memory.
94//! Therefore, a Rust reference to a `static` that is stored in program memory
95//! must be considered hazardous (if not **UB**),
96//! and it is recommended to only use raw pointers to those `static`s,
97//! e.g. obtained via the [`addr_of!`](core::ptr::addr_of) macro,
98//! which directly creates raw pointers without needing a reference.
99//!
100//! ## Example
101//!
102//! ```
103//! use avr_progmem::raw::read_byte;
104//! use core::ptr::addr_of;
105//!
106//! // This `static` must never be directly dereferenced/accessed!
107//! // So a `let data: u8 = P_BYTE;` ⚠️ is **undefined behavior**!!!
108//! /// Static byte stored in progmem!
109//! #[link_section = ".progmem.data"]
110//! static P_BYTE: u8 = b'A';
111//!
112//! // Load the byte from progmem
113//! // Here, it is sound, because due to the link_section it is indeed in the
114//! // program code memory.
115//! let data: u8 = unsafe { read_byte(addr_of!(P_BYTE)) };
116//! assert_eq!(b'A', data);
117//! ```
118//!
119//!
120//! # The best-effort Wrapper
121//!
122//! Since working with progmem data is inherently unsafe and rather
123//! difficult to do correctly, this crate introduces the best-effort 'safe'
124//! wrapper [`ProgMem`](crate::wrapper::ProgMem),
125//! that is supposed to only wrap data in progmem, thus
126//! offering only functions to load its content using the progmem loading
127//! function introduced above.
128//! Using these functions is sound, if that the wrapper data is really stored
129//! in the program memory. Therefore, to enforce this invariant,
130//! the constructor of `ProgMem` is `unsafe`.
131//!
132//! Additionally, since proper Rust references (unlike pointers) come with a lot
133//! special requirements, it should be considered hazardous to have a reference
134//! to data stored in program memory.
135//! Instead, only raw pointers to this kind of data should be kept,
136//! created e.g. via the [`addr_of!`](core::ptr::addr_of) macro.
137//! Consequently, the `ProgMem` just wrap a pointer to data in progmem,
138//! which in turn must be stored in a `static` marked with
139//! `#[link_section = ".progmem.data"]`.
140//! However, since, safe Rust can always create a "normal" Rust reference to any
141//! (accessible) `static`, it must be considered hazardous if not just unsound,
142//! to expose such a `static` to safe Rust code.
143//!
144//! To also make this easier (and less hazardous), this crate provides the
145//! [`progmem!`] macro, which will create a hidden `static` in program memory
146//! initialized with the data you give it,
147//! wrap it's pointer in the `ProgMem` struct,
148//! and put that wrapper into yet another (normal RAM) static, so you can
149//! access it.
150//! This will ensure that the `static` that is stored in program memory can not
151//! be referenced by safe Rust code (because it is not accessible),
152//! while the accessible `ProgMem` wrapper allows access to the underling data
153//! by loading it correctly from program memory.
154//!
155//! ## Example
156//!
157//! ```
158//! use avr_progmem::progmem;
159//!
160//! // It will be wrapped in the ProgMem struct and expand to:
161//! // ```
162//! // static P_BYTE: ProgMem<u8> = {
163//! //     #[link_section = ".progmem.data"]
164//! //     static INNER_HIDDEN: u8 = 42;
165//! //     unsafe { ProgMem::new(addr_of!(INNER_HIDDEN)) }
166//! // };
167//! // ```
168//! // Thus it is impossible for safe Rust to directly access the progmem data!
169//! progmem! {
170//!     /// Static byte stored in progmem!
171//!     static progmem P_BYTE: u8 = 42;
172//! }
173//!
174//! // Load the byte from progmem
175//! // This is sound, because the `ProgMem` always uses the special operation to
176//! // load the data from program memory.
177//! let data: u8 = P_BYTE.load();
178//! assert_eq!(42, data);
179//! ```
180//!
181//!
182//! # Strings
183//!
184//! Using strings such as `&str` with [`ProgMem`](crate::wrapper::ProgMem)
185//! is rather difficult, and
186//! surprisingly hard if Unicode support is needed
187//! (see [issue #3](https://github.com/Cryptjar/avr-progmem-rs/issues/3)).
188//! Thus, to make working with string convenient the
189//! [`PmString`](string::PmString) struct is provided on top of
190//! [`ProgMem`](crate::wrapper::ProgMem).
191//!
192//! [`PmString`](string::PmString) stores any given `&str` as statically sized
193//! UTF-8 byte array (with full Unicode support).
194//! To make its content usable, it provides a `Display` & `uDisplay`
195//! implementation, a lazy [`chars`](string::PmString::chars) iterator,
196//! and [`load`](string::PmString::load) function similar to
197//! [`ProgMem`](crate::wrapper::ProgMem)'s
198//! `load`,
199//! that yields a [`LoadedString`](string::LoadedString),
200//! which in turn defers to `&str`.
201//!
202//! For more details see the [string](crate::string) module.
203//!
204//! ## Example
205//!
206//! ```rust
207//! use avr_progmem::progmem;
208//!
209//! progmem! {
210//!     // A simple Unicode string in progmem.
211//!     static progmem string TEXT = "Hello 大賢者";
212//! }
213//!
214//! // You can load it and use that as `&str`
215//! let buffer = TEXT.load();
216//! assert_eq!("Hello 大賢者", &*buffer);
217//!
218//! // Or you use directly the `Display` impl
219//! assert_eq!("Hello 大賢者", format!("{}", TEXT));
220//! ```
221//!
222//! Additionally, two special macros are provided similar to the `F` macro
223//! of the Arduino IDE, that allows to "mark" a string as to be stored in
224//! progmem while being returned at this place as a loaded `&str`.
225//!
226//! ```rust
227//! // Or you skip the static and use in-line progmem strings:
228//! use avr_progmem::progmem_str as F;
229//! use avr_progmem::progmem_display as D;
230//!
231//! // Either as `&str`
232//! assert_eq!("Foo 大賢者", F!("Foo 大賢者"));
233//!
234//! // Or as some `impl Display + uDisplay`
235//! assert_eq!("Bar 大賢者", format!("{}", D!("Bar 大賢者")));
236//! ```
237//!
238//! If you enabled the `ufmt` crate feature (its a default feature),
239//! you can also use `uDisplay` in addition to `Display`.
240//!
241//! ```rust
242//! # #[cfg(feature = "ufmt")] // requires the `ufmt` crate feature
243//! # {
244//! #
245//! use avr_progmem::progmem;
246//! use avr_progmem::progmem_str as F;
247//! use avr_progmem::progmem_display as D;
248//!
249//! fn foo<W: ufmt::uWrite>(writer: &mut W) {
250//!     progmem! {
251//!         // A simple Unicode string in progmem.
252//!         static progmem string TEXT = "Hello 大賢者";
253//!     }
254//!
255//!     // You can use the `uDisplay` impl
256//!     ufmt::uwriteln!(writer, "{}", TEXT);
257//!
258//!     // Or use the in-line `&str`
259//!     writer.write_str(F!("Foo 大賢者\n"));
260//!
261//!     // Or the in-line `impl uDisplay`
262//!     ufmt::uwriteln!(writer, "{}", D!("Bar 大賢者"));
263//! }
264//! # struct MyWriter(String);
265//! # impl ufmt::uWrite for MyWriter {
266//! #     type Error = ();
267//! #     fn write_str(&mut self, s: &str) -> Result<(),()> {
268//! #         self.0.push_str(s);
269//! #         Ok(())
270//! #     }
271//! # }
272//! //
273//! # let mut writer = MyWriter(String::new());
274//! # foo(&mut writer);
275//! # assert_eq!("Hello 大賢者\nFoo 大賢者\nBar 大賢者\n", writer.0);
276//! # }
277//! ```
278//!
279//!
280//! # Other Architectures
281//!
282//! As mentioned before, this crate is specifically designed to be use with
283//! AVR-base micro-controllers. But since most of us don't write their programs
284//! on an AVR system but e.g. on x86 systems, and might want to test them
285//! there (well as far as it is possible), this crate also has a fallback
286//! implementation for all other architectures that are not AVR, falling back
287//! to a simple Rust `static` in the default data segment. And all the data
288//! loading functions will just dereference the pointed-to data, assuming that
289//! they just live in the default location.
290//!
291//! This fallback is perfectly safe on x86 and friend, and should also be fine
292//! on all further architectures, otherwise normal Rust `static`s would be
293//! broken. However, it is an important point to know when for instance writing
294//! a library that is not limited to AVR.
295//!
296//!
297//! # Implementation Limitations
298//!
299//! Aside from what has been already been covered, the current implementation
300//! has two further limitations.
301//!
302//! First, since this crate uses an inline assembly loop on a 8-bit
303//! architecture, the loop counter only allows values up to 255. This means
304//! that not more that 255 bytes can be loaded at once with any of the methods
305//! of this crate. However, this only applies to a single continuous load
306//! operation, so for instance `ProgMem<[u8;1024]>::load()` will panic, but
307//! accessing such a big type in smaller chunks e.g.
308//! `ProgMem<[u8;1024]>::load_sub_array::<[u8;128]>(512)` is perfectly fine
309//! because the to be loaded type `[u8;128]` is only 128 bytes in size.
310//! Notice that the same limitation holds for `PmString<N>::load()`
311//! (i.e. you can only use it if `N <= 255` holds.
312//! On the other hand, there is no such limitation on `PmString<N>::chars()`
313//! and `PmString`'s `Display`/`uDisplay` implementation,
314//! because those, just load each `char` individually
315//! (i.e. no more that 4 bytes at a time).
316//!
317//! Second, since this crate only uses the `lpm` instruction, which is limited
318//! by a 16-bit pointer, this crate may only be used with data stored in the
319//! lower 64 kiB of program memory. Since this property has not be tested it is
320//! unclear whether it will cause a panic or right-up undefined behavior, so be
321//! very wary when working with AVR chips that have more then 64 kiB of program
322//! memory.
323//!
324//! [`progmem!`]: https://docs.rs/avr-progmem/latest/avr_progmem/macro.progmem.html
325//! [`avr-libc`]: https://crates.io/crates/avr-libc
326//! [avr]: https://en.wikipedia.org/wiki/AVR_microcontrollers
327//!
328
329
330
331pub mod raw;
332pub mod string;
333pub mod wrapper;