msp430_rt/
lib.rs

1//! Startup code and minimal runtime for MSP430 microcontrollers
2//!
3//! This crate is based on [cortex-m-rt](https://docs.rs/cortex-m-rt)
4//! crate by Jorge Aparicio (@japaric).
5//!
6//! This crate contains all the required parts to build a `no_std` application (binary crate) that
7//! targets a MSP430 microcontroller.
8//!
9//! # Features
10//!
11//! This crates takes care of:
12//!
13//! - The memory layout of the program. In particular, it populates the vector table so the device
14//! can boot correctly, and properly dispatch interrupts.
15//!
16//! - Initializing `static` variables before the program entry point.
17//!
18//! This crate also provides the following attributes:
19//!
20//! - `#[entry]` to declare the entry point of the program
21//! - `#[pre_init]` to run code *before* `static` variables are initialized
22//!
23//! This crate also implements a related attribute called `#[interrupt]`, which allows you
24//! to define interrupt handlers. However, since which interrupts are available depends on the
25//! microcontroller in use, this attribute should be re-exported and used from a PAC crate.
26//!
27//! The documentation for these attributes can be found in the [Attribute Macros](#attributes)
28//! section.
29//!
30//! # Requirements
31//!
32//! ## `memory.x`
33//!
34//! This crate expects the user, or some other crate, to provide the memory layout of the target
35//! device via a linker script named `memory.x`. This section covers the contents of `memory.x`
36//!
37//! ### `MEMORY`
38//!
39//! The linker script must specify the memory available in the device as, at least, three `MEMORY`
40//! regions: one named `ROM`, one named `RAM`, and one named `VECTORS`. The `.text` and `.rodata`
41//! sections of the program will be placed in the `ROM` region, whereas the `.bss` and `.data`
42//! sections, as well as the heap, will be placed in the `RAM` region. The `.vector_table` section,
43//! which including the interrupt vectors and reset address, will be placed in the `VECTORS`
44//! region at the end of flash. The `ROM` region should end at the address the `VECTORS` region
45//! begins.
46//!
47//! A `VECTORS` region is required because between (_and within_) msp430 device families:
48//! * Devices do not have a constant single vector table size.
49//! * Devices do not have a constant vector table start address.
50//! Consult your Family User's Guide (e.g. MSP430x5xx Family User's Guide, slau208),
51//! particularly the Memory Map section, and your device's datasheet (e.g. msp430g2553) for
52//! information on vector table layout and size. _You may be able to get more program space if
53//! your device's datasheet explicitly marks a contiguous set of vectors as unused!_
54//!
55//!
56//! ``` text
57//! /* Linker script for the MSP430G2553 */
58//! MEMORY
59//! {
60//!   RAM : ORIGIN = 0x0200, LENGTH = 0x0200
61//!   ROM : ORIGIN = 0xC000, LENGTH = 0x3FE0
62//!   VECTORS : ORIGIN = 0xFFE0, LENGTH = 0x20
63//! }
64//! ```
65//!
66//! By default, the linker will check that the address after the vector table is 0x10000 to
67//! ensure that the vector table is placed correctly. For special cases like bootloader
68//! setups, you might want to place the vector table somewhere else. For theses cases,
69//! you can re-configure the expected end address by setting `__VECTORS_END_ADDR` in your
70//! `memory.x` file.
71//!
72//! # An example
73//!
74//! This section presents a minimal application built on top of `msp430-rt`.
75//!
76//! ``` ignore
77//! // IMPORTANT the standard `main` interface is not used because it requires nightly
78//! #![no_main]
79//! #![no_std]
80//!
81//! extern crate msp430_rt;
82//! // Simple panic handler that infinitely loops.
83//! extern crate panic_msp430;
84//!
85//! use msp430_rt::entry;
86//!
87//! // use `main` as the entry point of this application
88//! // `main` is not allowed to return
89//! #[entry]
90//! fn main() -> ! {
91//!     // initialization
92//!
93//!     loop {
94//!         // application logic
95//!     }
96//! }
97//!
98//! ```
99//!
100//! To actually build this program you need to place a `memory.x` linker script somewhere the linker
101//! can find it, e.g. in the current directory; and then link the program using `msp430-rt`'s
102//! linker script: `link.x`. The required steps are shown below:
103//!
104//! ``` text
105//! $ cat > memory.x <<EOF
106//! /* Memory layout of the MSP430G2553 */
107//! MEMORY
108//! {
109//!   RAM : ORIGIN = 0x0200, LENGTH = 0x0200
110//!   ROM : ORIGIN = 0xC000, LENGTH = 0x3FE0
111//!   VECTORS : ORIGIN = 0xFFE0, LENGTH = 0x20
112//! }
113//! EOF
114//!
115//! $ cargo rustc --target msp430-none-elf -Zbuild-std=core -- \
116//!       -C link-arg=-nostartfiles -C link-arg=-Tlink.x
117//!
118//! $ file target/msp430-none-elf/debug/app
119//! app: ELF 32-bit LSB executable, TI msp430, version 1 (embedded), statically linked, not stripped
120//! ```
121//!
122//! # Optional features
123//!
124//! ## `device`
125//!
126//! If this feature is disabled then this crate populates the whole vector table. All the interrupts
127//! in the vector table, even the ones unused by the target device, will be bound to the default
128//! interrupt handler. This makes the final application device agnostic: you will be able to run it
129//! on any MSP430 device -- provided that you correctly specified its memory layout in `memory.x`
130//! -- without hitting undefined behavior.
131//!
132//! If this feature is enabled then the interrupts section of the vector table is left unpopulated
133//! and some other crate, or the user, will have to populate it. This mode is meant to be used in
134//! conjunction with PAC crates generated using `svd2rust`. Those *PAC crates* will populate the
135//! missing part of the vector table when their `"rt"` feature is enabled.
136//!
137//! # Inspection
138//!
139//! This section covers how to inspect a binary that builds on top of `msp430-rt`.
140//!
141//! ## Sections (`size`)
142//!
143//! `msp430-rt` uses standard sections like `.text`, `.rodata`, `.bss` and `.data` as one would
144//! expect. `msp430-rt` separates the vector table in its own section, named `.vector_table`. This
145//! lets you distinguish how much space is taking the vector table in Flash vs how much is being
146//! used by actual instructions (`.text`) and constants (`.rodata`).
147//!
148//! ``` text
149//! $ size -Ax target/msp430-none-elf/examples/app
150//! section              size     addr
151//! .vector_table        0x20   0xffe0
152//! .text                0x44   0xc000
153//! .rodata               0x0   0xc044
154//! .bss                  0x0    0x200
155//! .data                 0x0    0x200
156//! .MSP430.attributes   0x17      0x0
157//! Total                0x7b
158//! ```
159//!
160//! Without the `-A` argument `size` reports the sum of the sizes of `.text`, `.rodata` and
161//! `.vector_table` under "text".
162//!
163//! ``` text
164//! $ size target/msp430-none-elf/examples/app
165//!    text    data     bss     dec     hex filename
166//!     100       0       0     100      64 target/msp430-none-elf/release/app
167//! ```
168//!
169//! ## Symbols (`objdump`, `nm`)
170//!
171//! One will always find the following (unmangled) symbols in `msp430-rt` applications:
172//!
173//! - `Reset`. This function will initialize the stack pointer, call `PreInit`, initialize static
174//! variables (`.data` and `.bss`) and then call the user program entry point using the `main`
175//! symbol (See `#[entry]`).
176//!
177//!   In previous versions of this crate (0.2.4 and below), the startup code was implemented in
178//! Rust, and `main` would sometimes be inlined into `Reset` (using a `ResetTrampoline` for stack
179//! pointer initialization). However, as of version 0.2.5, you should always find the `main` symbol
180//! in your program because the current startup code containing `Reset` is implemented in assembly.
181//!
182//! - `DefaultHandler`. This is the default interrupt handler. If not overridden using `#[interrupt]
183//! fn DefaultHandler(..` this will be an infinite loop.
184//!
185//! - `__RESET_VECTOR`. This is the reset vector, a pointer into `ResetTrampoline`. This vector is
186//! located at the end of the `.vector_table` section.
187//!
188//! - `__INTERRUPTS`. This is the device specific interrupt portion of the vector table. This array
189//! is located right before `__RESET_VECTOR` in the `.vector_table` section.
190//!
191//! - `PreInit`. This is a function to be run before RAM is initialized. It defaults to an empty
192//! function. The function called can be changed using the `#[pre_init]` attribute. In previous
193//! versions of this crate, an empty function marked with the `#[pre_init]` would be optimized out.
194//! As of version 0.2.5, a `PreInit` function will always be included.
195//!
196//! If you overrode any interrupt handler you'll find it as an unmangled symbol, e.g. `NMI` or
197//! `WDT`, in the output of `objdump`.
198//!
199//! # Advanced usage
200//!
201//! ## Setting the program entry point
202//!
203//! This section describes how `#[entry]` is implemented. This information is useful to developers
204//! who want to provide an alternative to `#[entry]` that provides extra guarantees.
205//!
206//! The `Reset` handler will call a symbol named `main` (unmangled) *after* initializing `.bss` and
207//! `.data`. `#[entry]` provides this symbol in its expansion:
208//!
209//! ``` ignore
210//! #[entry]
211//! fn main() -> ! {
212//!     /* user code */
213//! }
214//!
215//! // expands into
216//!
217//! #[no_mangle]
218//! extern "C" fn main() -> ! {
219//!     /* user code */
220//! }
221//! ```
222//!
223//! The unmangled `main` symbol must have signature `extern "C" fn() -> !` or its invocation from
224//! `Reset`  will result in undefined behavior.
225//!
226//! ## Incorporating device specific interrupts
227//!
228//! This section covers how an external crate can insert device specific interrupt handlers into the
229//! vector table. Most users don't need to concern themselves with these details, but if you are
230//! interested in how device crates generated using `svd2rust` integrate with `msp430-rt` read on.
231//!
232//! The information in this section applies when the `"device"` feature has been enabled.
233//!
234//! ### `__INTERRUPTS`
235//!
236//! The external crate must provide the interrupts portion of the vector table via a `static`
237//! variable named`__INTERRUPTS` (unmangled) that must be placed in the `.vector_table.interrupts`
238//! section of its object file.
239//!
240//! This `static` variable will be placed at `ORIGIN(VECTORS)`. This address corresponds to the
241//! spot where IRQ0 (IRQ number 0) is located.
242//!
243//! To conform to the MSP430 ABI `__INTERRUPTS` must be an array of function pointers; some spots
244//! in this array may need to be set to 0 if they are marked as *reserved* in the data sheet /
245//! reference manual. We recommend using a `union` to set the reserved spots to `0`; `None`
246//! (`Option<fn()>`) may also work but it's not guaranteed that the `None` variant will *always* be
247//! represented by the value `0`.
248//!
249//! Let's illustrate with an artificial example where a device only has two interrupt: `Foo`, with
250//! IRQ number = 2, and `Bar`, with IRQ number = 4.
251//!
252//! ``` ignore
253//! union Vector {
254//!     handler: extern "msp430-interrupt" fn(),
255//!     reserved: usize,
256//! }
257//!
258//! extern "msp430-interrupt" {
259//!     fn Foo();
260//!     fn Bar();
261//! }
262//!
263//! #[link_section = ".vector_table.interrupts"]
264//! #[no_mangle]
265//! static __INTERRUPTS: [Vector; 15] = [
266//!     // 0-1: Reserved
267//!     Vector { reserved: 0 },
268//!     Vector { reserved: 0 },
269//!
270//!     // 2: Foo
271//!     Vector { handler: Foo },
272//!
273//!     // 3: Reserved
274//!     Vector { reserved: 0 },
275//!
276//!     // 4: Bar
277//!     Vector { handler: Bar },
278//!
279//!     // 5-14: Reserved
280//!     Vector { reserved: 0 },
281//!     Vector { reserved: 0 },
282//!     Vector { reserved: 0 },
283//!     Vector { reserved: 0 },
284//!     Vector { reserved: 0 },
285//!     Vector { reserved: 0 },
286//!     Vector { reserved: 0 },
287//!     Vector { reserved: 0 },
288//!     Vector { reserved: 0 },
289//!     Vector { reserved: 0 },
290//! ];
291//! ```
292//!
293//! ### `device.x`
294//!
295//! Linking in `__INTERRUPTS` creates a bunch of undefined references. If the user doesn't set a
296//! handler for *all* the device specific interrupts then linking will fail with `"undefined
297//! reference"` errors.
298//!
299//! We want to provide a default handler for all the interrupts while still letting the user
300//! individually override each interrupt handler. In C projects, this is usually accomplished using
301//! weak aliases declared in external assembly files. In Rust, we could achieve something similar
302//! using `global_asm!`, but that's an unstable feature.
303//!
304//! A solution that doesn't require `global_asm!` or external assembly files is to use the `PROVIDE`
305//! command in a linker script to create the weak aliases. This is the approach that `msp430-rt`
306//! uses; when the `"device"` feature is enabled `msp430-rt`'s linker script (`link.x`) depends on
307//! a linker script named `device.x`. The crate that provides `__INTERRUPTS` must also provide this
308//! file.
309//!
310//! For our running example the `device.x` linker script looks like this:
311//!
312//! ``` text
313//! /* device.x */
314//! PROVIDE(Foo = DefaultHandler);
315//! PROVIDE(Bar = DefaultHandler);
316//! ```
317//!
318//! This weakly aliases both `Foo` and `Bar`. `DefaultHandler` is the default interrupt handler.
319//!
320//! Because this linker script is provided by a dependency of the final application the dependency
321//! must contain build script that puts `device.x` somewhere the linker can find. An example of such
322//! build script is shown below:
323//!
324//! ``` ignore
325//! use std::{env, fs::File, io::Write, path::PathBuf};
326//!
327//! fn main() {
328//!     // Put the linker script somewhere the linker can find it
329//!     let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
330//!     File::create(out.join("device.x"))
331//!         .unwrap()
332//!         .write_all(include_bytes!("device.x"))
333//!         .unwrap();
334//!     println!("cargo:rustc-link-search={}", out.display());
335//! }
336//! ```
337//!
338//! [attr-entry]: attr.entry.html
339//! [attr-exception]: attr.exception.html
340//! [attr-pre_init]: attr.pre_init.html
341
342#![deny(missing_docs)]
343#![feature(abi_msp430_interrupt)]
344#![no_std]
345
346use msp430::asm;
347pub use msp430_rt_macros::interrupt;
348pub use msp430_rt_macros::{entry, pre_init};
349
350/// Returns a pointer to the start of the heap
351///
352/// The returned pointer is guaranteed to be 4-byte aligned.
353#[inline]
354pub fn heap_start() -> *mut u32 {
355    extern "C" {
356        static mut __sheap: u32;
357    }
358
359    &raw mut __sheap
360}
361
362extern "msp430-interrupt" {
363    fn Reset() -> !;
364}
365
366#[link_section = ".__RESET_VECTOR"]
367#[no_mangle]
368static __RESET_VECTOR: unsafe extern "msp430-interrupt" fn() -> ! = Reset;
369
370#[no_mangle]
371unsafe extern "C" fn PreInit_() {}
372
373#[no_mangle]
374extern "msp430-interrupt" fn DefaultHandler_() -> ! {
375    // The interrupts are already disabled here.
376    loop {
377        // Prevent optimizations that can remove this loop.
378        asm::barrier();
379    }
380}
381
382// Interrupts for generic application
383#[cfg(not(feature = "device"))]
384#[no_mangle]
385#[link_section = ".vector_table.interrupts"]
386static __INTERRUPTS: [unsafe extern "msp430-interrupt" fn(); 15] = [{
387    extern "msp430-interrupt" {
388        fn DefaultHandler();
389    }
390
391    DefaultHandler
392}; 15];