Skip to main content

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}