From e8645818281703d5ff836bb51ac177140de5043a Mon Sep 17 00:00:00 2001 From: David Craven Date: Tue, 19 Sep 2017 16:04:12 +0200 Subject: [PATCH] Initial commit. --- .gitignore | 2 + Cargo.toml | 13 ++ README.md | 18 ++ src/asm.rs | 32 +++ src/csr.rs | 596 +++++++++++++++++++++++++++++++++++++++++++++++ src/interrupt.rs | 53 +++++ src/lib.rs | 18 ++ 7 files changed, 732 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/asm.rs create mode 100644 src/csr.rs create mode 100644 src/interrupt.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e7caa9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +target/ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7359c53 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "riscv" +version = "0.1.0" +repository = "https://github.com/dvc94ch/riscv" +authors = ["David Craven "] +categories = ["embedded", "hardware-support", "no-std"] +description = "Low level access to RISCV processors" +keywords = ["riscv", "register", "peripheral"] +license = "ISC" + +[dependencies] +bare-metal = "0.1.0" +volatile-register = "0.2.0" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..79424ad --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# `riscv` + +> Low level access to RISCV processors + +# License +Copyright 2017 David Craven + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/src/asm.rs b/src/asm.rs new file mode 100644 index 0000000..dce9600 --- /dev/null +++ b/src/asm.rs @@ -0,0 +1,32 @@ +//! Assembly instructions + +macro_rules! instruction { + ($fnname:ident, $asm:expr) => ( + #[inline(always)] + pub fn $fnname() { + match () { + #[cfg(target_arch = "riscv")] + () => unsafe { + asm!($asm :::: "volatile"); + }, + #[cfg(not(target_arch = "riscv"))] + () => {} + } + } + ) +} + + +/// User Level ISA instructions +instruction!(nop, "addi zero, zero, 0"); +instruction!(ecall, "ecall"); +instruction!(ebreak, "ebreak"); +instruction!(fence, "fence iorw, iorw"); +instruction!(fencei, "fence.i"); + +/// Priviledged ISA Instructions +instruction!(wfi, "wfi"); +instruction!(uret, "uret"); +instruction!(sret, "sret"); +instruction!(mret, "mret"); +instruction!(sfencevma, "sfence.vma"); diff --git a/src/csr.rs b/src/csr.rs new file mode 100644 index 0000000..745be51 --- /dev/null +++ b/src/csr.rs @@ -0,0 +1,596 @@ +//! Functions for accessing Control and Status Registers + +macro_rules! csr_asm { + ($op:ident, $csr:expr, $value:expr) => ( + { + let res: usize; + unsafe { + asm!(concat!(stringify!($op), " $0, ", stringify!($csr), ", $1") + : "=r"(res) + : "r"($value) + : + : "volatile"); + } + res + } + ) +} + +macro_rules! r { + ($MOD:ident, $TYPE:ident, $CSR:expr) => ( + pub struct R { + bits: u32, + } + + impl super::$TYPE { + #[inline(always)] + pub fn read(&self) -> R { + R { bits: csr_asm!(csrrs, $CSR, 0) as u32 } + } + } + + impl R { + #[inline(always)] + pub fn bits(&self) -> u32 { + self.bits + } + } + ) +} + +macro_rules! w { + ($MOD:ident, $TYPE:ident, $CSR:expr) => ( + macro_rules! func { + ($fnname:ident, $csrop:ident) => ( + #[inline(always)] + pub fn $fnname(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W, + { + let mut w = W { bits: 0 }; + f(&mut w); + csr_asm!($csrop, $CSR, w.bits as usize); + } + ) + } + + pub struct W { + bits: u32, + } + + impl super::$TYPE { + func!(write, csrrw); + func!(set, csrrs); + func!(clear, csrrc); + } + + impl W { + #[inline(always)] + pub fn bits(&mut self, value: u32) -> &mut W { + self.bits = value; + self + } + #[inline(always)] + pub fn set_bits(&mut self, value: u32) -> &mut W { + self.bits |= value; + self + } + #[inline(always)] + pub fn clear_bits(&mut self, value: u32) -> &mut W { + self.bits &= !value; + self + } + } + ) +} + +macro_rules! rw { + ($MOD:ident, $TYPE:ident, $CSR:expr) => ( + r!($MOD, $TYPE, $CSR); + w!($MOD, $TYPE, $CSR); + ) +} + +macro_rules! csr { + ($MOD:ident, $TYPE:ident, $CSR:expr, $MACRO:ident) => ( + pub struct $TYPE {} + #[allow(non_upper_case_globals)] + pub const $MOD: $TYPE = $TYPE {}; + + pub mod $MOD { + $MACRO!($MOD, $TYPE, $CSR); + } + ) +} + +/// User Trap Setup +csr!(ustatus, USTATUS, 0x000, rw); +csr!(uie, UIE, 0x004, rw); +csr!(utvec, UTVEC, 0x005, rw); +/// User Trap Handling +csr!(uscratch, USCRATCH, 0x040, rw); +csr!(uepc, UEPC, 0x041, rw); +csr!(ucause, UCAUSE, 0x042, rw); +csr!(utval, UTVAL, 0x043, rw); +csr!(uip, UIP, 0x044, r); +/// User Floating-Point CSRs +csr!(fflags, FFLAGS, 0x001, rw); +csr!(frm, FRM, 0x002, rw); +csr!(fcsr, FCSR, 0x003, rw); +/// User Counter/Timers +csr!(cycle, CYCLE, 0xC00, rw); +csr!(time, TIME, 0xC01, rw); +csr!(instret, INSTRET, 0xC02, rw); +// TODO: hpmcounter3 - hpmcounter31 +csr!(cycleh, CYCLEH, 0xC80, rw); +csr!(timeh, TIMEH, 0xC81, rw); +csr!(instreth, INSTRETH, 0xC82, rw); +// TODO: hpmcounter3h - hpmcounter31h + +/// Supervisor Trap Setup +csr!(sstatus, SSTATUS, 0x100, rw); +csr!(sedeleg, SEDELEG, 0x102, rw); +csr!(sideleg, SIDELEG, 0x103, rw); +csr!(sie, SIE, 0x104, rw); +csr!(stvec, STVEC, 0x105, rw); +csr!(scounteren, SCOUNTEREN, 0x106, rw); +/// Supervisor Trap Handling +csr!(sscratch, SSCRATCH, 0x140, rw); +csr!(sepc, SEPC, 0x141, rw); +csr!(scause, SCAUSE, 0x142, rw); +csr!(stval, STVAL, 0x143, rw); +csr!(sip, SIP, 0x144, r); +/// Supervisor Protection and Translation +csr!(satp, SATP, 0x180, rw); + +/// Machine Information Registers +csr!(mvendorid, MVENDORID, 0xF11, r); +csr!(marchid, MARCHID, 0xF12, r); +csr!(mimpid, MIMPID, 0xF13, r); +csr!(mhartid, MHARTID, 0xF14, r); +/// Machine Trap Setup +csr!(mstatus, MSTATUS, 0x300, rw); +csr!(misa, MISA, 0x301, r); +csr!(medeleg, MEDELEG, 0x302, rw); +csr!(mideleg, MIDELEG, 0x303, rw); +csr!(mie, MIE, 0x304, rw); +csr!(mtvec, MTVEC, 0x305, rw); +csr!(mcounteren, MCOUNTEREN, 0x306, rw); +/// Machine Trap Handling +csr!(mscratch, MSCRATCH, 0x340, rw); +csr!(mepc, MEPC, 0x341, rw); +csr!(mcause, MCAUSE, 0x342, rw); +csr!(mtval, MTVAL, 0x343, rw); +csr!(mip, MIP, 0x344, r); +/// Machine Protection and Translation +csr!(pmpcfg0, PMPCFG0, 0x3A0, rw); +csr!(pmpcfg1, PMPCFG1, 0x3A1, rw); +csr!(pmpcfg2, PMPCFG2, 0x3A2, rw); +csr!(pmpcfg3, PMPCFG3, 0x3A3, rw); +// TODO pmpaddr0 - pmpaddr15 + +/// Machine Counter/Timers +csr!(mcycle, MCYCLE, 0xB00, rw); +csr!(minstret, MINSTRET, 0xB02, rw); +// TODO mhpmcounter3 .. mhpmcounter31 +csr!(mcycleh, MCYCLEH, 0xB80, rw); +csr!(minstreth, MINSTRETH, 0xB82, rw); +// TODO mhpmcounter3h .. mhpmcounter31h +/// Machine Counter Setup +// TODO mhpmevent3 .. mhpmevent31 + +/// Debug/Trace Registers (shared with Debug Mode) +csr!(tselect, TSELECT, 0x7A0, rw); +csr!(tdata1, TDATA1, 0x7A1, rw); +csr!(tdata2, TDATA2, 0x7A2, rw); +csr!(tdata3, TDATA3, 0x7A3, rw); +/// Debug Mode Registers +csr!(dcsr, DCSR, 0x7B0, rw); +csr!(dpc, DPC, 0x7B1, rw); +csr!(dscratch, DSCRATCH, 0x7B2, rw); + +/// Machine Cause CSR (mcause) is ReadOnly. +/// Trap Cause +#[derive(Copy, Clone, Debug)] +pub enum Trap { + Interrupt(Interrupt), + Exception(Exception), +} + +/// Interrupt +#[derive(Copy, Clone, Debug)] +pub enum Interrupt { + UserSoft, + SupervisorSoft, + MachineSoft, + UserTimer, + SupervisorTimer, + MachineTimer, + UserExternal, + SupervisorExternal, + MachineExternal, +} + +impl Interrupt { + pub fn from(nr: u32) -> 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, + _ => unreachable!() + } + } +} + +/// Exception +#[derive(Copy, Clone, Debug)] +pub enum Exception { + InstructionMisaligned, + InstructionFault, + IllegalInstruction, + Breakpoint, + LoadMisaligned, + LoadFault, + StoreMisaligned, + StoreFault, + UserEnvCall, + SupervisorEnvCall, + MachineEnvCall, + InstructionPageFault, + LoadPageFault, + StorePageFault, +} + +impl Exception { + pub fn from(nr: u32) -> 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, + _ => unreachable!() + } + } +} + + +impl mcause::R { + #[inline(always)] + /// Trap Cause + pub fn cause(&self) -> Trap { + let code = self.bits() & !(1 << 31); + match self.bits() & (1 << 31) == 1 << 31 { + true => Trap::Interrupt(Interrupt::from(code)), + false => Trap::Exception(Exception::from(code)), + } + } + + #[inline(always)] + /// Is trap cause an interrupt. + pub fn is_interrupt(&self) -> bool { + match self.cause() { + Trap::Interrupt(_) => true, + _ => false, + } + } + + #[inline(always)] + /// Is trap cause an exception. + pub fn is_exception(&self) -> bool { + match self.cause() { + Trap::Exception(_) => true, + _ => false, + } + } +} + +/// Machine Status CSR is ReadWrite +// TODO: Virtualization, Memory Privilege and Extension Context Fields + +/// Machine Previous Privilege Mode +pub enum MPP { + Machine = 3, + Supervisor = 1, + User = 0, +} + +/// Supervisor Previous Privilege Mode +pub enum SPP { + Supervisor = 1, + User = 0, +} + + +impl mstatus::R { + #[inline(always)] + /// User Interrupt Enable + pub fn uie(&self) -> bool { + self.bits() & (1 << 0) == 1 << 0 + } + + #[inline(always)] + /// Supervisor Interrupt Enable + pub fn sie(&self) -> bool { + self.bits() & (1 << 1) == 1 << 1 + } + + #[inline(always)] + /// Machine Interrupt Enable + pub fn mie(&self) -> bool { + self.bits() & (1 << 3) == 1 << 3 + } + + #[inline(always)] + /// User Previous Interrupt Enable + pub fn upie(&self) -> bool { + self.bits() & (1 << 4) == 1 << 4 + } + + #[inline(always)] + /// Supervisor Previous Interrupt Enable + pub fn spie(&self) -> bool { + self.bits() & (1 << 5) == 1 << 5 + } + + #[inline(always)] + /// User Previous Interrupt Enable + pub fn mpie(&self) -> bool { + self.bits() & (1 << 7) == 1 << 7 + } + + #[inline(always)] + /// Supervisor Previous Privilege Mode + pub fn spp(&self) -> SPP { + match self.bits() & (1 << 8) == (1 << 8) { + true => SPP::Supervisor, + false => SPP::User, + } + } + + #[inline(always)] + /// Machine Previous Privilege Mode + pub fn mpp(&self) -> MPP { + match (self.bits() & (0b11 << 11)) >> 11 { + 0b00 => MPP::User, + 0b01 => MPP::Supervisor, + 0b11 => MPP::Machine, + _ => unreachable!(), + } + } +} + +impl mstatus::W { + #[inline(always)] + /// User Interrupt Enable + pub fn uie(&mut self) -> &mut mstatus::W { + self.set_bits(1 << 0) + } + + #[inline(always)] + /// Supervisor Interrupt Enable + pub fn sie(&mut self) -> &mut mstatus::W { + self.set_bits(1 << 1) + } + + #[inline(always)] + /// Machine Interrupt Enable + pub fn mie(&mut self) -> &mut mstatus::W { + self.set_bits(1 << 3) + } + + #[inline(always)] + /// User Previous Interrupt Enable + pub fn upie(&mut self) -> &mut mstatus::W { + self.set_bits(1 << 4) + } + + #[inline(always)] + /// User Previous Interrupt Enable + pub fn spie(&mut self) -> &mut mstatus::W { + self.set_bits(1 << 5) + } + + #[inline(always)] + /// User Previous Interrupt Enable + pub fn mpie(&mut self) -> &mut mstatus::W { + self.set_bits(1 << 7) + } + + #[inline(always)] + /// Supervisor Previous Privilege Mode + pub fn spp(&mut self, value: SPP) -> &mut mstatus::W { + self.set_bits((value as u32) << 8) + } + + #[inline(always)] + /// Machine Previous Privilege Mode + pub fn mpp(&mut self, value: MPP) -> &mut mstatus::W { + self.set_bits((value as u32) << 11) + } +} + +/// Machine Interrupt Enable CSR (mie) is ReadWrite. +impl mie::R { + #[inline(always)] + /// User Software Interrupt Enable + pub fn usoft(&self) -> bool { + self.bits() & (1 << 0) == 1 << 0 + } + + #[inline(always)] + /// Supervisor Software Interrupt Enable + pub fn ssoft(&self) -> bool { + self.bits() & (1 << 1) == 1 << 1 + } + + #[inline(always)] + /// Machine Software Interrupt Enable + pub fn msoft(&self) -> bool { + self.bits() & (1 << 3) == 1 << 3 + } + + #[inline(always)] + /// User Timer Interrupt Enable + pub fn utimer(&self) -> bool { + self.bits() & (1 << 4) == 1 << 4 + } + + #[inline(always)] + /// Supervisor Timer Interrupt Enable + pub fn stimer(&self) -> bool { + self.bits() & (1 << 5) == 1 << 5 + } + + #[inline(always)] + /// Machine Timer Interrupt Enable + pub fn mtimer(&self) -> bool { + self.bits() & (1 << 7) == 1 << 7 + } + + #[inline(always)] + /// User External Interrupt Enable + pub fn uext(&self) -> bool { + self.bits() & (1 << 8) == 1 << 8 + } + + #[inline(always)] + /// Supervisor External Interrupt Enable + pub fn sext(&self) -> bool { + self.bits() & (1 << 9) == 1 << 9 + } + + #[inline(always)] + /// Machine External Interrupt Enable + pub fn mext(&self) -> bool { + self.bits() & (1 << 11) == 1 << 11 + } +} + +impl mie::W { + #[inline(always)] + /// User Software Interrupt Enable + pub fn usoft(&mut self) -> &mut mie::W { + self.set_bits(1 << 0) + } + + #[inline(always)] + /// Supervisor Software Interrupt Enable + pub fn ssoft(&mut self) -> &mut mie::W { + self.set_bits(1 << 1) + } + + #[inline(always)] + /// Machine Software Interrupt Enable + pub fn msoft(&mut self) -> &mut mie::W { + self.set_bits(1 << 3) + } + + #[inline(always)] + /// User Timer Interrupt Enable + pub fn utimer(&mut self) -> &mut mie::W { + self.set_bits(1 << 4) + } + + #[inline(always)] + /// Supervisor Timer Interrupt Enable + pub fn stimer(&mut self) -> &mut mie::W { + self.set_bits(1 << 5) + } + + #[inline(always)] + /// Machine Timer Interrupt Enable + pub fn mtimer(&mut self) -> &mut mie::W { + self.set_bits(1 << 7) + } + + #[inline(always)] + /// User External Interrupt Enable + pub fn uext(&mut self) -> &mut mie::W { + self.set_bits(1 << 8) + } + + #[inline(always)] + /// Supervisor External Interrupt Enable + pub fn sext(&mut self) -> &mut mie::W { + self.set_bits(1 << 9) + } + + #[inline(always)] + /// Machine External Interrupt Enable + pub fn mext(&mut self) -> &mut mie::W { + self.set_bits(1 << 11) + } +} + +/// Machine Interrupt Pending CSR (mip) is ReadOnly. +impl mip::R { + #[inline(always)] + /// User Software Interrupt Enable + pub fn usoft(&self) -> bool { + self.bits() & (1 << 0) == 1 << 0 + } + + #[inline(always)] + /// Supervisor Software Interrupt Enable + pub fn ssoft(&self) -> bool { + self.bits() & (1 << 1) == 1 << 1 + } + + #[inline(always)] + /// Machine Software Interrupt Enable + pub fn msoft(&self) -> bool { + self.bits() & (1 << 3) == 1 << 3 + } + + #[inline(always)] + /// User Timer Interrupt Enable + pub fn utimer(&self) -> bool { + self.bits() & (1 << 4) == 1 << 4 + } + + #[inline(always)] + /// Supervisor Timer Interrupt Enable + pub fn stimer(&self) -> bool { + self.bits() & (1 << 5) == 1 << 5 + } + + #[inline(always)] + /// Machine Timer Interrupt Enable + pub fn mtimer(&self) -> bool { + self.bits() & (1 << 7) == 1 << 7 + } + + #[inline(always)] + /// User External Interrupt Enable + pub fn uext(&self) -> bool { + self.bits() & (1 << 8) == 1 << 8 + } + + #[inline(always)] + /// Supervisor External Interrupt Enable + pub fn sext(&self) -> bool { + self.bits() & (1 << 9) == 1 << 9 + } + + #[inline(always)] + /// Machine External Interrupt Enable + pub fn mext(&self) -> bool { + self.bits() & (1 << 11) == 1 << 11 + } +} diff --git a/src/interrupt.rs b/src/interrupt.rs new file mode 100644 index 0000000..20db4d5 --- /dev/null +++ b/src/interrupt.rs @@ -0,0 +1,53 @@ +//! Interrupts + +// NOTE: Adapted from cortex-m/src/interrupt.rs +pub use bare_metal::{CriticalSection, Mutex, Nr}; + +/// Disables all interrupts +#[inline(always)] +pub fn disable() { + match () { + #[cfg(target_arch = "riscv")] + () => ::csr::mstatus.clear(|w| w.mie()), + #[cfg(not(target_arch = "riscv"))] + () => {} + } +} + +/// Enables all the interrupts +/// +/// # Safety +/// +/// - Do not call this function inside an `interrupt::free` critical section +#[inline(always)] +pub unsafe fn enable() { + match () { + #[cfg(target_arch = "riscv")] + () => ::csr::mstatus.set(|w| w.mie()), + #[cfg(not(target_arch = "riscv"))] + () => {} + } +} + +/// 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 = ::csr::mstatus.read(); + + // disable interrupts + 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/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..fa10060 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,18 @@ +//! Low level access to RISCV processors +//! +//! This crate provides: +//! +//! - Access to core registers like mstatus or mcause. +//! - Interrupt manipulation mechanisms. +//! - Safe wrappers around assembly instructions like `mret`. + +#![no_std] +#![deny(warnings)] +#![feature(asm)] +#![feature(const_fn)] + +extern crate bare_metal; + +pub mod asm; +pub mod csr; +pub mod interrupt;