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, hpmcounter29h)
RO32(0xC9E, hpmcounter30h)
RO32(0xC9F, hpmcounter31h)
// Supervisor Trap Setup
RW(0x100, sstatus) // Supervisor status register
RW(0x102, sedeleg) // Supervisor exception delegation register
RW(0x103, sideleg) // Supervisor interrupt delegation register
RW(0x104, sie) // Supervisor interrupt-enable register
RW(0x105, stvec) // Supervisor trap handler base address
RW(0x106, scounteren) // Supervisor counter enable
// Supervisor Trap Handling
RW(0x140, sscratch) // Scratch register for supervisor trap handlers
RW(0x141, sepc) // Supervisor exception program counter
RW(0x142, scause) // Supervisor trap cause
RW(0x143, stval) // Supervisor bad address or instruction
RW(0x144, sip) // Supervisor interrupt pending
// Supervisor Protection and Translation
RW(0x180, satp) // Supervisor address translation and protection
// Machine Information Registers
RO(0xF11, mvendorid) // Vendor ID
RO(0xF12, marchid) // Architecture ID
RO(0xF13, mimpid) // Implementation ID
RO(0xF14, mhartid) // Hardware thread ID
// Machine Trap Setup
RW(0x300, mstatus) // Machine status register
RW(0x301, misa) // ISA and extensions
RW(0x302, medeleg) // Machine exception delegation register
RW(0x303, mideleg) // Machine interrupt delegation register
RW(0x304, mie) // Machine interrupt-enable register
RW(0x305, mtvec) // Machine trap handler base address
RW(0x306, mcounteren) // Machine counter enable
// Machine Trap Handling
RW(0x340, mscratch) // Scratch register for machine trap handlers
RW(0x341, mepc) // Machine exception program counter
RW(0x342, mcause) // Machine trap cause
RW(0x343, mtval) // Machine bad address or instruction
RW(0x344, mip) // Machine interrupt pending
// Machine Protection and Translation
RW( 0x3A0, pmpcfg0) // Physical memory protection configuration
RW32(0x3A1, pmpcfg1) // Physical memory protection configuration, RV32 only
RW( 0x3A2, pmpcfg2) // Physical memory protection configuration
RW32(0x3A3, pmpcfg3) // Physical memory protection configuration, RV32 only
RW( 0x3B0, pmpaddr0) // Physical memory protection address register
RW( 0x3B1, pmpaddr1) // Physical memory protection address register
RW( 0x3B2, pmpaddr2) // Physical memory protection address register
RW( 0x3B3, pmpaddr3) // Physical memory protection address register
RW( 0x3B4, pmpaddr4) // Physical memory protection address register
RW( 0x3B5, pmpaddr5) // Physical memory protection address register
RW( 0x3B6, pmpaddr6) // Physical memory protection address register
RW( 0x3B7, pmpaddr7) // Physical memory protection address register
RW( 0x3B8, pmpaddr8) // Physical memory protection address register
RW( 0x3B9, pmpaddr9) // Physical memory protection address register
RW( 0x3BA, pmpaddr10) // Physical memory protection address register
RW( 0x3BB, pmpaddr11) // Physical memory protection address register
RW( 0x3BC, pmpaddr12) // Physical memory protection address register
RW( 0x3BD, pmpaddr13) // Physical memory protection address register
RW( 0x3BE, pmpaddr14) // Physical memory protection address register
RW( 0x3BF, pmpaddr15) // Physical memory protection address register
// Machine Counter/Timers
RO( 0xB00, mcycle) // Machine cycle counter
RO( 0xB02, minstret) // Machine instructions-retired counter
RO( 0xB03, mhpmcounter3) // Machine performance-monitoring counter
RO( 0xB04, mhpmcounter4) // Machine performance-monitoring counter
RO( 0xB05, mhpmcounter5) // Machine performance-monitoring counter
RO( 0xB06, mhpmcounter6) // Machine performance-monitoring counter
RO( 0xB07, mhpmcounter7) // Machine performance-monitoring counter
RO( 0xB08, mhpmcounter8) // Machine performance-monitoring counter
RO( 0xB09, mhpmcounter9) // Machine performance-monitoring counter
RO( 0xB0A, mhpmcounter10) // Machine performance-monitoring counter
RO( 0xB0B, mhpmcounter11) // Machine performance-monitoring counter
RO( 0xB0C, mhpmcounter12) // Machine performance-monitoring counter
RO( 0xB0D, mhpmcounter13) // Machine performance-monitoring counter
RO( 0xB0E, mhpmcounter14) // Machine performance-monitoring counter
RO( 0xB0F, mhpmcounter15) // Machine performance-monitoring counter
RO( 0xB10, mhpmcounter16) // Machine performance-monitoring counter
RO( 0xB11, mhpmcounter17) // Machine performance-monitoring counter
RO( 0xB12, mhpmcounter18) // Machine performance-monitoring counter
RO( 0xB13, mhpmcounter19) // Machine performance-monitoring counter
RO( 0xB14, mhpmcounter20) // Machine performance-monitoring counter
RO( 0xB15, mhpmcounter21) // Machine performance-monitoring counter
RO( 0xB16, mhpmcounter22) // Machine performance-monitoring counter
RO( 0xB17, mhpmcounter23) // Machine performance-monitoring counter
RO( 0xB18, mhpmcounter24) // Machine performance-monitoring counter
RO( 0xB19, mhpmcounter25) // Machine performance-monitoring counter
RO( 0xB1A, mhpmcounter26) // Machine performance-monitoring counter
RO( 0xB1B, mhpmcounter27) // Machine performance-monitoring counter
RO( 0xB1C, mhpmcounter28) // Machine performance-monitoring counter
RO( 0xB1D, mhpmcounter29) // Machine performance-monitoring counter
RO( 0xB1E, mhpmcounter30) // Machine performance-monitoring counter
RO( 0xB1F, mhpmcounter31) // Machine performance-monitoring counter
RO32(0xB80, mcycleh) // Upper 32 bits of mcycle, RV32I only
RO32(0xB82, minstreth) // Upper 32 bits of minstret, RV32I only
RO32(0xB83, mhpmcounter3h) // Upper 32 bits of mhpmcounter3, RV32I only
RO32(0xB84, mhpmcounter4h)
RO32(0xB85, mhpmcounter5h)
RO32(0xB86, mhpmcounter6h)
RO32(0xB87, mhpmcounter7h)
RO32(0xB88, mhpmcounter8h)
RO32(0xB89, mhpmcounter9h)
RO32(0xB8A, mhpmcounter10h)
RO32(0xB8B, mhpmcounter11h)
RO32(0xB8C, mhpmcounter12h)
RO32(0xB8D, mhpmcounter13h)
RO32(0xB8E, mhpmcounter14h)
RO32(0xB8F, mhpmcounter15h)
RO32(0xB90, mhpmcounter16h)
RO32(0xB91, mhpmcounter17h)
RO32(0xB92, mhpmcounter18h)
RO32(0xB93, mhpmcounter19h)
RO32(0xB94, mhpmcounter20h)
RO32(0xB95, mhpmcounter21h)
RO32(0xB96, mhpmcounter22h)
RO32(0xB97, mhpmcounter23h)
RO32(0xB98, mhpmcounter24h)
RO32(0xB99, mhpmcounter25h)
RO32(0xB9A, mhpmcounter26h)
RO32(0xB9B, mhpmcounter27h)
RO32(0xB9C, mhpmcounter28h)
RO32(0xB9D, mhpmcounter29h)
RO32(0xB9E, mhpmcounter30h)
RO32(0xB9F, mhpmcounter31h)
RW(0x323, mhpmevent3) // Machine performance-monitoring event selector
RW(0x324, mhpmevent4) // Machine performance-monitoring event selector
RW(0x325, mhpmevent5) // Machine performance-monitoring event selector
RW(0x326, mhpmevent6) // Machine performance-monitoring event selector
RW(0x327, mhpmevent7) // Machine performance-monitoring event selector
RW(0x328, mhpmevent8) // Machine performance-monitoring event selector
RW(0x329, mhpmevent9) // Machine performance-monitoring event selector
RW(0x32A, mhpmevent10) // Machine performance-monitoring event selector
RW(0x32B, mhpmevent11) // Machine performance-monitoring event selector
RW(0x32C, mhpmevent12) // Machine performance-monitoring event selector
RW(0x32D, mhpmevent13) // Machine performance-monitoring event selector
RW(0x32E, mhpmevent14) // Machine performance-monitoring event selector
RW(0x32F, mhpmevent15) // Machine performance-monitoring event selector
RW(0x330, mhpmevent16) // Machine performance-monitoring event selector
RW(0x331, mhpmevent17) // Machine performance-monitoring event selector
RW(0x332, mhpmevent18) // Machine performance-monitoring event selector
RW(0x333, mhpmevent19) // Machine performance-monitoring event selector
RW(0x334, mhpmevent20) // Machine performance-monitoring event selector
RW(0x335, mhpmevent21) // Machine performance-monitoring event selector
RW(0x336, mhpmevent22) // Machine performance-monitoring event selector
RW(0x337, mhpmevent23) // Machine performance-monitoring event selector
RW(0x338, mhpmevent24) // Machine performance-monitoring event selector
RW(0x339, mhpmevent25) // Machine performance-monitoring event selector
RW(0x33A, mhpmevent26) // Machine performance-monitoring event selector
RW(0x33B, mhpmevent27) // Machine performance-monitoring event selector
RW(0x33C, mhpmevent28) // Machine performance-monitoring event selector
RW(0x33D, mhpmevent29) // Machine performance-monitoring event selector
RW(0x33E, mhpmevent30) // Machine performance-monitoring event selector
RW(0x33F, mhpmevent31) // Machine performance-monitoring event selector
// Debug/Trace Registers (shared with Debug Mode)
RW(0x7A0, tselect) // Debug/Trace trigger register select
RW(0x7A1, tdata1) // First Debug/Trace trigger data register
RW(0x7A2, tdata2) // Second Debug/Trace trigger data register
RW(0x7A3, tdata3) // Third Debug/Trace trigger data register
// Debug Mode Registers
RW(0x7B0, dcsr) // Debug control and status register
RW(0x7B1, dpc) // Debug PC
RW(0x7B2, dscratch) // Debug scratch register

19
xous-riscv/assemble.ps1 Normal file
View File

