sifive/
plic.rs

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
260
261
262
263
264
265
266
267
268
269
270
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Platform Level Interrupt Control peripheral driver.

use kernel::utilities::cells::VolatileCell;
use kernel::utilities::registers::interfaces::{Readable, Writeable};
use kernel::utilities::registers::LocalRegisterCopy;
use kernel::utilities::registers::{register_bitfields, ReadOnly, ReadWrite};
use kernel::utilities::StaticRef;

/// Place the register map definition in a private module to disallow direct access to it's
/// fields from the Plic struct implementation, which should only use a getter/setter with
/// appropriate bounds set

///    The generic SiFive PLIC specification:
///    <https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc>
///    is defining maximum of 1023 interrupt sources

// TODO: replace with const generic for `priority` and `_reserved1` field
// in the [PlicRegisters] when const generic expressions are stable
const MAX_INTERRUPTS: usize = 1023;
/// maximum number of bit-coded registers, 1 bit per interrupt
const MAX_BIT_REGS: usize = MAX_INTERRUPTS.div_ceil(32);

/// PLIC registers for *machine mode* context only at this time.
///
/// The spec defines extra sets of registers for additional contexts,
/// that is supervisor, user and other modes, but these aren't supported
/// by the current code.
#[repr(C)]
pub struct PlicRegisters {
    /// Interrupt Priority Register
    _reserved0: u32,
    priority: [ReadWrite<u32, priority::Register>; MAX_INTERRUPTS],
    _reserved1: [u8; 0x1000 - (MAX_INTERRUPTS + 1) * 4],
    /// Interrupt Pending Register
    pending: [ReadOnly<u32>; MAX_BIT_REGS],
    _reserved2: [u8; 0x1000 - MAX_BIT_REGS * 4],
    /// Interrupt Enable Register
    enable: [ReadWrite<u32>; MAX_BIT_REGS],
    _reserved3: [u8; 0x20_0000 - 0x2000 - MAX_BIT_REGS * 4],
    /// Priority Threshold Register
    threshold: ReadWrite<u32, priority::Register>,
    /// Claim/Complete Register
    claim: ReadWrite<u32>,
}

/// Check that the registers are aligned to the PLIC memory map
const _: () = assert!(core::mem::offset_of!(PlicRegisters, priority) == 0x4);
const _: () = assert!(core::mem::offset_of!(PlicRegisters, pending) == 0x1000);
const _: () = assert!(core::mem::offset_of!(PlicRegisters, enable) == 0x2000);
const _: () = assert!(core::mem::offset_of!(PlicRegisters, threshold) == 0x20_0000);
const _: () = assert!(core::mem::offset_of!(PlicRegisters, claim) == 0x20_0004);

/// A wrapper around the PLIC registers to provide safe access to the registers
/// within the defined interrupt number range
struct RegsWrapper {
    registers: StaticRef<PlicRegisters>,
    total_ints: usize,
}

impl RegsWrapper {
    const fn new(registers: StaticRef<PlicRegisters>, total_ints: usize) -> Self {
        Self {
            registers,
            total_ints,
        }
    }

    fn get_enable_regs(&self) -> &[ReadWrite<u32>] {
        // One bit per interrupt, total number of registers is
        // the number of interrupts divided by 32 rounded up
        &self.registers.enable[0..self.total_ints.div_ceil(32)]
    }

    // Unused by the current code
    #[allow(dead_code)]
    fn get_pending_regs(&self) -> &[ReadOnly<u32>] {
        // One bit per interrupt, total number of registers is
        // the number of interrupts divided by 32 rounded up
        &self.registers.pending[0..self.total_ints.div_ceil(32)]
    }

    fn get_priority_regs(&self) -> &[ReadWrite<u32, priority::Register>] {
        // One 32-bit register per interrupt source
        &self.registers.priority[0..self.total_ints]
    }

    fn get_threshold_reg(&self) -> &ReadWrite<u32, priority::Register> {
        &self.registers.threshold
    }

    fn get_claim_reg(&self) -> &ReadWrite<u32> {
        &self.registers.claim
    }
}

register_bitfields![u32,
    priority [
        Priority OFFSET(0) NUMBITS(3) []
    ]
];

/// The PLIC instance generic parameter indicates the total number of
/// interrupt sources implemented on the specific chip.
///
/// 51 is a default for backwards compatibility with the SiFive based
/// platforms implemented without the generic parameter.
pub struct Plic<const TOTAL_INTS: usize = 51> {
    registers: RegsWrapper,
    saved: [VolatileCell<LocalRegisterCopy<u32>>; 2],
}

impl<const TOTAL_INTS: usize> Plic<TOTAL_INTS> {
    pub const fn new(base: StaticRef<PlicRegisters>) -> Self {
        Plic {
            registers: RegsWrapper::new(base, TOTAL_INTS),
            saved: [
                VolatileCell::new(LocalRegisterCopy::new(0)),
                VolatileCell::new(LocalRegisterCopy::new(0)),
            ],
        }
    }

