diff --git a/Cargo.toml b/Cargo.toml index f1747f8..0365b93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ license = "ISC" [dependencies] bare-metal = "0.2.0" +bit_field = "0.9.0" [features] inline-asm = [] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index c0f93d5..aa952cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ #![feature(const_fn)] extern crate bare_metal; +extern crate bit_field; pub mod asm; pub mod interrupt; diff --git a/src/register/macros.rs b/src/register/macros.rs new file mode 100644 index 0000000..db38665 --- /dev/null +++ b/src/register/macros.rs @@ -0,0 +1,129 @@ +macro_rules! read_csr { + ($csr_number:expr) => { + /// Reads the CSR + #[inline] + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + unsafe fn _read() -> usize { + let r: usize; + asm!("csrrs $0, $1, x0" : "=r"(r) : "i"($csr_number) :: "volatile"); + r + } + + #[inline] + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] + unsafe fn _read() -> usize { + unimplemented!() + } + }; +} + +macro_rules! read_csr_as { + ($register:ident, $csr_number:expr) => { + read_csr!($csr_number); + + /// Reads the CSR + #[inline] + pub fn read() -> $register { + $register { bits: unsafe{ _read() } } + } + }; +} +macro_rules! read_csr_as_usize { + ($csr_number:expr) => { + read_csr!($csr_number); + + /// Reads the CSR + #[inline] + pub fn read() -> usize { + unsafe{ _read() } + } + }; +} + +macro_rules! write_csr { + ($csr_number:expr) => { + /// Writes the CSR + #[inline] + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + unsafe fn _write(bits: usize) { + asm!("csrrw x0, $1, $0" :: "r"(bits), "i"($csr_number) :: "volatile"); + } + + #[inline] + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] + unsafe fn _write(_bits: usize) { + unimplemented!() + } + }; +} + +macro_rules! write_csr_as_usize { + ($csr_number:expr) => { + write_csr!($csr_number); + + /// Writes the CSR + #[inline] + pub fn write(bits: usize) { + unsafe{ _write(bits) } + } + }; +} + +macro_rules! set { + ($csr_number:expr) => { + /// Set the CSR + #[inline] + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + unsafe fn _set(bits: usize) { + asm!("csrrs x0, $1, $0" :: "r"(bits), "i"($csr_number) :: "volatile"); + } + + #[inline] + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] + unsafe fn _set(_bits: usize) { + unimplemented!() + } + }; +} + +macro_rules! clear { + ($csr_number:expr) => { + /// Clear the CSR + #[inline] + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + unsafe fn _clear(bits: usize) { + asm!("csrrc x0, $1, $0" :: "r"(bits), "i"($csr_number) :: "volatile"); + } + + #[inline] + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] + unsafe fn _clear(_bits: usize) { + 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); + } +} \ No newline at end of file diff --git a/src/register/mod.rs b/src/register/mod.rs index 3387b4e..0e8c009 100644 --- a/src/register/mod.rs +++ b/src/register/mod.rs @@ -10,6 +10,9 @@ //! - minstreth //! - mhpmcounter[3-31]h +#[macro_use] +mod macros; + pub mod mcause; pub mod mcycle; pub mod mcycleh; @@ -22,3 +25,16 @@ pub mod misa; pub mod mstatus; pub mod mtvec; pub mod mvendorid; + +pub mod sstatus; +pub mod stvec; +pub mod sie; +pub mod sip; +pub mod scause; +pub mod stval; +pub mod satp; +pub mod sscratch; +pub mod sepc; + +pub mod time; +pub mod timeh; diff --git a/src/register/satp.rs b/src/register/satp.rs new file mode 100644 index 0000000..37c50fe --- /dev/null +++ b/src/register/satp.rs @@ -0,0 +1,108 @@ +//! satp register + +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +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(target_arch = "riscv32")] + pub fn mode(&self) -> Mode { + match self.bits.get_bit(31) { + false => Mode::Bare, + true => Mode::Sv32, + } + } + + /// Current address-translation scheme + #[inline] + #[cfg(target_arch = "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(target_arch = "riscv32")] + pub fn asid(&self) -> usize { + self.bits.get_bits(22..31) + } + + /// Address space identifier + #[inline] + #[cfg(target_arch = "riscv64")] + pub fn asid(&self) -> usize { + self.bits.get_bits(44..60) + } + + /// Physical page number + #[inline] + #[cfg(target_arch = "riscv32")] + pub fn ppn(&self) -> usize { + self.bits.get_bits(0..22) + } + + /// Physical page number + #[inline] + #[cfg(target_arch = "riscv64")] + pub fn ppn(&self) -> usize { + self.bits.get_bits(0..44) + } +} + +#[cfg(target_arch = "riscv32")] +pub enum Mode { + Bare = 0, + Sv32 = 1, +} + +#[cfg(target_arch = "riscv64")] +pub enum Mode { + Bare = 0, + Sv39 = 8, + Sv48 = 9, + Sv57 = 10, + Sv64 = 11, +} + +read_csr_as!(Satp, 0x180); +write_csr!(0x180); + +#[inline] +#[cfg(target_arch = "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(target_arch = "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); +} \ No newline at end of file diff --git a/src/register/scause.rs b/src/register/scause.rs new file mode 100644 index 0000000..505202d --- /dev/null +++ b/src/register/scause.rs @@ -0,0 +1,118 @@ +//! 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)] +pub enum Trap { + Interrupt(Interrupt), + Exception(Exception), +} + +/// Interrupt +#[derive(Copy, Clone, Debug)] +pub enum Interrupt { + UserSoft, + SupervisorSoft, + UserTimer, + SupervisorTimer, + UserExternal, + SupervisorExternal, + Unknown, +} + +/// Exception +#[derive(Copy, Clone, Debug)] +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); diff --git a/src/register/sepc.rs b/src/register/sepc.rs new file mode 100644 index 0000000..a39d1bd --- /dev/null +++ b/src/register/sepc.rs @@ -0,0 +1,4 @@ +//! sepc register + +read_csr_as_usize!(0x141); +write_csr_as_usize!(0x141); diff --git a/src/register/sie.rs b/src/register/sie.rs new file mode 100644 index 0000000..10d3845 --- /dev/null +++ b/src/register/sie.rs @@ -0,0 +1,70 @@ +//! 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); +set!(0x104); +clear!(0x104); + +/// 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); +/// 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); +/// 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); diff --git a/src/register/sip.rs b/src/register/sip.rs new file mode 100644 index 0000000..bd419e7 --- /dev/null +++ b/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); diff --git a/src/register/sscratch.rs b/src/register/sscratch.rs new file mode 100644 index 0000000..d910d6e --- /dev/null +++ b/src/register/sscratch.rs @@ -0,0 +1,4 @@ +//! sscratch register + +read_csr_as_usize!(0x140); +write_csr_as_usize!(0x140); diff --git a/src/register/sstatus.rs b/src/register/sstatus.rs new file mode 100644 index 0000000..8181fb2 --- /dev/null +++ b/src/register/sstatus.rs @@ -0,0 +1,136 @@ +//! sstatus register + +use bit_field::BitField; +use core::mem::size_of; + +/// Supervisor Status Register +#[derive(Clone, Copy, Debug)] +pub struct Sstatus { + bits: usize, +} + +/// Supervisor Previous Privilege Mode +#[derive(Eq, PartialEq)] +pub enum SPP { + Supervisor = 1, + User = 0, +} + +/// Floating-point unit Status +#[derive(Eq, PartialEq)] +pub enum FS { + Off = 0, + Initial = 1, + Clean = 2, + Dirty = 3, +} + +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); +set!(0x100); +clear!(0x100); + +/// User Interrupt Enable +set_clear_csr!(set_uie, clear_uie, 1 << 0); +/// Supervisor Interrupt Enable +set_clear_csr!(set_sie, clear_sie, 1 << 1); +/// User Previous Interrupt Enable +set_csr!(set_upie, 1 << 4); +/// Supervisor Previous Interrupt Enable +set_csr!(set_spie, 1 << 5); +/// Make eXecutable Readable +set_clear_csr!(set_mxr, clear_mxr, 1 << 19); +/// Permit Supervisor User Memory access +set_clear_csr!(set_sum, clear_sum, 1 << 18); + +/// Supervisor Previous Privilege Mode +#[inline] +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +pub unsafe fn set_spp(spp: SPP) { + _set((spp as usize) << 8); +} + +/// The status of the floating-point unit +#[inline] +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +pub unsafe fn set_fs(fs: FS) { + _set((fs as usize) << 13); +} diff --git a/src/register/stval.rs b/src/register/stval.rs new file mode 100644 index 0000000..fb37ee9 --- /dev/null +++ b/src/register/stval.rs @@ -0,0 +1,3 @@ +//! stval register + +read_csr_as_usize!(0x143); diff --git a/src/register/stvec.rs b/src/register/stvec.rs new file mode 100644 index 0000000..276329e --- /dev/null +++ b/src/register/stvec.rs @@ -0,0 +1,45 @@ +//! stvec register + +/// stvec register +#[derive(Clone, Copy, Debug)] +pub struct Stvec { + bits: usize, +} + +/// Trap mode +pub enum TrapMode { + Direct = 0, + Vectored = 1, +} + +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); +write_csr!(0x105); + +/// Writes the CSR +#[inline] +#[cfg_attr(not(any(target_arch = "riscv32", target_arch = "riscv64")), allow(unused_variables))] +pub unsafe fn write(addr: usize, mode: TrapMode) { + _write(addr + mode as usize); +} diff --git a/src/register/time.rs b/src/register/time.rs new file mode 100644 index 0000000..e626742 --- /dev/null +++ b/src/register/time.rs @@ -0,0 +1,3 @@ +//! time register + +read_csr_as_usize!(0xC01); diff --git a/src/register/timeh.rs b/src/register/timeh.rs new file mode 100644 index 0000000..97c4965 --- /dev/null +++ b/src/register/timeh.rs @@ -0,0 +1,18 @@ +//! timeh register + +/// Reads the CSR +#[inline] +pub fn read() -> usize { + match () { + #[cfg(target_arch = "riscv32")] + () => { + let r: usize; + unsafe { + asm!("csrrs $0, 0xC81, x0" : "=r"(r) ::: "volatile"); + } + r + } + #[cfg(not(target_arch = "riscv32"))] + () => unimplemented!(), + } +} \ No newline at end of file