Initial commit.

This commit is contained in:
David Craven 2017-09-19 16:04:12 +02:00
commit e864581828
No known key found for this signature in database
GPG Key ID: 33B9E9FDE28D2C23
7 changed files with 732 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
Cargo.lock
target/

13
Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "riscv"
version = "0.1.0"
repository = "https://github.com/dvc94ch/riscv"
authors = ["David Craven <david@craven.ch>"]
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"

18
README.md Normal file
View File

@ -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.

32
src/asm.rs Normal file
View File

@ -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");

596
src/csr.rs Normal file
View File

@ -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<F>(&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
}
}

53
src/interrupt.rs Normal file
View File

@ -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, R>(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
}

18
src/lib.rs Normal file
View File

@ -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;