Skip to content
29 changes: 26 additions & 3 deletions core/src/cpus/cortex_m.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std");
const builtin = @import("builtin");
const microzig = @import("microzig");
const mmio = microzig.mmio;
const app = microzig.app;
Expand Down Expand Up @@ -673,13 +674,35 @@ pub const startup_logic = struct {

for (@typeInfo(@TypeOf(microzig.options.interrupts)).@"struct".fields) |field| {
const maybe_handler = @field(microzig.options.interrupts, field.name);
if (maybe_handler) |handler| {
@field(tmp, field.name) = handler;
}
@field(tmp, field.name) = if (maybe_handler) |handler|
handler
else
default_exception_handler(field.name);
}

break :blk tmp;
};

fn default_exception_handler(comptime name: []const u8) microzig.interrupt.Handler {
return switch (builtin.mode) {
.Debug => .{ .c = DebugExceptionHandler(name).handle },
else => .{ .c = ReleaseExceptionHandler.handle },
};
}

fn DebugExceptionHandler(comptime name: []const u8) type {
return struct {
fn handle() callconv(.c) void {
@panic("Unhandled exception: " ++ name);
}
};
}

const ReleaseExceptionHandler = struct {
Copy link
Collaborator

Choose a reason for hiding this comment

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

is this just to save on code + data size on release builds?

fn handle() callconv(.c) void {
@panic("Unhandled exception");
}
};
};

