riscv_minimal_rt/
lib.rs

1//! Minimal startup / runtime for RISC-V CPU's
2//!
3//! # Minimum Supported Rust Version (MSRV)
4//!
5//! This crate is guaranteed to compile on stable Rust 1.31 and up. It *might*
6//! compile with older versions but that may change in any new patch release.
7//! Note that `riscv64imac-unknown-none-elf` and `riscv64gc-unknown-none-elf` targets
8//! are not supported on stable yet.
9//!
10//! # Features
11//!
12//! This crate provides
13//!
14//! - Before main initialization of the `.bss` and `.data` sections.
15//!
16//! - Before main initialization of the FPU (for targets that have a FPU).
17//!
18//! - `#[entry]` to declare the entry point of the program
19//! - `#[pre_init]` to run code *before* `static` variables are initialized
20//!
21//! - A linker script that encodes the memory layout of a generic RISC-V
22//!   microcontroller. This linker script is missing some information that must
23//!   be supplied through a `memory.x` file (see example below).
24//!
25//! - A `_sheap` symbol at whose address you can locate a heap.
26//!
27//! ``` text
28//! $ cargo new --bin app && cd $_
29//!
30//! $ # add this crate as a dependency
31//! $ edit Cargo.toml && cat $_
32//! [dependencies]
33//! riscv-minimal-rt = "0.4.0"
34//! panic-halt = "0.2.0"
35//!
36//! $ # memory layout of the device
37//! $ edit memory.x && cat $_
38//! MEMORY
39//! {
40//!   /* NOTE K = KiBi = 1024 bytes */
41//!   FLASH : ORIGIN = 0x20000000, LENGTH = 16M
42//!   RAM : ORIGIN = 0x80000000, LENGTH = 16K
43//! }
44//!
45//! $ edit src/main.rs && cat $_
46//! ```
47//!
48//! ``` ignore,no_run
49//! #![no_std]
50//! #![no_main]
51//!
52//! extern crate panic_halt;
53//!
54//! use riscv_rt::entry;
55//!
56//! // use `main` as the entry point of this application
57//! // `main` is not allowed to return
58//! #[entry]
59//! fn main() -> ! {
60//!     // do something here
61//!     loop { }
62//! }
63//! ```
64//!
65//! ``` text
66//! $ mkdir .cargo && edit .cargo/config && cat $_
67//! [target.riscv32imac-unknown-none-elf]
68//! rustflags = [
69//!   "-C", "link-arg=-Tlink.x"
70//! ]
71//!
72//! [build]
73//! target = "riscv32imac-unknown-none-elf"
74//! $ edit build.rs && cat $_
75//! ```
76//!
77//! ``` ignore,no_run
78//! use std::env;
79//! use std::fs::File;
80//! use std::io::Write;
81//! use std::path::Path;
82//!
83//! /// Put the linker script somewhere the linker can find it.
84//! fn main() {
85//!     let out_dir = env::var("OUT_DIR").expect("No out dir");
86//!     let dest_path = Path::new(&out_dir);
87//!     let mut f = File::create(&dest_path.join("memory.x"))
88//!         .expect("Could not create file");
89//!
90//!     f.write_all(include_bytes!("memory.x"))
91//!         .expect("Could not write file");
92//!
93//!     println!("cargo:rustc-link-search={}", dest_path.display());
94//!
95//!     println!("cargo:rerun-if-changed=memory.x");
96//!     println!("cargo:rerun-if-changed=build.rs");
97//! }
98//! ```
99//!
100//! ``` text
101//! $ cargo build
102//!
103//! $ riscv32-unknown-elf-objdump -Cd $(find target -name app) | head
104//!
105//! Disassembly of section .text:
106//!
107//! 20000000 <_start>:
108//! 20000000:	800011b7          	lui	gp,0x80001
109//! 20000004:	80018193          	addi	gp,gp,-2048 # 80000800 <_stack_start+0xffffc800>
110//! 20000008:	80004137          	lui	sp,0x80004
111//! ```
112//!
113//! # Symbol interfaces
114//!
115//! This crate makes heavy use of symbols, linker sections and linker scripts to
116//! provide most of its functionality. Below are described the main symbol
117//! interfaces.
118//!
119//! ## `memory.x`
120//!
121//! This file supplies the information about the device to the linker.
122//!
123//! ### `MEMORY`
124//!
125//! The main information that this file must provide is the memory layout of
126//! the device in the form of the `MEMORY` command. The command is documented
127//! [here][2], but at a minimum you'll want to create two memory regions: one
128//! for Flash memory and another for RAM.
129//!
130//! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html
131//!
132//! The program instructions (the `.text` section) will be stored in the memory
133//! region named FLASH, and the program `static` variables (the sections `.bss`
134//! and `.data`) will be allocated in the memory region named RAM.
135//!
136//! ### `_stack_start`
137//!
138//! This symbol provides the address at which the call stack will be allocated.
139//! The call stack grows downwards so this address is usually set to the highest
140//! valid RAM address plus one (this *is* an invalid address but the processor
141//! will decrement the stack pointer *before* using its value as an address).
142//!
143//! If omitted this symbol value will default to `ORIGIN(RAM) + LENGTH(RAM)`.
144//!
145//! #### Example
146//!
147//! Allocating the call stack on a different RAM region.
148//!
149//! ```
150//! MEMORY
151//! {
152//!   /* call stack will go here */
153//!   CCRAM : ORIGIN = 0x10000000, LENGTH = 8K
154//!   FLASH : ORIGIN = 0x08000000, LENGTH = 256K
155//!   /* static variables will go here */
156//!   RAM : ORIGIN = 0x20000000, LENGTH = 40K
157//! }
158//!
159//! _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM);
160//! ```
161//!
162//! ### `_heap_size`
163//!
164//! This symbol provides the size of a heap region. The default value is 0. You can set `_heap_size`
165//! to a non-zero value if you are planning to use heap allocations.
166//!
167//! ### `_sheap`
168//!
169//! This symbol is located in RAM right after the `.bss` and `.data` sections.
170//! You can use the address of this symbol as the start address of a heap
171//! region. This symbol is 4 byte aligned so that address will be a multiple of 4.
172//!
173//! #### Example
174//!
175//! ```
176//! extern crate some_allocator;
177//!
178//! extern "C" {
179//!     static _sheap: u8;
180//!     static _heap_size: u8;
181//! }
182//!
183//! fn main() {
184//!     unsafe {
185//!         let heap_bottom = &_sheap as *const u8 as usize;
186//!         let heap_size = &_heap_size as *const u8 as usize;
187//!         some_allocator::initialize(heap_bottom, heap_size);
188//!     }
189//! }
190//! ```
191//!
192//! ## `pre_init!`
193//!
194//! A user-defined function can be run at the start of the reset handler, before RAM is
195//! initialized. The macro `pre_init!` can be called to set the function to be run. The function is
196//! intended to perform actions that cannot wait the time it takes for RAM to be initialized, such
197//! as disabling a watchdog. As the function is called before RAM is initialized, any access of
198//! static variables will result in undefined behavior.
199
200// NOTE: Adapted from cortex-m/src/lib.rs
201#![no_std]
202#![deny(missing_docs)]
203#![deny(warnings)]
204
205extern crate riscv;
206extern crate riscv_minimal_rt_macros as macros;
207extern crate r0;
208
209pub use macros::{entry, pre_init};
210
211#[cfg(feature = "interrupts")]
212use riscv::register::{mstatus, mtvec};
213
214extern "C" {
215    // Boundaries of the .bss section
216    static mut _ebss: u32;
217    static mut _sbss: u32;
218
219    // Boundaries of the .data section
220    static mut _edata: u32;
221    static mut _sdata: u32;
222
223    // Initial values of the .data section (stored in Flash)
224    static _sidata: u32;
225
226    // Address of _start_trap
227    #[cfg(feature = "interrupts")]
228    static _start_trap: u32;
229}
230
231
232/// Rust entry point (_start_rust)
233///
234/// Zeros bss section, initializes data section and calls main. This function
235/// never returns.
236#[link_section = ".init.rust"]
237#[export_name = "_start_rust"]
238pub unsafe extern "C" fn start_rust() -> ! {
239    extern "Rust" {
240        // This symbol will be provided by the user via `#[entry]`
241        fn main() -> !;
242
243        // This symbol will be provided by the user via `#[pre_init]`
244        fn __pre_init();
245    }
246
247    __pre_init();
248
249    r0::zero_bss(&mut _sbss, &mut _ebss);
250    r0::init_data(&mut _sdata, &mut _edata, &_sidata);
251
252    // TODO: Enable FPU when available
253
254    // Set mtvec to _start_trap
255    #[cfg(feature = "interrupts")]
256    mtvec::write(&_start_trap as *const _ as usize, mtvec::TrapMode::Direct);
257
258    main();
259}
260
261
262/// Trap entry point rust (_start_trap_rust)
263///
264/// mcause is read to determine the cause of the trap. XLEN-1 bit indicates
265/// if it's an interrupt or an exception. The result is converted to an element
266/// of the Interrupt or Exception enum and passed to handle_interrupt or
267/// handle_exception.
268#[link_section = ".trap.rust"]
269#[export_name = "_start_trap_rust"]
270pub extern "C" fn start_trap_rust() {
271    extern "C" {
272        fn trap_handler();
273    }
274
275    unsafe {
276        // dispatch trap to handler
277        trap_handler();
278
279        // mstatus, remain in M-mode after mret
280        #[cfg(feature = "interrupts")]
281        mstatus::set_mpp(mstatus::MPP::Machine);
282    }
283}
284
285
286/// Default Trap Handler
287#[no_mangle]
288pub fn default_trap_handler() {}
289
290#[doc(hidden)]
291#[no_mangle]
292pub unsafe extern "Rust" fn default_pre_init() {}