Skip to content

Commit

Permalink
rebase
Browse files Browse the repository at this point in the history
  • Loading branch information
tact1m4n3 committed Jan 29, 2025
1 parent 68b5ad1 commit 2a27d7b
Show file tree
Hide file tree
Showing 14 changed files with 733 additions and 180 deletions.
112 changes: 62 additions & 50 deletions core/src/cpus/cortex_m.zig
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
const std = @import("std");
const microzig = @import("microzig");
const mmio = microzig.mmio;
const app = microzig.app;
const root = @import("root");

pub const Interrupt = microzig.utilities.GenerateInterruptEnum();
pub const InterruptOptions = microzig.utilities.GenerateInterruptOptions(fn () callconv(.C) void, .{Interrupt});
const interrupts: InterruptOptions = if (@hasDecl(app, "interrupts")) app.interrupts else .{};

pub fn executing_isr() bool {
return peripherals.scb.ICSR.read().VECTACTIVE != 0;
}
Expand All @@ -15,6 +20,42 @@ pub fn disable_interrupts() 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(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 globally_enabled() bool {
var mrs: u32 = undefined;
asm volatile ("mrs %[mrs], 16"
: [mrs] "+r" (mrs),
);
return mrs & 0x1 == 0;
}

pub fn enable_fault_irq() void {
asm volatile ("cpsie f");
}
Expand Down Expand Up @@ -82,63 +123,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.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.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.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.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.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
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/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
55 changes: 55 additions & 0 deletions core/src/utilities.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std");
const microzig = @import("microzig.zig");

/// A helper class that allows operating on a slice of slices
/// with similar operations to those of a slice.
Expand Down Expand Up @@ -188,6 +189,60 @@ pub fn Slice_Vector(comptime Slice: type) type {
};
}

pub fn max_enum_tag(T: type) @typeInfo(T).Enum.tag_type {
var max_tag: @typeInfo(T).Enum.tag_type = 0;
for (@typeInfo(T).Enum.fields) |field| {
if (field.value > max_tag) {
max_tag = field.value;
}
}
return max_tag;
}

pub fn GenerateInterruptEnum() type {
var fields: [microzig.chip.INTERRUPTS.len]std.builtin.Type.EnumField = undefined;

for (&fields, microzig.chip.INTERRUPTS) |*field, interrupt| {
field.* = .{
.name = interrupt.name,
.value = interrupt.index,
};
}

return @Type(.{ .Enum = .{
.tag_type = i16,
.fields = &fields,
.decls = &.{},
.is_exhaustive = true,
} });
}

pub fn GenerateInterruptOptions(HandlerType: type, sources: anytype) type {
const sources_type_info = @typeInfo(@TypeOf(sources)).Struct;
var ret_fields: []const std.builtin.Type.StructField = &.{};

for (sources_type_info.fields) |sources_field| {
for (@typeInfo(@field(sources, sources_field.name)).Enum.fields) |enum_field| {
ret_fields = ret_fields ++ .{.{
.name = enum_field.name,
.type = ?HandlerType,
.default_value = @as(*const anyopaque, @ptrCast(&@as(?HandlerType, null))),
.is_comptime = false,
.alignment = @alignOf(?HandlerType),
}};
}
}

return @Type(.{
.Struct = .{
.layout = .auto,
.fields = ret_fields,
.decls = &.{},
.is_tuple = false,
},
});
}

test Slice_Vector {
const vec = Slice_Vector([]const u8).init(&.{
"Hello,",
Expand Down
Loading

0 comments on commit 2a27d7b

Please sign in to comment.