fn is_ramimage() bool {
Expand Down
13 changes: 13 additions & 0 deletions port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ pub const Define = struct {
pub const Program = struct {
name: []const u8,
defines: []const Define,

/// Stores the raw instruction stream.
instructions: []const u16,
/// For each instruction, stores the kind of relocation required on load.
relocations: []const Relocation,

origin: ?u5,
side_set: ?encoder.SideSet,
wrap_target: ?u5,
Expand All @@ -27,6 +32,14 @@ pub const Program = struct {
}
};

pub const Relocation = union(enum) {
/// Keep the instruction as-is.
none,

/// Add the program origin to the lower 5 bit of the instruction.
jmpslot,
};

pub const Output = struct {
defines: []const Define,
programs: []const Program,
Expand Down
19 changes: 15 additions & 4 deletions port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub fn Encoder(comptime chip: Chip, comptime options: Options) type {
const BoundedDefines = std.BoundedArray(DefineWithIndex, options.max_defines);
const BoundedPrograms = std.BoundedArray(BoundedProgram, options.max_programs);
const BoundedInstructions = std.BoundedArray(Instruction(chip), 32);
const BoundedRelocations = std.BoundedArray(assembler.Relocation, 32);
const BoundedLabels = std.BoundedArray(Label, 32);
const Label = struct {
name: []const u8,
Expand All @@ -71,6 +72,7 @@ pub fn Encoder(comptime chip: Chip, comptime options: Options) type {
defines: BoundedDefines,
private_defines: BoundedDefines,
instructions: BoundedInstructions,
relocations: BoundedRelocations,
labels: BoundedLabels,
origin: ?u5,
side_set: ?SideSet,
Expand Down Expand Up @@ -99,6 +101,7 @@ pub fn Encoder(comptime chip: Chip, comptime options: Options) type {
break :blk defines_const.constSlice();
},
.instructions = @as([]const u16, @ptrCast(bounded.instructions.constSlice())),
.relocations = @as([]const assembler.Relocation, @ptrCast(bounded.relocations.constSlice())),
.origin = bounded.origin,
.side_set = bounded.side_set,
.wrap_target = bounded.wrap_target,
Expand Down Expand Up @@ -458,6 +461,13 @@ pub fn Encoder(comptime chip: Chip, comptime options: Options) type {
.payload = payload,
.delay_side_set = delay_side_set,
});

program.relocations.append(switch (tag) {
.jmp => .jmpslot,
else => .none,
}) catch unreachable;

std.debug.assert(program.instructions.len == program.relocations.len);
}

fn calc_delay_side_set(
Expand Down Expand Up @@ -546,10 +556,11 @@ pub fn Encoder(comptime chip: Chip, comptime options: Options) type {

var program = BoundedProgram{
.name = program_token.data.program,
.defines = BoundedDefines.init(0) catch unreachable,
.private_defines = BoundedDefines.init(0) catch unreachable,
.instructions = BoundedInstructions.init(0) catch unreachable,
.labels = BoundedLabels.init(0) catch unreachable,
.defines = .{},
.private_defines = .{},
.instructions = .{},
.relocations = .{},
.labels = .{},
.side_set = null,
.origin = null,
.wrap_target = null,
Expand Down
148 changes: 69 additions & 79 deletions port/raspberrypi/rp2xxx/src/hal/pio/common.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const std = @import("std");
const assert = std.debug.assert;

const microzig = @import("microzig");

const Chip = @import("../chip.zig").Chip;

pub const PIO = microzig.chip.types.peripherals.PIO0;
Expand Down Expand Up @@ -100,7 +101,7 @@ pub const ClkDivOptions = struct {
};

pub const ExecOptions = struct {
jmp_pin: u5 = 0,
jmp_pin: ?gpio.Pin = null,
wrap: u5 = 31,
wrap_target: u5 = 0,
side_pindir: bool = false,
Expand All @@ -109,16 +110,25 @@ pub const ExecOptions = struct {

pub fn PinMapping(comptime Count: type) type {
return struct {
base: u5 = 0,
count: Count = 0,
/// Creates a mapping with a single pin, length 1
pub fn single(pin: gpio.Pin) PinMapping(Count) {
return .{ .low = pin, .high = pin };
}

low: gpio.Pin,
high: gpio.Pin,

fn count(range: @This()) Count {
return @intCast(@intFromEnum(range.high) - @intFromEnum(range.low) + 1);
}
};
}

pub const PinMappingOptions = struct {
out: PinMapping(u6) = .{},
set: PinMapping(u3) = .{ .count = 5 },
side_set: PinMapping(u3) = .{},
in_base: u5 = 0,
out: ?PinMapping(u6) = null,
set: ?PinMapping(u3) = null,
side_set: ?PinMapping(u3) = null,
in_base: ?gpio.Pin = null,
};

pub fn PioImpl(EnumType: type, chip: Chip) type {
Expand Down Expand Up @@ -163,8 +173,16 @@ pub fn PioImpl(EnumType: type, chip: Chip) type {
return error.NoSpace;

const instruction_memory = self.get_instruction_memory();
for (program.instructions, offset..) |insn, i|
instruction_memory[i] = insn;
for (program.instructions, program.relocations, offset..) |insn, reloc, i|
instruction_memory[i] = switch (reloc) {
.none => insn,
.jmpslot => blk: {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would you mind just adding a comment here reminding us how you're doing the reloc

const Jmp = packed struct(u16) { address: u5, reset: u11 };
var jmp: Jmp = @bitCast(insn);
jmp.address += offset;
Copy link
Collaborator

Choose a reason for hiding this comment

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

In release this won't catch if offset + address overflows. Do we have a guarantee that the offset will be in the correct range? Probably worse a comment asserting that.

break :blk @as(u16, @bitCast(jmp));
},
};

const program_mask = program.get_mask();
UsedInstructionSpace(chip).val[@intFromEnum(self)] |= program_mask << offset;
Expand Down Expand Up @@ -225,15 +243,24 @@ pub fn PioImpl(EnumType: type, chip: Chip) type {
});
}

pub fn sm_set_exec_options(self: EnumType, sm: StateMachine, options: ExecOptions) void {
fn pin_to_index(self: EnumType, pin: gpio.Pin) error{InvalidPin}!u5 {
const index = @intFromEnum(pin);
const base = (0x10 & self.get_regs().GPIOBASE.raw);
if (index < base or index >= base + 32) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this OK for rp2350? the 32 is the number of pins that be setup in pio, not how many pins the chip has, correct?

return error.InvalidPin;
}
return @intCast(index - base);
}

pub fn sm_set_exec_options(self: EnumType, sm: StateMachine, options: ExecOptions) !void {
const sm_regs = self.get_sm_regs(sm);
sm_regs.execctrl.modify(.{
// NOTE: EXEC_STALLED is RO
.WRAP_BOTTOM = options.wrap_target,
.WRAP_TOP = options.wrap,
.SIDE_PINDIR = @intFromBool(options.side_pindir),
.SIDE_EN = @intFromBool(options.side_set_optional),
.JMP_PIN = options.jmp_pin,
.JMP_PIN = if (options.jmp_pin) |jmp_pin| try pin_to_index(self, jmp_pin) else 0,

// TODO: plug in rest of the options
// STATUS_N
Expand All @@ -244,55 +271,37 @@ pub fn PioImpl(EnumType: type, chip: Chip) type {
// EXEC_STALLED
});
}
pub fn sm_set_pin_mappings(self: EnumType, sm: StateMachine, options: PinMappingOptions) void {

pub fn sm_set_pin_mappings(self: EnumType, sm: StateMachine, options: PinMappingOptions) !void {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is InvalidPin the only error we can get here?

const sm_regs = self.get_sm_regs(sm);
sm_regs.pinctrl.modify(.{
.OUT_BASE = options.out.base,
.OUT_COUNT = options.out.count,
.OUT_BASE = if (options.out) |out| try pin_to_index(self, out.low) else 0,
.OUT_COUNT = if (options.out) |out| out.count() else 0,

.SET_BASE = options.set.base,
.SET_COUNT = options.set.count,
.SET_BASE = if (options.set) |set| try pin_to_index(self, set.low) else 0,
.SET_COUNT = if (options.set) |set| set.count() else 0,

.SIDESET_BASE = options.side_set.base,
.SIDESET_COUNT = options.side_set.count,
.SIDESET_BASE = if (options.side_set) |side_set| try pin_to_index(self, side_set.low) else 0,
.SIDESET_COUNT = if (options.side_set) |side_set| side_set.count() else 0,

.IN_BASE = options.in_base,
.IN_BASE = if (options.in_base) |in_base| try pin_to_index(self, in_base) else 0,
});
}

pub fn sm_set_pindir(self: EnumType, sm: StateMachine, base: u5, count: u5, dir: gpio.Direction) void {
pub fn sm_set_pindir(self: EnumType, sm: StateMachine, base: gpio.Pin, count: u5, dir: gpio.Direction) !void {
const sm_regs = self.get_sm_regs(sm);
const reg_pinctrl__copy = sm_regs.pinctrl;
const reg_execctrl__copy = sm_regs.execctrl;

for (base..base + count) |pin__number| {
for (0..count) |counter| {
const pin_config: PinMappingOptions = .{
.out = .{
.base = 0,
.count = 0,
},
.set = .{
.base = @intCast(pin__number),
.count = 1,
},
.side_set = .{
.base = 0,
.count = 0,
},
.in_base = 0,
.out = null,
.set = .single(@enumFromInt(@intFromEnum(base) + counter)),
.side_set = null,
.in_base = null,
};
sm_regs.pinctrl.modify(.{
.OUT_BASE = pin_config.out.base,
.OUT_COUNT = pin_config.out.count,

.SET_BASE = pin_config.set.base,
.SET_COUNT = pin_config.set.count,
try sm_set_pin_mappings(self, sm, pin_config);

.SIDESET_BASE = pin_config.side_set.base,
.SIDESET_COUNT = pin_config.side_set.count,

.IN_BASE = pin_config.in_base,
});
self.sm_exec(sm, .{
.payload = .{
.set = .{
Expand All @@ -308,39 +317,19 @@ pub fn PioImpl(EnumType: type, chip: Chip) type {
sm_regs.execctrl = reg_execctrl__copy;
}

pub fn sm_set_pin(self: EnumType, sm: StateMachine, base: u5, count: u5, value: u1) void {
pub fn sm_set_pin(self: EnumType, sm: StateMachine, base: gpio.Pin, count: u5, value: u1) !void {
const sm_regs = self.get_sm_regs(sm);
const reg_pinctrl__copy = sm_regs.pinctrl;
const reg_execctrl__copy = sm_regs.execctrl;

for (base..base + count) |pin__number| {
for (0..count) |counter| {
const pin_config: PinMappingOptions = .{
.out = .{
.base = 0,
.count = 0,
},
.set = .{
.base = @intCast(pin__number),
.count = 1,
},
.side_set = .{
.base = 0,
.count = 0,
},
.in_base = 0,
.out = null,
.set = .single(@enumFromInt(@intFromEnum(base) + counter)),
.side_set = null,
.in_base = null,
};
sm_regs.pinctrl.modify(.{
.OUT_BASE = pin_config.out.base,
.OUT_COUNT = pin_config.out.count,

.SET_BASE = pin_config.set.base,
.SET_COUNT = pin_config.set.count,

.SIDESET_BASE = pin_config.side_set.base,
.SIDESET_COUNT = pin_config.side_set.count,

.IN_BASE = pin_config.in_base,
});
try sm_set_pin_mappings(self, sm, pin_config);
self.sm_exec(sm, .{
.payload = .{
.set = .{
Expand Down Expand Up @@ -491,13 +480,13 @@ pub fn PioImpl(EnumType: type, chip: Chip) type {
sm: StateMachine,
initial_pc: u5,
options: StateMachineInitOptions(chip),
) void {
) !void {
// Halt the machine, set some sensible defaults
self.sm_set_enabled(sm, false);
self.sm_set_clkdiv(sm, options.clkdiv);
self.sm_set_exec_options(sm, options.exec);
try self.sm_set_exec_options(sm, options.exec);
self.sm_set_shift_options(sm, options.shift);
self.sm_set_pin_mappings(sm, options.pin_mappings);
try self.sm_set_pin_mappings(sm, options.pin_mappings);

self.sm_clear_fifos(sm);
self.sm_clear_debug(sm);
Expand Down Expand Up @@ -537,23 +526,24 @@ pub fn PioImpl(EnumType: type, chip: Chip) type {
else
0;

assert(expected_side_set_pins == options.pin_mappings.side_set.count);
const config_count = if (options.pin_mappings.side_set) |side_set| side_set.count() else 0;
assert(expected_side_set_pins == config_count);

// TODO: check program settings vs pin mapping
const offset = try self.add_program(program);

self.sm_init(sm, offset, .{
try self.sm_init(sm, offset, .{
.clkdiv = options.clkdiv,
.shift = options.shift,
.pin_mappings = options.pin_mappings,
.exec = .{
.wrap = if (program.wrap) |wrap|
wrap
offset + wrap
else
offset + @as(u5, @intCast(program.instructions.len)),

.wrap_target = if (program.wrap_target) |wrap_target|
wrap_target
offset + wrap_target
else
offset,

Expand Down
Loading
Loading