lib.rs 7.7 KB

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