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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//! Platform-Level Interrupt Controller
use core::marker::PhantomData;
use riscv::register::{mie, mip};
use riscv::interrupt::Nr;
use e310x::PLIC;
use e310x::Interrupt;

/// Priority of a plic::Interrupt.
#[derive(Clone, Copy)]
pub enum Priority {
    /// Priority 0: Never interrupt
    P0,
    /// Priority 1: Lowest active priority
    P1,
    /// Priority 2
    P2,
    /// Priority 3
    P3,
    /// Priority 4
    P4,
    /// Priority 5
    P5,
    /// Priority 6
    P6,
    /// Priority 7: Highest priority
    P7,
}

impl Priority {
    /// Takes a read interrupt priority or plic threshold
    /// register value and returns a plic::Priority enum.
    fn from(prio: u32) -> Option<Priority> {
        match prio {
            0 => Some(Priority::P0),
            1 => Some(Priority::P1),
            2 => Some(Priority::P2),
            3 => Some(Priority::P3),
            4 => Some(Priority::P4),
            5 => Some(Priority::P5),
            6 => Some(Priority::P6),
            7 => Some(Priority::P7),
            _ => None,
        }
    }
}

impl Into<u32> for Priority {
    /// Returns the numeric priority for writing to a
    /// interrupt priority or the plic threshold register.
    fn into(self) -> u32 {
        match self {
            Priority::P0 => 0,
            Priority::P1 => 1,
            Priority::P2 => 2,
            Priority::P3 => 3,
            Priority::P4 => 4,
            Priority::P5 => 5,
            Priority::P6 => 6,
            Priority::P7 => 7,
        }
    }
}

/// Watchdog interrupt (type state)
pub struct IrqWatchdog;
/// Realtime clock interrupt (type state)
pub struct IrqRtc;
/// Uart0 interrupt (type state)
pub struct IrqUart0;

/// Parts of `PLIC` peripheral for fine grained permissions.
pub struct Plic {
    /// Opaque mext register
    pub mext: MEXT,
    /// Opaque threshold register
    pub threshold: THRESHOLD,
    /// Opaque claim register
    pub claim: CLAIM,
    /// Opaque watchdog register
    pub wdog: INTERRUPT<IrqWatchdog>,
    /// Opaque rtc register
    pub rtc: INTERRUPT<IrqRtc>,
    /// Opaque uart0 register
    pub uart0: INTERRUPT<IrqUart0>,
}

impl From<PLIC> for Plic {
    fn from(_: PLIC) -> Self {
        Plic {
            mext: MEXT { _0: () },
            threshold: THRESHOLD { _0: () },
            claim: CLAIM { _0: () },
            wdog: INTERRUPT {
                offset: 0,
                mask: 1 << (Interrupt::WATCHDOG as u8),
                priority_offset: Interrupt::WATCHDOG as usize,
                _marker: PhantomData,
            },
            rtc: INTERRUPT {
                offset: 0,
                mask: 1 << (Interrupt::RTC as u8),
                priority_offset: Interrupt::RTC as usize,
                _marker: PhantomData,
            },
            uart0: INTERRUPT {
                offset: 0,
                mask: 1 << (Interrupt::UART0 as u8),
                priority_offset: Interrupt::UART0 as usize,
                _marker: PhantomData,
            }
        }
    }
}


/// Opaque MEXT register.
pub struct MEXT {
    _0: (),
}

impl MEXT {
    /// Enable MachineExternal interrupt.
    #[inline]
    pub fn enable(&mut self) {
        unsafe { mie::set_mext() };
    }

    /// Disable MachineExternal interrupt.
    #[inline]
    pub fn disable(&mut self) {
        unsafe { mie::clear_mext() };
    }

    /// Returns true when MachineExternal interrupt is pending.
    #[inline]
    pub fn is_pending(&self) -> bool {
        mip::read().mext()
    }
}

/// Opaque THRESHOLD register.
pub struct THRESHOLD {
    _0: (),
}

impl THRESHOLD {
    /// Returns the current active priority threshold.
    pub fn get(&self) -> Priority {
        // NOTE: Atomic read with no side effects.
        let threshold = unsafe { (*PLIC::ptr()).threshold.read() };
        Priority::from(threshold.bits()).unwrap()
    }

    /// Sets the current active priority threshold. This
    /// deactivates all interrupts with a lower priority.
    pub fn set(&mut self, priority: Priority) {
        // NOTE: Atomic write with no side effects.
        unsafe {
            (*PLIC::ptr()).threshold.write(|w| w.bits(priority.into()));
        }
    }
}

/// Opaque CLAIM register.
pub struct CLAIM {
    _0: (),
}

impl CLAIM {
    /// Claims the interrupt with the highest priority.
    pub fn claim(&mut self) -> Option<Interrupt> {
        // NOTE: Atomic read with side effects.
        let intr = unsafe { (*PLIC::ptr()).claim.read().bits() };

        // If no interrupt is pending return None
        if intr == 0 {
            None
        } else {
            Some(Interrupt::try_from(intr as u8).unwrap())
        }
    }

    /// Notifies the PLIC that a claimed interrupt is complete.
    pub fn complete(&mut self, intr: Interrupt) {
        // NOTE: Atomic write with side effects.
        unsafe {
            (*PLIC::ptr()).claim.write(|w| w.bits(intr.nr() as u32));
        }
    }
}

/// Fine grained interrupt handling.
pub struct INTERRUPT<IRQ> {
    /// Offset in to enable and pending plic registers
    offset: usize,
    /// Bitmask for enable and pending plic registers
    mask: u32,
    /// Offset in to priority plic registers
    priority_offset: usize,
    _marker: PhantomData<IRQ>,
}

impl<IRQ> INTERRUPT<IRQ> {
    /// Enable IRQ interrupt.
    #[inline]
    pub fn enable(&mut self) {
        // NOTE: should use atomic operations
        unsafe {
            (*PLIC::ptr()).enable[self.offset]
                .modify(|r, w| w.bits(r.bits() | self.mask));
        }
    }

    /// Disable IRQ interrupt.
    #[inline]
    pub fn disable(&mut self) {
        // NOTE: should use atomic operations
        unsafe {
            (*PLIC::ptr()).enable[self.offset]
                .modify(|r, w| w.bits(r.bits() & !self.mask));
        }
    }

    /// Returns true when IRQ interrupt is pending.
    pub fn is_pending(&self) -> bool {
        // NOTE: Atomic write without side effects.
        let pending = unsafe {
            (*PLIC::ptr()).pending[self.offset].read()
        };
        pending.bits() & self.mask == self.mask
    }

    /// Returns true when WDOG interrupt is enabled.
    pub fn is_enabled(&self) -> bool {
        // NOTE: Atomic write without side effects.
        let enabled = unsafe {
            (*PLIC::ptr()).enable[self.offset].read()
        };
        enabled.bits() & self.mask == self.mask
    }

    /// Returns the priority of the IRQ interrupt.
    pub fn priority(&self) -> Priority {
        // NOTE: Atomic read without side effects.
        let priority = unsafe {
            (*PLIC::ptr()).priority[self.priority_offset].read()
        };
        Priority::from(priority.bits()).unwrap()
    }

    /// Sets the priority of the IRQ interrupt.
    pub fn set_priority(&mut self, priority: Priority) {
        // NOTE: Atomic write without side effects.
        unsafe {
            (*PLIC::ptr()).priority[self.priority_offset]
                .write(|w| w.bits(priority as u32));
        }
    }
}