use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Milliwatts(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Milliamps(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Megahertz(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Seconds(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DegreesCelsius(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ProchotDeassertionRamp(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OcVid(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CurveOptimizerOffset(pub i32);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CoreAddress {
bits: u32,
}
impl CoreAddress {
pub fn new(ccd: u8, ccx: u8, core: u8) -> Result<Self> {
if ccd > 15 || ccx > 15 || core > 15 {
return Err(Error::InvalidCoreAddress { ccd, ccx, core });
}
Ok(Self {
bits: (u32::from(ccd) << 28) | (u32::from(ccx) << 24) | (u32::from(core) << 20),
})
}
pub(crate) fn encode_curve_optimizer(self, offset: CurveOptimizerOffset) -> Result<u32> {
let offset = i16::try_from(offset.0)
.map_err(|_| Error::PerCoreCurveOptimizerOffsetOutOfRange(offset.0))?;
Ok(self.bits | u32::from(offset as u16))
}
pub(crate) fn encode_oc_clock(self, frequency: Megahertz) -> Result<u32> {
if frequency.0 > 0xF_FFFF {
return Err(Error::PerCoreOcClockOutOfRange(frequency.0));
}
Ok(self.bits | frequency.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PerformancePreference {
PowerSaving,
MaxPerformance,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn core_address_fields_do_not_overlap() {
for ((ccd, ccx, core), expected) in [
((0, 0, 0), 0),
((15, 0, 0), 0xF000_0000),
((0, 15, 0), 0x0F00_0000),
((0, 0, 15), 0x00F0_0000),
((15, 15, 15), 0xFFF0_0000),
] {
let address = CoreAddress::new(ccd, ccx, core).unwrap();
assert_eq!(address.encode_oc_clock(Megahertz(0)), Ok(expected));
}
}
#[test]
fn core_address_rejects_each_overflowing_index() {
for (ccd, ccx, core) in [(16, 0, 0), (0, 16, 0), (0, 0, 16)] {
assert_eq!(
CoreAddress::new(ccd, ccx, core),
Err(Error::InvalidCoreAddress { ccd, ccx, core }),
);
}
}
#[test]
fn per_core_co_preserves_address_and_reserved_bits() {
let address = CoreAddress::new(1, 2, 3).unwrap();
for (offset, expected) in [
(-32768, 0x1230_8000),
(-30, 0x1230_FFE2),
(-1, 0x1230_FFFF),
(0, 0x1230_0000),
(30, 0x1230_001E),
(32767, 0x1230_7FFF),
] {
assert_eq!(
address.encode_curve_optimizer(CurveOptimizerOffset(offset)),
Ok(expected),
);
}
}
#[test]
fn per_core_co_rejects_offsets_outside_signed_16_bits() {
let address = CoreAddress::new(1, 2, 3).unwrap();
for offset in [-32769, 32768] {
assert_eq!(
address.encode_curve_optimizer(CurveOptimizerOffset(offset)),
Err(Error::PerCoreCurveOptimizerOffsetOutOfRange(offset)),
);
}
}
#[test]
fn per_core_oc_uses_only_the_low_20_bits() {
let address = CoreAddress::new(1, 2, 3).unwrap();
for (frequency, expected) in [
(0, 0x1230_0000),
(4200, 0x1230_1068),
(0xF_FFFF, 0x123F_FFFF),
] {
assert_eq!(address.encode_oc_clock(Megahertz(frequency)), Ok(expected));
}
assert_eq!(
address.encode_oc_clock(Megahertz(0x10_0000)),
Err(Error::PerCoreOcClockOutOfRange(0x10_0000)),
);
}
}