zig-riscv-embedded

Experimental Zig-based CoAP node for the HiFive1 RISC-V board

git clone https://git.8pit.net/zig-riscv-embedded.git

 1// Copyright © 2020 Sören Tempel
 2//
 3// This program is free software: you can redistribute it and/or modify
 4// it under the terms of the GNU Affero General Public License as
 5// published by the Free Software Foundation, either version 3 of the
 6// License, or (at your option) any later version.
 7//
 8// This program is distributed in the hope that it will be useful, but
 9// WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11// Affero General Public License for more details.
12//
13// You should have received a copy of the GNU Affero General Public License
14// along with this program. If not, see <https://www.gnu.org/licenses/>.
15
16const console = @import("console.zig");
17
18// Type alias for IRQ values, largest possible IRQ on the FE310 is
19// 52 (see INTERRUPT_SOURCES below), thus representable by a u6.
20pub const Irq = u6;
21
22// Type alias for PLIC interrupt handler functions.
23pub const Handler = fn (args: ?*anyopaque) void;
24
25pub const Plic = struct {
26    base_addr: usize,
27
28    // Offsets for memory mapped PLIC control registers.
29    const PLIC_PRIO_OFF: usize = 0x0000;
30    const PLIC_PENDING_OFF: usize = 0x1000;
31    const PLIC_ENABLE_OFF: usize = 0x2000;
32    const PLIC_CONTEXT_OFF: usize = 0x200000;
33
34    // Amount of interrupt sources supported by plic.
35    const INTERRUPT_SOURCES: Irq = 52;
36
37    // TODO: Get rid of anyopaque in the long run.
38    var irq_handlers = [_]?Handler{null} ** INTERRUPT_SOURCES;
39    var irq_contexts = [_]?*anyopaque{null} ** INTERRUPT_SOURCES;
40
41    pub fn setThreshold(self: Plic, threshold: u3) void {
42        const plic_thres = @intToPtr(*volatile u32, self.base_addr + PLIC_CONTEXT_OFF);
43        plic_thres.* = threshold;
44    }
45
46    fn setPriority(self: Plic, irq: Irq, prio: u3) void {
47        // Set PLIC priority for IRQ
48        const plic_prio = @intToPtr(*volatile u32, self.base_addr +
49            PLIC_PRIO_OFF + (@as(u32, irq) * @sizeOf(u32)));
50        plic_prio.* = @as(u32, prio);
51    }
52
53    fn setEnable(self: Plic, irq: Irq, enable: bool) void {
54        const idx = irq / 32;
55
56        const enable_addr: usize = self.base_addr + PLIC_ENABLE_OFF;
57        const plic_enable = @intToPtr(*volatile u32, enable_addr + (idx * @sizeOf(u32)));
58
59        const off = @intCast(u5, irq % 32);
60        if (enable) {
61            plic_enable.* |= @intCast(u32, 1) << off;
62        } else {
63            plic_enable.* &= ~(@intCast(u32, 1) << off);
64        }
65    }
66
67    pub fn registerHandler(self: Plic, irq: Irq, func: Handler, ctx: ?*anyopaque) !void {
68        if (irq >= irq_handlers.len)
69            return error.OutOfBounds;
70
71        irq_handlers[irq] = func;
72        irq_contexts[irq] = ctx;
73
74        self.setPriority(irq, 1);
75        self.setEnable(irq, true);
76    }
77
78    pub fn invokeHandler(self: Plic) void {
79        const claim_reg = @intToPtr(*volatile u32, self.base_addr + PLIC_CONTEXT_OFF + @sizeOf(u32));
80        const irq = @intCast(Irq, claim_reg.*);
81
82        if (irq_handlers[irq]) |handler|
83            handler(irq_contexts[irq]);
84
85        // Mark interrupt as completed
86        claim_reg.* = irq;
87    }
88
89    pub fn init(self: Plic) void {
90        // Threshold is uninitialized by default.
91        self.setThreshold(0);
92
93        var i: Irq = 1;
94        while (i <= INTERRUPT_SOURCES) : (i += 1) {
95            self.setEnable(i, false);
96            self.setPriority(i, 0);
97        }
98    }
99};