lib.rs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. //! Minimal startup / runtime for RISCV 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. //! - A `panic_fmt` implementation that just calls abort that you can opt into
  12. //! through the "abort-on-panic" Cargo feature. If you don't use this feature
  13. //! you'll have to provide the `panic_fmt` lang item yourself. Documentation
  14. //! [here][1]
  15. //!
  16. //! [1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html
  17. //!
  18. //! - A minimal `start` lang item to support the standard `fn main()`
  19. //! interface. (The processor goes to sleep after returning from `main`)
  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.riscv-rt]
  33. //! version = "0.1.0"
  34. //!
  35. //! $ # tell Xargo which standard crates to build
  36. //! $ edit Xargo.toml && cat $_
  37. //! [dependencies.core]
  38. //! stage = 0
  39. //!
  40. //! [dependencies.compiler_builtins]
  41. //! features = ["mem"]
  42. //! stage = 1
  43. //!
  44. //! $ # memory layout of the device
  45. //! $ edit memory.x && cat $_
  46. //! MEMORY
  47. //! {
  48. //! /* NOTE K = KiBi = 1024 bytes */
  49. //! FLASH : ORIGIN = 0x08000000, LENGTH = 128K
  50. //! RAM : ORIGIN = 0x20000000, LENGTH = 8K
  51. //! }
  52. //!
  53. //! $ edit src/main.rs && cat $_
  54. //! ```
  55. //!
  56. //! ``` ignore,no_run
  57. //! #![no_std]
  58. //! #![no_main]
  59. //!
  60. //! #[macro_use(entry)]
  61. //! extern crate riscv_rt;
  62. //!
  63. //! // use `main` as the entry point of this application
  64. //! entry!(main);
  65. //!
  66. //! fn main() -> ! {
  67. //! // do something here
  68. //! }
  69. //! ```
  70. //!
  71. //! ``` text
  72. //! $ cargo install xargo
  73. //!
  74. //! $ xargo rustc --target riscv32-unknown-none -- \
  75. //! -C link-arg=-Tlink.x -C linker=riscv32-unknown-elf-ld -Z linker-flavor=ld
  76. //!
  77. //! $ riscv32-unknown-elf-objdump -Cd $(find target -name app) | head
  78. //!
  79. //! Disassembly of section .text:
  80. //!
  81. //! 20400000 <_start>:
  82. //! 20400000: 800011b7 lui gp,0x80001
  83. //! 20400004: 80018193 addi gp,gp,-2048 # 80000800 <_stack_start+0xffffc800>
  84. //! 20400008: 80004137 lui sp,0x80004
  85. //! ```
  86. //!
  87. //! # Symbol interfaces
  88. //!
  89. //! This crate makes heavy use of symbols, linker sections and linker scripts to
  90. //! provide most of its functionality. Below are described the main symbol
  91. //! interfaces.
  92. //!
  93. //! ## `memory.x`
  94. //!
  95. //! This file supplies the information about the device to the linker.
  96. //!
  97. //! ### `MEMORY`
  98. //!
  99. //! The main information that this file must provide is the memory layout of
  100. //! the device in the form of the `MEMORY` command. The command is documented
  101. //! [here][2], but at a minimum you'll want to create two memory regions: one
  102. //! for Flash memory and another for RAM.
  103. //!
  104. //! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html
  105. //!
  106. //! The program instructions (the `.text` section) will be stored in the memory
  107. //! region named FLASH, and the program `static` variables (the sections `.bss`
  108. //! and `.data`) will be allocated in the memory region named RAM.
  109. //!
  110. //! ### `_stack_start`
  111. //!
  112. //! This symbol provides the address at which the call stack will be allocated.
  113. //! The call stack grows downwards so this address is usually set to the highest
  114. //! valid RAM address plus one (this *is* an invalid address but the processor
  115. //! will decrement the stack pointer *before* using its value as an address).
  116. //!
  117. //! If omitted this symbol value will default to `ORIGIN(RAM) + LENGTH(RAM)`.
  118. //!
  119. //! #### Example
  120. //!
  121. //! Allocating the call stack on a different RAM region.
  122. //!
  123. //! ```
  124. //! MEMORY
  125. //! {
  126. //! /* call stack will go here */
  127. //! CCRAM : ORIGIN = 0x10000000, LENGTH = 8K
  128. //! FLASH : ORIGIN = 0x08000000, LENGTH = 256K
  129. //! /* static variables will go here */
  130. //! RAM : ORIGIN = 0x20000000, LENGTH = 40K
  131. //! }
  132. //!
  133. //! _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM);
  134. //! ```
  135. //!
  136. //! ### `_sheap`
  137. //!
  138. //! This symbol is located in RAM right after the `.bss` and `.data` sections.
  139. //! You can use the address of this symbol as the start address of a heap
  140. //! region. This symbol is 4 byte aligned so that address will be a multiple of
  141. //! 4.
  142. //!
  143. //! #### Example
  144. //!
  145. //! ```
  146. //! extern crate some_allocator;
  147. //!
  148. //! // Size of the heap in bytes
  149. //! const SIZE: usize = 1024;
  150. //!
  151. //! extern "C" {
  152. //! static mut _sheap: u8;
  153. //! }
  154. //!
  155. //! fn main() {
  156. //! unsafe {
  157. //! let start_address = &mut _sheap as *mut u8;
  158. //! some_allocator::initialize(start_address, SIZE);
  159. //! }
  160. //! }
  161. //! ```
  162. // NOTE: Adapted from cortex-m/src/lib.rs
  163. #![no_std]
  164. #![deny(missing_docs)]
  165. #![deny(warnings)]
  166. #![feature(asm)]
  167. #![feature(compiler_builtins_lib)]
  168. #![feature(const_fn)]
  169. #![feature(extern_prelude)]
  170. #![feature(global_asm)]
  171. #![feature(lang_items)]
  172. #![feature(linkage)]
  173. #![feature(panic_implementation)]
  174. #![feature(used)]
  175. extern crate riscv;
  176. extern crate r0;
  177. mod lang_items;
  178. use riscv::register::{mcause, mstatus};
  179. extern "C" {
  180. // Boundaries of the .bss section
  181. static mut _ebss: u32;
  182. static mut _sbss: u32;
  183. // Boundaries of the .data section
  184. static mut _edata: u32;
  185. static mut _sdata: u32;
  186. // Initial values of the .data section (stored in Flash)
  187. static _sidata: u32;
  188. // Address of _start_trap
  189. static _start_trap: u32;
  190. }
  191. /// Entry point of all programs (_start).
  192. ///
  193. /// It initializes DWARF call frame information, the stack pointer, the
  194. /// frame pointer (needed for closures to work in start_rust) and the global
  195. /// pointer. Then it calls _start_rust.
  196. #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
  197. global_asm!(r#"
  198. .section .init
  199. .globl _start
  200. _start:
  201. .cfi_startproc
  202. .cfi_undefined ra
  203. // .option push
  204. // .option norelax
  205. lui gp, %hi(__global_pointer$)
  206. addi gp, gp, %lo(__global_pointer$)
  207. // .option pop
  208. lui sp, %hi(_stack_start)
  209. addi sp, sp, %lo(_stack_start)
  210. add s0, sp, zero
  211. jal zero, _start_rust
  212. .cfi_endproc
  213. "#);
  214. /// Rust entry point (_start_rust)
  215. ///
  216. /// Zeros bss section, initializes data section and calls main. This function
  217. /// never returns.
  218. #[link_section = ".init.rust"]
  219. #[export_name = "_start_rust"]
  220. pub extern "C" fn start_rust() -> ! {
  221. extern "C" {
  222. // This symbol will be provided by the user via the `entry!` macro
  223. fn main() -> !;
  224. }
  225. unsafe {
  226. r0::zero_bss(&mut _sbss, &mut _ebss);
  227. r0::init_data(&mut _sdata, &mut _edata, &_sidata);
  228. }
  229. // TODO: Enable FPU when available
  230. // Set mtvec to _start_trap
  231. #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
  232. unsafe {
  233. //mtvec::write(_start_trap as usize, mtvec::TrapMode::Direct);
  234. asm!("csrrw zero, 0x305, $0"
  235. :
  236. : "r"(&_start_trap)
  237. :
  238. : "volatile");
  239. }
  240. unsafe {
  241. main();
  242. }
  243. }
  244. /// Macro to define the entry point of the program
  245. ///
  246. /// **NOTE** This macro must be invoked once and must be invoked from an accessible module, ideally
  247. /// from the root of the crate.
  248. ///
  249. /// Usage: `entry!(path::to::entry::point)`
  250. ///
  251. /// The specified function will be called by the reset handler *after* RAM has been initialized.
  252. ///
  253. /// The signature of the specified function must be `fn() -> !` (never ending function).
  254. #[macro_export]
  255. macro_rules! entry {
  256. ($path:expr) => {
  257. #[inline(never)]
  258. #[export_name = "main"]
  259. pub extern "C" fn __impl_main() -> ! {
  260. // validate the signature of the program entry point
  261. let f: fn() -> ! = $path;
  262. f()
  263. }
  264. };
  265. }
  266. /// Trap entry point (_start_trap)
  267. ///
  268. /// Saves caller saved registers ra, t0..6, a0..7, calls _start_trap_rust,
  269. /// restores caller saved registers and then returns.
  270. #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
  271. global_asm!(r#"
  272. .section .trap
  273. .align 4
  274. .global _start_trap
  275. _start_trap:
  276. addi sp, sp, -16*4
  277. sw ra, 0*4(sp)
  278. sw t0, 1*4(sp)
  279. sw t1, 2*4(sp)
  280. sw t2, 3*4(sp)
  281. sw t3, 4*4(sp)
  282. sw t4, 5*4(sp)
  283. sw t5, 6*4(sp)
  284. sw t6, 7*4(sp)
  285. sw a0, 8*4(sp)
  286. sw a1, 9*4(sp)
  287. sw a2, 10*4(sp)
  288. sw a3, 11*4(sp)
  289. sw a4, 12*4(sp)
  290. sw a5, 13*4(sp)
  291. sw a6, 14*4(sp)
  292. sw a7, 15*4(sp)
  293. jal ra, _start_trap_rust
  294. lw ra, 0*4(sp)
  295. lw t0, 1*4(sp)
  296. lw t1, 2*4(sp)
  297. lw t2, 3*4(sp)
  298. lw t3, 4*4(sp)
  299. lw t4, 5*4(sp)
  300. lw t5, 6*4(sp)
  301. lw t6, 7*4(sp)
  302. lw a0, 8*4(sp)
  303. lw a1, 9*4(sp)
  304. lw a2, 10*4(sp)
  305. lw a3, 11*4(sp)
  306. lw a4, 12*4(sp)
  307. lw a5, 13*4(sp)
  308. lw a6, 14*4(sp)
  309. lw a7, 15*4(sp)
  310. addi sp, sp, 16*4
  311. mret
  312. "#);
  313. /// Trap entry point rust (_start_trap_rust)
  314. ///
  315. /// mcause is read to determine the cause of the trap. XLEN-1 bit indicates
  316. /// if it's an interrupt or an exception. The result is converted to an element
  317. /// of the Interrupt or Exception enum and passed to handle_interrupt or
  318. /// handle_exception.
  319. #[link_section = ".trap.rust"]
  320. #[export_name = "_start_trap_rust"]
  321. pub extern "C" fn start_trap_rust() {
  322. // dispatch trap to handler
  323. trap_handler(mcause::read().cause());
  324. // mstatus, remain in M-mode after mret
  325. unsafe {
  326. mstatus::set_mpp(mstatus::MPP::Machine);
  327. }
  328. }
  329. /// Default Trap Handler
  330. #[no_mangle]
  331. #[linkage = "weak"]
  332. pub fn trap_handler(_: mcause::Trap) {}
  333. // Make sure there is an abort when linking
  334. #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
  335. global_asm!(r#"
  336. .section .init
  337. .globl abort
  338. abort:
  339. jal zero, _start
  340. "#);