    /// Clear all pending interrupts. The [`PLIC specification`] section 7:
    /// > A successful claim will also atomically clear the corresponding pending bit on the interrupt source..
    /// Note that this function will only clear the enabled interrupt sources, as only those can be claimed.
    /// [`PLIC specification`]: <https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc>
    pub fn clear_all_pending(&self) {
        let claim = self.registers.get_claim_reg();
        loop {
            let id = claim.get();
            if id == 0 {
                break;
            }
            claim.set(id);
        }
    }

    /// Enable a list of interrupt IDs. The IDs must be in the range 1..TOTAL_INTS.
    pub fn enable_specific_interrupts(&self, interrupts: &[u32]) {
        let enable_regs = self.registers.get_enable_regs();
        for interrupt in interrupts {
            let offset = interrupt / 32;
            let irq = interrupt % 32;
            let old_value = enable_regs[offset as usize].get();
            enable_regs[offset as usize].set(old_value | (1 << irq));

            // Set some default priority for each interrupt. This is not really used
            // at this point.
            // The priority registers indexed 0 for interrupt 1, 1 for interrupt 2, etc.
            // so we subtract 1 from the interrupt number to get the correct index.
            self.registers.get_priority_regs()[*interrupt as usize - 1]
                .write(priority::Priority.val(4));
        }
        // Accept all interrupts.
        self.registers
            .get_threshold_reg()
            .write(priority::Priority.val(0));
    }

    pub fn disable_specific_interrupts(&self, interrupts: &[u32]) {
        let enable_regs = self.registers.get_enable_regs();
        for interrupt in interrupts {
            let offset = interrupt / 32;
            let irq = interrupt % 32;
            let old_value = enable_regs[offset as usize].get();
            enable_regs[offset as usize].set(old_value & !(1 << irq));
        }
    }

    /// Enable all interrupts.
    pub fn enable_all(&self) {
        let enable_regs = self.registers.get_enable_regs();
        let priority_regs = &self.registers.get_priority_regs();

        for enable in enable_regs.iter() {
            enable.set(0xFFFF_FFFF);
        }

        // Set some default priority for each interrupt. This is not really used
        // at this point.
        for priority in priority_regs.iter() {
            priority.write(priority::Priority.val(4));
        }

        // Accept all interrupts.
        self.registers
            .get_threshold_reg()
            .write(priority::Priority.val(0));
    }

    /// Disable all interrupts.
    pub fn disable_all(&self) {
        let enable_regs = self.registers.get_enable_regs();

        for enable in enable_regs.iter() {
            enable.set(0);
        }
    }

    /// Get the index (0-256) of the lowest number pending interrupt, or `None` if
    /// none is pending. RISC-V PLIC has a "claim" register which makes it easy
    /// to grab the highest priority pending interrupt.
    pub fn next_pending(&self) -> Option<u32> {
        let claim = self.registers.get_claim_reg().get();
        if claim == 0 {
            None
        } else {
            Some(claim)
        }
    }

    /// Save the current interrupt to be handled later
    /// This will save the interrupt at index internally to be handled later.
    /// Interrupts must be disabled before this is called.
    /// Saved interrupts can be retrieved by calling `get_saved_interrupts()`.
    /// Saved interrupts are cleared when `'complete()` is called.
    pub unsafe fn save_interrupt(&self, index: u32) {
        let offset = usize::from(index >= 32);
        let irq = index % 32;

        // OR the current saved state with the new value
        let new_saved = self.saved[offset].get().get() | 1 << irq;

        // Set the new state
        self.saved[offset].set(LocalRegisterCopy::new(new_saved));
    }

    /// The `next_pending()` function will only return enabled interrupts.
    /// This function will return a pending interrupt that has been disabled by
    /// `save_interrupt()`.
    pub fn get_saved_interrupts(&self) -> Option<u32> {
        for (i, pending) in self.saved.iter().enumerate() {
            let saved = pending.get().get();
            if saved != 0 {
                return Some(saved.trailing_zeros() + (i as u32 * 32));
            }
        }

        None
    }

    /// Signal that an interrupt is finished being handled. In Tock, this should be
    /// called from the normal main loop (not the interrupt handler).
    /// Interrupts must be disabled before this is called.
    pub unsafe fn complete(&self, index: u32) {
        self.registers.get_claim_reg().set(index);

        let offset = usize::from(index >= 32);
        let irq = index % 32;

        // OR the current saved state with the new value
        let new_saved = self.saved[offset].get().get() & !(1 << irq);

        // Set the new state
        self.saved[offset].set(LocalRegisterCopy::new(new_saved));
    }

    /// This is a generic implementation. There may be board specific versions as
    /// some platforms have added more bits to the `mtvec` register.
    pub fn suppress_all(&self) {
        // Accept all interrupts.
        self.registers
            .get_threshold_reg()
            .write(priority::Priority.val(0));
    }
}