Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/timeint #453

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ subtle = { version = "2.2", default-features = false, features = ["nightly"] }
serde_json = { version = "=1.0.69", default-features = false, features = ["alloc"] }
embedded-time = "0.12.1"
arbitrary = { version = "0.4.7", features = ["derive"], optional = true }
num = { version = "0.4.0", default-features = false }

[features]
debug_allocations = ["lang_items/debug_allocations"]
Expand Down
223 changes: 223 additions & 0 deletions src/api/clock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
use embedded_time::TimeInt;
use embedded_time::fraction::Fraction;
use core::fmt;
use core::ops::{Add, Sub, Mul, Div, Rem};

#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, fmt::Debug)]
pub struct U24(u32);
impl U24 {
pub const unsafe fn new_unchecked(n: u32) -> Self {
// SAFETY: this is guaranteed to be safe by the caller.
Self(n)
}

pub const fn new(n: u32) -> Option<Self> {
if n <= 0xffffff {
Some(Self(n))
} else {
None
}
}

pub const fn new_wrap(n: u32) -> Self {
Self(n & 0xffffff)
}

pub const fn get(self) -> u32 {
self.0
}
}

impl fmt::Display for U24 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}

impl Add for U24 {
type Output = Self;
fn add(self, other: Self) -> Self {
let res = self.get().add(other.get());
debug_assert!(res <= 0xffffff);
Self::new_wrap(res)
}
}

impl Sub for U24 {
type Output = Self;
fn sub(self, other: Self) -> Self {
let res = self.get().sub(other.get());
// Note: underflow detection in debug is already done by u32's sub
Self::new_wrap(res)
}
}

impl Mul for U24 {
type Output = Self;
fn mul(self, other: Self) -> Self {
let res = self.get().mul(other.get());
debug_assert!(res <= 0xffffff);
Self::new_wrap(res)
}
}

impl Div for U24 {
type Output = Self;
fn div(self, other: Self) -> Self {
// SAFETY: division by zero detection is already done by u32's div
// and division cannot overflow
unsafe { Self::new_unchecked(self.get().div(other.get())) }
}
}

impl Rem for U24 {
type Output = Self;
fn rem(self, other: Self) -> Self {
// SAFETY: rem cannot overflow
unsafe { Self::new_unchecked(self.get().rem(other.get())) }
}
}

impl num::One for U24 {
fn one() -> Self {
Self(1)
}
}

impl num::Zero for U24 {
fn zero() -> Self {
Self(0)
}

fn is_zero(&self) -> bool {
self.get() == 0
}
}

impl num::Num for U24 {
type FromStrRadixErr = core::num::ParseIntError;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
u32::from_str_radix(str, radix)
.and_then(|n|
// If we are too big, we manually generate a ParseIntError
Self::new(n).ok_or_else(||
u32::from_str_radix("100000000", 16).unwrap_err()
)
)
}
}

impl num::Integer for U24 {
fn div_floor(&self, other: &Self) -> Self {
// SAFETY: div_floor cannot overflow
unsafe { Self::new_unchecked(self.get().div_floor(&other.get())) }
}
fn mod_floor(&self, other: &Self) -> Self {
// SAFETY: div_floor cannot overflow
unsafe { Self::new_unchecked(self.get().mod_floor(&other.get())) }
}
fn gcd(&self, other: &Self) -> Self {
// SAFETY: gcd cannot overflow
unsafe { Self::new_unchecked(self.get().gcd(&other.get())) }
}
fn lcm(&self, other: &Self) -> Self {
Self::new(self.get().lcm(&other.get())).unwrap()
}
fn divides(&self, other: &Self) -> bool {
self.get().divides(&other.get())
}
fn is_multiple_of(&self, other: &Self) -> bool {
self.get().is_multiple_of(&other.get())
}
fn is_even(&self) -> bool {
self.get().is_even()
}
fn is_odd(&self) -> bool {
self.get().is_odd()
}
fn div_rem(&self, other: &Self) -> (Self, Self) {
let (div, rem) = self.get().div_rem(&other.get());
// SAFETY: div_floor cannot overflow
unsafe { ( Self::new_unchecked(div), Self::new_unchecked(rem) ) }
}
}

impl num::Bounded for U24 {
fn min_value() -> Self {
// SAFETY: 0 is within bounds
unsafe { Self::new_unchecked(0) }
}
fn max_value() -> Self {
// SAFETY: 0xffffff is within bounds
unsafe { Self::new_unchecked(0xffffff) }
}
}

impl num::traits::WrappingAdd for U24 {
fn wrapping_add(&self, v: &Self) -> Self {
Self::new_wrap(self.get().wrapping_add(v.get()))
}
}

impl num::traits::WrappingSub for U24 {
fn wrapping_sub(&self, v: &Self) -> Self {
Self::new_wrap(self.get().wrapping_sub(v.get()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to & 0xffffff here and in WrappingAdd too.

}
}

impl num::traits::CheckedAdd for U24 {
fn checked_add(&self, v: &Self) -> Option<Self> {
self.get()
.checked_add(v.get())
.and_then(|n| Self::new(n))
}
}

impl num::traits::CheckedSub for U24 {
fn checked_sub(&self, v: &Self) -> Option<Self> {
self.get()
.checked_sub(v.get())
// SAFETY: checked_sub cannot overflow
.and_then(|n| Some(unsafe { Self::new_unchecked(n) }))
}
}

impl num::traits::CheckedMul for U24 {
fn checked_mul(&self, v: &Self) -> Option<Self> {
self.get()
.checked_mul(v.get())
.and_then(|n| Self::new(n))
}
}

impl num::traits::CheckedDiv for U24 {
fn checked_div(&self, v: &Self) -> Option<Self> {
self.get()
.checked_div(v.get())
// SAFETY: checked_div cannot overflow
.and_then(|n| Some(unsafe { Self::new_unchecked(n) }))
}
}

impl From<u32> for U24 {
/// Use the 24 lowest bits from n, ignore the rest
fn from(n: u32) -> Self {
Self::new_wrap(n)
}
}

impl Mul<Fraction> for U24 {
type Output = Self;
fn mul(self, other: Fraction) -> Self {
Self::new(self.get() * other).unwrap()
}
}

impl Div<Fraction> for U24 {
type Output = Self;
fn div(self, other: Fraction) -> Self {
Self::new(self.get() / other).unwrap()
}
}

impl TimeInt for U24 {}
1 change: 1 addition & 0 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
pub mod firmware_protection;
pub mod upgrade_storage;
pub mod clock;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be sorted.

4 changes: 3 additions & 1 deletion src/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use embedded_time::duration::Milliseconds;
pub use embedded_time::Clock;
#[cfg(not(feature = "std"))]
use libtock_drivers::result::FlexUnwrap;
#[cfg(target_pointer_width = "32")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we always want to use U24 for now, in both test and prod.

use super::api::clock::U24;

#[cfg(not(feature = "std"))]
pub struct LibtockClock<const CLOCK_FREQUENCY: u32>(libtock_drivers::timer::Timer<'static>);
Expand All @@ -28,7 +30,7 @@ const KEEPALIVE_DELAY_MS: ClockInt = 100;
pub const KEEPALIVE_DELAY: Milliseconds<ClockInt> = Milliseconds(KEEPALIVE_DELAY_MS);

#[cfg(target_pointer_width = "32")]
pub type ClockInt = u32;
pub type ClockInt = U24;
#[cfg(target_pointer_width = "64")]
pub type ClockInt = u64;

Expand Down