Skip to content

Commit

Permalink
HAL implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
romancardenas committed Nov 3, 2023
1 parent 13deb05 commit 012aa79
Show file tree
Hide file tree
Showing 14 changed files with 473 additions and 46 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/clippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ jobs:
runs-on: ubuntu-latest
if: always()
steps:
- run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
- run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
embedded-hal = "1.0.0-rc.1"
embedded-hal-async = { version = "1.0.0-rc.1", optional = true }
riscv = { git = "/~https://github.com/rust-embedded/riscv", branch = "master" }

[features]
hal-async = ["embedded-hal-async"]

[package.metadata.docs.rs]
default-target = "riscv64imac-unknown-none-elf"
targets = [
Expand Down
165 changes: 165 additions & 0 deletions examples/e310x.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
use riscv_peripheral::{
aclint::HartIdNumber,
plic::{ContextNumber, InterruptNumber, PriorityNumber},
};

#[repr(u16)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum HartId {
H0 = 0,
}

unsafe impl HartIdNumber for HartId {
const MAX_HART_ID_NUMBER: u16 = 0;

#[inline]
fn number(self) -> u16 {
self as _
}

#[inline]
fn from_number(number: u16) -> Result<Self, u16> {
if number > Self::MAX_HART_ID_NUMBER {
Err(number)
} else {
// SAFETY: valid context number
Ok(unsafe { core::mem::transmute(number) })
}
}
}

unsafe impl ContextNumber for HartId {
const MAX_CONTEXT_NUMBER: u16 = 0;

#[inline]
fn number(self) -> u16 {
self as _
}

#[inline]
fn from_number(number: u16) -> Result<Self, u16> {
if number > Self::MAX_CONTEXT_NUMBER {
Err(number)
} else {
// SAFETY: valid context number
Ok(unsafe { core::mem::transmute(number) })
}
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u16)]
pub enum Interrupt {
WATCHDOG = 1,
RTC = 2,
UART0 = 3,
UART1 = 4,
QSPI0 = 5,
QSPI1 = 6,
QSPI2 = 7,
GPIO0 = 8,
GPIO1 = 9,
GPIO2 = 10,
GPIO3 = 11,
GPIO4 = 12,
GPIO5 = 13,
GPIO6 = 14,
GPIO7 = 15,
GPIO8 = 16,
GPIO9 = 17,
GPIO10 = 18,
GPIO11 = 19,
GPIO12 = 20,
GPIO13 = 21,
GPIO14 = 22,
GPIO15 = 23,
GPIO16 = 24,
GPIO17 = 25,
GPIO18 = 26,
GPIO19 = 27,
GPIO20 = 28,
GPIO21 = 29,
GPIO22 = 30,
GPIO23 = 31,
GPIO24 = 32,
GPIO25 = 33,
GPIO26 = 34,
GPIO27 = 35,
GPIO28 = 36,
GPIO29 = 37,
GPIO30 = 38,
GPIO31 = 39,
PWM0CMP0 = 40,
PWM0CMP1 = 41,
PWM0CMP2 = 42,
PWM0CMP3 = 43,
PWM1CMP0 = 44,
PWM1CMP1 = 45,
PWM1CMP2 = 46,
PWM1CMP3 = 47,
PWM2CMP0 = 48,
PWM2CMP1 = 49,
PWM2CMP2 = 50,
PWM2CMP3 = 51,
I2C0 = 52,
}

