commit 79114aa65aa03a21f25e3972a4b28b81501a110e Author: Sean Cross Date: Wed Dec 18 09:59:00 2019 +0800 initial commit Signed-off-by: Sean Cross diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..95f24e4 --- /dev/null +++ b/.cargo/config @@ -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" \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d1725ed --- /dev/null +++ b/.gitattributes @@ -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 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53eaa21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f642efa --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..06b7379 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "xous-kernel" +version = "0.1.0" +authors = ["Sean Cross "] +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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..18bcf1c --- /dev/null +++ b/README.md @@ -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. diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..63598ca --- /dev/null +++ b/memory.x @@ -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; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..43098f8 --- /dev/null +++ b/src/main.rs @@ -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() }; + } +} diff --git a/xous-riscv-rt/Cargo.toml b/xous-riscv-rt/Cargo.toml new file mode 100644 index 0000000..f7f808e --- /dev/null +++ b/xous-riscv-rt/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "xous-riscv-rt" +version = "0.6.1" +repository = "https://github.com/xous/xous-riscv-rt" +authors = ["Sean Cross ", "The RISC-V Team "] +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" diff --git a/xous-riscv-rt/README.md b/xous-riscv-rt/README.md new file mode 100644 index 0000000..e69de29 diff --git a/xous-riscv-rt/asm.S b/xous-riscv-rt/asm.S new file mode 100644 index 0000000..d616c36 --- /dev/null +++ b/xous-riscv-rt/asm.S @@ -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 diff --git a/xous-riscv-rt/assemble.ps1 b/xous-riscv-rt/assemble.ps1 new file mode 100644 index 0000000..f411870 --- /dev/null +++ b/xous-riscv-rt/assemble.ps1 @@ -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 diff --git a/xous-riscv-rt/assemble.sh b/xous-riscv-rt/assemble.sh new file mode 100644 index 0000000..8994b7e --- /dev/null +++ b/xous-riscv-rt/assemble.sh @@ -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 diff --git a/xous-riscv-rt/bin/riscv32i-unknown-none-elf.a b/xous-riscv-rt/bin/riscv32i-unknown-none-elf.a new file mode 100644 index 0000000..c59bd6e Binary files /dev/null and b/xous-riscv-rt/bin/riscv32i-unknown-none-elf.a differ diff --git a/xous-riscv-rt/bin/riscv32imac-unknown-none-elf.a b/xous-riscv-rt/bin/riscv32imac-unknown-none-elf.a new file mode 100644 index 0000000..8e8db7d Binary files /dev/null and b/xous-riscv-rt/bin/riscv32imac-unknown-none-elf.a differ diff --git a/xous-riscv-rt/bin/riscv32imc-unknown-none-elf.a b/xous-riscv-rt/bin/riscv32imc-unknown-none-elf.a new file mode 100644 index 0000000..8e8db7d Binary files /dev/null and b/xous-riscv-rt/bin/riscv32imc-unknown-none-elf.a differ diff --git a/xous-riscv-rt/bin/riscv64gc-unknown-none-elf.a b/xous-riscv-rt/bin/riscv64gc-unknown-none-elf.a new file mode 100644 index 0000000..23e0a13 Binary files /dev/null and b/xous-riscv-rt/bin/riscv64gc-unknown-none-elf.a differ diff --git a/xous-riscv-rt/bin/riscv64imac-unknown-none-elf.a b/xous-riscv-rt/bin/riscv64imac-unknown-none-elf.a new file mode 100644 index 0000000..23e0a13 Binary files /dev/null and b/xous-riscv-rt/bin/riscv64imac-unknown-none-elf.a differ diff --git a/xous-riscv-rt/build.rs b/xous-riscv-rt/build.rs new file mode 100644 index 0000000..4589309 --- /dev/null +++ b/xous-riscv-rt/build.rs @@ -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"); +} diff --git a/xous-riscv-rt/link.x b/xous-riscv-rt/link.x new file mode 100644 index 0000000..048bef0 --- /dev/null +++ b/xous-riscv-rt/link.x @@ -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 | */ diff --git a/xous-riscv-rt/macros/Cargo.toml b/xous-riscv-rt/macros/Cargo.toml new file mode 100644 index 0000000..31de3cb --- /dev/null +++ b/xous-riscv-rt/macros/Cargo.toml @@ -0,0 +1,28 @@ +[package] +authors = [ + "The RISC-V Team ", + "Jorge Aparicio ", +] +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 diff --git a/xous-riscv-rt/macros/src/lib.rs b/xous-riscv-rt/macros/src/lib.rs new file mode 100644 index 0000000..8efe092 --- /dev/null +++ b/xous-riscv-rt/macros/src/lib.rs @@ -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::() % 25) as char + } else { + ('0' as u8 + rng.gen::() % 10) as char + } + }) + .collect::(), + Span::call_site(), + ) +} diff --git a/xous-riscv-rt/src/lib.rs b/xous-riscv-rt/src/lib.rs new file mode 100644 index 0000000..5624697 --- /dev/null +++ b/xous-riscv-rt/src/lib.rs @@ -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() } + }, + } +} diff --git a/xous-riscv/Cargo.toml b/xous-riscv/Cargo.toml new file mode 100644 index 0000000..34eee8c --- /dev/null +++ b/xous-riscv/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "xous-riscv" +version = "0.5.4" +repository = "https://github.com/rust-embedded/riscv" +authors = ["The RISC-V Team "] +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 = [] \ No newline at end of file diff --git a/xous-riscv/asm.S b/xous-riscv/asm.S new file mode 100644 index 0000000..e861189 --- /dev/null +++ b/xous-riscv/asm.S @@ -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 diff --git a/xous-riscv/assemble.ps1 b/xous-riscv/assemble.ps1 new file mode 100644 index 0000000..079eae1 --- /dev/null +++ b/xous-riscv/assemble.ps1 @@ -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 diff --git a/xous-riscv/assemble.sh b/xous-riscv/assemble.sh new file mode 100644 index 0000000..8994b7e --- /dev/null +++ b/xous-riscv/assemble.sh @@ -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 diff --git a/xous-riscv/bin/riscv32i-unknown-none-elf.a b/xous-riscv/bin/riscv32i-unknown-none-elf.a new file mode 100644 index 0000000..72d93bc Binary files /dev/null and b/xous-riscv/bin/riscv32i-unknown-none-elf.a differ diff --git a/xous-riscv/bin/riscv32imac-unknown-none-elf.a b/xous-riscv/bin/riscv32imac-unknown-none-elf.a new file mode 100644 index 0000000..799dc79 Binary files /dev/null and b/xous-riscv/bin/riscv32imac-unknown-none-elf.a differ diff --git a/xous-riscv/bin/riscv32imc-unknown-none-elf.a b/xous-riscv/bin/riscv32imc-unknown-none-elf.a new file mode 100644 index 0000000..799dc79 Binary files /dev/null and b/xous-riscv/bin/riscv32imc-unknown-none-elf.a differ diff --git a/xous-riscv/bin/riscv64gc-unknown-none-elf.a b/xous-riscv/bin/riscv64gc-unknown-none-elf.a new file mode 100644 index 0000000..2231451 Binary files /dev/null and b/xous-riscv/bin/riscv64gc-unknown-none-elf.a differ diff --git a/xous-riscv/bin/riscv64imac-unknown-none-elf.a b/xous-riscv/bin/riscv64imac-unknown-none-elf.a new file mode 100644 index 0000000..2231451 Binary files /dev/null and b/xous-riscv/bin/riscv64imac-unknown-none-elf.a differ diff --git a/xous-riscv/build.rs b/xous-riscv/build.rs new file mode 100644 index 0000000..765c4da --- /dev/null +++ b/xous-riscv/build.rs @@ -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"); + } +} diff --git a/xous-riscv/src/asm.rs b/xous-riscv/src/asm.rs new file mode 100644 index 0000000..4a1f22d --- /dev/null +++ b/xous-riscv/src/asm.rs @@ -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!(), + } +} diff --git a/xous-riscv/src/interrupt.rs b/xous-riscv/src/interrupt.rs new file mode 100644 index 0000000..dcc04de --- /dev/null +++ b/xous-riscv/src/interrupt.rs @@ -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: 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 +} diff --git a/xous-riscv/src/lib.rs b/xous-riscv/src/lib.rs new file mode 100644 index 0000000..00bd91c --- /dev/null +++ b/xous-riscv/src/lib.rs @@ -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; diff --git a/xous-riscv/src/register/fcsr.rs b/xous-riscv/src/register/fcsr.rs new file mode 100644 index 0000000..e8e9559 --- /dev/null +++ b/xous-riscv/src/register/fcsr.rs @@ -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); +} diff --git a/xous-riscv/src/register/hpmcounterx.rs b/xous-riscv/src/register/hpmcounterx.rs new file mode 100644 index 0000000..d97ba2a --- /dev/null +++ b/xous-riscv/src/register/hpmcounterx.rs @@ -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); diff --git a/xous-riscv/src/register/macros.rs b/xous-riscv/src/register/macros.rs new file mode 100644 index 0000000..b58c310 --- /dev/null +++ b/xous-riscv/src/register/macros.rs @@ -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, + } + } + } +} diff --git a/xous-riscv/src/register/marchid.rs b/xous-riscv/src/register/marchid.rs new file mode 100644 index 0000000..00761ea --- /dev/null +++ b/xous-riscv/src/register/marchid.rs @@ -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 { + 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 }) +} diff --git a/xous-riscv/src/register/mcause.rs b/xous-riscv/src/register/mcause.rs new file mode 100644 index 0000000..d19b2e6 --- /dev/null +++ b/xous-riscv/src/register/mcause.rs @@ -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); diff --git a/xous-riscv/src/register/mcycle.rs b/xous-riscv/src/register/mcycle.rs new file mode 100644 index 0000000..a6ad46d --- /dev/null +++ b/xous-riscv/src/register/mcycle.rs @@ -0,0 +1,4 @@ +//! mcycle register + +read_csr_as_usize!(0xB00, __read_mcycle); +read_composite_csr!(super::mcycleh::read(), read()); diff --git a/xous-riscv/src/register/mcycleh.rs b/xous-riscv/src/register/mcycleh.rs new file mode 100644 index 0000000..0acd217 --- /dev/null +++ b/xous-riscv/src/register/mcycleh.rs @@ -0,0 +1,3 @@ +//! mcycleh register + +read_csr_as_usize_rv32!(0xB80, __read_mcycleh); diff --git a/xous-riscv/src/register/mepc.rs b/xous-riscv/src/register/mepc.rs new file mode 100644 index 0000000..5527419 --- /dev/null +++ b/xous-riscv/src/register/mepc.rs @@ -0,0 +1,4 @@ +//! mepc register + +read_csr_as_usize!(0x341, __read_mepc); +write_csr_as_usize!(0x341, __write_mepc); diff --git a/xous-riscv/src/register/mhartid.rs b/xous-riscv/src/register/mhartid.rs new file mode 100644 index 0000000..d129254 --- /dev/null +++ b/xous-riscv/src/register/mhartid.rs @@ -0,0 +1,3 @@ +//! mhartid register + +read_csr_as_usize!(0xf14, __read_mhartid); diff --git a/xous-riscv/src/register/mhpmcounterx.rs b/xous-riscv/src/register/mhpmcounterx.rs new file mode 100644 index 0000000..ce532dd --- /dev/null +++ b/xous-riscv/src/register/mhpmcounterx.rs @@ -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); diff --git a/xous-riscv/src/register/mhpmeventx.rs b/xous-riscv/src/register/mhpmeventx.rs new file mode 100644 index 0000000..2e8b976 --- /dev/null +++ b/xous-riscv/src/register/mhpmeventx.rs @@ -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); diff --git a/xous-riscv/src/register/mie.rs b/xous-riscv/src/register/mie.rs new file mode 100644 index 0000000..f03d8b0 --- /dev/null +++ b/xous-riscv/src/register/mie.rs @@ -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); diff --git a/xous-riscv/src/register/mimpid.rs b/xous-riscv/src/register/mimpid.rs new file mode 100644 index 0000000..1ba26cb --- /dev/null +++ b/xous-riscv/src/register/mimpid.rs @@ -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 { + 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 }) +} diff --git a/xous-riscv/src/register/minstret.rs b/xous-riscv/src/register/minstret.rs new file mode 100644 index 0000000..8ffb6f3 --- /dev/null +++ b/xous-riscv/src/register/minstret.rs @@ -0,0 +1,4 @@ +//! minstret register + +read_csr_as_usize!(0xB02, __read_minstret); +read_composite_csr!(super::minstreth::read(), read()); diff --git a/xous-riscv/src/register/minstreth.rs b/xous-riscv/src/register/minstreth.rs new file mode 100644 index 0000000..f0315b5 --- /dev/null +++ b/xous-riscv/src/register/minstreth.rs @@ -0,0 +1,3 @@ +//! minstreth register + +read_csr_as_usize_rv32!(0xB82, __read_minstreth); diff --git a/xous-riscv/src/register/mip.rs b/xous-riscv/src/register/mip.rs new file mode 100644 index 0000000..a595b38 --- /dev/null +++ b/xous-riscv/src/register/mip.rs @@ -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); diff --git a/xous-riscv/src/register/misa.rs b/xous-riscv/src/register/misa.rs new file mode 100644 index 0000000..bd9da1d --- /dev/null +++ b/xous-riscv/src/register/misa.rs @@ -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 { + 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 }) +} diff --git a/xous-riscv/src/register/mod.rs b/xous-riscv/src/register/mod.rs new file mode 100644 index 0000000..1660e41 --- /dev/null +++ b/xous-riscv/src/register/mod.rs @@ -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 diff --git a/xous-riscv/src/register/mscratch.rs b/xous-riscv/src/register/mscratch.rs new file mode 100644 index 0000000..533483b --- /dev/null +++ b/xous-riscv/src/register/mscratch.rs @@ -0,0 +1,4 @@ +//! mscratch register + +read_csr_as_usize!(0x340, __read_mscratch); +write_csr_as_usize!(0x340, __write_mscratch); diff --git a/xous-riscv/src/register/mstatus.rs b/xous-riscv/src/register/mstatus.rs new file mode 100644 index 0000000..21bd75d --- /dev/null +++ b/xous-riscv/src/register/mstatus.rs @@ -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 `f0–f31`. + #[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::() * 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); +} diff --git a/xous-riscv/src/register/mtval.rs b/xous-riscv/src/register/mtval.rs new file mode 100644 index 0000000..6c7fb28 --- /dev/null +++ b/xous-riscv/src/register/mtval.rs @@ -0,0 +1,3 @@ +//! mtval register + +read_csr_as_usize!(0x343, __read_mtval); diff --git a/xous-riscv/src/register/mtvec.rs b/xous-riscv/src/register/mtvec.rs new file mode 100644 index 0000000..b62ee4a --- /dev/null +++ b/xous-riscv/src/register/mtvec.rs @@ -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); +} diff --git a/xous-riscv/src/register/mvendorid.rs b/xous-riscv/src/register/mvendorid.rs new file mode 100644 index 0000000..f6eeba4 --- /dev/null +++ b/xous-riscv/src/register/mvendorid.rs @@ -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 { + 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 }) +} diff --git a/xous-riscv/src/register/pmpaddrx.rs b/xous-riscv/src/register/pmpaddrx.rs new file mode 100644 index 0000000..75f7984 --- /dev/null +++ b/xous-riscv/src/register/pmpaddrx.rs @@ -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); diff --git a/xous-riscv/src/register/pmpcfgx.rs b/xous-riscv/src/register/pmpcfgx.rs new file mode 100644 index 0000000..1dbcf8d --- /dev/null +++ b/xous-riscv/src/register/pmpcfgx.rs @@ -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); +} diff --git a/xous-riscv/src/register/satp.rs b/xous-riscv/src/register/satp.rs new file mode 100644 index 0000000..b22189c --- /dev/null +++ b/xous-riscv/src/register/satp.rs @@ -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); +} diff --git a/xous-riscv/src/register/scause.rs b/xous-riscv/src/register/scause.rs new file mode 100644 index 0000000..663309d --- /dev/null +++ b/xous-riscv/src/register/scause.rs @@ -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::() * 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::() * 8 - 1) + } + + /// Is trap cause an exception. + #[inline] + pub fn is_exception(&self) -> bool { + !self.is_interrupt() + } +} + +read_csr_as!(Scause, 0x142, __read_scause); diff --git a/xous-riscv/src/register/sepc.rs b/xous-riscv/src/register/sepc.rs new file mode 100644 index 0000000..934bf59 --- /dev/null +++ b/xous-riscv/src/register/sepc.rs @@ -0,0 +1,4 @@ +//! sepc register + +read_csr_as_usize!(0x141, __read_sepc); +write_csr_as_usize!(0x141, __write_sepc); diff --git a/xous-riscv/src/register/sie.rs b/xous-riscv/src/register/sie.rs new file mode 100644 index 0000000..47dbc89 --- /dev/null +++ b/xous-riscv/src/register/sie.rs @@ -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); diff --git a/xous-riscv/src/register/sip.rs b/xous-riscv/src/register/sip.rs new file mode 100644 index 0000000..a267584 --- /dev/null +++ b/xous-riscv/src/register/sip.rs @@ -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); diff --git a/xous-riscv/src/register/sscratch.rs b/xous-riscv/src/register/sscratch.rs new file mode 100644 index 0000000..fe36ce1 --- /dev/null +++ b/xous-riscv/src/register/sscratch.rs @@ -0,0 +1,4 @@ +//! sscratch register + +read_csr_as_usize!(0x140, __read_sscratch); +write_csr_as_usize!(0x140, __write_sscratch); diff --git a/xous-riscv/src/register/sstatus.rs b/xous-riscv/src/register/sstatus.rs new file mode 100644 index 0000000..0c5f53a --- /dev/null +++ b/xous-riscv/src/register/sstatus.rs @@ -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::() * 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); +} diff --git a/xous-riscv/src/register/stval.rs b/xous-riscv/src/register/stval.rs new file mode 100644 index 0000000..304047c --- /dev/null +++ b/xous-riscv/src/register/stval.rs @@ -0,0 +1,3 @@ +//! stval register + +read_csr_as_usize!(0x143, __read_stval); diff --git a/xous-riscv/src/register/stvec.rs b/xous-riscv/src/register/stvec.rs new file mode 100644 index 0000000..ae77132 --- /dev/null +++ b/xous-riscv/src/register/stvec.rs @@ -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); +} diff --git a/xous-riscv/src/register/time.rs b/xous-riscv/src/register/time.rs new file mode 100644 index 0000000..8fa15d0 --- /dev/null +++ b/xous-riscv/src/register/time.rs @@ -0,0 +1,4 @@ +//! time register + +read_csr_as_usize!(0xC01, __read_time); +read_composite_csr!(super::timeh::read(), read()); diff --git a/xous-riscv/src/register/timeh.rs b/xous-riscv/src/register/timeh.rs new file mode 100644 index 0000000..923bb50 --- /dev/null +++ b/xous-riscv/src/register/timeh.rs @@ -0,0 +1,3 @@ +//! timeh register + +read_csr_as_usize_rv32!(0xC81, __read_timeh); diff --git a/xous-riscv/src/register/ucause.rs b/xous-riscv/src/register/ucause.rs new file mode 100644 index 0000000..d3bbfcf --- /dev/null +++ b/xous-riscv/src/register/ucause.rs @@ -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); diff --git a/xous-riscv/src/register/uepc.rs b/xous-riscv/src/register/uepc.rs new file mode 100644 index 0000000..b511525 --- /dev/null +++ b/xous-riscv/src/register/uepc.rs @@ -0,0 +1,4 @@ +//! uepc register + +read_csr_as_usize!(0x041, __read_uepc); +write_csr_as_usize!(0x041, __write_uepc); diff --git a/xous-riscv/src/register/uie.rs b/xous-riscv/src/register/uie.rs new file mode 100644 index 0000000..81e0728 --- /dev/null +++ b/xous-riscv/src/register/uie.rs @@ -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); diff --git a/xous-riscv/src/register/uip.rs b/xous-riscv/src/register/uip.rs new file mode 100644 index 0000000..06b3886 --- /dev/null +++ b/xous-riscv/src/register/uip.rs @@ -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); diff --git a/xous-riscv/src/register/uscratch.rs b/xous-riscv/src/register/uscratch.rs new file mode 100644 index 0000000..a2d1cb0 --- /dev/null +++ b/xous-riscv/src/register/uscratch.rs @@ -0,0 +1,4 @@ +//! uscratch register + +read_csr_as_usize!(0x040, __read_uscratch); +write_csr_as_usize!(0x040, __write_uscratch); diff --git a/xous-riscv/src/register/ustatus.rs b/xous-riscv/src/register/ustatus.rs new file mode 100644 index 0000000..2995ebb --- /dev/null +++ b/xous-riscv/src/register/ustatus.rs @@ -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); diff --git a/xous-riscv/src/register/utval.rs b/xous-riscv/src/register/utval.rs new file mode 100644 index 0000000..c765f94 --- /dev/null +++ b/xous-riscv/src/register/utval.rs @@ -0,0 +1,3 @@ +//! utval register + +read_csr_as_usize!(0x043, __read_utval); diff --git a/xous-riscv/src/register/utvec.rs b/xous-riscv/src/register/utvec.rs new file mode 100644 index 0000000..a081035 --- /dev/null +++ b/xous-riscv/src/register/utvec.rs @@ -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); +}