fomu_rt/lib.rs
1//! Minimal startup / runtime for RISC-V Fomu CPU
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//!
8//! # Features
9//!
10//! This crate provides
11//!
12//! - Before main initialization of the `.bss` and `.data` sections.
13//!
14//! - `#[entry]` to declare the entry point of the program
15//! - `#[pre_init]` to run code *before* `static` variables are initialized
16//!
17//! - A linker script that encodes the memory layout of a generic RISC-V
18//! microcontroller. This linker script is missing some information that must
19//! be supplied through a `memory.x` file (see example below). This file
20//! must be supplied using rustflags and listed *before* `link.x`. Arbitrary
21//! filename can be use instead of `memory.x`.
22//!
23//! - A `_sheap` symbol at whose address you can locate a heap.
24//!
25//! ``` text
26//! $ cargo new --bin app && cd $_
27//!
28//! $ # add this crate as a dependency
29//! $ edit Cargo.toml && cat $_
30//! [dependencies]
31//! fomu-rt = "0.0.1"
32//! panic-halt = "0.2.0"
33//!
34//! $ edit src/main.rs && cat $_
35//! ```
36//!
37//! ``` ignore,no_run
38//! #![no_std]
39//! #![no_main]
40//!
41//! extern crate panic_halt;
42//!
43//! use fomu_rt::entry;
44//!
45//! // use `main` as the entry point of this application
46//! // `main` is not allowed to return
47//! #[entry]
48//! fn main() -> ! {
49//! // do something here
50//! loop { }
51//! }
52//! ```
53//!
54//! ``` text
55//! $ mkdir .cargo && edit .cargo/config && cat $_
56//! [target.riscv32i-unknown-none-elf]
57//! rustflags = [
58//! "-C", "link-arg=-Tlink.x",
59//! ]
60//!
61//! [build]
62//! target = "riscv32i-unknown-none-elf"
63//! $
64//! ```
65//!
66//! ``` text
67//! $ cargo build
68//!
69//! $ riscv32-unknown-elf-objdump -Cd $(find target -name app) | head
70//!
71//! Disassembly of section .text:
72//!
73//! 20000000 <_start>:
74//! 20000000: 800011b7 lui gp,0x80001
75//! 20000004: 80018193 addi gp,gp,-2048 # 80000800 <_stack_start+0xffffc800>
76//! 20000008: 80004137 lui sp,0x80004
77//! ```
78//!
79//! # Symbol interfaces
80//!
81//! This crate makes heavy use of symbols, linker sections and linker scripts to
82//! provide most of its functionality. Below are described the main symbol
83//! interfaces.
84//!
85//! #### Example
86//!
87//! ``` no_run
88//! extern crate some_allocator;
89//!
90//! extern "C" {
91//! static _sheap: u8;
92//! static _heap_size: u8;
93//! }
94//!
95//! fn main() {
96//! unsafe {
97//! let heap_bottom = &_sheap as *const u8 as usize;
98//! let heap_size = &_heap_size as *const u8 as usize;
99//! some_allocator::initialize(heap_bottom, heap_size);
100//! }
101//! }
102//! ```
103//!
104//! ### `_mp_hook`
105//!
106//! This function is called from all the harts and must return true only for one hart,
107//! which will perform memory initialization. For other harts it must return false
108//! and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt).
109//!
110//! This function can be redefined in the following way:
111//!
112//! ``` no_run
113//! #[export_name = "_mp_hook"]
114//! pub extern "Rust" fn mp_hook() -> bool {
115//! // ...
116//! }
117//! ```
118//!
119//! Default implementation of this function wakes hart 0 and busy-loops all the other harts.
120
121// NOTE: Adapted from cortex-m/src/lib.rs
122#![no_std]
123#![deny(missing_docs)]
124#![deny(warnings)]
125
126extern crate vexriscv;
127extern crate riscv;
128extern crate riscv_rt_macros as macros;
129extern crate r0;
130
131pub use macros::{entry, pre_init};
132
133use riscv::register::mstatus;
134
135#[export_name = "error: fomu-rt appears more than once in the dependency graph"]
136#[doc(hidden)]
137pub static __ONCE__: () = ();
138
139extern "C" {
140 // Boundaries of the .bss section
141 static mut _ebss: u32;
142 static mut _sbss: u32;
143
144 // Boundaries of the .data section
145 static mut _edata: u32;
146 static mut _sdata: u32;
147
148 // Initial values of the .data section (stored in Flash)
149 static _sidata: u32;
150}
151
152
153/// Rust entry point (_start_rust)
154///
155/// Zeros bss section, initializes data section and calls main. This function
156/// never returns.
157#[link_section = ".init.rust"]
158#[export_name = "_start_rust"]
159pub unsafe extern "C" fn start_rust() -> ! {
160 extern "Rust" {
161 // This symbol will be provided by the user via `#[entry]`
162 fn main() -> !;
163
164 // This symbol will be provided by the user via `#[pre_init]`
165 fn __pre_init();
166
167 fn _mp_hook() -> bool;
168 }
169
170 if _mp_hook() {
171 __pre_init();
172
173 r0::zero_bss(&mut _sbss, &mut _ebss);
174 r0::init_data(&mut _sdata, &mut _edata, &_sidata);
175 }
176
177 main();
178}
179
180
181/// Trap entry point rust (_start_trap_rust)
182///
183/// mcause is read to determine the cause of the trap. XLEN-1 bit indicates
184/// if it's an interrupt or an exception. The result is converted to an element
185/// of the Interrupt or Exception enum and passed to handle_interrupt or
186/// handle_exception.
187#[link_section = ".trap.rust"]
188#[export_name = "_start_trap_rust"]
189pub extern "C" fn start_trap_rust() {
190 extern "C" {
191 fn trap_handler();
192 }
193
194 unsafe {
195 // dispatch trap to handler
196 trap_handler();
197
198 // mstatus, remain in M-mode after mret
199 mstatus::set_mpp(mstatus::MPP::Machine);
200 }
201}
202
203
204#[doc(hidden)]
205#[no_mangle]
206pub fn default_trap_handler() {}
207
208#[doc(hidden)]
209#[no_mangle]
210pub unsafe extern "Rust" fn default_pre_init() {}
211
212#[doc(hidden)]
213#[no_mangle]
214pub extern "Rust" fn default_mp_hook() -> bool {
215 use riscv::register::mhartid;
216 match mhartid::read() {
217 0 => true,
218 _ => loop {
219 unsafe { riscv::asm::wfi() }
220 },
221 }
222}