unsafe impl InterruptNumber for Interrupt {
const MAX_INTERRUPT_NUMBER: u16 = 52;

#[inline]
fn number(self) -> u16 {
self as _
}

#[inline]
fn from_number(number: u16) -> Result<Self, u16> {
if number == 0 || number > Self::MAX_INTERRUPT_NUMBER {
Err(number)
} else {
// SAFETY: valid interrupt number
Ok(unsafe { core::mem::transmute(number) })
}
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Priority {
P0 = 0,
P1 = 1,
P2 = 2,
P3 = 3,
P4 = 4,
P5 = 5,
P6 = 6,
P7 = 7,
}

unsafe impl PriorityNumber for Priority {
const MAX_PRIORITY_NUMBER: u8 = 7;

#[inline]
fn number(self) -> u8 {
self as _
}

#[inline]
fn from_number(number: u8) -> Result<Self, u8> {
if number > Self::MAX_PRIORITY_NUMBER {
Err(number)
} else {
// SAFETY: valid priority number
Ok(unsafe { core::mem::transmute(number) })
}
}
}

riscv_peripheral::clint_codegen!(
base 0x0200_0000,
freq 32_768,
mtimecmps [mtimecmp0=(HartId::H0,"`H0`")],
msips [msip0=(HartId::H0,"`H0`")],
);

fn main() {}
26 changes: 9 additions & 17 deletions src/aclint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ pub(crate) mod test {
crate::clint_codegen!(
base 0x0200_0000,
mtimecmps [mtimecmp0=(HartId::H0,"`H0`"), mtimecmp1=(HartId::H1,"`H1`"), mtimecmp2=(HartId::H2,"`H2`")],
msips [msip0=(HartId::H0,"`H0`"), msip1=(HartId::H1,"`H1`"), msip2=(HartId::H2,"`H2`")],
);

let mswi = CLINT::mswi();
Expand All @@ -150,25 +151,16 @@ pub(crate) mod test {
let mtimecmp2 = mtimer.mtimecmp(HartId::H2);

assert_eq!(mtimecmp0.get_ptr() as usize, 0x0200_4000);
assert_eq!(mtimecmp1.get_ptr() as usize, 0x0200_4000 + 1 * 8); // 8 bytes per register
assert_eq!(mtimecmp1.get_ptr() as usize, 0x0200_4000 + 8); // 8 bytes per register
assert_eq!(mtimecmp2.get_ptr() as usize, 0x0200_4000 + 2 * 8);

// Check that the mtimecmpX functions are equivalent to the mtimer.mtimecmp(X) function.
let mtimecmp0 = CLINT::mtimecmp0();
let mtimecmp1 = CLINT::mtimecmp1();
let mtimecmp2 = CLINT::mtimecmp2();
assert_eq!(CLINT::mtime(), mtimer.mtime);
assert_eq!(CLINT::mtimecmp0(), mtimer.mtimecmp(HartId::H0));
assert_eq!(CLINT::mtimecmp1(), mtimer.mtimecmp(HartId::H1));
assert_eq!(CLINT::mtimecmp2(), mtimer.mtimecmp(HartId::H2));

assert_eq!(
mtimecmp0.get_ptr() as usize,
mtimer.mtimecmp(HartId::H0).get_ptr() as usize
);
assert_eq!(
mtimecmp1.get_ptr() as usize,
mtimer.mtimecmp(HartId::H1).get_ptr() as usize
);
assert_eq!(
mtimecmp2.get_ptr() as usize,
mtimer.mtimecmp(HartId::H2).get_ptr() as usize
);
assert_eq!(CLINT::msip0(), mswi.msip(HartId::H0));
assert_eq!(CLINT::msip1(), mswi.msip(HartId::H1));
assert_eq!(CLINT::msip2(), mswi.msip(HartId::H2));
}
}
27 changes: 27 additions & 0 deletions src/aclint/mswi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,30 @@ impl MSIP {
self.register.write(0);
}
}

#[cfg(test)]
mod test {
use super::super::test::HartId;
use super::*;

#[test]
fn test_mswi() {
// slice to emulate the interrupt pendings register
let raw_reg = [0u32; HartId::MAX_HART_ID_NUMBER as usize + 1];
// SAFETY: valid memory address
let mswi = unsafe { MSWI::new(raw_reg.as_ptr() as _) };

for i in 0..=HartId::MAX_HART_ID_NUMBER {
let hart_id = HartId::from_number(i).unwrap();
let msip = mswi.msip(hart_id);
assert!(!msip.is_pending());
assert_eq!(raw_reg[i as usize], 0);
msip.pend();
assert!(msip.is_pending());
assert_ne!(raw_reg[i as usize], 0);
msip.unpend();
assert!(!msip.is_pending());
assert_eq!(raw_reg[i as usize], 0);
}
}
}
35 changes: 34 additions & 1 deletion src/aclint/mtimer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl MTIMER {
///
/// # Safety
///
/// The base address must point to a valid `MTIMER` peripheral.
/// The base addresses must point to valid `MTIMECMP` and `MTIME` peripherals.
#[inline]
pub const unsafe fn new(mtimecmp: usize, mtime: usize) -> Self {
Self {
Expand All @@ -44,3 +44,36 @@ safe_peripheral!(MTIMECMP, u64, RW);

// MTIME register.
safe_peripheral!(MTIME, u64, RW);

#[cfg(test)]
mod test {
use super::super::test::HartId;
use super::*;

#[test]
fn check_mtimer() {
// slice to emulate the mtimecmp registers
let raw_mtimecmp = [0u64; HartId::MAX_HART_ID_NUMBER as usize + 1];
let raw_mtime = 0u64;
// SAFETY: valid memory addresses
let mtimer =
unsafe { MTIMER::new(raw_mtimecmp.as_ptr() as _, &raw_mtime as *const u64 as _) };

assert_eq!(
mtimer.mtimecmp(HartId::H0).get_ptr() as usize,
raw_mtimecmp.as_ptr() as usize
);
assert_eq!(mtimer.mtimecmp(HartId::H1).get_ptr() as usize, unsafe {
raw_mtimecmp.as_ptr().offset(1)
}
as usize);
assert_eq!(mtimer.mtimecmp(HartId::H2).get_ptr() as usize, unsafe {
raw_mtimecmp.as_ptr().offset(2)
}
as usize);
assert_eq!(
mtimer.mtime.get_ptr() as usize,
&raw_mtime as *const u64 as _
);
}
}
27 changes: 27 additions & 0 deletions src/aclint/sswi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,30 @@ impl SETSSIP {
self.register.write(0);
}
}

#[cfg(test)]
mod test {
use super::super::test::HartId;
use super::*;

#[test]
fn test_sswi() {
// slice to emulate the interrupt pendings register
let raw_reg = [0u32; HartId::MAX_HART_ID_NUMBER as usize + 1];
// SAFETY: valid memory address
let mswi = unsafe { SSWI::new(raw_reg.as_ptr() as _) };

for i in 0..=HartId::MAX_HART_ID_NUMBER {
let hart_id = HartId::from_number(i).unwrap();
let setssip = mswi.setssip(hart_id);
assert!(!setssip.is_pending());
assert_eq!(raw_reg[i as usize], 0);
setssip.pend();
assert!(setssip.is_pending());
assert_ne!(raw_reg[i as usize], 0);
setssip.unpend();
assert!(!setssip.is_pending());
assert_eq!(raw_reg[i as usize], 0);
}
}
}
5 changes: 5 additions & 0 deletions src/hal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//! trait implementations for embedded-hal
pub use embedded_hal::*; // re-export embedded-hal to allow macros to use it

pub mod aclint; // ACLINT and CLINT peripherals
47 changes: 47 additions & 0 deletions src/hal/aclint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! Delay trait implementation for (A)CLINT peripherals
use crate::aclint::mtimer::MTIME;
pub use crate::hal::delay::DelayUs;

/// Delay implementation for (A)CLINT peripherals.
pub struct Delay {
mtime: MTIME,
freq: usize,
}

impl Delay {
/// Creates a new `Delay` instance.
#[inline]
pub const fn new(mtime: MTIME, freq: usize) -> Self {
Self { mtime, freq }
}

/// Returns the frequency of the `MTIME` register.
#[inline]
pub const fn get_freq(&self) -> usize {
self.freq
}

/// Sets the frequency of the `MTIME` register.
#[inline]
pub fn set_freq(&mut self, freq: usize) {
self.freq = freq;
}

/// Returns the `MTIME` register.
#[inline]
pub const fn get_mtime(&self) -> MTIME {
self.mtime
}
}

impl DelayUs for Delay {
#[inline]
fn delay_us(&mut self, us: u32) {
let time_from = self.mtime.read();
let time_to = time_from.wrapping_add(us as u64 * self.freq as u64 / 1_000_000);

while time_to < self.mtime.read() {} // wait for overflow
while time_to > self.mtime.read() {} // wait for time to pass
}
}
5 changes: 5 additions & 0 deletions src/hal_async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//! async trait implementations for embedded-hal
pub use embedded_hal_async::*; // re-export embedded-hal-async to allow macros to use it

pub mod aclint; // ACLINT and CLINT peripherals
Loading

0 comments on commit 012aa79

Please sign in to comment.