123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- //! Minimal startup / runtime for RISCV CPU's
- //!
- //! # Features
- //!
- //! This crate provides
- //!
- //! - Before main initialization of the `.bss` and `.data` sections.
- //!
- //! - Before main initialization of the FPU (for targets that have a FPU).
- //!
- //! - A `panic_fmt` implementation that just calls abort that you can opt into
- //! through the "abort-on-panic" Cargo feature. If you don't use this feature
- //! you'll have to provide the `panic_fmt` lang item yourself. Documentation
- //! [here][1]
- //!
- //! [1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html
- //!
- //! - A minimal `start` lang item to support the standard `fn main()`
- //! interface. (The processor goes to sleep after returning from `main`)
- //!
- //! - A linker script that encodes the memory layout of a generic RISC-V
- //! microcontroller. This linker script is missing some information that must
- //! be supplied through a `memory.x` file (see example below).
- //!
- //! - A `_sheap` symbol at whose address you can locate a heap.
- //!
- //! ``` text
- //! $ cargo new --bin app && cd $_
- //!
- //! $ # add this crate as a dependency
- //! $ edit Cargo.toml && cat $_
- //! [dependencies.riscv-rt]
- //! version = "0.1.0"
- //!
- //! $ # tell Xargo which standard crates to build
- //! $ edit Xargo.toml && cat $_
- //! [dependencies.core]
- //! stage = 0
- //!
- //! [dependencies.compiler_builtins]
- //! features = ["mem"]
- //! stage = 1
- //!
- //! $ # memory layout of the device
- //! $ edit memory.x && cat $_
- //! MEMORY
- //! {
- //! /* NOTE K = KiBi = 1024 bytes */
- //! FLASH : ORIGIN = 0x08000000, LENGTH = 128K
- //! RAM : ORIGIN = 0x20000000, LENGTH = 8K
- //! }
- //!
- //! $ edit src/main.rs && cat $_
- //! ```
- //!
- //! ``` ignore,no_run
- //! #![no_std]
- //! #![no_main]
- //!
- //! #[macro_use(entry)]
- //! extern crate riscv_rt;
- //!
- //! // use `main` as the entry point of this application
- //! entry!(main);
- //!
- //! fn main() -> ! {
- //! // do something here
- //! }
- //! ```
- //!
- //! ``` text
- //! $ cargo install xargo
- //!
- //! $ xargo rustc --target riscv32-unknown-none -- \
- //! -C link-arg=-Tlink.x -C linker=riscv32-unknown-elf-ld -Z linker-flavor=ld
- //!
- //! $ riscv32-unknown-elf-objdump -Cd $(find target -name app) | head
- //!
- //! Disassembly of section .text:
- //!
- //! 20400000 <_start>:
- //! 20400000: 800011b7 lui gp,0x80001
- //! 20400004: 80018193 addi gp,gp,-2048 # 80000800 <_stack_start+0xffffc800>
- //! 20400008: 80004137 lui sp,0x80004
- //! ```
- //!
- //! # Symbol interfaces
- //!
- //! This crate makes heavy use of symbols, linker sections and linker scripts to
- //! provide most of its functionality. Below are described the main symbol
- //! interfaces.
- //!
- //! ## `memory.x`
- //!
- //! This file supplies the information about the device to the linker.
- //!
- //! ### `MEMORY`
- //!
- //! The main information that this file must provide is the memory layout of
- //! the device in the form of the `MEMORY` command. The command is documented
- //! [here][2], but at a minimum you'll want to create two memory regions: one
- //! for Flash memory and another for RAM.
- //!
- //! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html
- //!
- //! The program instructions (the `.text` section) will be stored in the memory
- //! region named FLASH, and the program `static` variables (the sections `.bss`
- //! and `.data`) will be allocated in the memory region named RAM.
- //!
- //! ### `_stack_start`
- //!
- //! This symbol provides the address at which the call stack will be allocated.
- //! The call stack grows downwards so this address is usually set to the highest
- //! valid RAM address plus one (this *is* an invalid address but the processor
- //! will decrement the stack pointer *before* using its value as an address).
- //!
- //! If omitted this symbol value will default to `ORIGIN(RAM) + LENGTH(RAM)`.
- //!
- //! #### Example
- //!
- //! Allocating the call stack on a different RAM region.
- //!
- //! ```
- //! MEMORY
- //! {
- //! /* call stack will go here */
- //! CCRAM : ORIGIN = 0x10000000, LENGTH = 8K
- //! FLASH : ORIGIN = 0x08000000, LENGTH = 256K
- //! /* static variables will go here */
- //! RAM : ORIGIN = 0x20000000, LENGTH = 40K
- //! }
- //!
- //! _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM);
- //! ```
- //!
- //! ### `_sheap`
- //!
- //! This symbol is located in RAM right after the `.bss` and `.data` sections.
- //! You can use the address of this symbol as the start address of a heap
- //! region. This symbol is 4 byte aligned so that address will be a multiple of
- //! 4.
- //!
- //! #### Example
- //!
- //! ```
- //! extern crate some_allocator;
- //!
- //! // Size of the heap in bytes
- //! const SIZE: usize = 1024;
- //!
- //! extern "C" {
- //! static mut _sheap: u8;
- //! }
- //!
- //! fn main() {
- //! unsafe {
- //! let start_address = &mut _sheap as *mut u8;
- //! some_allocator::initialize(start_address, SIZE);
- //! }
- //! }
- //! ```
- // NOTE: Adapted from cortex-m/src/lib.rs
- #![no_std]
- #![deny(missing_docs)]
- #![deny(warnings)]
- #![feature(asm)]
- #![feature(compiler_builtins_lib)]
- #![feature(const_fn)]
- #![feature(extern_prelude)]
- #![feature(global_asm)]
- #![feature(lang_items)]
- #![feature(linkage)]
- #![feature(panic_implementation)]
- #![feature(used)]
- extern crate riscv;
- extern crate r0;
- mod lang_items;
- use riscv::register::{mcause, mstatus};
- extern "C" {
- // Boundaries of the .bss section
- static mut _ebss: u32;
- static mut _sbss: u32;
- // Boundaries of the .data section
- static mut _edata: u32;
- static mut _sdata: u32;
- // Initial values of the .data section (stored in Flash)
- static _sidata: u32;
- // Address of _start_trap
- static _start_trap: u32;
- }
- /// Entry point of all programs (_start).
- ///
- /// It initializes DWARF call frame information, the stack pointer, the
- /// frame pointer (needed for closures to work in start_rust) and the global
- /// pointer. Then it calls _start_rust.
- #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
- global_asm!(r#"
- .section .init
- .globl _start
- _start:
- .cfi_startproc
- .cfi_undefined ra
- // .option push
- // .option norelax
- lui gp, %hi(__global_pointer$)
- addi gp, gp, %lo(__global_pointer$)
- // .option pop
- lui sp, %hi(_stack_start)
- addi sp, sp, %lo(_stack_start)
- add s0, sp, zero
- jal zero, _start_rust
- .cfi_endproc
- "#);
- /// Rust entry point (_start_rust)
- ///
- /// Zeros bss section, initializes data section and calls main. This function
- /// never returns.
- #[link_section = ".init.rust"]
- #[export_name = "_start_rust"]
- pub extern "C" fn start_rust() -> ! {
- extern "C" {
- // This symbol will be provided by the user via the `entry!` macro
- fn main() -> !;
- }
- unsafe {
- r0::zero_bss(&mut _sbss, &mut _ebss);
- r0::init_data(&mut _sdata, &mut _edata, &_sidata);
- }
- // TODO: Enable FPU when available
- // Set mtvec to _start_trap
- #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
- unsafe {
- //mtvec::write(_start_trap as usize, mtvec::TrapMode::Direct);
- asm!("csrrw zero, 0x305, $0"
- :
- : "r"(&_start_trap)
- :
- : "volatile");
- }
- unsafe {
- main();
- }
- }
- /// Macro to define the entry point of the program
- ///
- /// **NOTE** This macro must be invoked once and must be invoked from an accessible module, ideally
- /// from the root of the crate.
- ///
- /// Usage: `entry!(path::to::entry::point)`
- ///
- /// The specified function will be called by the reset handler *after* RAM has been initialized.
- ///
- /// The signature of the specified function must be `fn() -> !` (never ending function).
- #[macro_export]
- macro_rules! entry {
- ($path:expr) => {
- #[inline(never)]
- #[export_name = "main"]
- pub extern "C" fn __impl_main() -> ! {
- // validate the signature of the program entry point
- let f: fn() -> ! = $path;
- f()
- }
- };
- }
- /// Trap entry point (_start_trap)
- ///
- /// Saves caller saved registers ra, t0..6, a0..7, calls _start_trap_rust,
- /// restores caller saved registers and then returns.
- #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
- global_asm!(r#"
- .section .trap
- .align 4
- .global _start_trap
- _start_trap:
- addi sp, sp, -16*4
- sw ra, 0*4(sp)
- sw t0, 1*4(sp)
- sw t1, 2*4(sp)
- sw t2, 3*4(sp)
- sw t3, 4*4(sp)
- sw t4, 5*4(sp)
- sw t5, 6*4(sp)
- sw t6, 7*4(sp)
- sw a0, 8*4(sp)
- sw a1, 9*4(sp)
- sw a2, 10*4(sp)
- sw a3, 11*4(sp)
- sw a4, 12*4(sp)
- sw a5, 13*4(sp)
- sw a6, 14*4(sp)
- sw a7, 15*4(sp)
- jal ra, _start_trap_rust
- lw ra, 0*4(sp)
- lw t0, 1*4(sp)
- lw t1, 2*4(sp)
- lw t2, 3*4(sp)
- lw t3, 4*4(sp)
- lw t4, 5*4(sp)
- lw t5, 6*4(sp)
- lw t6, 7*4(sp)
- lw a0, 8*4(sp)
- lw a1, 9*4(sp)
- lw a2, 10*4(sp)
- lw a3, 11*4(sp)
- lw a4, 12*4(sp)
- lw a5, 13*4(sp)
- lw a6, 14*4(sp)
- lw a7, 15*4(sp)
- addi sp, sp, 16*4
- mret
- "#);
- /// Trap entry point rust (_start_trap_rust)
- ///
- /// mcause is read to determine the cause of the trap. XLEN-1 bit indicates
- /// if it's an interrupt or an exception. The result is converted to an element
- /// of the Interrupt or Exception enum and passed to handle_interrupt or
- /// handle_exception.
- #[link_section = ".trap.rust"]
- #[export_name = "_start_trap_rust"]
- pub extern "C" fn start_trap_rust() {
- // dispatch trap to handler
- trap_handler(mcause::read().cause());
- // mstatus, remain in M-mode after mret
- unsafe {
- mstatus::set_mpp(mstatus::MPP::Machine);
- }
- }
- /// Default Trap Handler
- #[no_mangle]
- #[linkage = "weak"]
- pub fn trap_handler(_: mcause::Trap) {}
- // Make sure there is an abort when linking
- #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
- global_asm!(r#"
- .section .init
- .globl abort
- abort:
- jal zero, _start
- "#);
|