1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Device electronic signature
//!
//! (stored in flash memory)
use core::fmt;
use core::str;

const UID_PTR: u32 = 0x1FFF_F7AC;

use core::convert::TryInto;

impl Uid {
    fn ptr() -> *const Self {
        UID_PTR as *const _
    }

    /// Returns a wrapped reference to the value in flash memory
    #[must_use]
    pub fn get() -> &'static Self {
        // SAFETY: The Uid pointer is definitly correct and as it is only ever shared immutable
        // mutliple references to it are fine.
        unsafe { &*Self::ptr() }
    }
}

/// Uniqure Device ID register
#[repr(C, packed)]
pub struct Uid {
    x: u16,
    y: u16,
    waf: u8,
    lot: [u8; 7],
}

#[cfg(feature = "defmt")]
impl defmt::Format for Uid {
    fn format(&self, f: defmt::Formatter) {
        defmt::write!(
            f,
            "Peripheral {{ x: {:x}, y: {:x}, waf: {}, lum: {}}}",
            { self.x },
            { self.y },
            { self.waf },
            { self.lot_number() },
        );
    }
}

impl fmt::Debug for Uid {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Uid")
            .field("x", &self.x())
            .field("y", &self.y())
            .field("waf", &self.waf)
            .field("lot", &self.lot_number())
            .finish()
    }
}

fn bcd_to_num(bcd_num: u16) -> u16 {
    bcd_num
        .to_ne_bytes()
        .iter()
        .enumerate()
        .map(|(i, byte)| (i * 2, (*byte & 0xF0) >> 4, *byte & 0x0F))
        .map(|(i, high_nibble, low_nibble)| {
            let i: u32 = i.try_into().unwrap_or_default(); // This should never overflow
            u16::from(high_nibble) * 10u16.pow(i + 1) + u16::from(low_nibble) * 10u16.pow(i)
        })
        .sum()
}

impl Uid {
    /// X coordinate on wafer in BCD format
    #[must_use]
    pub fn x_bcd(&self) -> u16 {
        self.x
    }

    /// X coordinate on wafer
    #[must_use]
    pub fn x(&self) -> u16 {
        bcd_to_num(self.x)
    }

    /// Y coordinate on wafer in BCD format
    #[must_use]
    pub fn y_bcd(&self) -> u16 {
        self.y
    }

    /// Y coordinate on wafer
    #[must_use]
    pub fn y(&self) -> u16 {
        bcd_to_num(self.y)
    }

    /// Wafer number
    #[must_use]
    pub fn wafer_number(&self) -> u8 {
        self.waf
    }

    /// Lot number
    #[must_use]
    pub fn lot_number(&self) -> &str {
        // Lets ignore the last byte, because it is a '\0' character.
        // TODO: change to core::ffi::CStr
        // SAFETY: It is assumed that the lot number only contains valid ASCII characters
        unsafe { str::from_utf8_unchecked(&self.lot[..6]) }
    }
}

/// Size of integrated flash
#[derive(Debug)]
#[repr(C)]
pub struct FlashSize(u16);

const FLASH_PTR: u32 = 0x1FFF_F7CC;
impl FlashSize {
    fn ptr() -> *const Self {
        FLASH_PTR as *const _
    }

    /// Returns a wrapped reference to the value in flash memory
    #[must_use]
    pub fn get() -> &'static Self {
        // SAFETY: The FlashSize pointer is definitly correct and as it is only ever shared immutable
        // mutliple references to it are fine.
        unsafe { &*Self::ptr() }
    }
}

impl FlashSize {
    /// Read flash size in kilobytes
    #[must_use]
    pub fn kilo_bytes(&self) -> u16 {
        self.0
    }

    /// Read flash size in bytes
    #[must_use]
    pub fn bytes(&self) -> usize {
        usize::from(self.kilo_bytes()) * 1024
    }
}