diff --git a/Cargo.toml b/Cargo.toml index 7887b0b..6ac48dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,5 +9,4 @@ keywords = ["riscv", "register", "peripheral"] license = "ISC" [dependencies] -bare-metal = "^0.1.1" -volatile-register = "^0.2.0" \ No newline at end of file +bare-metal = "0.1.1" \ No newline at end of file diff --git a/README.md b/README.md index 79424ad..93fb329 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,85 @@ > Low level access to RISCV processors +## Implemented Peripherals +- [ ] plic +- [ ] clint + +## Implemented privileged ASM instructions +- [x] ecall +- [x] ebreak +- [x] uret +- [x] sret +- [x] mret +- [x] wfi +- [ ] sfence.vma + +## Implemented CSR's + +### User mode +- [ ] ustatus +- [ ] uie +- [ ] utvec +- [ ] uscratch +- [ ] uepc +- [ ] ucause +- [ ] utval +- [ ] uip +- [ ] fflags +- [ ] frm +- [ ] fcsr +- [ ] cycle +- [ ] time +- [ ] instret +- [ ] hpmcounter[3-31] +- [ ] cycleh +- [ ] timeh +- [ ] instreth +- [ ] hpmcounter[3-31]h + +### Supervisor mode +- [ ] sstatus +- [ ] sedeleg +- [ ] sideleg +- [ ] sie +- [ ] stvec +- [ ] scounteren +- [ ] sscratch +- [ ] sepc +- [ ] scause +- [ ] stval +- [ ] sip +- [ ] satp + +### Machine mode +- [x] mvendorid +- [ ] marchid +- [ ] mimpid +- [ ] mhartid +- [x] mstatus +- [x] misa +- [ ] medeleg +- [ ] mideleg +- [x] mie +- [x] mtvec +- [ ] mcounteren +- [ ] mscratch +- [ ] mepc +- [x] mcause +- [ ] mtval +- [x] mip +- [ ] pmpcfg[0-3] +- [ ] pmpaddr[0-15] +- [x] mcycle +- [x] minstret +- [ ] mhpmcounter[3-31] +- [x] mcycleh +- [x] minstreth +- [ ] mhpmcounter[3-31]h +- [ ] mhpmevent[3-31] +- [ ] tselect +- [ ] tdata[1-3] + # License Copyright 2017 David Craven diff --git a/src/asm.rs b/src/asm.rs index a50b623..7f29cd5 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -17,16 +17,10 @@ macro_rules! instruction { } -/// User Level ISA instructions -instruction!(nop, "addi zero, zero, 0"); +/// Priviledged ISA Instructions 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"); +instruction!(wfi, "wfi"); diff --git a/src/csr.rs b/src/csr.rs deleted file mode 100644 index 4ef9e1c..0000000 --- a/src/csr.rs +++ /dev/null @@ -1,606 +0,0 @@ -//! Functions for accessing Control and Status Registers - -#[cfg(target_arch = "riscv")] -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 - } - ) -} - - -#[cfg(not(target_arch = "riscv"))] -macro_rules! csr_asm { - ($op:ident, $csr:expr, $value:expr) => { - 0 - } -} - -macro_rules! r { - ($MOD:ident, $TYPE:ident, $CSR:expr) => ( - pub struct R { - bits: u32, - } - - impl super::$TYPE { - #[inline] - pub fn read(&self) -> R { - R { bits: csr_asm!(csrrs, $CSR, 0) as u32 } - } - } - - impl R { - #[inline] - 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] - pub fn bits(&mut self, value: u32) -> &mut W { - self.bits = value; - self - } - #[inline] - pub fn set_bits(&mut self, value: u32) -> &mut W { - self.bits |= value; - self - } - #[inline] - 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, r); -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] - /// Trap Cause - pub fn cause(&self) -> Trap { - let bits = self.bits(); - let code = bits & !(1 << 31); - match bits & (1 << 31) == 1 << 31 { - true => Trap::Interrupt(Interrupt::from(code)), - false => Trap::Exception(Exception::from(code)), - } - } - - #[inline] - /// Is trap cause an interrupt. - pub fn is_interrupt(&self) -> bool { - match self.cause() { - Trap::Interrupt(_) => true, - _ => false, - } - } - - #[inline] - /// 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] - /// User Interrupt Enable - pub fn uie(&self) -> bool { - self.bits() & (1 << 0) == 1 << 0 - } - - #[inline] - /// Supervisor Interrupt Enable - pub fn sie(&self) -> bool { - self.bits() & (1 << 1) == 1 << 1 - } - - #[inline] - /// Machine Interrupt Enable - pub fn mie(&self) -> bool { - self.bits() & (1 << 3) == 1 << 3 - } - - #[inline] - /// User Previous Interrupt Enable - pub fn upie(&self) -> bool { - self.bits() & (1 << 4) == 1 << 4 - } - - #[inline] - /// Supervisor Previous Interrupt Enable - pub fn spie(&self) -> bool { - self.bits() & (1 << 5) == 1 << 5 - } - - #[inline] - /// User Previous Interrupt Enable - pub fn mpie(&self) -> bool { - self.bits() & (1 << 7) == 1 << 7 - } - - #[inline] - /// Supervisor Previous Privilege Mode - pub fn spp(&self) -> SPP { - match self.bits() & (1 << 8) == (1 << 8) { - true => SPP::Supervisor, - false => SPP::User, - } - } - - #[inline] - /// 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] - /// User Interrupt Enable - pub fn uie(&mut self) -> &mut mstatus::W { - self.set_bits(1 << 0) - } - - #[inline] - /// Supervisor Interrupt Enable - pub fn sie(&mut self) -> &mut mstatus::W { - self.set_bits(1 << 1) - } - - #[inline] - /// Machine Interrupt Enable - pub fn mie(&mut self) -> &mut mstatus::W { - self.set_bits(1 << 3) - } - - #[inline] - /// User Previous Interrupt Enable - pub fn upie(&mut self) -> &mut mstatus::W { - self.set_bits(1 << 4) - } - - #[inline] - /// User Previous Interrupt Enable - pub fn spie(&mut self) -> &mut mstatus::W { - self.set_bits(1 << 5) - } - - #[inline] - /// User Previous Interrupt Enable - pub fn mpie(&mut self) -> &mut mstatus::W { - self.set_bits(1 << 7) - } - - #[inline] - /// Supervisor Previous Privilege Mode - pub fn spp(&mut self, value: SPP) -> &mut mstatus::W { - self.set_bits((value as u32) << 8) - } - - #[inline] - /// 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] - /// User Software Interrupt Enable - pub fn usoft(&self) -> bool { - self.bits() & (1 << 0) == 1 << 0 - } - - #[inline] - /// Supervisor Software Interrupt Enable - pub fn ssoft(&self) -> bool { - self.bits() & (1 << 1) == 1 << 1 - } - - #[inline] - /// Machine Software Interrupt Enable - pub fn msoft(&self) -> bool { - self.bits() & (1 << 3) == 1 << 3 - } - - #[inline] - /// User Timer Interrupt Enable - pub fn utimer(&self) -> bool { - self.bits() & (1 << 4) == 1 << 4 - } - - #[inline] - /// Supervisor Timer Interrupt Enable - pub fn stimer(&self) -> bool { - self.bits() & (1 << 5) == 1 << 5 - } - - #[inline] - /// Machine Timer Interrupt Enable - pub fn mtimer(&self) -> bool { - self.bits() & (1 << 7) == 1 << 7 - } - - #[inline] - /// User External Interrupt Enable - pub fn uext(&self) -> bool { - self.bits() & (1 << 8) == 1 << 8 - } - - #[inline] - /// Supervisor External Interrupt Enable - pub fn sext(&self) -> bool { - self.bits() & (1 << 9) == 1 << 9 - } - - #[inline] - /// Machine External Interrupt Enable - pub fn mext(&self) -> bool { - self.bits() & (1 << 11) == 1 << 11 - } -} - -impl mie::W { - #[inline] - /// User Software Interrupt Enable - pub fn usoft(&mut self) -> &mut mie::W { - self.set_bits(1 << 0) - } - - #[inline] - /// Supervisor Software Interrupt Enable - pub fn ssoft(&mut self) -> &mut mie::W { - self.set_bits(1 << 1) - } - - #[inline] - /// Machine Software Interrupt Enable - pub fn msoft(&mut self) -> &mut mie::W { - self.set_bits(1 << 3) - } - - #[inline] - /// User Timer Interrupt Enable - pub fn utimer(&mut self) -> &mut mie::W { - self.set_bits(1 << 4) - } - - #[inline] - /// Supervisor Timer Interrupt Enable - pub fn stimer(&mut self) -> &mut mie::W { - self.set_bits(1 << 5) - } - - #[inline] - /// Machine Timer Interrupt Enable - pub fn mtimer(&mut self) -> &mut mie::W { - self.set_bits(1 << 7) - } - - #[inline] - /// User External Interrupt Enable - pub fn uext(&mut self) -> &mut mie::W { - self.set_bits(1 << 8) - } - - #[inline] - /// Supervisor External Interrupt Enable - pub fn sext(&mut self) -> &mut mie::W { - self.set_bits(1 << 9) - } - - #[inline] - /// 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] - /// User Software Interrupt Enable - pub fn usoft(&self) -> bool { - self.bits() & (1 << 0) == 1 << 0 - } - - #[inline] - /// Supervisor Software Interrupt Enable - pub fn ssoft(&self) -> bool { - self.bits() & (1 << 1) == 1 << 1 - } - - #[inline] - /// Machine Software Interrupt Enable - pub fn msoft(&self) -> bool { - self.bits() & (1 << 3) == 1 << 3 - } - - #[inline] - /// User Timer Interrupt Enable - pub fn utimer(&self) -> bool { - self.bits() & (1 << 4) == 1 << 4 - } - - #[inline] - /// Supervisor Timer Interrupt Enable - pub fn stimer(&self) -> bool { - self.bits() & (1 << 5) == 1 << 5 - } - - #[inline] - /// Machine Timer Interrupt Enable - pub fn mtimer(&self) -> bool { - self.bits() & (1 << 7) == 1 << 7 - } - - #[inline] - /// User External Interrupt Enable - pub fn uext(&self) -> bool { - self.bits() & (1 << 8) == 1 << 8 - } - - #[inline] - /// Supervisor External Interrupt Enable - pub fn sext(&self) -> bool { - self.bits() & (1 << 9) == 1 << 9 - } - - #[inline] - /// 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 index d3adae2..edb2be9 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -2,13 +2,14 @@ // NOTE: Adapted from cortex-m/src/interrupt.rs pub use bare_metal::{CriticalSection, Mutex, Nr}; +use register::mstatus; /// Disables all interrupts #[inline] -pub fn disable() { +pub unsafe fn disable() { match () { #[cfg(target_arch = "riscv")] - () => ::csr::mstatus.clear(|w| w.mie()), + () => mstatus::clear_mie(), #[cfg(not(target_arch = "riscv"))] () => {} } @@ -23,7 +24,7 @@ pub fn disable() { pub unsafe fn enable() { match () { #[cfg(target_arch = "riscv")] - () => ::csr::mstatus.set(|w| w.mie()), + () => mstatus::set_mie(), #[cfg(not(target_arch = "riscv"))] () => {} } @@ -36,17 +37,17 @@ pub fn free(f: F) -> R where F: FnOnce(&CriticalSection) -> R, { - let mstatus = ::csr::mstatus.read(); + let mstatus = mstatus::read(); // disable interrupts - disable(); + 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() } + unsafe { enable(); } } r diff --git a/src/lib.rs b/src/lib.rs index fa10060..c0f93d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,5 +14,5 @@ extern crate bare_metal; pub mod asm; -pub mod csr; pub mod interrupt; +pub mod register; diff --git a/src/register/mcause.rs b/src/register/mcause.rs new file mode 100644 index 0000000..fef8530 --- /dev/null +++ b/src/register/mcause.rs @@ -0,0 +1,154 @@ +//! mcause register + +/// mcause register +#[derive(Clone, Copy, Debug)] +pub struct Mcause { + bits: usize, +} + +/// 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, + Unknown, +} + +/// Exception +#[derive(Copy, Clone, Debug)] +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() + } +} + +/// Reads the CSR +#[inline] +pub fn read() -> Mcause { + match () { + #[cfg(target_arch = "riscv")] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0x342, x0" : "=r"(r) ::: "volatile"); + } + Mcause { bits: r } + } + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} diff --git a/src/register/mcycle.rs b/src/register/mcycle.rs new file mode 100644 index 0000000..d98da32 --- /dev/null +++ b/src/register/mcycle.rs @@ -0,0 +1,18 @@ +//! mcycle register + +/// Reads the CSR +#[inline] +pub fn read() -> usize { + match () { + #[cfg(target_arch = "riscv")] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0xB00, x0" : "=r"(r) ::: "volatile"); + } + r + } + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} diff --git a/src/register/mcycleh.rs b/src/register/mcycleh.rs new file mode 100644 index 0000000..e03f6e5 --- /dev/null +++ b/src/register/mcycleh.rs @@ -0,0 +1,18 @@ +//! mcycleh register + +/// Reads the CSR +#[inline] +pub fn read() -> usize { + match () { + #[cfg(all(target_arch = "riscv", target_pointer_width = "32"))] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0xB80, x0" : "=r"(r) ::: "volatile"); + } + r + } + #[cfg(any(not(target_arch = "riscv"), not(target_pointer_width = "32")))] + () => unimplemented!(), + } +} diff --git a/src/register/mie.rs b/src/register/mie.rs new file mode 100644 index 0000000..d572976 --- /dev/null +++ b/src/register/mie.rs @@ -0,0 +1,154 @@ +//! mie register + +/// 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 & (1 << 0) == 1 << 0 + } + + /// Supervisor Software Interrupt Enable + #[inline] + pub fn ssoft(&self) -> bool { + self.bits & (1 << 1) == 1 << 1 + } + + /// Machine Software Interrupt Enable + #[inline] + pub fn msoft(&self) -> bool { + self.bits & (1 << 3) == 1 << 3 + } + + /// User Timer Interrupt Enable + #[inline] + pub fn utimer(&self) -> bool { + self.bits & (1 << 4) == 1 << 4 + } + + /// Supervisor Timer Interrupt Enable + #[inline] + pub fn stimer(&self) -> bool { + self.bits & (1 << 5) == 1 << 5 + } + + /// Machine Timer Interrupt Enable + #[inline] + pub fn mtimer(&self) -> bool { + self.bits & (1 << 7) == 1 << 7 + } + + /// User External Interrupt Enable + #[inline] + pub fn uext(&self) -> bool { + self.bits & (1 << 8) == 1 << 8 + } + + /// Supervisor External Interrupt Enable + #[inline] + pub fn sext(&self) -> bool { + self.bits & (1 << 9) == 1 << 9 + } + + /// Machine External Interrupt Enable + #[inline] + pub fn mext(&self) -> bool { + self.bits & (1 << 11) == 1 << 11 + } +} + +/// Reads the CSR +#[inline] +pub fn read() -> Mie { + match () { + #[cfg(target_arch = "riscv")] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0x304, x0" : "=r"(r) ::: "volatile"); + } + Mie { bits: r } + } + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} + +/// Sets the CSR +#[cfg_attr(not(target_arch = "riscv"), allow(unused_variables))] +#[inline] +unsafe fn set(bits: usize) { + match () { + #[cfg(target_arch = "riscv")] + () => asm!("csrrs x0, 0x304, $0" :: "r"(bits) :: "volatile"), + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} + +/// Clears the CSR +#[cfg_attr(not(target_arch = "riscv"), allow(unused_variables))] +#[inline] +unsafe fn clear(bits: usize) { + match () { + #[cfg(target_arch = "riscv")] + () => asm!("csrrc x0, 0x304, $0" :: "r"(bits) :: "volatile"), + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} + +macro_rules! set_csr { + ($set_field:ident, $e:expr) => { + #[inline] + pub unsafe fn $set_field() { + set($e); + } + } +} + +macro_rules! clear_csr { + ($clear_field:ident, $e:expr) => { + #[inline] + pub unsafe fn $clear_field() { + clear($e); + } + } +} + +macro_rules! set_clear_csr { + ($set_field:ident, $clear_field:ident, $e:expr) => { + set_csr!($set_field, $e); + clear_csr!($clear_field, $e); + } +} + +/// User Software Interrupt Enable +set_clear_csr!(set_usoft, clear_usoft, 1 << 0); +/// Supervisor Software Interrupt Enable +set_clear_csr!(set_ssoft, clear_ssoft, 1 << 1); +/// Machine Software Interrupt Enable +set_clear_csr!(set_msoft, clear_msoft, 1 << 3); +/// User Timer Interrupt Enable +set_clear_csr!(set_utimer, clear_utimer, 1 << 4); +/// Supervisor Timer Interrupt Enable +set_clear_csr!(set_stimer, clear_stimer, 1 << 5); +/// Machine Timer Interrupt Enable +set_clear_csr!(set_mtimer, clear_mtimer, 1 << 7); +/// User External Interrupt Enable +set_clear_csr!(set_uext, clear_uext, 1 << 8); +/// Supervisor External Interrupt Enable +set_clear_csr!(set_sext, clear_sext, 1 << 9); +/// Machine External Interrupt Enable +set_clear_csr!(set_mext, clear_mext, 1 << 11); diff --git a/src/register/minstret.rs b/src/register/minstret.rs new file mode 100644 index 0000000..3576800 --- /dev/null +++ b/src/register/minstret.rs @@ -0,0 +1,18 @@ +//! minstret register + +/// Reads the CSR +#[inline] +pub fn read() -> usize { + match () { + #[cfg(target_arch = "riscv")] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0xB02, x0" : "=r"(r) ::: "volatile"); + } + r + } + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} diff --git a/src/register/minstreth.rs b/src/register/minstreth.rs new file mode 100644 index 0000000..8ec4afa --- /dev/null +++ b/src/register/minstreth.rs @@ -0,0 +1,18 @@ +//! minstreth register + +/// Reads the CSR +#[inline] +pub fn read() -> usize { + match () { + #[cfg(all(target_arch = "riscv", target_pointer_width = "32"))] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0xB82, x0" : "=r"(r) ::: "volatile"); + } + r + }, + #[cfg(any(not(target_arch = "riscv"), not(target_pointer_width = "32")))] + () => unimplemented!(), + } +} diff --git a/src/register/mip.rs b/src/register/mip.rs new file mode 100644 index 0000000..09eb10a --- /dev/null +++ b/src/register/mip.rs @@ -0,0 +1,86 @@ +//! mip register + +/// 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 & (1 << 0) == 1 << 0 + } + + /// Supervisor Software Interrupt Pending + #[inline] + pub fn ssoft(&self) -> bool { + self.bits & (1 << 1) == 1 << 1 + } + + /// Machine Software Interrupt Pending + #[inline] + pub fn msoft(&self) -> bool { + self.bits & (1 << 3) == 1 << 3 + } + + /// User Timer Interrupt Pending + #[inline] + pub fn utimer(&self) -> bool { + self.bits & (1 << 4) == 1 << 4 + } + + /// Supervisor Timer Interrupt Pending + #[inline] + pub fn stimer(&self) -> bool { + self.bits & (1 << 5) == 1 << 5 + } + + /// Machine Timer Interrupt Pending + #[inline] + pub fn mtimer(&self) -> bool { + self.bits & (1 << 7) == 1 << 7 + } + + /// User External Interrupt Pending + #[inline] + pub fn uext(&self) -> bool { + self.bits & (1 << 8) == 1 << 8 + } + + /// Supervisor External Interrupt Pending + #[inline] + pub fn sext(&self) -> bool { + self.bits & (1 << 9) == 1 << 9 + } + + /// Machine External Interrupt Pending + #[inline] + pub fn mext(&self) -> bool { + self.bits & (1 << 11) == 1 << 11 + } +} + +/// Reads the CSR +#[inline] +pub fn read() -> Mip { + match () { + #[cfg(target_arch = "riscv")] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0x344, x0" : "=r"(r) ::: "volatile"); + } + Mip { bits: r } + } + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} diff --git a/src/register/misa.rs b/src/register/misa.rs new file mode 100644 index 0000000..eb3f246 --- /dev/null +++ b/src/register/misa.rs @@ -0,0 +1,69 @@ +//! misa register + +/// misa register +#[derive(Clone, Copy, Debug)] +pub struct Misa { + bits: usize, +} + +/// Machine XLEN +pub enum MXL { + XLEN32, + XLEN64, + XLEN128, +} + +impl Misa { + /// Returns the contents of the register as raw bits + pub fn bits(&self) -> usize { + self.bits + } + + /// 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_widht = "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) + } +} + +/// Reads the CSR +#[inline] +pub fn read() -> Option { + match () { + #[cfg(target_arch = "riscv")] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0x301, x0" : "=r"(r) ::: "volatile"); + } + // When misa is hardwired to zero it means that the misa csr + // isn't implemented. + if r == 0 { + None + } else { + Some(Misa { bits: r }) + } + }, + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} diff --git a/src/register/mod.rs b/src/register/mod.rs new file mode 100644 index 0000000..df94575 --- /dev/null +++ b/src/register/mod.rs @@ -0,0 +1,23 @@ +//! RISCV 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 + +pub mod mcause; +pub mod mcycle; +pub mod mcycleh; +pub mod mie; +pub mod mip; +pub mod minstret; +pub mod minstreth; +pub mod misa; +pub mod mstatus; +pub mod mtvec; +pub mod mvendorid; diff --git a/src/register/mstatus.rs b/src/register/mstatus.rs new file mode 100644 index 0000000..b49bebb --- /dev/null +++ b/src/register/mstatus.rs @@ -0,0 +1,169 @@ +//! mstatus register +// TODO: Virtualization, Memory Privilege and Extension Context Fields + +/// mstatus register +#[derive(Clone, Copy, Debug)] +pub struct Mstatus { + bits: usize, +} + +/// 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 { + /// User Interrupt Enable + #[inline] + pub fn uie(&self) -> bool { + self.bits & (1 << 0) == 1 << 0 + } + + /// Supervisor Interrupt Enable + #[inline] + pub fn sie(&self) -> bool { + self.bits & (1 << 1) == 1 << 1 + } + + /// Machine Interrupt Enable + #[inline] + pub fn mie(&self) -> bool { + self.bits & (1 << 3) == 1 << 3 + } + + /// User Previous Interrupt Enable + #[inline] + pub fn upie(&self) -> bool { + self.bits & (1 << 4) == 1 << 4 + } + + /// Supervisor Previous Interrupt Enable + #[inline] + pub fn spie(&self) -> bool { + self.bits & (1 << 5) == 1 << 5 + } + + /// User Previous Interrupt Enable + #[inline] + pub fn mpie(&self) -> bool { + self.bits & (1 << 7) == 1 << 7 + } + + /// Supervisor Previous Privilege Mode + #[inline] + pub fn spp(&self) -> SPP { + match self.bits & (1 << 8) == (1 << 8) { + true => SPP::Supervisor, + false => SPP::User, + } + } + + /// Machine Previous Privilege Mode + #[inline] + pub fn mpp(&self) -> MPP { + match (self.bits & (0b11 << 11)) >> 11 { + 0b00 => MPP::User, + 0b01 => MPP::Supervisor, + 0b11 => MPP::Machine, + _ => unreachable!(), + } + } +} + + +/// Reads the CSR +#[inline] +pub fn read() -> Mstatus { + match () { + #[cfg(target_arch = "riscv")] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0x300, x0" : "=r"(r) ::: "volatile"); + } + Mstatus { bits: r } + } + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} + +/// Sets the CSR +#[cfg_attr(not(target_arch = "riscv"), allow(unused_variables))] +#[inline] +unsafe fn set(bits: usize) { + match () { + #[cfg(target_arch = "riscv")] + () => asm!("csrrs x0, 0x305, $0" :: "r"(bits) :: "volatile"), + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} + +/// Clears the CSR +#[cfg_attr(not(target_arch = "riscv"), allow(unused_variables))] +#[inline] +unsafe fn clear(bits: usize) { + match () { + #[cfg(target_arch = "riscv")] + () => asm!("csrrc x0, 0x305, $0" :: "r"(bits) :: "volatile"), + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} + +macro_rules! set_csr { + ($set_field:ident, $e:expr) => { + #[inline] + pub unsafe fn $set_field() { + set($e); + } + } +} + +macro_rules! clear_csr { + ($clear_field:ident, $e:expr) => { + #[inline] + pub unsafe fn $clear_field() { + clear($e); + } + } +} + +macro_rules! set_clear_csr { + ($set_field:ident, $clear_field:ident, $e:expr) => { + set_csr!($set_field, $e); + clear_csr!($clear_field, $e); + } +} + +/// User Interrupt Enable +set_clear_csr!(set_uie, clear_uie, 1 << 0); +/// Supervisor Interrupt Enable +set_clear_csr!(set_sie, clear_sie, 1 << 1); +/// Machine Interrupt Enable +set_clear_csr!(set_mie, clear_mie, 1 << 3); +/// User Previous Interrupt Enable +set_csr!(set_upie, 1 << 4); +/// Supervisor Previous Interrupt Enable +set_csr!(set_spie, 1 << 5); +/// Machine Previous Interrupt Enable +set_csr!(set_mpie, 1 << 7); +/// Supervisor Previous Privilege Mode +#[inline] +pub unsafe fn set_spp(spp: SPP) { + set((spp as usize) << 8); +} +/// Machine Previous Privilege Mode +#[inline] +pub unsafe fn set_mpp(mpp: MPP) { + set((mpp as usize) << 11); +} diff --git a/src/register/mtvec.rs b/src/register/mtvec.rs new file mode 100644 index 0000000..393e0f7 --- /dev/null +++ b/src/register/mtvec.rs @@ -0,0 +1,65 @@ +//! mtvec register + +/// mtvec register +#[derive(Clone, Copy, Debug)] +pub struct Mtvec { + bits: usize, +} + +/// Trap mode +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!() + } + } +} + +/// Reads the CSR +#[inline] +pub fn read() -> Mtvec { + match () { + #[cfg(target_arch = "riscv")] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0x305, x0" : "=r"(r) ::: "volatile"); + } + Mtvec { bits: r } + } + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} + +/// Writes the CSR +#[cfg_attr(not(target_arch = "riscv"), allow(unused_variables))] +#[inline] +pub unsafe fn write(addr: usize, mode: TrapMode) { + let bits = addr + mode as usize; + match () { + #[cfg(target_arch = "riscv")] + () => asm!("csrrw x0, 0x305, $0" :: "r"(bits) :: "volatile"), + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +} diff --git a/src/register/mvendorid.rs b/src/register/mvendorid.rs new file mode 100644 index 0000000..9c56735 --- /dev/null +++ b/src/register/mvendorid.rs @@ -0,0 +1,42 @@ +//! mvendorid register + +/// mvendorid register +#[derive(Clone, Copy, Debug)] +pub struct Mvendorid { + bits: usize, +} + +impl Mvendorid { + /// Returns the contents of the register as raw bits + pub fn bits(&self) -> usize { + self.bits + } + + /// Returns the JEDEC manufacturer ID + pub fn jedec_manufacturer(&self) -> usize { + self.bits >> 7 + } +} + +/// Reads the CSR +#[inline] +pub fn read() -> Option { + match () { + #[cfg(target_arch = "riscv")] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0xF11, x0" : "=r"(r) ::: "volatile"); + } + // When mvendorid is hardwired to zero it means that the mvendorid + // csr isn't implemented. + if r == 0 { + None + } else { + Some(Mvendorid { bits: r }) + } + } + #[cfg(not(target_arch = "riscv"))] + () => unimplemented!(), + } +}