@ -0,0 +1,19 @@
New-Item -Force -Name bin -Type Directory
# remove existing blobs because otherwise this will append object files to the old blobs
Remove-Item -Force bin/*.a
$crate = "xous-riscv"
riscv64-unknown-elf-gcc -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 -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 -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/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.

26
xous-riscv/build.rs Normal file
View File

@ -0,0 +1,26 @@
use std::path::PathBuf;
use std::{env, fs};
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") && env::var_os("CARGO_FEATURE_INLINE_ASM").is_none() {
fs::copy(
format!("bin/{}.a", target),
out_dir.join(format!("lib{}.a", name)),
).unwrap();
println!("cargo:rustc-link-lib=static={}", name);
println!("cargo:rustc-link-search={}", out_dir.display());
}
if target.starts_with("riscv32") {
println!("cargo:rustc-cfg=riscv");
println!("cargo:rustc-cfg=riscv32");
} else if target.starts_with("riscv64") {
println!("cargo:rustc-cfg=riscv");
println!("cargo:rustc-cfg=riscv64");
}
}

77
xous-riscv/src/asm.rs Normal file
View File

@ -0,0 +1,77 @@
//! Assembly instructions
macro_rules! instruction {
($(#[$attr:meta])*, $fnname:ident, $asm:expr, $asm_fn:ident) => (
$(#[$attr])*
#[inline]
pub unsafe fn $fnname() {
match () {
#[cfg(all(riscv, feature = "inline-asm"))]
() => asm!($asm :::: "volatile"),
#[cfg(all(riscv, not(feature = "inline-asm")))]
() => {
extern "C" {
fn $asm_fn();
}
$asm_fn();
}
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
)
}
instruction!(
/// `EBREAK` instruction wrapper
///
/// Generates a breakpoint exception.
, ebreak, "ebreak", __ebreak);
instruction!(
/// `WFI` instruction wrapper
///
/// Provides a hint to the implementation that the current hart can be stalled until an interrupt might need servicing.
/// The WFI instruction is just a hint, and a legal implementation is to implement WFI as a NOP.
, wfi, "wfi", __wfi);
instruction!(
/// `SFENCE.VMA` instruction wrapper (all address spaces and page table levels)
///
/// Synchronizes updates to in-memory memory-management data structures with current execution.
/// Instruction execution causes implicit reads and writes to these data structures; however, these implicit references
/// are ordinarily not ordered with respect to loads and stores in the instruction stream.
/// Executing an `SFENCE.VMA` instruction guarantees that any stores in the instruction stream prior to the
/// `SFENCE.VMA` are ordered before all implicit references subsequent to the `SFENCE.VMA`.
, sfence_vma_all, "sfence.vma", __sfence_vma_all);
/// `SFENCE.VMA` instruction wrapper
///
/// Synchronizes updates to in-memory memory-management data structures with current execution.
/// Instruction execution causes implicit reads and writes to these data structures; however, these implicit references
/// are ordinarily not ordered with respect to loads and stores in the instruction stream.
/// Executing an `SFENCE.VMA` instruction guarantees that any stores in the instruction stream prior to the
/// `SFENCE.VMA` are ordered before all implicit references subsequent to the `SFENCE.VMA`.
#[inline]
#[allow(unused_variables)]
pub unsafe fn sfence_vma(asid: usize, addr: usize) {
match () {
#[cfg(all(riscv, feature = "inline-asm"))]
() => asm!("sfence.vma $0, $1" :: "r"(asid), "r"(addr) :: "volatile"),
#[cfg(all(riscv, not(feature = "inline-asm")))]
() => {
extern "C" {
fn __sfence_vma(asid: usize, addr: usize);
}
__sfence_vma(asid, addr);
}
#[cfg(not(riscv))]
() => unimplemented!(),
}
}

View File

@ -0,0 +1,54 @@
//! Interrupts
// NOTE: Adapted from cortex-m/src/interrupt.rs
pub use bare_metal::{CriticalSection, Mutex, Nr};
use register::mstatus;
/// Disables all interrupts
#[inline]
pub unsafe fn disable() {
match () {
#[cfg(riscv)]
() => mstatus::clear_mie(),
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
/// Enables all the interrupts
///
/// # Safety
///
/// - Do not call this function inside an `interrupt::free` critical section
#[inline]
pub unsafe fn enable() {
match () {
#[cfg(riscv)]
() => mstatus::set_mie(),
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
/// Execute closure `f` in an interrupt-free context.
///
/// This as also known as a "critical section".
pub fn free<F, R>(f: F) -> R
where
F: FnOnce(&CriticalSection) -> R,
{
let mstatus = mstatus::read();
// disable interrupts
unsafe { disable(); }
let r = f(unsafe { &CriticalSection::new() });
// If the interrupts were active before our `disable` call, then re-enable
// them. Otherwise, keep them disabled
if mstatus.mie() {
unsafe { enable(); }
}
r
}

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

@ -0,0 +1,27 @@
//! Low level access to RISC-V processors
//!
//! # Minimum Supported Rust Version (MSRV)
//!
//! This crate is guaranteed to compile on stable Rust 1.30 and up. It *might*
//! compile with older versions but that may change in any new patch release.
//! Note that `riscv64imac-unknown-none-elf` and `riscv64gc-unknown-none-elf` targets
//! are not supported on stable yet.
//!
//! # Features
//!
//! This crate provides:
//!
//! - Access to core registers like `mstatus` or `mcause`.
//! - Interrupt manipulation mechanisms.
//! - Wrappers around assembly instructions like `WFI`.
#![no_std]
#![deny(warnings)]
#![cfg_attr(feature = "inline-asm", feature(asm))]
extern crate bare_metal;
extern crate bit_field;
pub mod asm;
pub mod interrupt;
pub mod register;

View File

@ -0,0 +1,132 @@
//! Floating-point control and status register
use bit_field::BitField;
/// Floating-point control and status register
#[derive(Clone, Copy, Debug)]
pub struct FCSR {
bits: u32,
}
/// Accrued Exception Flags
#[derive(Clone, Copy, Debug)]
pub struct Flags(u32);
/// Accrued Exception Flag
#[derive(Clone, Copy, Debug)]
pub enum Flag {
/// Inexact
NX = 0b00001,
/// Underflow
UF = 0b00010,
/// Overflow
OF = 0b00100,
/// Divide by Zero
DZ = 0b01000,
/// Invalid Operation
NV = 0b10000,
}
impl Flags {
/// Inexact
#[inline]
pub fn nx(&self) -> bool {
self.0.get_bit(0)
}
/// Underflow
#[inline]
pub fn uf(&self) -> bool {
self.0.get_bit(1)
}
/// Overflow
#[inline]
pub fn of(&self) -> bool {
self.0.get_bit(2)
}
/// Divide by Zero
#[inline]
pub fn dz(&self) -> bool {
self.0.get_bit(3)
}
/// Invalid Operation
#[inline]
pub fn nv(&self) -> bool {
self.0.get_bit(4)
}
}
/// Rounding Mode
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RoundingMode {
RoundToNearestEven = 0b000,
RoundTowardsZero = 0b001,
RoundDown = 0b010,
RoundUp = 0b011,
RoundToNearestMaxMagnitude = 0b100,
Invalid = 0b111,
}
impl FCSR {
/// Returns the contents of the register as raw bits
pub fn bits(&self) -> u32 {
self.bits
}
/// Accrued Exception Flags
#[inline]
pub fn fflags(&self) -> Flags {
Flags(self.bits.get_bits(0..5))
}
/// Rounding Mode
#[inline]
pub fn frm(&self) -> RoundingMode {
match self.bits.get_bits(5..8) {
0b000 => RoundingMode::RoundToNearestEven,
0b001 => RoundingMode::RoundTowardsZero,
0b010 => RoundingMode::RoundDown,
0b011 => RoundingMode::RoundUp,
0b100 => RoundingMode::RoundToNearestMaxMagnitude,
_ => RoundingMode::Invalid,
}
}
}
read_csr!(0x003, __read_fcsr);
write_csr!(0x003, __write_fcsr);
clear!(0x003, __clear_fcsr);
/// Reads the CSR
#[inline]
pub fn read() -> FCSR {
FCSR { bits: unsafe{ _read() as u32 } }
}
/// Writes the CSR
#[inline]
pub unsafe fn set_rounding_mode(frm: RoundingMode) {
let old = read();
let bits = ((frm as u32) << 5) | old.fflags().0;
_write(bits as usize);
}
/// Resets `fflags` field bits
#[inline]
pub unsafe fn clear_flags() {
let mask = 0b11111;
_clear(mask);
}
/// Resets `fflags` field bit
#[inline]
pub unsafe fn clear_flag(flag: Flag) {
_clear(flag as usize);
}

View File

@ -0,0 +1,82 @@
macro_rules! reg {
(
$addr:expr, $csrl:ident, $csrh:ident, $readf:ident, $writef:ident
) => {
/// Performance-monitoring counter
pub mod $csrl {
read_csr_as_usize!($addr, $readf);
read_composite_csr!(super::$csrh::read(), read());
}
}
}
macro_rules! regh {
(
$addr:expr, $csrh:ident, $readf:ident, $writef:ident
) => {
/// Upper 32 bits of performance-monitoring counter (RV32I only)
pub mod $csrh {
read_csr_as_usize_rv32!($addr, $readf);
}
}
}
reg!(0xC03, hpmcounter3, hpmcounter3h, __read_hpmcounter3, __write_hpmcounter3);
reg!(0xC04, hpmcounter4, hpmcounter4h, __read_hpmcounter4, __write_hpmcounter4);
reg!(0xC05, hpmcounter5, hpmcounter5h, __read_hpmcounter5, __write_hpmcounter5);
reg!(0xC06, hpmcounter6, hpmcounter6h, __read_hpmcounter6, __write_hpmcounter6);
reg!(0xC07, hpmcounter7, hpmcounter7h, __read_hpmcounter7, __write_hpmcounter7);
reg!(0xC08, hpmcounter8, hpmcounter8h, __read_hpmcounter8, __write_hpmcounter8);
reg!(0xC09, hpmcounter9, hpmcounter9h, __read_hpmcounter9, __write_hpmcounter9);
reg!(0xC0A, hpmcounter10, hpmcounter10h, __read_hpmcounter10, __write_hpmcounter10);
reg!(0xC0B, hpmcounter11, hpmcounter11h, __read_hpmcounter11, __write_hpmcounter11);
reg!(0xC0C, hpmcounter12, hpmcounter12h, __read_hpmcounter12, __write_hpmcounter12);
reg!(0xC0D, hpmcounter13, hpmcounter13h, __read_hpmcounter13, __write_hpmcounter13);
reg!(0xC0E, hpmcounter14, hpmcounter14h, __read_hpmcounter14, __write_hpmcounter14);
reg!(0xC0F, hpmcounter15, hpmcounter15h, __read_hpmcounter15, __write_hpmcounter15);
reg!(0xC10, hpmcounter16, hpmcounter16h, __read_hpmcounter16, __write_hpmcounter16);
reg!(0xC11, hpmcounter17, hpmcounter17h, __read_hpmcounter17, __write_hpmcounter17);
reg!(0xC12, hpmcounter18, hpmcounter18h, __read_hpmcounter18, __write_hpmcounter18);
reg!(0xC13, hpmcounter19, hpmcounter19h, __read_hpmcounter19, __write_hpmcounter19);
reg!(0xC14, hpmcounter20, hpmcounter20h, __read_hpmcounter20, __write_hpmcounter20);
reg!(0xC15, hpmcounter21, hpmcounter21h, __read_hpmcounter21, __write_hpmcounter21);
reg!(0xC16, hpmcounter22, hpmcounter22h, __read_hpmcounter22, __write_hpmcounter22);
reg!(0xC17, hpmcounter23, hpmcounter23h, __read_hpmcounter23, __write_hpmcounter23);
reg!(0xC18, hpmcounter24, hpmcounter24h, __read_hpmcounter24, __write_hpmcounter24);
reg!(0xC19, hpmcounter25, hpmcounter25h, __read_hpmcounter25, __write_hpmcounter25);
reg!(0xC1A, hpmcounter26, hpmcounter26h, __read_hpmcounter26, __write_hpmcounter26);
reg!(0xC1B, hpmcounter27, hpmcounter27h, __read_hpmcounter27, __write_hpmcounter27);
reg!(0xC1C, hpmcounter28, hpmcounter28h, __read_hpmcounter28, __write_hpmcounter28);
reg!(0xC1D, hpmcounter29, hpmcounter29h, __read_hpmcounter29, __write_hpmcounter29);
reg!(0xC1E, hpmcounter30, hpmcounter30h, __read_hpmcounter30, __write_hpmcounter30);
reg!(0xC1F, hpmcounter31, hpmcounter31h, __read_hpmcounter31, __write_hpmcounter31);
regh!(0xC83, hpmcounter3h, __read_hpmcounter3h, __write_hpmcounter3h);
regh!(0xC84, hpmcounter4h, __read_hpmcounter4h, __write_hpmcounter4h);
regh!(0xC85, hpmcounter5h, __read_hpmcounter5h, __write_hpmcounter5h);
regh!(0xC86, hpmcounter6h, __read_hpmcounter6h, __write_hpmcounter6h);
regh!(0xC87, hpmcounter7h, __read_hpmcounter7h, __write_hpmcounter7h);
regh!(0xC88, hpmcounter8h, __read_hpmcounter8h, __write_hpmcounter8h);
regh!(0xC89, hpmcounter9h, __read_hpmcounter9h, __write_hpmcounter9h);
regh!(0xC8A, hpmcounter10h, __read_hpmcounter10h, __write_hpmcounter10h);
regh!(0xC8B, hpmcounter11h, __read_hpmcounter11h, __write_hpmcounter11h);
regh!(0xC8C, hpmcounter12h, __read_hpmcounter12h, __write_hpmcounter12h);
regh!(0xC8D, hpmcounter13h, __read_hpmcounter13h, __write_hpmcounter13h);
regh!(0xC8E, hpmcounter14h, __read_hpmcounter14h, __write_hpmcounter14h);
regh!(0xC8F, hpmcounter15h, __read_hpmcounter15h, __write_hpmcounter15h);
regh!(0xC90, hpmcounter16h, __read_hpmcounter16h, __write_hpmcounter16h);
regh!(0xC91, hpmcounter17h, __read_hpmcounter17h, __write_hpmcounter17h);
regh!(0xC92, hpmcounter18h, __read_hpmcounter18h, __write_hpmcounter18h);
regh!(0xC93, hpmcounter19h, __read_hpmcounter19h, __write_hpmcounter19h);
regh!(0xC94, hpmcounter20h, __read_hpmcounter20h, __write_hpmcounter20h);
regh!(0xC95, hpmcounter21h, __read_hpmcounter21h, __write_hpmcounter21h);
regh!(0xC96, hpmcounter22h, __read_hpmcounter22h, __write_hpmcounter22h);
regh!(0xC97, hpmcounter23h, __read_hpmcounter23h, __write_hpmcounter23h);
regh!(0xC98, hpmcounter24h, __read_hpmcounter24h, __write_hpmcounter24h);
regh!(0xC99, hpmcounter25h, __read_hpmcounter25h, __write_hpmcounter25h);
regh!(0xC9A, hpmcounter26h, __read_hpmcounter26h, __write_hpmcounter26h);
regh!(0xC9B, hpmcounter27h, __read_hpmcounter27h, __write_hpmcounter27h);
regh!(0xC9C, hpmcounter28h, __read_hpmcounter28h, __write_hpmcounter28h);
regh!(0xC9D, hpmcounter29h, __read_hpmcounter29h, __write_hpmcounter29h);
regh!(0xC9E, hpmcounter30h, __read_hpmcounter30h, __write_hpmcounter30h);
regh!(0xC9F, hpmcounter31h, __read_hpmcounter31h, __write_hpmcounter31h);

View File

@ -0,0 +1,270 @@
macro_rules! read_csr {
($csr_number:expr, $asm_fn: ident) => {
/// Reads the CSR
#[inline]
unsafe fn _read() -> usize {
match () {
#[cfg(all(riscv, feature = "inline-asm"))]
() => {
let r: usize;
asm!("csrrs $0, $1, x0" : "=r"(r) : "i"($csr_number) :: "volatile");
r
}
#[cfg(all(riscv, not(feature = "inline-asm")))]
() => {
extern "C" {
fn $asm_fn() -> usize;
}
$asm_fn()
}
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
};
}
macro_rules! read_csr_rv32 {
($csr_number:expr, $asm_fn: ident) => {
/// Reads the CSR
#[inline]
unsafe fn _read() -> usize {
match () {
#[cfg(all(riscv32, feature = "inline-asm"))]
() => {
let r: usize;
asm!("csrrs $0, $1, x0" : "=r"(r) : "i"($csr_number) :: "volatile");
r
}
#[cfg(all(riscv32, not(feature = "inline-asm")))]
() => {
extern "C" {
fn $asm_fn() -> usize;
}
$asm_fn()
}
#[cfg(not(riscv32))]
() => unimplemented!(),
}
}
};
}
macro_rules! read_csr_as {
($register:ident, $csr_number:expr, $asm_fn: ident) => {
read_csr!($csr_number, $asm_fn);
/// Reads the CSR
#[inline]
pub fn read() -> $register {
$register { bits: unsafe{ _read() } }
}
};
}
macro_rules! read_csr_as_usize {
($csr_number:expr, $asm_fn: ident) => {
read_csr!($csr_number, $asm_fn);
/// Reads the CSR
#[inline]
pub fn read() -> usize {
unsafe{ _read() }
}
};
}
macro_rules! read_csr_as_usize_rv32 {
($csr_number:expr, $asm_fn: ident) => {
read_csr_rv32!($csr_number, $asm_fn);
/// Reads the CSR
#[inline]
pub fn read() -> usize {
unsafe{ _read() }
}
};
}
macro_rules! write_csr {
($csr_number:expr, $asm_fn: ident) => {
/// Writes the CSR
#[inline]
#[allow(unused_variables)]
unsafe fn _write(bits: usize) {
match () {
#[cfg(all(riscv, feature = "inline-asm"))]
() => asm!("csrrw x0, $1, $0" :: "r"(bits), "i"($csr_number) :: "volatile"),
#[cfg(all(riscv, not(feature = "inline-asm")))]
() => {
extern "C" {
fn $asm_fn(bits: usize);
}
$asm_fn(bits);
}
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
};
}
macro_rules! write_csr_rv32 {
($csr_number:expr, $asm_fn: ident) => {
/// Writes the CSR
#[inline]
#[allow(unused_variables)]
unsafe fn _write(bits: usize) {
match () {
#[cfg(all(riscv32, feature = "inline-asm"))]
() => asm!("csrrw x0, $1, $0" :: "r"(bits), "i"($csr_number) :: "volatile"),
#[cfg(all(riscv32, not(feature = "inline-asm")))]
() => {
extern "C" {
fn $asm_fn(bits: usize);
}
$asm_fn(bits);
}
#[cfg(not(riscv32))]
() => unimplemented!(),
}
}
};
}
macro_rules! write_csr_as_usize {
($csr_number:expr, $asm_fn: ident) => {
write_csr!($csr_number, $asm_fn);
/// Writes the CSR
#[inline]
pub fn write(bits: usize) {
unsafe{ _write(bits) }
}
};
}
macro_rules! write_csr_as_usize_rv32 {
($csr_number:expr, $asm_fn: ident) => {
write_csr_rv32!($csr_number, $asm_fn);
/// Writes the CSR
#[inline]
pub fn write(bits: usize) {
unsafe{ _write(bits) }
}
};
}
macro_rules! set {
($csr_number:expr, $asm_fn: ident) => {
/// Set the CSR
#[inline]
#[allow(unused_variables)]
unsafe fn _set(bits: usize) {
match () {
#[cfg(all(riscv, feature = "inline-asm"))]
() => asm!("csrrs x0, $1, $0" :: "r"(bits), "i"($csr_number) :: "volatile"),
#[cfg(all(riscv, not(feature = "inline-asm")))]
() => {
extern "C" {
fn $asm_fn(bits: usize);
}
$asm_fn(bits);
}
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
};
}
macro_rules! clear {
($csr_number:expr, $asm_fn: ident) => {
/// Clear the CSR
#[inline]
#[allow(unused_variables)]
unsafe fn _clear(bits: usize) {
match () {
#[cfg(all(riscv, feature = "inline-asm"))]
() => asm!("csrrc x0, $1, $0" :: "r"(bits), "i"($csr_number) :: "volatile"),
#[cfg(all(riscv, not(feature = "inline-asm")))]
() => {
extern "C" {
fn $asm_fn(bits: usize);
}
$asm_fn(bits);
}
#[cfg(not(riscv))]
() => unimplemented!(),
}
}
};
}
macro_rules! set_csr {
($(#[$attr:meta])*, $set_field:ident, $e:expr) => {
$(#[$attr])*
#[inline]
pub unsafe fn $set_field() {
_set($e);
}
}
}
macro_rules! clear_csr {
($(#[$attr:meta])*, $clear_field:ident, $e:expr) => {
$(#[$attr])*
#[inline]
pub unsafe fn $clear_field() {
_clear($e);
}
}
}
macro_rules! set_clear_csr {
($(#[$attr:meta])*, $set_field:ident, $clear_field:ident, $e:expr) => {
set_csr!($(#[$attr])*, $set_field, $e);
clear_csr!($(#[$attr])*, $clear_field, $e);
}
}
macro_rules! read_composite_csr {
($hi:expr, $lo:expr) => {
/// Reads the CSR as a 64-bit value
#[inline]
pub fn read64() -> u64 {
match () {
#[cfg(riscv32)]
() => loop {
let hi = $hi;
let lo = $lo;
if hi == $hi {
return ((hi as u64) << 32) | lo as u64;
}
},
#[cfg(not(riscv32))]
() => $lo as u64,
}
}
}
}

View File

@ -0,0 +1,27 @@
//! marchid register
use core::num::NonZeroUsize;
/// marchid register
#[derive(Clone, Copy, Debug)]
pub struct Marchid {
bits: NonZeroUsize,
}
impl Marchid {
/// Returns the contents of the register as raw bits
pub fn bits(&self) -> usize {
self.bits.get()
}
}
read_csr!(0xF11, __read_marchid);
/// Reads the CSR
#[inline]
pub fn read() -> Option<Marchid> {
let r = unsafe{ _read() };
// When marchid is hardwired to zero it means that the marchid
// csr isn't implemented.
NonZeroUsize::new(r).map(|bits| Marchid { bits })
}

View File

@ -0,0 +1,139 @@
//! mcause register
/// mcause register
#[derive(Clone, Copy, Debug)]
pub struct Mcause {
bits: usize,
}
/// Trap Cause
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Trap {
Interrupt(Interrupt),
Exception(Exception),
}
/// Interrupt
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Interrupt {
UserSoft,
SupervisorSoft,
MachineSoft,
UserTimer,
SupervisorTimer,
MachineTimer,
UserExternal,
SupervisorExternal,
MachineExternal,
Unknown,
}
/// Exception
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Exception {
InstructionMisaligned,
InstructionFault,
IllegalInstruction,
Breakpoint,
LoadMisaligned,
LoadFault,
StoreMisaligned,
StoreFault,
UserEnvCall,
SupervisorEnvCall,
MachineEnvCall,
InstructionPageFault,
LoadPageFault,
StorePageFault,
Unknown,
}
impl Interrupt {
pub fn from(nr: usize) -> Self {
match nr {
0 => Interrupt::UserSoft,
1 => Interrupt::SupervisorSoft,
3 => Interrupt::MachineSoft,
4 => Interrupt::UserTimer,
5 => Interrupt::SupervisorTimer,
7 => Interrupt::MachineTimer,
8 => Interrupt::UserExternal,
9 => Interrupt::SupervisorExternal,
11 => Interrupt::MachineExternal,
_ => Interrupt::Unknown,
}
}
}
impl Exception {
pub fn from(nr: usize) -> Self {
match nr {
0 => Exception::InstructionMisaligned,
1 => Exception::InstructionFault,
2 => Exception::IllegalInstruction,
3 => Exception::Breakpoint,
4 => Exception::LoadMisaligned,
5 => Exception::LoadFault,
6 => Exception::StoreMisaligned,
7 => Exception::StoreFault,
8 => Exception::UserEnvCall,
9 => Exception::SupervisorEnvCall,
11 => Exception::MachineEnvCall,
12 => Exception::InstructionPageFault,
13 => Exception::LoadPageFault,
15 => Exception::StorePageFault,
_ => Exception::Unknown,
}
}
}
impl Mcause {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Returns the code field
pub fn code(&self) -> usize {
match () {
#[cfg(target_pointer_width = "32")]
() => self.bits & !(1 << 31),
#[cfg(target_pointer_width = "64")]
() => self.bits & !(1 << 63),
#[cfg(target_pointer_width = "128")]
() => self.bits & !(1 << 127),
}
}
/// Trap Cause
#[inline]
pub fn cause(&self) -> Trap {
if self.is_interrupt() {
Trap::Interrupt(Interrupt::from(self.code()))
} else {
Trap::Exception(Exception::from(self.code()))
}
}
/// Is trap cause an interrupt.
#[inline]
pub fn is_interrupt(&self) -> bool {
match () {
#[cfg(target_pointer_width = "32")]
() => self.bits & (1 << 31) == 1 << 31,
#[cfg(target_pointer_width = "64")]
() => self.bits & (1 << 63) == 1 << 63,
#[cfg(target_pointer_width = "128")]
() => self.bits & (1 << 127) == 1 << 127,
}
}
/// Is trap cause an exception.
#[inline]
pub fn is_exception(&self) -> bool {
!self.is_interrupt()
}
}
read_csr_as!(Mcause, 0x342, __read_mcause);

View File

@ -0,0 +1,4 @@
//! mcycle register
read_csr_as_usize!(0xB00, __read_mcycle);
read_composite_csr!(super::mcycleh::read(), read());

View File

@ -0,0 +1,3 @@
//! mcycleh register
read_csr_as_usize_rv32!(0xB80, __read_mcycleh);

View File

@ -0,0 +1,4 @@
//! mepc register
read_csr_as_usize!(0x341, __read_mepc);
write_csr_as_usize!(0x341, __write_mepc);

View File

@ -0,0 +1,3 @@
//! mhartid register
read_csr_as_usize!(0xf14, __read_mhartid);

View File

@ -0,0 +1,84 @@
macro_rules! reg {
(
$addr:expr, $csrl:ident, $csrh:ident, $readf:ident, $writef:ident
) => {
/// Machine performance-monitoring counter
pub mod $csrl {
read_csr_as_usize!($addr, $readf);
write_csr_as_usize!($addr, $writef);
read_composite_csr!(super::$csrh::read(), read());
}
}
}
macro_rules! regh {
(
$addr:expr, $csrh:ident, $readf:ident, $writef:ident
) => {
/// Upper 32 bits of machine performance-monitoring counter (RV32I only)
pub mod $csrh {
read_csr_as_usize_rv32!($addr, $readf);
write_csr_as_usize_rv32!($addr, $writef);
}
}
}
reg!(0xB03, mhpmcounter3, mhpmcounter3h, __read_mhpmcounter3, __write_mhpmcounter3);
reg!(0xB04, mhpmcounter4, mhpmcounter4h, __read_mhpmcounter4, __write_mhpmcounter4);
reg!(0xB05, mhpmcounter5, mhpmcounter5h, __read_mhpmcounter5, __write_mhpmcounter5);
reg!(0xB06, mhpmcounter6, mhpmcounter6h, __read_mhpmcounter6, __write_mhpmcounter6);
reg!(0xB07, mhpmcounter7, mhpmcounter7h, __read_mhpmcounter7, __write_mhpmcounter7);
reg!(0xB08, mhpmcounter8, mhpmcounter8h, __read_mhpmcounter8, __write_mhpmcounter8);
reg!(0xB09, mhpmcounter9, mhpmcounter9h, __read_mhpmcounter9, __write_mhpmcounter9);
reg!(0xB0A, mhpmcounter10, mhpmcounter10h, __read_mhpmcounter10, __write_mhpmcounter10);
reg!(0xB0B, mhpmcounter11, mhpmcounter11h, __read_mhpmcounter11, __write_mhpmcounter11);
reg!(0xB0C, mhpmcounter12, mhpmcounter12h, __read_mhpmcounter12, __write_mhpmcounter12);
reg!(0xB0D, mhpmcounter13, mhpmcounter13h, __read_mhpmcounter13, __write_mhpmcounter13);
reg!(0xB0E, mhpmcounter14, mhpmcounter14h, __read_mhpmcounter14, __write_mhpmcounter14);
reg!(0xB0F, mhpmcounter15, mhpmcounter15h, __read_mhpmcounter15, __write_mhpmcounter15);
reg!(0xB10, mhpmcounter16, mhpmcounter16h, __read_mhpmcounter16, __write_mhpmcounter16);
reg!(0xB11, mhpmcounter17, mhpmcounter17h, __read_mhpmcounter17, __write_mhpmcounter17);
reg!(0xB12, mhpmcounter18, mhpmcounter18h, __read_mhpmcounter18, __write_mhpmcounter18);
reg!(0xB13, mhpmcounter19, mhpmcounter19h, __read_mhpmcounter19, __write_mhpmcounter19);
reg!(0xB14, mhpmcounter20, mhpmcounter20h, __read_mhpmcounter20, __write_mhpmcounter20);
reg!(0xB15, mhpmcounter21, mhpmcounter21h, __read_mhpmcounter21, __write_mhpmcounter21);
reg!(0xB16, mhpmcounter22, mhpmcounter22h, __read_mhpmcounter22, __write_mhpmcounter22);
reg!(0xB17, mhpmcounter23, mhpmcounter23h, __read_mhpmcounter23, __write_mhpmcounter23);
reg!(0xB18, mhpmcounter24, mhpmcounter24h, __read_mhpmcounter24, __write_mhpmcounter24);
reg!(0xB19, mhpmcounter25, mhpmcounter25h, __read_mhpmcounter25, __write_mhpmcounter25);
reg!(0xB1A, mhpmcounter26, mhpmcounter26h, __read_mhpmcounter26, __write_mhpmcounter26);
reg!(0xB1B, mhpmcounter27, mhpmcounter27h, __read_mhpmcounter27, __write_mhpmcounter27);
reg!(0xB1C, mhpmcounter28, mhpmcounter28h, __read_mhpmcounter28, __write_mhpmcounter28);
reg!(0xB1D, mhpmcounter29, mhpmcounter29h, __read_mhpmcounter29, __write_mhpmcounter29);
reg!(0xB1E, mhpmcounter30, mhpmcounter30h, __read_mhpmcounter30, __write_mhpmcounter30);
reg!(0xB1F, mhpmcounter31, mhpmcounter31h, __read_mhpmcounter31, __write_mhpmcounter31);
regh!(0xB83, mhpmcounter3h, __read_mhpmcounter3h, __write_mhpmcounter3h);
regh!(0xB84, mhpmcounter4h, __read_mhpmcounter4h, __write_mhpmcounter4h);
regh!(0xB85, mhpmcounter5h, __read_mhpmcounter5h, __write_mhpmcounter5h);
regh!(0xB86, mhpmcounter6h, __read_mhpmcounter6h, __write_mhpmcounter6h);
regh!(0xB87, mhpmcounter7h, __read_mhpmcounter7h, __write_mhpmcounter7h);
regh!(0xB88, mhpmcounter8h, __read_mhpmcounter8h, __write_mhpmcounter8h);
regh!(0xB89, mhpmcounter9h, __read_mhpmcounter9h, __write_mhpmcounter9h);
regh!(0xB8A, mhpmcounter10h, __read_mhpmcounter10h, __write_mhpmcounter10h);
regh!(0xB8B, mhpmcounter11h, __read_mhpmcounter11h, __write_mhpmcounter11h);
regh!(0xB8C, mhpmcounter12h, __read_mhpmcounter12h, __write_mhpmcounter12h);
regh!(0xB8D, mhpmcounter13h, __read_mhpmcounter13h, __write_mhpmcounter13h);
regh!(0xB8E, mhpmcounter14h, __read_mhpmcounter14h, __write_mhpmcounter14h);
regh!(0xB8F, mhpmcounter15h, __read_mhpmcounter15h, __write_mhpmcounter15h);
regh!(0xB90, mhpmcounter16h, __read_mhpmcounter16h, __write_mhpmcounter16h);
regh!(0xB91, mhpmcounter17h, __read_mhpmcounter17h, __write_mhpmcounter17h);
regh!(0xB92, mhpmcounter18h, __read_mhpmcounter18h, __write_mhpmcounter18h);
regh!(0xB93, mhpmcounter19h, __read_mhpmcounter19h, __write_mhpmcounter19h);
regh!(0xB94, mhpmcounter20h, __read_mhpmcounter20h, __write_mhpmcounter20h);
regh!(0xB95, mhpmcounter21h, __read_mhpmcounter21h, __write_mhpmcounter21h);
regh!(0xB96, mhpmcounter22h, __read_mhpmcounter22h, __write_mhpmcounter22h);
regh!(0xB97, mhpmcounter23h, __read_mhpmcounter23h, __write_mhpmcounter23h);
regh!(0xB98, mhpmcounter24h, __read_mhpmcounter24h, __write_mhpmcounter24h);
regh!(0xB99, mhpmcounter25h, __read_mhpmcounter25h, __write_mhpmcounter25h);
regh!(0xB9A, mhpmcounter26h, __read_mhpmcounter26h, __write_mhpmcounter26h);
regh!(0xB9B, mhpmcounter27h, __read_mhpmcounter27h, __write_mhpmcounter27h);
regh!(0xB9C, mhpmcounter28h, __read_mhpmcounter28h, __write_mhpmcounter28h);
regh!(0xB9D, mhpmcounter29h, __read_mhpmcounter29h, __write_mhpmcounter29h);
regh!(0xB9E, mhpmcounter30h, __read_mhpmcounter30h, __write_mhpmcounter30h);
regh!(0xB9F, mhpmcounter31h, __read_mhpmcounter31h, __write_mhpmcounter31h);

View File

@ -0,0 +1,41 @@
macro_rules! reg {
(
$addr:expr, $csr:ident, $readf:ident, $writef:ident
) => {
/// Machine performance-monitoring event selector
pub mod $csr {
read_csr_as_usize!($addr, $readf);
write_csr_as_usize!($addr, $writef);
}
}
}
reg!(0x323, mhpmevent3, __read_mhpmevent3, __write_mhpmevent3);
reg!(0x324, mhpmevent4, __read_mhpmevent4, __write_mhpmevent4);
reg!(0x325, mhpmevent5, __read_mhpmevent5, __write_mhpmevent5);
reg!(0x326, mhpmevent6, __read_mhpmevent6, __write_mhpmevent6);
reg!(0x327, mhpmevent7, __read_mhpmevent7, __write_mhpmevent7);
reg!(0x328, mhpmevent8, __read_mhpmevent8, __write_mhpmevent8);
reg!(0x329, mhpmevent9, __read_mhpmevent9, __write_mhpmevent9);
reg!(0x32A, mhpmevent10, __read_mhpmevent10, __write_mhpmevent10);
reg!(0x32B, mhpmevent11, __read_mhpmevent11, __write_mhpmevent11);
reg!(0x32C, mhpmevent12, __read_mhpmevent12, __write_mhpmevent12);
reg!(0x32D, mhpmevent13, __read_mhpmevent13, __write_mhpmevent13);
reg!(0x32E, mhpmevent14, __read_mhpmevent14, __write_mhpmevent14);
reg!(0x32F, mhpmevent15, __read_mhpmevent15, __write_mhpmevent15);
reg!(0x330, mhpmevent16, __read_mhpmevent16, __write_mhpmevent16);
reg!(0x331, mhpmevent17, __read_mhpmevent17, __write_mhpmevent17);
reg!(0x332, mhpmevent18, __read_mhpmevent18, __write_mhpmevent18);
reg!(0x333, mhpmevent19, __read_mhpmevent19, __write_mhpmevent19);
reg!(0x334, mhpmevent20, __read_mhpmevent20, __write_mhpmevent20);
reg!(0x335, mhpmevent21, __read_mhpmevent21, __write_mhpmevent21);
reg!(0x336, mhpmevent22, __read_mhpmevent22, __write_mhpmevent22);
reg!(0x337, mhpmevent23, __read_mhpmevent23, __write_mhpmevent23);
reg!(0x338, mhpmevent24, __read_mhpmevent24, __write_mhpmevent24);
reg!(0x339, mhpmevent25, __read_mhpmevent25, __write_mhpmevent25);
reg!(0x33A, mhpmevent26, __read_mhpmevent26, __write_mhpmevent26);
reg!(0x33B, mhpmevent27, __read_mhpmevent27, __write_mhpmevent27);
reg!(0x33C, mhpmevent28, __read_mhpmevent28, __write_mhpmevent28);
reg!(0x33D, mhpmevent29, __read_mhpmevent29, __write_mhpmevent29);
reg!(0x33E, mhpmevent30, __read_mhpmevent30, __write_mhpmevent30);
reg!(0x33F, mhpmevent31, __read_mhpmevent31, __write_mhpmevent31);

View File

@ -0,0 +1,103 @@
//! mie register
use bit_field::BitField;
/// mie register
#[derive(Clone, Copy, Debug)]
pub struct Mie {
bits: usize,
}
impl Mie {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// User Software Interrupt Enable
#[inline]
pub fn usoft(&self) -> bool {
self.bits.get_bit(0)
}
/// Supervisor Software Interrupt Enable
#[inline]
pub fn ssoft(&self) -> bool {
self.bits.get_bit(1)
}
/// Machine Software Interrupt Enable
#[inline]
pub fn msoft(&self) -> bool {
self.bits.get_bit(3)
}
/// User Timer Interrupt Enable
#[inline]
pub fn utimer(&self) -> bool {
self.bits.get_bit(4)
}
/// Supervisor Timer Interrupt Enable
#[inline]
pub fn stimer(&self) -> bool {
self.bits.get_bit(5)
}
/// Machine Timer Interrupt Enable
#[inline]
pub fn mtimer(&self) -> bool {
self.bits.get_bit(7)
}
/// User External Interrupt Enable
#[inline]
pub fn uext(&self) -> bool {
self.bits.get_bit(8)
}
/// Supervisor External Interrupt Enable
#[inline]
pub fn sext(&self) -> bool {
self.bits.get_bit(9)
}
/// Machine External Interrupt Enable
#[inline]
pub fn mext(&self) -> bool {
self.bits.get_bit(11)
}
}
read_csr_as!(Mie, 0x304, __read_mie);
set!(0x304, __set_mie);
clear!(0x304, __clear_mie);
set_clear_csr!(
/// User Software Interrupt Enable
, set_usoft, clear_usoft, 1 << 0);
set_clear_csr!(
/// Supervisor Software Interrupt Enable
, set_ssoft, clear_ssoft, 1 << 1);
set_clear_csr!(
/// Machine Software Interrupt Enable
, set_msoft, clear_msoft, 1 << 3);
set_clear_csr!(
/// User Timer Interrupt Enable
, set_utimer, clear_utimer, 1 << 4);
set_clear_csr!(
/// Supervisor Timer Interrupt Enable
, set_stimer, clear_stimer, 1 << 5);
set_clear_csr!(
/// Machine Timer Interrupt Enable
, set_mtimer, clear_mtimer, 1 << 7);
set_clear_csr!(
/// User External Interrupt Enable
, set_uext, clear_uext, 1 << 8);
set_clear_csr!(
/// Supervisor External Interrupt Enable
, set_sext, clear_sext, 1 << 9);
set_clear_csr!(
/// Machine External Interrupt Enable
, set_mext, clear_mext, 1 << 11);

View File

@ -0,0 +1,27 @@
//! mimpid register
use core::num::NonZeroUsize;
/// mimpid register
#[derive(Clone, Copy, Debug)]
pub struct Mimpid {
bits: NonZeroUsize,
}
impl Mimpid {
/// Returns the contents of the register as raw bits
pub fn bits(&self) -> usize {
self.bits.get()
}
}
read_csr!(0xF11, __read_mimpid);
/// Reads the CSR
#[inline]
pub fn read() -> Option<Mimpid> {
let r = unsafe{ _read() };
// When mimpid is hardwired to zero it means that the mimpid
// csr isn't implemented.
NonZeroUsize::new(r).map(|bits| Mimpid { bits })
}

View File

@ -0,0 +1,4 @@
//! minstret register
read_csr_as_usize!(0xB02, __read_minstret);
read_composite_csr!(super::minstreth::read(), read());

View File

@ -0,0 +1,3 @@
//! minstreth register
read_csr_as_usize_rv32!(0xB82, __read_minstreth);

View File

@ -0,0 +1,73 @@
//! mip register
use bit_field::BitField;
/// mip register
#[derive(Clone, Copy, Debug)]
pub struct Mip {
bits: usize,
}
impl Mip {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// User Software Interrupt Pending
#[inline]
pub fn usoft(&self) -> bool {
self.bits.get_bit(0)
}
/// Supervisor Software Interrupt Pending
#[inline]
pub fn ssoft(&self) -> bool {
self.bits.get_bit(1)
}
/// Machine Software Interrupt Pending
#[inline]
pub fn msoft(&self) -> bool {
self.bits.get_bit(3)
}
/// User Timer Interrupt Pending
#[inline]
pub fn utimer(&self) -> bool {
self.bits.get_bit(4)
}
/// Supervisor Timer Interrupt Pending
#[inline]
pub fn stimer(&self) -> bool {
self.bits.get_bit(5)
}
/// Machine Timer Interrupt Pending
#[inline]
pub fn mtimer(&self) -> bool {
self.bits.get_bit(7)
}
/// User External Interrupt Pending
#[inline]
pub fn uext(&self) -> bool {
self.bits.get_bit(8)
}
/// Supervisor External Interrupt Pending
#[inline]
pub fn sext(&self) -> bool {
self.bits.get_bit(9)
}
/// Machine External Interrupt Pending
#[inline]
pub fn mext(&self) -> bool {
self.bits.get_bit(11)
}
}
read_csr_as!(Mip, 0x344, __read_mip);

View File

@ -0,0 +1,60 @@
//! misa register
use core::num::NonZeroUsize;
/// misa register
#[derive(Clone, Copy, Debug)]
pub struct Misa {
bits: NonZeroUsize,
}
/// Machine XLEN
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MXL {
XLEN32,
XLEN64,
XLEN128,
}
impl Misa {
/// Returns the contents of the register as raw bits
pub fn bits(&self) -> usize {
self.bits.get()
}
/// Returns the machine xlen.
pub fn mxl(&self) -> MXL {
let value = match () {
#[cfg(target_pointer_width = "32")]
() => (self.bits() >> 30) as u8,
#[cfg(target_pointer_width = "64")]
() => (self.bits() >> 62) as u8,
};
match value {
1 => MXL::XLEN32,
2 => MXL::XLEN64,
3 => MXL::XLEN128,
_ => unreachable!(),
}
}
/// Returns true when the atomic extension is implemented.
pub fn has_extension(&self, extension: char) -> bool {
let bit = extension as u8 - 65;
if bit > 25 {
return false;
}
self.bits() & (1 << bit) == (1 << bit)
}
}
read_csr!(0x301, __read_misa);
/// Reads the CSR
#[inline]
pub fn read() -> Option<Misa> {
let r = unsafe{ _read() };
// When misa is hardwired to zero it means that the misa csr
// isn't implemented.
NonZeroUsize::new(r).map(|bits| Misa { bits })
}

View File

@ -0,0 +1,109 @@
//! RISC-V CSR's
//!
//! The following registers are not available on 64-bit implementations.
//!
//! - cycleh
//! - timeh
//! - instreth
//! - hpmcounter[3-31]h
//! - mcycleh
//! - minstreth
//! - mhpmcounter[3-31]h
#[macro_use]
mod macros;
// User Trap Setup
pub mod ustatus;
pub mod uie;
pub mod utvec;
// User Trap Handling
pub mod uscratch;
pub mod uepc;
pub mod ucause;
pub mod utval;
pub mod uip;
// User Floating-Point CSRs
// TODO: frm, fflags
pub mod fcsr;
// User Counter/Timers
// TODO: cycle[h], instret[h]
pub mod time;
mod hpmcounterx;
pub use self::hpmcounterx::*;
pub mod timeh;
// Supervisor Trap Setup
// TODO: sedeleg, sideleg
pub mod sstatus;
pub mod sie;
pub mod stvec;
// TODO: scounteren
// Supervisor Trap Handling
pub mod sscratch;
pub mod sepc;
pub mod scause;
pub mod stval;
pub mod sip;
// Supervisor Protection and Translation
pub mod satp;
// Machine Information Registers
pub mod mvendorid;
pub mod marchid;
pub mod mimpid;
pub mod mhartid;
// Machine Trap Setup
pub mod mstatus;
pub mod misa;
// TODO: medeleg, mideleg
pub mod mie;
pub mod mtvec;
// TODO: mcounteren
// Machine Trap Handling
pub mod mscratch;
pub mod mepc;
pub mod mcause;
pub mod mtval;
pub mod mip;
// Machine Protection and Translation
mod pmpcfgx;
pub use self::pmpcfgx::*;
mod pmpaddrx;
pub use self::pmpaddrx::*;
// Machine Counter/Timers
pub mod mcycle;
pub mod minstret;
mod mhpmcounterx;
pub use self::mhpmcounterx::*;
pub mod mcycleh;
pub mod minstreth;
// Machine Counter Setup
mod mhpmeventx;
pub use self::mhpmeventx::*;
// TODO: Debug/Trace Registers (shared with Debug Mode)
// TODO: Debug Mode Registers

View File

@ -0,0 +1,4 @@
//! mscratch register
read_csr_as_usize!(0x340, __read_mscratch);
write_csr_as_usize!(0x340, __write_mscratch);

View File

@ -0,0 +1,200 @@
//! mstatus register
// TODO: Virtualization, Memory Privilege and Extension Context Fields
use bit_field::BitField;
use core::mem::size_of;
/// mstatus register
#[derive(Clone, Copy, Debug)]
pub struct Mstatus {
bits: usize,
}
/// Additional extension state
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum XS {
/// All off
AllOff = 0,
/// None dirty or clean, some on
NoneDirtyOrClean = 1,
/// None dirty, some clean
NoneDirtySomeClean = 2,
/// Some dirty
SomeDirty = 3,
}
/// Floating-point extension state
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FS {
Off = 0,
Initial = 1,
Clean = 2,
Dirty = 3,
}
/// Machine Previous Privilege Mode
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MPP {
Machine = 3,
Supervisor = 1,
User = 0,
}
/// Supervisor Previous Privilege Mode
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SPP {
Supervisor = 1,
User = 0,
}
impl Mstatus {
/// User Interrupt Enable
#[inline]
pub fn uie(&self) -> bool {
self.bits.get_bit(0)
}
/// Supervisor Interrupt Enable
#[inline]
pub fn sie(&self) -> bool {
self.bits.get_bit(1)
}
/// Machine Interrupt Enable
#[inline]
pub fn mie(&self) -> bool {
self.bits.get_bit(3)
}
/// User Previous Interrupt Enable
#[inline]
pub fn upie(&self) -> bool {
self.bits.get_bit(4)
}
/// Supervisor Previous Interrupt Enable
#[inline]
pub fn spie(&self) -> bool {
self.bits.get_bit(5)
}
/// Machine Previous Interrupt Enable
#[inline]
pub fn mpie(&self) -> bool {
self.bits.get_bit(7)
}
/// Supervisor Previous Privilege Mode
#[inline]
pub fn spp(&self) -> SPP {
match self.bits.get_bit(8) {
true => SPP::Supervisor,
false => SPP::User,
}
}
/// Machine Previous Privilege Mode
#[inline]
pub fn mpp(&self) -> MPP {
match self.bits.get_bits(11..13) {
0b00 => MPP::User,
0b01 => MPP::Supervisor,
0b11 => MPP::Machine,
_ => unreachable!(),
}
}
/// Floating-point extension state
///
/// Encodes the status of the floating-point unit,
/// including the CSR `fcsr` and floating-point data registers `f0f31`.
#[inline]
pub fn fs(&self) -> FS {
match self.bits.get_bits(13..15) {
0b00 => FS::Off,
0b01 => FS::Initial,
0b10 => FS::Clean,
0b11 => FS::Dirty,
_ => unreachable!(),
}
}
/// Additional extension state
///
/// Encodes the status of additional user-mode extensions and associated state.
#[inline]
pub fn xs(&self) -> XS {
match self.bits.get_bits(15..17) {
0b00 => XS::AllOff,
0b01 => XS::NoneDirtyOrClean,
0b10 => XS::NoneDirtySomeClean,
0b11 => XS::SomeDirty,
_ => unreachable!(),
}
}
/// Whether either the FS field or XS field
/// signals the presence of some dirty state
#[inline]
pub fn sd(&self) -> bool {
self.bits.get_bit(size_of::<usize>() * 8 - 1)
}
}
read_csr_as!(Mstatus, 0x300, __read_mstatus);
write_csr!(0x300, __write_mstatus);
set!(0x300, __set_mstatus);
clear!(0x300, __clear_mstatus);
set_clear_csr!(
/// User Interrupt Enable
, set_uie, clear_uie, 1 << 0);
set_clear_csr!(
/// Supervisor Interrupt Enable
, set_sie, clear_sie, 1 << 1);
set_clear_csr!(
/// Machine Interrupt Enable
, set_mie, clear_mie, 1 << 3);
set_csr!(
/// User Previous Interrupt Enable
, set_upie, 1 << 4);
set_csr!(
/// Supervisor Previous Interrupt Enable
, set_spie, 1 << 5);
set_csr!(
/// Machine Previous Interrupt Enable
, set_mpie, 1 << 7);
/// Supervisor Previous Privilege Mode
#[inline]
pub unsafe fn set_spp(spp: SPP) {
match spp {
SPP::Supervisor => _set(1 << 8),
SPP::User => _clear(1 << 8),
}
}
/// Machine Previous Privilege Mode
#[inline]
pub unsafe fn set_mpp(mpp: MPP) {
let mut value = _read();
value.set_bits(11..13, mpp as usize);
_write(value);
}
/// Floating-point extension state
#[inline]
pub unsafe fn set_fs(fs: FS) {
let mut value = _read();
value.set_bits(13..15, fs as usize);
_write(value);
}

View File

@ -0,0 +1,3 @@
//! mtval register
read_csr_as_usize!(0x343, __read_mtval);

View File

@ -0,0 +1,47 @@
//! mtvec register
/// mtvec register
#[derive(Clone, Copy, Debug)]
pub struct Mtvec {
bits: usize,
}
/// Trap mode
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum TrapMode {
Direct = 0,
Vectored = 1,
}
impl Mtvec {
/// Returns the contents of the register as raw bits
pub fn bits(&self) -> usize {
self.bits
}
/// Returns the trap-vector base-address
pub fn address(&self) -> usize {
self.bits - (self.bits & 0b11)
}
/// Returns the trap-vector mode
pub fn trap_mode(&self) -> TrapMode {
let mode = self.bits & 0b11;
match mode {
0 => TrapMode::Direct,
1 => TrapMode::Vectored,
_ => unimplemented!()
}
}
}
read_csr_as!(Mtvec, 0x305, __read_mtvec);
write_csr!(0x305, __write_mtvec);
/// Writes the CSR
#[inline]
pub unsafe fn write(addr: usize, mode: TrapMode) {
let bits = addr + mode as usize;
_write(bits);
}

View File

@ -0,0 +1,32 @@
//! mvendorid register
use core::num::NonZeroUsize;
/// mvendorid register
#[derive(Clone, Copy, Debug)]
pub struct Mvendorid {
bits: NonZeroUsize,
}
impl Mvendorid {
/// Returns the contents of the register as raw bits
pub fn bits(&self) -> usize {
self.bits.get()
}
/// Returns the JEDEC manufacturer ID
pub fn jedec_manufacturer(&self) -> usize {
self.bits() >> 7
}
}
read_csr!(0xF11, __read_mvendorid);
/// Reads the CSR
#[inline]
pub fn read() -> Option<Mvendorid> {
let r = unsafe{ _read() };
// When mvendorid is hardwired to zero it means that the mvendorid
// csr isn't implemented.
NonZeroUsize::new(r).map(|bits| Mvendorid { bits })
}

View File

@ -0,0 +1,28 @@
macro_rules! reg {
(
$addr:expr, $csr:ident, $readf:ident, $writef:ident
) => {
/// Physical memory protection address register
pub mod $csr {
read_csr_as_usize!($addr, $readf);
write_csr_as_usize!($addr, $writef);
}
}
}
reg!(0x3B0, pmpaddr0, __read_pmpaddr0, __write_pmpaddr0);
reg!(0x3B1, pmpaddr1, __read_pmpaddr1, __write_pmpaddr1);
reg!(0x3B2, pmpaddr2, __read_pmpaddr2, __write_pmpaddr2);
reg!(0x3B3, pmpaddr3, __read_pmpaddr3, __write_pmpaddr3);
reg!(0x3B4, pmpaddr4, __read_pmpaddr4, __write_pmpaddr4);
reg!(0x3B5, pmpaddr5, __read_pmpaddr5, __write_pmpaddr5);
reg!(0x3B6, pmpaddr6, __read_pmpaddr6, __write_pmpaddr6);
reg!(0x3B7, pmpaddr7, __read_pmpaddr7, __write_pmpaddr7);
reg!(0x3B8, pmpaddr8, __read_pmpaddr8, __write_pmpaddr8);
reg!(0x3B9, pmpaddr9, __read_pmpaddr9, __write_pmpaddr9);
reg!(0x3BA, pmpaddr10, __read_pmpaddr10, __write_pmpaddr10);
reg!(0x3BB, pmpaddr11, __read_pmpaddr11, __write_pmpaddr11);
reg!(0x3BC, pmpaddr12, __read_pmpaddr12, __write_pmpaddr12);
reg!(0x3BD, pmpaddr13, __read_pmpaddr13, __write_pmpaddr13);
reg!(0x3BE, pmpaddr14, __read_pmpaddr14, __write_pmpaddr14);
reg!(0x3BF, pmpaddr15, __read_pmpaddr15, __write_pmpaddr15);

View File

@ -0,0 +1,23 @@
/// Physical memory protection configuration
pub mod pmpcfg0 {
read_csr_as_usize!(0x3A0, __read_pmpcfg0);
write_csr_as_usize!(0x3A0, __write_pmpcfg0);
}
/// Physical memory protection configuration, RV32 only
pub mod pmpcfg1 {
read_csr_as_usize_rv32!(0x3A1, __read_pmpcfg1);
write_csr_as_usize_rv32!(0x3A1, __write_pmpcfg1);
}
/// Physical memory protection configuration
pub mod pmpcfg2 {
read_csr_as_usize!(0x3A2, __read_pmpcfg2);
write_csr_as_usize!(0x3A2, __write_pmpcfg2);
}
/// Physical memory protection configuration, RV32 only
pub mod pmpcfg3 {
read_csr_as_usize_rv32!(0x3A3, __read_pmpcfg3);
write_csr_as_usize_rv32!(0x3A3, __write_pmpcfg3);
}

View File

@ -0,0 +1,110 @@
//! satp register
#[cfg(riscv)]
use bit_field::BitField;
/// satp register
#[derive(Clone, Copy, Debug)]
pub struct Satp {
bits: usize,
}
impl Satp {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Current address-translation scheme
#[inline]
#[cfg(riscv32)]
pub fn mode(&self) -> Mode {
match self.bits.get_bit(31) {
false => Mode::Bare,
true => Mode::Sv32,
}
}
/// Current address-translation scheme
#[inline]
#[cfg(riscv64)]
pub fn mode(&self) -> Mode {
match self.bits.get_bits(60..64) {
0 => Mode::Bare,
8 => Mode::Sv39,
9 => Mode::Sv48,
10 => Mode::Sv57,
11 => Mode::Sv64,
_ => unreachable!(),
}
}
/// Address space identifier
#[inline]
#[cfg(riscv32)]
pub fn asid(&self) -> usize {
self.bits.get_bits(22..31)
}
/// Address space identifier
#[inline]
#[cfg(riscv64)]
pub fn asid(&self) -> usize {
self.bits.get_bits(44..60)
}
/// Physical page number
#[inline]
#[cfg(riscv32)]
pub fn ppn(&self) -> usize {
self.bits.get_bits(0..22)
}
/// Physical page number
#[inline]
#[cfg(riscv64)]
pub fn ppn(&self) -> usize {
self.bits.get_bits(0..44)
}
}
#[cfg(riscv32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Mode {
Bare = 0,
Sv32 = 1,
}
#[cfg(riscv64)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Mode {
Bare = 0,
Sv39 = 8,
Sv48 = 9,
Sv57 = 10,
Sv64 = 11,
}
read_csr_as!(Satp, 0x180, __read_satp);
write_csr!(0x180, __write_satp);
#[inline]
#[cfg(riscv32)]
pub unsafe fn set(mode: Mode, asid: usize, ppn: usize) {
let mut bits = 0usize;
bits.set_bits(31..32, mode as usize);
bits.set_bits(22..31, asid);
bits.set_bits(0..22, ppn);
_write(bits);
}
#[inline]
#[cfg(riscv64)]
pub unsafe fn set(mode: Mode, asid: usize, ppn: usize) {
let mut bits = 0usize;
bits.set_bits(60..64, mode as usize);
bits.set_bits(44..60, asid);
bits.set_bits(0..44, ppn);
_write(bits);
}

View File

@ -0,0 +1,117 @@
//! scause register
use bit_field::BitField;
use core::mem::size_of;
/// scause register
#[derive(Clone, Copy)]
pub struct Scause {
bits: usize,
}
/// Trap Cause
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Trap {
Interrupt(Interrupt),
Exception(Exception),
}
/// Interrupt
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Interrupt {
UserSoft,
SupervisorSoft,
UserTimer,
SupervisorTimer,
UserExternal,
SupervisorExternal,
Unknown,
}
/// Exception
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Exception {
InstructionMisaligned,
InstructionFault,
IllegalInstruction,
Breakpoint,
LoadFault,
StoreMisaligned,
StoreFault,
UserEnvCall,
InstructionPageFault,
LoadPageFault,
StorePageFault,
Unknown,
}
impl Interrupt {
pub fn from(nr: usize) -> Self {
match nr {
0 => Interrupt::UserSoft,
1 => Interrupt::SupervisorSoft,
4 => Interrupt::UserTimer,
5 => Interrupt::SupervisorTimer,
8 => Interrupt::UserExternal,
9 => Interrupt::SupervisorExternal,
_ => Interrupt::Unknown,
}
}
}
impl Exception {
pub fn from(nr: usize) -> Self {
match nr {
0 => Exception::InstructionMisaligned,
1 => Exception::InstructionFault,
2 => Exception::IllegalInstruction,
3 => Exception::Breakpoint,
5 => Exception::LoadFault,
6 => Exception::StoreMisaligned,
7 => Exception::StoreFault,
8 => Exception::UserEnvCall,
12 => Exception::InstructionPageFault,
13 => Exception::LoadPageFault,
15 => Exception::StorePageFault,
_ => Exception::Unknown,
}
}
}
impl Scause {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// Returns the code field
pub fn code(&self) -> usize {
let bit = 1 << (size_of::<usize>() * 8 - 1);
self.bits & !bit
}
/// Trap Cause
#[inline]
pub fn cause(&self) -> Trap {
if self.is_interrupt() {
Trap::Interrupt(Interrupt::from(self.code()))
} else {
Trap::Exception(Exception::from(self.code()))
}
}
/// Is trap cause an interrupt.
#[inline]
pub fn is_interrupt(&self) -> bool {
self.bits.get_bit(size_of::<usize>() * 8 - 1)
}
/// Is trap cause an exception.
#[inline]
pub fn is_exception(&self) -> bool {
!self.is_interrupt()
}
}
read_csr_as!(Scause, 0x142, __read_scause);

View File

@ -0,0 +1,4 @@
//! sepc register
read_csr_as_usize!(0x141, __read_sepc);
write_csr_as_usize!(0x141, __write_sepc);

View File

@ -0,0 +1,76 @@
//! sie register
use bit_field::BitField;
/// sie register
#[derive(Clone, Copy, Debug)]
pub struct Sie {
bits: usize,
}
impl Sie {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// User Software Interrupt Enable
#[inline]
pub fn usoft(&self) -> bool {
self.bits.get_bit(0)
}
/// Supervisor Software Interrupt Enable
#[inline]
pub fn ssoft(&self) -> bool {
self.bits.get_bit(1)
}
/// User Timer Interrupt Enable
#[inline]
pub fn utimer(&self) -> bool {
self.bits.get_bit(4)
}
/// Supervisor Timer Interrupt Enable
#[inline]
pub fn stimer(&self) -> bool {
self.bits.get_bit(5)
}
/// User External Interrupt Enable
#[inline]
pub fn uext(&self) -> bool {
self.bits.get_bit(8)
}
/// Supervisor External Interrupt Enable
#[inline]
pub fn sext(&self) -> bool {
self.bits.get_bit(9)
}
}
read_csr_as!(Sie, 0x104, __read_sie);
set!(0x104, __set_sie);
clear!(0x104, __clear_sie);
set_clear_csr!(
/// User Software Interrupt Enable
, set_usoft, clear_usoft, 1 << 0);
set_clear_csr!(
/// Supervisor Software Interrupt Enable
, set_ssoft, clear_ssoft, 1 << 1);
set_clear_csr!(
/// User Timer Interrupt Enable
, set_utimer, clear_utimer, 1 << 4);
set_clear_csr!(
/// Supervisor Timer Interrupt Enable
, set_stimer, clear_stimer, 1 << 5);
set_clear_csr!(
/// User External Interrupt Enable
, set_uext, clear_uext, 1 << 8);
set_clear_csr!(
/// Supervisor External Interrupt Enable
, set_sext, clear_sext, 1 << 9);

View File

@ -0,0 +1,55 @@
//! sip register
use bit_field::BitField;
/// sip register
#[derive(Clone, Copy, Debug)]
pub struct Sip {
bits: usize,
}
impl Sip {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// User Software Interrupt Pending
#[inline]
pub fn usoft(&self) -> bool {
self.bits.get_bit(0)
}
/// Supervisor Software Interrupt Pending
#[inline]
pub fn ssoft(&self) -> bool {
self.bits.get_bit(1)
}
/// User Timer Interrupt Pending
#[inline]
pub fn utimer(&self) -> bool {
self.bits.get_bit(4)
}
/// Supervisor Timer Interrupt Pending
#[inline]
pub fn stimer(&self) -> bool {
self.bits.get_bit(5)
}
/// User External Interrupt Pending
#[inline]
pub fn uext(&self) -> bool {
self.bits.get_bit(8)
}
/// Supervisor External Interrupt Pending
#[inline]
pub fn sext(&self) -> bool {
self.bits.get_bit(9)
}
}
read_csr_as!(Sip, 0x144, __read_sip);

View File

@ -0,0 +1,4 @@
//! sscratch register
read_csr_as_usize!(0x140, __read_sscratch);
write_csr_as_usize!(0x140, __write_sscratch);

View File

@ -0,0 +1,140 @@
//! sstatus register
use bit_field::BitField;
use core::mem::size_of;
pub use super::mstatus::FS;
/// Supervisor Status Register
#[derive(Clone, Copy, Debug)]
pub struct Sstatus {
bits: usize,
}
/// Supervisor Previous Privilege Mode
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SPP {
Supervisor = 1,
User = 0,
}
impl Sstatus {
/// User Interrupt Enable
#[inline]
pub fn uie(&self) -> bool {
self.bits.get_bit(0)
}
/// Supervisor Interrupt Enable
#[inline]
pub fn sie(&self) -> bool {
self.bits.get_bit(1)
}
/// User Previous Interrupt Enable
#[inline]
pub fn upie(&self) -> bool {
self.bits.get_bit(4)
}
/// Supervisor Previous Interrupt Enable
#[inline]
pub fn spie(&self) -> bool {
self.bits.get_bit(5)
}
/// Supervisor Previous Privilege Mode
#[inline]
pub fn spp(&self) -> SPP {
match self.bits.get_bit(8) {
true => SPP::Supervisor,
false => SPP::User,
}
}
/// The status of the floating-point unit
#[inline]
pub fn fs(&self) -> FS {
match self.bits.get_bits(13..15) {
0 => FS::Off,
1 => FS::Initial,
2 => FS::Clean,
3 => FS::Dirty,
_ => unreachable!(),
}
}
/// The status of additional user-mode extensions
/// and associated state
#[inline]
pub fn xs(&self) -> FS {
match self.bits.get_bits(15..17) {
0 => FS::Off,
1 => FS::Initial,
2 => FS::Clean,
3 => FS::Dirty,
_ => unreachable!(),
}
}
/// Permit Supervisor User Memory access
#[inline]
pub fn sum(&self) -> bool {
self.bits.get_bit(18)
}
/// Make eXecutable Readable
#[inline]
pub fn mxr(&self) -> bool {
self.bits.get_bit(19)
}
/// Whether either the FS field or XS field
/// signals the presence of some dirty state
#[inline]
pub fn sd(&self) -> bool {
self.bits.get_bit(size_of::<usize>() * 8 - 1)
}
}
read_csr_as!(Sstatus, 0x100, __read_sstatus);
write_csr!(0x100, __write_sstatus);
set!(0x100, __set_sstatus);
clear!(0x100, __clear_sstatus);
set_clear_csr!(
/// User Interrupt Enable
, set_uie, clear_uie, 1 << 0);
set_clear_csr!(
/// Supervisor Interrupt Enable
, set_sie, clear_sie, 1 << 1);
set_csr!(
/// User Previous Interrupt Enable
, set_upie, 1 << 4);
set_csr!(
/// Supervisor Previous Interrupt Enable
, set_spie, 1 << 5);
set_clear_csr!(
/// Make eXecutable Readable
, set_mxr, clear_mxr, 1 << 19);
set_clear_csr!(
/// Permit Supervisor User Memory access
, set_sum, clear_sum, 1 << 18);
/// Supervisor Previous Privilege Mode
#[inline]
#[cfg(riscv)]
pub unsafe fn set_spp(spp: SPP) {
match spp {
SPP::Supervisor => _set(1 << 8),
SPP::User => _clear(1 << 8),
}
}
/// The status of the floating-point unit
#[inline]
#[cfg(riscv)]
pub unsafe fn set_fs(fs: FS) {
let mut value = _read();
value.set_bits(13..15, fs as usize);
_write(value);
}

View File

@ -0,0 +1,3 @@
//! stval register
read_csr_as_usize!(0x143, __read_stval);

View File

@ -0,0 +1,40 @@
//! stvec register
pub use crate::register::mtvec::TrapMode;
/// stvec register
#[derive(Clone, Copy, Debug)]
pub struct Stvec {
bits: usize,
}
impl Stvec {
/// Returns the contents of the register as raw bits
pub fn bits(&self) -> usize {
self.bits
}
/// Returns the trap-vector base-address
pub fn address(&self) -> usize {
self.bits - (self.bits & 0b11)
}
/// Returns the trap-vector mode
pub fn trap_mode(&self) -> TrapMode {
let mode = self.bits & 0b11;
match mode {
0 => TrapMode::Direct,
1 => TrapMode::Vectored,
_ => unimplemented!()
}
}
}
read_csr_as!(Stvec, 0x105, __read_stvec);
write_csr!(0x105, __write_stvec);
/// Writes the CSR
#[inline]
pub unsafe fn write(addr: usize, mode: TrapMode) {
_write(addr + mode as usize);
}

View File

@ -0,0 +1,4 @@
//! time register
read_csr_as_usize!(0xC01, __read_time);
read_composite_csr!(super::timeh::read(), read());

View File

@ -0,0 +1,3 @@
//! timeh register
read_csr_as_usize_rv32!(0xC81, __read_timeh);

View File

@ -0,0 +1,17 @@
//! ucause register
/// ucause register
#[derive(Clone, Copy, Debug)]
pub struct Ucause {
bits: usize,
}
impl Ucause {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
}
read_csr_as!(Ucause, 0x042, __read_ucause);

View File

@ -0,0 +1,4 @@
//! uepc register
read_csr_as_usize!(0x041, __read_uepc);
write_csr_as_usize!(0x041, __write_uepc);

View File

@ -0,0 +1,49 @@
//! uie register
use bit_field::BitField;
/// uie register
#[derive(Clone, Copy, Debug)]
pub struct Uie {
bits: usize,
}
impl Uie {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// User Software Interrupt Enable
#[inline]
pub fn usoft(&self) -> bool {
self.bits.get_bit(0)
}
/// User Timer Interrupt Enable
#[inline]
pub fn utimer(&self) -> bool {
self.bits.get_bit(4)
}
/// User External Interrupt Enable
#[inline]
pub fn uext(&self) -> bool {
self.bits.get_bit(8)
}
}
read_csr_as!(Uie, 0x004, __read_uie);
set!(0x004, __set_uie);
clear!(0x004, __clear_uie);
set_clear_csr!(
/// User Software Interrupt Enable
, set_usoft, clear_usoft, 1 << 0);
set_clear_csr!(
/// User Timer Interrupt Enable
, set_utimer, clear_utimer, 1 << 4);
set_clear_csr!(
/// User External Interrupt Enable
, set_uext, clear_uext, 1 << 8);

View File

@ -0,0 +1,37 @@
//! uip register
use bit_field::BitField;
/// uip register
#[derive(Clone, Copy, Debug)]
pub struct Uip {
bits: usize,
}
impl Uip {
/// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
/// User Software Interrupt Pending
#[inline]
pub fn usoft(&self) -> bool {
self.bits.get_bit(0)
}
/// User Timer Interrupt Pending
#[inline]
pub fn utimer(&self) -> bool {
self.bits.get_bit(4)
}
/// User External Interrupt Pending
#[inline]
pub fn uext(&self) -> bool {
self.bits.get_bit(8)
}
}
read_csr_as!(Uip, 0x044, __read_uip);

View File

@ -0,0 +1,4 @@
//! uscratch register
read_csr_as_usize!(0x040, __read_uscratch);
write_csr_as_usize!(0x040, __write_uscratch);

View File

@ -0,0 +1,37 @@
//! ustatus register
// TODO: Virtualization, Memory Privilege and Extension Context Fields
use bit_field::BitField;
/// ustatus register
#[derive(Clone, Copy, Debug)]
pub struct Ustatus {
bits: usize,
}
impl Ustatus {
/// User Interrupt Enable
#[inline]
pub fn uie(&self) -> bool {
self.bits.get_bit(0)
}
/// User Previous Interrupt Enable
#[inline]
pub fn upie(&self) -> bool {
self.bits.get_bit(4)
}
}
read_csr_as!(Ustatus, 0x000, __read_ustatus);
write_csr!(0x000, __write_ustatus);
set!(0x000, __set_ustatus);
clear!(0x000, __clear_ustatus);
set_clear_csr!(
/// User Interrupt Enable
, set_uie, clear_uie, 1 << 0);
set_csr!(
/// User Previous Interrupt Enable
, set_upie, 1 << 4);

View File

@ -0,0 +1,3 @@
//! utval register
read_csr_as_usize!(0x043, __read_utval);

View File

@ -0,0 +1,40 @@
//! stvec register
pub use crate::register::mtvec::TrapMode;
/// stvec register
#[derive(Clone, Copy, Debug)]
pub struct Utvec {
bits: usize,
}
impl Utvec {
/// Returns the contents of the register as raw bits
pub fn bits(&self) -> usize {
self.bits
}
/// Returns the trap-vector base-address
pub fn address(&self) -> usize {
self.bits - (self.bits & 0b11)
}
/// Returns the trap-vector mode
pub fn trap_mode(&self) -> TrapMode {
let mode = self.bits & 0b11;
match mode {
0 => TrapMode::Direct,
1 => TrapMode::Vectored,
_ => unimplemented!()
}
}
}
read_csr_as!(Utvec, 0x005, __read_utvec);
write_csr!(0x005, __write_utvec);
/// Writes the CSR
#[inline]
pub unsafe fn write(addr: usize, mode: TrapMode) {
_write(addr + mode as usize);
}