initial commit

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2019-12-18 09:59:00 +08:00
commit 79114aa65a
80 changed files with 4021 additions and 0 deletions

9
.cargo/config Normal file
View File

@ -0,0 +1,9 @@
[target.riscv32i-unknown-none-elf]
#runner = "riscv64-unknown-elf-gdb -x gdb_init"
rustflags = [
"-C", "link-arg=-Tmemory.x",
"-C", "link-arg=-Tlink.x",
]
[build]
target = "riscv32i-unknown-none-elf"

18
.gitattributes vendored Normal file
View File

@ -0,0 +1,18 @@
*.py text eol=lf
*.dfu binary
*.png binary
*.jpg binary
*.bin binary
*.elf binary
*.h text eol=lf
*.c text eol=lf
*.s text eol=lf
*.S text eol=lf
README.* text eol=lf
LICENSE text eol=lf
Makefile text eol=lf
*.mk text eol=lf
*.sh text eol=lf
*.ps1 text eol=crlf
.gitignore text eol=lf
.gitattributes text eol=lf

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

142
Cargo.lock generated Normal file
View File

@ -0,0 +1,142 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "bare-metal"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bit_field"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "r0"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "riscv-rt-macros"
version = "0.1.6"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "xous-kernel"
version = "0.1.0"
dependencies = [
"xous-riscv 0.5.4",
"xous-riscv-rt 0.6.1",
]
[[package]]
name = "xous-riscv"
version = "0.5.4"
dependencies = [
"bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "xous-riscv-rt"
version = "0.6.1"
dependencies = [
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"riscv-rt-macros 0.1.6",
"xous-riscv 0.5.4",
]
[metadata]
"checksum bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a3caf393d93b2d453e80638d0674597020cef3382ada454faacd43d1a55a735a"
"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"

16
Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "xous-kernel"
version = "0.1.0"
authors = ["Sean Cross <sean@xobs.io>"]
edition = "2018"
description = "Core kernel for Xous, including task switching and memory management"
[dependencies]
xous-riscv = { path = "xous-riscv" }
xous-riscv-rt = { path = "xous-riscv-rt" }
[profile.release]
codegen-units = 1 # 1 better optimizations
debug = true # symbols are nice and they don't increase the size on Flash
lto = true
#opt-level=0

4
README.md Normal file
View File

@ -0,0 +1,4 @@
# Xous Kernel
This contains the core kernel for Xous. It is implemented as
a library that you include with your `pid 0` program.

16
memory.x Normal file
View File

@ -0,0 +1,16 @@
MEMORY
{
RAM : ORIGIN = 0x40000000, LENGTH = 16M
FLASH : ORIGIN = 0x20500000, LENGTH = 16M
MEMLCD: ORIGIN = 0xB0000000, LENGTH = 32k
}
REGION_ALIAS("REGION_TEXT", FLASH);
REGION_ALIAS("REGION_RODATA", FLASH);
REGION_ALIAS("REGION_DATA", RAM);
REGION_ALIAS("REGION_BSS", RAM);
REGION_ALIAS("REGION_HEAP", RAM);
REGION_ALIAS("REGION_STACK", RAM);
_lcdfb = ORIGIN(MEMLCD);
_heap_size = LENGTH(RAM) - 1M;

18
src/main.rs Normal file
View File

@ -0,0 +1,18 @@
#![no_std]
#![no_main]
extern crate xous_riscv;
use core::panic::PanicInfo;
#[panic_handler]
fn handle_panic(arg: &PanicInfo) -> ! {
loop {}
}
use xous_riscv_rt::entry;
#[entry]
fn xous_main() -> ! {
loop {
unsafe { xous_riscv::asm::wfi() };
}
}

18
xous-riscv-rt/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "xous-riscv-rt"
version = "0.6.1"
repository = "https://github.com/xous/xous-riscv-rt"
authors = ["Sean Cross <sean@xobs.io>", "The RISC-V Team <risc-v@teams.rust-embedded.org>"]
categories = ["embedded", "no-std", "xous"]
description = "Minimal runtime / startup for RISC-V CPU's"
keywords = ["riscv", "runtime", "startup"]
license = "ISC"
[dependencies]
r0 = "0.2.2"
xous-riscv = { path = "../xous-riscv", version = "0.5.1" }
riscv-rt-macros = { path = "macros", version = "0.1.6" }
[dev-dependencies]
xous-riscv = { path = "../xous-riscv", version = "0.5.1" }
panic-halt = "0.2.0"

0
xous-riscv-rt/README.md Normal file
View File

151
xous-riscv-rt/asm.S Normal file
View File

@ -0,0 +1,151 @@
#if __riscv_xlen == 64
# define STORE sd
# define LOAD ld
# define LOG_REGBYTES 3
#else
# define STORE sw
# define LOAD lw
# define LOG_REGBYTES 2
#endif
#define REGBYTES (1 << LOG_REGBYTES)
/*
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.
*/
.section .text.init, "ax"
.global _start
_start:
.cfi_startproc
.cfi_undefined ra
csrw mie, 0
csrw mip, 0
li x1, 0
li x2, 0
li x3, 0
li x4, 0
li x5, 0
li x6, 0
li x7, 0
li x8, 0
li x9, 0
li x10,0
li x11,0
li x12,0
li x13,0
li x14,0
li x15,0
li x16,0
li x17,0
li x18,0
li x19,0
li x20,0
li x21,0
li x22,0
li x23,0
li x24,0
li x25,0
li x26,0
li x27,0
li x28,0
li x29,0
li x30,0
li x31,0
.option push
.option norelax
la gp, __global_pointer$
.option pop
#if !defined(SKIP_MULTICORE)
// Check hart id
csrr a2, mhartid
lui t0, %hi(_max_hart_id)
add t0, t0, %lo(_max_hart_id)
bgtu a2, t0, abort
#endif // SKIP_MULTICORE
// Allocate stacks
la sp, _stack_start
#if !defined(SKIP_MULTICORE)
lui t0, %hi(_hart_stack_size)
add t0, t0, %lo(_hart_stack_size)
mul t0, a2, t0
sub sp, sp, t0
#endif // SKIP_MULTICORE
// Set frame pointer
add s0, sp, zero
// Set trap handler
la t0, _start_trap
csrw mtvec, t0
jal zero, _start_rust
.cfi_endproc
/*
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.
*/
.section .trap, "ax"
.global _start_trap
_start_trap:
addi sp, sp, -16*REGBYTES
STORE ra, 0*REGBYTES(sp)
STORE t0, 1*REGBYTES(sp)
STORE t1, 2*REGBYTES(sp)
STORE t2, 3*REGBYTES(sp)
STORE t3, 4*REGBYTES(sp)
STORE t4, 5*REGBYTES(sp)
STORE t5, 6*REGBYTES(sp)
STORE t6, 7*REGBYTES(sp)
STORE a0, 8*REGBYTES(sp)
STORE a1, 9*REGBYTES(sp)
STORE a2, 10*REGBYTES(sp)
STORE a3, 11*REGBYTES(sp)
STORE a4, 12*REGBYTES(sp)
STORE a5, 13*REGBYTES(sp)
STORE a6, 14*REGBYTES(sp)
STORE a7, 15*REGBYTES(sp)
jal ra, _start_trap_rust
LOAD ra, 0*REGBYTES(sp)
LOAD t0, 1*REGBYTES(sp)
LOAD t1, 2*REGBYTES(sp)
LOAD t2, 3*REGBYTES(sp)
LOAD t3, 4*REGBYTES(sp)
LOAD t4, 5*REGBYTES(sp)
LOAD t5, 6*REGBYTES(sp)
LOAD t6, 7*REGBYTES(sp)
LOAD a0, 8*REGBYTES(sp)
LOAD a1, 9*REGBYTES(sp)
LOAD a2, 10*REGBYTES(sp)
LOAD a3, 11*REGBYTES(sp)
LOAD a4, 12*REGBYTES(sp)
LOAD a5, 13*REGBYTES(sp)
LOAD a6, 14*REGBYTES(sp)
LOAD a7, 15*REGBYTES(sp)
addi sp, sp, 16*REGBYTES
mret
/* Make sure there is an abort when linking */
.section .text.init
.globl abort
abort:
j abort

View File

@ -0,0 +1,17 @@
# remove existing blobs because otherwise this will append object files to the old blobs
Remove-Item -Force bin/*.a
$crate = "xous-riscv-rt"
riscv64-unknown-elf-gcc -ggdb3 -c -mabi=ilp32 -march=rv32imac asm.S -o bin/$crate.o
riscv64-unknown-elf-ar crs bin/riscv32imac-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-ar crs bin/riscv32imc-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-gcc -ggdb3 -c -mabi=ilp32 -march=rv32i asm.S -DSKIP_MULTICORE -o bin/$crate.o
riscv64-unknown-elf-ar crs bin/riscv32i-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-gcc -ggdb3 -c -mabi=lp64 -march=rv64imac asm.S -o bin/$crate.o
riscv64-unknown-elf-ar crs bin/riscv64imac-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-ar crs bin/riscv64gc-unknown-none-elf.a bin/$crate.o
Remove-Item bin/$crate.o

21
xous-riscv-rt/assemble.sh Normal file
View File

@ -0,0 +1,21 @@
#!/bin/bash
set -euxo pipefail
crate=riscv-rt
# remove existing blobs because otherwise this will append object files to the old blobs
rm -f bin/*.a
riscv64-unknown-elf-gcc -c -mabi=ilp32 -march=rv32imac asm.S -o bin/$crate.o
ar crs bin/riscv32imac-unknown-none-elf.a bin/$crate.o
ar crs bin/riscv32imc-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-gcc -c -mabi=ilp32 -march=rv32i asm.S -DSKIP_MULTICORE -o bin/$crate.o
ar crs bin/riscv32i-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-gcc -c -mabi=lp64 -march=rv64imac asm.S -o bin/$crate.o
ar crs bin/riscv64imac-unknown-none-elf.a bin/$crate.o
ar crs bin/riscv64gc-unknown-none-elf.a bin/$crate.o
rm bin/$crate.o

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

32
xous-riscv-rt/build.rs Normal file
View File

@ -0,0 +1,32 @@
// NOTE: Adapted from cortex-m/build.rs
use std::env;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
fn main() {
let target = env::var("TARGET").unwrap();
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let name = env::var("CARGO_PKG_NAME").unwrap();
if target.starts_with("riscv") {
fs::copy(
format!("bin/{}.a", target),
out_dir.join(format!("lib{}.a", name)),
).unwrap();
println!("cargo:rerun-if-changed=bin/{}.a", target);
println!("cargo:rustc-link-lib=static={}", name);
println!("cargo:rustc-link-search={}", out_dir.display());
}
// Put the linker script somewhere the linker can find it
fs::File::create(out_dir.join("link.x"))
.unwrap()
.write_all(include_bytes!("link.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out_dir.display());
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=link.x");
}

159
xous-riscv-rt/link.x Normal file
View File

@ -0,0 +1,159 @@
PROVIDE(_stext = ORIGIN(REGION_TEXT));
PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK));
PROVIDE(_max_hart_id = 0);
PROVIDE(_hart_stack_size = 2K);
PROVIDE(_heap_size = 0);
PROVIDE(trap_handler = default_trap_handler);
/* # Pre-initialization function */
/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function,
then the function this points to will be called before the RAM is initialized. */
PROVIDE(__pre_init = default_pre_init);
/* # Multi-processing hook function
fn _mp_hook() -> bool;
This function is called from all the harts and must return true only for one hart,
which will perform memory initialization. For other harts it must return false
and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt).
*/
PROVIDE(_mp_hook = default_mp_hook);
ENTRY(_start)
SECTIONS
{
.text.dummy (NOLOAD) :
{
/* This section is intended to make _stext address work */
. = _stext;
} > REGION_TEXT
.text _stext :
{
/* Put reset handler first in .text section so it ends up as the entry */
/* point of the program. */
KEEP(*(.text.init));
KEEP(*(.init));
KEEP(*(.init.rust));
. = ALIGN(4);
KEEP(*(.trap));
KEEP(*(.trap.rust));
*(.text .text.*);
} > REGION_TEXT
.rodata : ALIGN(4)
{
*(.rodata .rodata.*);
/* 4-byte align the end (VMA) of this section.
This is required by LLD to ensure the LMA of the following .data
section will have the correct alignment. */
. = ALIGN(4);
} > REGION_RODATA
.data : ALIGN(4)
{
_sidata = LOADADDR(.data);
_sdata = .;
/* Must be called __global_pointer$ for linker relaxations to work. */
PROVIDE(__global_pointer$ = . + 0x800);
*(.sdata .sdata.* .sdata2 .sdata2.*);
*(.data .data.*);
. = ALIGN(4);
_edata = .;
} > REGION_DATA AT > REGION_RODATA
.bss (NOLOAD) :
{
_sbss = .;
*(.sbss .sbss.* .bss .bss.*);
. = ALIGN(4);
_ebss = .;
} > REGION_BSS
/* fictitious region that represents the memory available for the heap */
.heap (NOLOAD) :
{
_sheap = .;
. += _heap_size;
. = ALIGN(4);
_eheap = .;
} > REGION_HEAP
/* fictitious region that represents the memory available for the stack */
.stack (NOLOAD) :
{
_estack = .;
. = _stack_start;
_sstack = .;
} > REGION_STACK
/* fake output .got section */
/* Dynamic relocations are unsupported. This section is only used to detect
relocatable code in the input files and raise an error if relocatable code
is found */
.got (INFO) :
{
KEEP(*(.got .got.*));
}
/* Discard .eh_frame, we are not doing unwind on panic so it is not needed */
/DISCARD/ :
{
*(.eh_frame);
}
}
/* Do not exceed this mark in the error messages above | */
ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned");
ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned");
ASSERT(ORIGIN(REGION_DATA) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_DATA must be 4-byte aligned");
ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned");
ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned");
ASSERT(ORIGIN(REGION_STACK) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned");
ASSERT(_stext % 4 == 0, "
ERROR(riscv-rt): `_stext` must be 4-byte aligned");
ASSERT(_sdata % 4 == 0 && _edata % 4 == 0, "
BUG(riscv-rt): .data is not 4-byte aligned");
ASSERT(_sidata % 4 == 0, "
BUG(riscv-rt): the LMA of .data is not 4-byte aligned");
ASSERT(_sbss % 4 == 0 && _ebss % 4 == 0, "
BUG(riscv-rt): .bss is not 4-byte aligned");
ASSERT(_sheap % 4 == 0, "
BUG(riscv-rt): start of .heap is not 4-byte aligned");
ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), "
ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region.
Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'");
ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, "
ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts.
Consider changing `_max_hart_id` or `_hart_stack_size`.");
ASSERT(SIZEOF(.got) == 0, "
.got section detected in the input files. Dynamic relocations are not
supported. If you are linking to C code compiled using the `gcc` crate
then modify your build script to compile the C code _without_ the
-fPIC flag. See the documentation of the `gcc::Config.fpic` method for
details.");
/* Do not exceed this mark in the error messages above | */

View File

@ -0,0 +1,28 @@
[package]
authors = [
"The RISC-V Team <risc-v@teams.rust-embedded.org>",
"Jorge Aparicio <jorge@japaric.io>",
]
categories = ["embedded", "no-std"]
description = "Attributes re-exported in `riscv-rt`"
documentation = "https://docs.rs/riscv-rt"
keywords = ["riscv", "runtime", "startup"]
license = "MIT OR Apache-2.0"
name = "riscv-rt-macros"
repository = "https://github.com/rust-embedded/riscv-rt"
version = "0.1.6"
[lib]
proc-macro = true
[dependencies]
quote = "0.6.8"
proc-macro2 = "0.4.20"
[dependencies.syn]
features = ["extra-traits", "full"]
version = "0.15.13"
[dependencies.rand]
version = "0.5.5"
default-features = false

View File

@ -0,0 +1,210 @@
#![deny(warnings)]
extern crate proc_macro;
extern crate rand;
#[macro_use]
extern crate quote;
extern crate core;
extern crate proc_macro2;
#[macro_use]
extern crate syn;
use proc_macro2::Span;
use rand::Rng;
use rand::SeedableRng;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use syn::{
parse, spanned::Spanned, Ident, ItemFn, ReturnType, Type, Visibility,
};
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
use proc_macro::TokenStream;
/// Attribute to declare the entry point of the program
///
/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you
/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
/// private modules between the item and the root of the crate); if the item is in the root of the
/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases.
///
/// The specified function will be called by the reset handler *after* RAM has been initialized.
/// If present, the FPU will also be enabled before the function is called.
///
/// The type of the specified function must be `[unsafe] fn() -> !` (never ending function)
///
/// # Properties
///
/// The entry point will be called by the reset handler.
///
/// # Examples
///
/// - Simple entry point
///
/// ``` no_run
/// #![no_main]
/// use xous_riscv_rt::entry;
/// #[entry]
/// fn main() -> ! {
/// loop {
/// /* .. */
/// }
/// }
/// ```
#[proc_macro_attribute]
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
let f = parse_macro_input!(input as ItemFn);
// check the function signature
let valid_signature = f.constness.is_none()
&& f.vis == Visibility::Inherited
&& f.abi.is_none()
&& f.decl.inputs.is_empty()
&& f.decl.generics.params.is_empty()
&& f.decl.generics.where_clause.is_none()
&& f.decl.variadic.is_none()
&& match f.decl.output {
ReturnType::Default => false,
ReturnType::Type(_, ref ty) => match **ty {
Type::Never(_) => true,
_ => false,
},
};
if !valid_signature {
return parse::Error::new(
f.span(),
"`#[entry]` function must have signature `[unsafe] fn() -> !`",
)
.to_compile_error()
.into();
}
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
.to_compile_error()
.into();
}
// XXX should we blacklist other attributes?
let attrs = f.attrs;
let unsafety = f.unsafety;
let hash = random_ident();
let stmts = f.block.stmts;
quote!(
#[export_name = "xous_main"]
#(#attrs)*
pub #unsafety fn #hash() -> ! {
#(#stmts)*
}
)
.into()
}
/// Attribute to mark which function will be called at the beginning of the reset handler.
///
/// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you
/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
/// private modules between the item and the root of the crate); if the item is in the root of the
/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer
/// releases.
///
/// The function must have the signature of `unsafe fn()`.
///
/// The function passed will be called before static variables are initialized. Any access of static
/// variables will result in undefined behavior.
///
/// # Examples
///
/// ```
/// # use riscv_rt_macros::pre_init;
/// #[pre_init]
/// unsafe fn before_main() {
/// // do something here
/// }
///
/// # fn main() {}
/// ```
#[proc_macro_attribute]
pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream {
let f = parse_macro_input!(input as ItemFn);
// check the function signature
let valid_signature = f.constness.is_none()
&& f.vis == Visibility::Inherited
&& f.unsafety.is_some()
&& f.abi.is_none()
&& f.decl.inputs.is_empty()
&& f.decl.generics.params.is_empty()
&& f.decl.generics.where_clause.is_none()
&& f.decl.variadic.is_none()
&& match f.decl.output {
ReturnType::Default => true,
ReturnType::Type(_, ref ty) => match **ty {
Type::Tuple(ref tuple) => tuple.elems.is_empty(),
_ => false,
},
};
if !valid_signature {
return parse::Error::new(
f.span(),
"`#[pre_init]` function must have signature `unsafe fn()`",
)
.to_compile_error()
.into();
}
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
.to_compile_error()
.into();
}
// XXX should we blacklist other attributes?
let attrs = f.attrs;
let ident = f.ident;
let block = f.block;
quote!(
#[export_name = "__pre_init"]
#(#attrs)*
pub unsafe fn #ident() #block
)
.into()
}
// Creates a random identifier
fn random_ident() -> Ident {
let secs = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let count: u64 = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u64;
let mut seed: [u8; 16] = [0; 16];
for (i, v) in seed.iter_mut().take(8).enumerate() {
*v = ((secs >> (i * 8)) & 0xFF) as u8
}
for (i, v) in seed.iter_mut().skip(8).enumerate() {
*v = ((count >> (i * 8)) & 0xFF) as u8
}
let mut rng = rand::rngs::SmallRng::from_seed(seed);
Ident::new(
&(0..16)
.map(|i| {
if i == 0 || rng.gen() {
('a' as u8 + rng.gen::<u8>() % 25) as char
} else {
('0' as u8 + rng.gen::<u8>() % 10) as char
}
})
.collect::<String>(),
Span::call_site(),
)
}

351
xous-riscv-rt/src/lib.rs Normal file
View File

@ -0,0 +1,351 @@
//! Minimal startup / runtime for RISC-V CPU's
//!
//! # Minimum Supported Rust Version (MSRV)
//!
//! This crate is guaranteed to compile on stable Rust 1.31 and up. It *might*
//! compile with older versions but that may change in any new patch release.
//!
//! # Features
//!
//! This crate provides
//!
//! - Before main initialization of the `.bss` and `.data` sections.
//!
//! - `#[entry]` to declare the entry point of the program
//! - `#[pre_init]` to run code *before* `static` variables are initialized
//!
//! - 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). This file
//! must be supplied using rustflags and listed *before* `link.x`. Arbitrary
//! filename can be use instead of `memory.x`.
//!
//! - 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 = "0.6.1"
//! panic-halt = "0.2.0"
//!
//! $ # memory layout of the device
//! $ edit memory.x && cat $_
//! MEMORY
//! {
//! RAM : ORIGIN = 0x80000000, LENGTH = 16K
//! FLASH : ORIGIN = 0x20000000, LENGTH = 16M
//! }
//!
//! REGION_ALIAS("REGION_TEXT", FLASH);
//! REGION_ALIAS("REGION_RODATA", FLASH);
//! REGION_ALIAS("REGION_DATA", RAM);
//! REGION_ALIAS("REGION_BSS", RAM);
//! REGION_ALIAS("REGION_HEAP", RAM);
//! REGION_ALIAS("REGION_STACK", RAM);
//!
//! $ edit src/main.rs && cat $_
//! ```
//!
//! ``` ignore,no_run
//! #![no_std]
//! #![no_main]
//!
//! extern crate panic_halt;
//!
//! use riscv_rt::entry;
//!
//! // use `main` as the entry point of this application
//! // `main` is not allowed to return
//! #[entry]
//! fn main() -> ! {
//! // do something here
//! loop { }
//! }
//! ```
//!
//! ``` text
//! $ mkdir .cargo && edit .cargo/config && cat $_
//! [target.riscv32imac-unknown-none-elf]
//! rustflags = [
//! "-C", "link-arg=-Tmemory.x",
//! "-C", "link-arg=-Tlink.x",
//! ]
//!
//! [build]
//! target = "riscv32imac-unknown-none-elf"
//! $ edit build.rs && cat $_
//! ```
//!
//! ``` ignore,no_run
//! use std::env;
//! use std::fs::File;
//! use std::io::Write;
//! use std::path::Path;
//!
//! /// Put the linker script somewhere the linker can find it.
//! fn main() {
//! let out_dir = env::var("OUT_DIR").expect("No out dir");
//! let dest_path = Path::new(&out_dir);
//! let mut f = File::create(&dest_path.join("memory.x"))
//! .expect("Could not create file");
//!
//! f.write_all(include_bytes!("memory.x"))
//! .expect("Could not write file");
//!
//! println!("cargo:rustc-link-search={}", dest_path.display());
//!
//! println!("cargo:rerun-if-changed=memory.x");
//! println!("cargo:rerun-if-changed=build.rs");
//! }
//! ```
//!
//! ``` text
//! $ cargo build
//!
//! $ riscv32-unknown-elf-objdump -Cd $(find target -name app) | head
//!
//! Disassembly of section .text:
//!
//! 20000000 <_start>:
//! 20000000: 800011b7 lui gp,0x80001
//! 20000004: 80018193 addi gp,gp,-2048 # 80000800 <_stack_start+0xffffc800>
//! 20000008: 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 at least one memory region.
//!
//! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html
//!
//! To support different relocation models (RAM-only, FLASH+RAM) multiple regions are used:
//!
//! - `REGION_TEXT` - for `.init`, `.trap` and `.text` sections
//! - `REGION_RODATA` - for `.rodata` section and storing initial values for `.data` section
//! - `REGION_DATA` - for `.data` section
//! - `REGION_BSS` - for `.bss` section
//! - `REGION_HEAP` - for the heap area
//! - `REGION_STACK` - for hart stacks
//!
//! Specific aliases for these regions must be defined in `memory.x` file (see example below).
//!
//! ### `_stext`
//!
//! This symbol provides the loading address of `.text` section. This value can be changed
//! to override the loading address of the firmware (for example, in case of bootloader present).
//!
//! If omitted this symbol value will default to `ORIGIN(REGION_TEXT)`.
//!
//! ### `_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).
//!
//! In case of multiple harts present, this address defines the initial stack pointer for hart 0.
//! Stack pointer for hart `N` is calculated as `_stack_start - N * _hart_stack_size`.
//!
//! If omitted this symbol value will default to `ORIGIN(REGION_STACK) + LENGTH(REGION_STACK)`.
//!
//! #### Example
//!
//! Allocating the call stack on a different RAM region.
//!
//! ``` text
//! MEMORY
//! {
//! L2_LIM : ORIGIN = 0x08000000, LENGTH = 1M
//! RAM : ORIGIN = 0x80000000, LENGTH = 16K
//! FLASH : ORIGIN = 0x20000000, LENGTH = 16M
//! }
//!
//! REGION_ALIAS("REGION_TEXT", FLASH);
//! REGION_ALIAS("REGION_RODATA", FLASH);
//! REGION_ALIAS("REGION_DATA", RAM);
//! REGION_ALIAS("REGION_BSS", RAM);
//! REGION_ALIAS("REGION_HEAP", RAM);
//! REGION_ALIAS("REGION_STACK", L2_LIM);
//!
//! _stack_start = ORIGIN(L2_LIM) + LENGTH(L2_LIM);
//! ```
//!
//! ### `_max_hart_id`
//!
//! This symbol defines the maximum hart id suppoted. All harts with id
//! greater than `_max_hart_id` will be redirected to `abort()`.
//!
//! This symbol is supposed to be redefined in platform support crates for
//! multi-core targets.
//!
//! If omitted this symbol value will default to 0 (single core).
//!
//! ### `_hart_stack_size`
//!
//! This symbol defines stack area size for *one* hart.
//!
//! If omitted this symbol value will default to 2K.
//!
//! ### `_heap_size`
//!
//! This symbol provides the size of a heap region. The default value is 0. You can set `_heap_size`
//! to a non-zero value if you are planning to use heap allocations.
//!
//! ### `_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
//!
//! ``` no_run
//! extern crate some_allocator;
//!
//! extern "C" {
//! static _sheap: u8;
//! static _heap_size: u8;
//! }
//!
//! fn main() {
//! unsafe {
//! let heap_bottom = &_sheap as *const u8 as usize;
//! let heap_size = &_heap_size as *const u8 as usize;
//! some_allocator::initialize(heap_bottom, heap_size);
//! }
//! }
//! ```
//!
//! ### `_mp_hook`
//!
//! This function is called from all the harts and must return true only for one hart,
//! which will perform memory initialization. For other harts it must return false
//! and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt).
//!
//! This function can be redefined in the following way:
//!
//! ``` no_run
//! #[export_name = "_mp_hook"]
//! pub extern "Rust" fn mp_hook() -> bool {
//! // ...
//! }
//! ```
//!
//! Default implementation of this function wakes hart 0 and busy-loops all the other harts.
// NOTE: Adapted from cortex-m/src/lib.rs
#![no_std]
#![deny(missing_docs)]
#![deny(warnings)]
extern crate xous_riscv;
extern crate riscv_rt_macros as macros;
extern crate r0;
pub use macros::{entry, pre_init};
use xous_riscv::register::mstatus;
#[export_name = "error: riscv-rt appears more than once in the dependency graph"]
#[doc(hidden)]
pub static __ONCE__: () = ();
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;
}
/// 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 unsafe extern "C" fn start_rust() -> ! {
extern "Rust" {
// This symbol will be provided by the kernel
fn xous_main() -> !;
// This symbol will be provided by the user via `#[pre_init]`
fn __pre_init();
fn _mp_hook() -> bool;
}
if _mp_hook() {
__pre_init();
r0::zero_bss(&mut _sbss, &mut _ebss);
r0::init_data(&mut _sdata, &mut _edata, &_sidata);
}
xous_main();
}
/// 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() {
extern "C" {
fn trap_handler();
}
unsafe {
// dispatch trap to handler
trap_handler();
// mstatus, remain in M-mode after mret
mstatus::set_mpp(mstatus::MPP::Machine);
}
}
#[doc(hidden)]
#[no_mangle]
pub fn default_trap_handler() {}
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "Rust" fn default_pre_init() {}
#[doc(hidden)]
#[no_mangle]
pub extern "Rust" fn default_mp_hook() -> bool {
use xous_riscv::register::mhartid;
match mhartid::read() {
0 => true,
_ => loop {
unsafe { xous_riscv::asm::wfi() }
},
}
}

16
xous-riscv/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "xous-riscv"
version = "0.5.4"
repository = "https://github.com/rust-embedded/riscv"
authors = ["The RISC-V Team <risc-v@teams.rust-embedded.org>"]
categories = ["embedded", "hardware-support", "no-std"]
description = "Low level access to RISC-V processors"
keywords = ["riscv", "register", "peripheral"]
license = "ISC"
[dependencies]
bare-metal = ">=0.2.0,<0.2.5"
bit_field = "0.9.0"
[features]
inline-asm = []

317
xous-riscv/asm.S Normal file
View File

@ -0,0 +1,317 @@
#define REG_READ(name, offset) \
.section .text.__read_ ## name; \
.global __read_ ## name; \
__read_ ## name: \
csrrs a0, offset, x0; \
ret
#define REG_WRITE(name, offset) \
.section .text.__write_ ## name; \
.global __write_ ## name; \
__write_ ## name: \
csrrw x0, offset, a0; \
ret
#define REG_SET(name, offset) \
.section .text.__set_ ## name; \
.global __set_ ## name; \
__set_ ## name: \
csrrs x0, offset, a0; \
ret
#define REG_CLEAR(name, offset) \
.section .text.__clear_ ## name; \
.global __clear_ ## name; \
__clear_ ## name: \
csrrc x0, offset, a0; \
ret
#define REG_READ_WRITE(name, offset) REG_READ(name, offset); REG_WRITE(name, offset)
#define REG_SET_CLEAR(name, offset) REG_SET(name, offset); REG_CLEAR(name, offset)
#define RW(offset, name) REG_READ_WRITE(name, offset); REG_SET_CLEAR(name, offset)
#define RO(offset, name) REG_READ(name, offset)
#if __riscv_xlen == 32
#define RW32(offset, name) RW(offset, name)
#define RO32(offset, name) RO(offset, name)
#else
#define RW32(offset, name)
#define RO32(offset, name)
#endif
// ----------------------- //
.section .text.__ebreak
.global __ebreak
__ebreak:
ebreak
ret
.section .text.__wfi
.global __wfi
__wfi:
wfi
ret
.section .text.__sfence_vma_all
.global __sfence_vma_all
__sfence_vma_all:
sfence.vma
ret
.section .text.__sfence_vma
.global __sfence_vma
__sfence_vma:
sfence.vma a0, a1
ret
// User Trap Setup
RW(0x000, ustatus) // User status register
RW(0x004, uie) // User interrupt-enable register
RW(0x005, utvec) // User trap handler base address
// User Trap Handling
RW(0x040, uscratch) // Scratch register for user trap handlers
RW(0x041, uepc) // User exception program counter
RW(0x042, ucause) // User trap cause
RW(0x043, utval) // User bad address or instruction
RW(0x044, uip) // User interrupt pending
// User Floating-Point CSRs
RW(0x001, fflags) // Floating-Point Accrued Exceptions
RW(0x002, frm) // Floating-Point Dynamic Rounding Mode
RW(0x003, fcsr) // Floating-Point Control and Status Register (frm + fflags)
// User Counter/Timers
RO( 0xC00, cycle) // Cycle counter for RDCYCLE instruction
RO( 0xC01, time) // Timer for RDTIME instruction
RO( 0xC02, instret) // Instructions-retired counter for RDINSTRET instruction
RO( 0xC03, hpmcounter3) // Performance-monitoring counter
RO( 0xC04, hpmcounter4) // Performance-monitoring counter
RO( 0xC05, hpmcounter5) // Performance-monitoring counter
RO( 0xC06, hpmcounter6) // Performance-monitoring counter
RO( 0xC07, hpmcounter7) // Performance-monitoring counter
RO( 0xC08, hpmcounter8) // Performance-monitoring counter
RO( 0xC09, hpmcounter9) // Performance-monitoring counter
RO( 0xC0A, hpmcounter10) // Performance-monitoring counter
RO( 0xC0B, hpmcounter11) // Performance-monitoring counter
RO( 0xC0C, hpmcounter12) // Performance-monitoring counter
RO( 0xC0D, hpmcounter13) // Performance-monitoring counter
RO( 0xC0E, hpmcounter14) // Performance-monitoring counter
RO( 0xC0F, hpmcounter15) // Performance-monitoring counter
RO( 0xC10, hpmcounter16) // Performance-monitoring counter
RO( 0xC11, hpmcounter17) // Performance-monitoring counter
RO( 0xC12, hpmcounter18) // Performance-monitoring counter
RO( 0xC13, hpmcounter19) // Performance-monitoring counter
RO( 0xC14, hpmcounter20) // Performance-monitoring counter
RO( 0xC15, hpmcounter21) // Performance-monitoring counter
RO( 0xC16, hpmcounter22) // Performance-monitoring counter
RO( 0xC17, hpmcounter23) // Performance-monitoring counter
RO( 0xC18, hpmcounter24) // Performance-monitoring counter
RO( 0xC19, hpmcounter25) // Performance-monitoring counter
RO( 0xC1A, hpmcounter26) // Performance-monitoring counter
RO( 0xC1B, hpmcounter27) // Performance-monitoring counter
RO( 0xC1C, hpmcounter28) // Performance-monitoring counter
RO( 0xC1D, hpmcounter29) // Performance-monitoring counter
RO( 0xC1E, hpmcounter30) // Performance-monitoring counter
RO( 0xC1F, hpmcounter31) // Performance-monitoring counter
RO32(0xC80, cycleh) // Upper 32 bits of cycle, RV32I only
RO32(0xC81, timeh) // Upper 32 bits of time, RV32I only
RO32(0xC82, instreth) // Upper 32 bits of instret, RV32I only
RO32(0xC83, hpmcounter3h) // Upper 32 bits of hpmcounter3, RV32I only
RO32(0xC84, hpmcounter4h)
RO32(0xC85, hpmcounter5h)
RO32(0xC86, hpmcounter6h)
RO32(0xC87, hpmcounter7h)
RO32(0xC88, hpmcounter8h)
RO32(0xC89, hpmcounter9h)
RO32(0xC8A, hpmcounter10h)
RO32(0xC8B, hpmcounter11h)
RO32(0xC8C, hpmcounter12h)
RO32(0xC8D, hpmcounter13h)
RO32(0xC8E, hpmcounter14h)
RO32(0xC8F, hpmcounter15h)
RO32(0xC90, hpmcounter16h)
RO32(0xC91, hpmcounter17h)
RO32(0xC92, hpmcounter18h)
RO32(0xC93, hpmcounter19h)
RO32(0xC94, hpmcounter20h)
RO32(0xC95, hpmcounter21h)
RO32(0xC96, hpmcounter22h)
RO32(0xC97, hpmcounter23h)
RO32(0xC98, hpmcounter24h)
RO32(0xC99, hpmcounter25h)
RO32(0xC9A, hpmcounter26h)
RO32(0xC9B, hpmcounter27h)
RO32(0xC9C, hpmcounter28h)
RO32(0xC9D, hpmcou