Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interrupt refactor #369

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 72 additions & 58 deletions core/src/cpus/cortex_m.zig
Original file line number Diff line number Diff line change
@@ -1,18 +1,61 @@
const std = @import("std");
const microzig = @import("microzig");
const mmio = microzig.mmio;
const app = microzig.app;
const root = @import("root");

pub fn executing_isr() bool {
return peripherals.scb.ICSR.read().VECTACTIVE != 0;
}
pub const Interrupt = microzig.utilities.GenerateInterruptEnum();
pub const InterruptOptions = microzig.utilities.GenerateInterruptOptions(fn () callconv(.C) void, .{Interrupt});
const interrupt_options: InterruptOptions = if (@hasDecl(app, "interrupts")) app.interrupts else .{};

pub const interrupts = struct {
pub fn are_globally_enabled() bool {
var mrs: u32 = undefined;
asm volatile ("mrs %[mrs], 16"
: [mrs] "+r" (mrs),
);
return mrs & 0x1 == 0;
}

pub fn enable_interrupts() void {
asm volatile ("cpsie i");
}
pub fn globally_enable() void {
asm volatile ("cpsie i");
}

pub fn globally_disable() void {
asm volatile ("cpsid i");
}

pub fn enable(comptime interrupt: anytype) void {
const interrupt_name = @tagName(interrupt);
if (@hasField(Interrupt, interrupt_name)) {
const num = @intFromEnum(@field(Interrupt, interrupt_name));
if (num >= 0) {
peripherals.nvic.unmask(num);
} else {
@compileError("can't enable exception: " ++ interrupt_name);
}
} else {
@compileError("interrupt not found: " ++ interrupt_name);
}
}

pub fn disable_interrupts() void {
asm volatile ("cpsid i");
pub fn disable(comptime interrupt: anytype) void {
const interrupt_name = @tagName(interrupt);
if (@hasField(Interrupt, interrupt_name)) {
const num = @intFromEnum(@field(Interrupt, interrupt_name));
if (num >= 0) {
peripherals.nvic.mask(num);
} else {
@compileError("can't disable exception: " ++ interrupt_name);
}
} else {
@compileError("interrupt not found: " ++ interrupt_name);
}
}
};

pub fn executing_isr() bool {
return peripherals.scb.ICSR.read().VECTACTIVE != 0;
}

pub fn enable_fault_irq() void {
Expand Down Expand Up @@ -82,63 +125,34 @@ pub const startup_logic = struct {

microzig_main();
}
};

pub fn export_startup_logic() void {
@export(startup_logic._start, .{
.name = "_start",
});
}
const VectorTable = microzig.chip.VectorTable;

fn is_valid_field(field_name: []const u8) bool {
return !std.mem.startsWith(u8, field_name, "reserved") and
!std.mem.eql(u8, field_name, "initial_stack_pointer") and
!std.mem.eql(u8, field_name, "reset");
}
// will be imported by microzig.zig to allow system startup.
pub const _vector_table: VectorTable = blk: {
var tmp: VectorTable = .{
.initial_stack_pointer = microzig.config.end_of_stack,
.Reset = microzig.cpu.startup_logic._start,
};

const VectorTable = microzig.chip.VectorTable;
for (@typeInfo(@TypeOf(app.interrupts)).Struct.fields) |field| {
@field(tmp, field.name) = @field(app.interrupts, field.name);
}

// will be imported by microzig.zig to allow system startup.
pub const vector_table: VectorTable = blk: {
var tmp: VectorTable = .{
.initial_stack_pointer = microzig.config.end_of_stack,
.Reset = .{ .C = microzig.cpu.startup_logic._start },
break :blk tmp;
};

if (@hasDecl(root, "microzig_options")) {
for (@typeInfo(root.VectorTableOptions).Struct.fields) |field|
@field(tmp, field.name) = @field(root.microzig_options.interrupts, field.name);
}

break :blk tmp;
};

fn create_interrupt_vector(
comptime function: anytype,
) microzig.interrupt.Handler {
const calling_convention = @typeInfo(@TypeOf(function)).Fn.calling_convention;
return switch (calling_convention) {
.C => .{ .C = function },
.Naked => .{ .Naked = function },
// for unspecified calling convention we are going to generate small wrapper
.Unspecified => .{
.C = struct {
fn wrapper() callconv(.C) void {
if (calling_convention == .Unspecified) // TODO: workaround for some weird stage1 bug
@call(.always_inline, function, .{});
}
}.wrapper,
},

else => |val| {
const conv_name = inline for (std.meta.fields(std.builtin.CallingConvention)) |field| {
if (val == @field(std.builtin.CallingConvention, field.name))
break field.name;
} else unreachable;

@compileError("unsupported calling convention for interrupt vector: " ++ conv_name);
},
};
pub fn export_startup_logic() void {
@export(startup_logic._start, .{
.name = "_start",
});

@export(startup_logic._vector_table, .{
.name = "_vector_table",
.section = "microzig_flash_start",
.linkage = .strong,
});
}

const scs_base = 0xE000E000;
Expand Down
8 changes: 8 additions & 0 deletions core/src/cpus/cortex_m/m0.zig
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,12 @@ pub const NestedVectorInterruptController = extern struct {
/// field, bits [5:0] read as zero and ignore writes. This means writing 255 to a priority
/// register saves value 192 to the register.
IPR: [8]u32,

pub fn unmask(nvic: *volatile NestedVectorInterruptController, num: comptime_int) void {
nvic.ISER |= 1 << num;
}

pub fn mask(nvic: *volatile NestedVectorInterruptController, num: comptime_int) void {
nvic.ISER &= !(1 << num);
}
};
12 changes: 12 additions & 0 deletions core/src/cpus/cortex_m/m33.zig
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ pub const NestedVectorInterruptController = extern struct {
reserved6: [584]u32,
/// Software Trigger Interrupt Register.
STIR: u32,

pub fn unmask(nvic: *volatile NestedVectorInterruptController, num: comptime_int) void {
const bank = num / 32;
const index = num % 32;
nvic.ISER[bank] |= 1 << index;
}

pub fn mask(nvic: *volatile NestedVectorInterruptController, num: comptime_int) void {
const bank = num / 32;
const index = num % 32;
nvic.ISER[bank] &= !(1 << index);
}
};

pub const SecurityAttributionUnit = extern struct {
Expand Down
50 changes: 20 additions & 30 deletions core/src/interrupt.zig
Original file line number Diff line number Diff line change
@@ -1,40 +1,38 @@
const std = @import("std");
const micro = @import("microzig.zig");
const microzig = @import("microzig.zig");

/// Unmasks the given interrupt and enables its execution.
/// Note that interrupts must be globally enabled with `sei()` as well.
pub fn enable(comptime interrupt: anytype) void {
_ = interrupt;
@compileError("not implemented yet!");
/// Note that interrupts must be globally enabled with `enable_interrupts()` as well.
pub inline fn enable(comptime interrupt: anytype) void {
microzig.cpu.interrupt.enable(interrupt);
}

/// Masks the given interrupt and disables its execution.
pub fn disable(comptime interrupt: anytype) void {
_ = interrupt;
@compileError("not implemented yet!");
pub inline fn disable(comptime interrupt: anytype) void {
microzig.cpu.interrupt.disable(interrupt);
}

/// Returns true when the given interrupt is unmasked.
pub fn is_enabled(comptime interrupt: anytype) bool {
pub inline fn is_enabled(comptime interrupt: anytype) bool {
_ = interrupt;
@compileError("not implemented yet!");
}

/// *Set Enable Interrupt*, will enable IRQs globally, but keep the masking done via
/// *Set Enable Interrupt*, will enable interrupts globally, but keep the masking done via
/// `enable` and `disable` intact.
pub fn enable_interrupts() void {
micro.cpu.enable_interrupts();
pub inline fn enable_interrupts() void {
microzig.cpu.interrupt.enable_interrupts();
}

/// *Clear Enable Interrupt*, will disable IRQs globally, but keep the masking done via
/// *Clear Enable Interrupt*, will disable interrupts globally, but keep the masking done via
/// `enable` and `disable` intact.
pub fn disable_interrupts() void {
micro.cpu.disable_interrupts();
pub inline fn disable_interrupts() void {
microzig.cpu.interrupt.disable_interrupts();
}

/// Returns true, when interrupts are globally enabled via `sei()`.
pub fn globally_enabled() bool {
@compileError("not implemented yet!");
pub inline fn globally_enabled() bool {
return microzig.cpu.interrupt.globally_enabled();
}

/// Enters a critical section and disables interrupts globally.
Expand All @@ -60,17 +58,9 @@ const CriticalSection = struct {
}
};

// TODO: update with arch specifics
pub const Handler = extern union {
C: *const fn () callconv(.C) void,
Naked: *const fn () callconv(.Naked) void,
// Interrupt is not supported on arm
};
// TODO: remove this once the vector table uses it's own implementation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo it's -> its

pub const Handler = *const fn () callconv(.C) void;

pub const unhandled = Handler{
.C = struct {
fn tmp() callconv(.C) noreturn {
@panic("unhandled interrupt");
}
}.tmp,
};
pub fn unhandled() callconv(.C) void {
@panic("unhandled interrupt");
}
2 changes: 1 addition & 1 deletion core/src/microzig.zig
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noretu

/// Hangs the processor and will stop doing anything useful. Use with caution!
pub fn hang() noreturn {
cpu.disable_interrupts();
cpu.interrupt.disable_interrupts();
while (true) {
// "this loop has side effects, don't optimize the endless loop away please. thanks!"
asm volatile ("" ::: "memory");
Expand Down
2 changes: 1 addition & 1 deletion core/src/mmio.zig
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub fn Mmio(comptime PackedT: type) type {
addr.write_raw(@bitCast(val));
}

pub fn write_raw(addr: *volatile Self, val: IntT) void {
pub inline fn write_raw(addr: *volatile Self, val: IntT) void {
addr.raw = val;
}

Expand Down
53 changes: 0 additions & 53 deletions core/src/start.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,7 @@ const app = @import("app");
// Use microzig panic handler if not defined by an application
pub const panic = if (!@hasDecl(app, "panic")) microzig.panic else app.panic;

pub const VectorTableOptions = if (@hasDecl(microzig.chip, "VectorTable")) blk: {
const VectorTable = microzig.chip.VectorTable;
const fields_with_default = fields_with_default: {
var count = 0;
for (@typeInfo(VectorTable).Struct.fields) |field| {
if (field.default_value != null)
count += 1;
}

break :fields_with_default count;
};

var fields: [fields_with_default]std.builtin.Type.StructField = undefined;
var idx = 0;
for (@typeInfo(VectorTable).Struct.fields) |field| {
if (field.default_value == null)
continue;

fields[idx] = field;
idx += 1;
}

break :blk @Type(.{
.Struct = .{
.fields = &fields,
.layout = .auto,
.decls = &.{},
.is_tuple = false,
},
});
} else struct {};

pub const Options = struct {
interrupts: VectorTableOptions = .{},
log_level: std.log.Level = std.log.default_level,
log_scope_levels: []const std.log.ScopeLevel = &.{},
logFn: fn (
Expand Down Expand Up @@ -82,26 +49,6 @@ comptime {
// can just index flash, while harvard or flash-less architectures need
// to copy .rodata into RAM).
microzig.cpu.export_startup_logic();

// Export the vector table to flash start if we have any.
// For a lot of systems, the vector table provides a reset vector
// that is either called (Cortex-M) or executed (AVR) when initalized.

// Allow board and chip to override CPU vector table.
const export_opts = .{
.name = "vector_table",
.section = "microzig_flash_start",
.linkage = .strong,
};

if ((microzig.board != void and @hasDecl(microzig.board, "vector_table")))
@export(microzig.board.vector_table, export_opts)
else if (@hasDecl(microzig.chip, "vector_table"))
@export(microzig.chip.vector_table, export_opts)
else if (@hasDecl(microzig.cpu, "vector_table"))
@export(microzig.cpu.vector_table, export_opts)
else if (@hasDecl(app, "interrupts"))
@compileError("interrupts not configured");
}

/// This is the logical entry point for microzig.
Expand Down
Loading