Skip to content
Open
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
18 changes: 7 additions & 11 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ pub const LinkerScript = internals.LinkerScript;
pub const Stack = internals.Stack;
pub const MemoryRegion = internals.MemoryRegion;

const regz = @import("tools/regz");

// If more ports are available, the error "error: evaluation exceeded 1000 backwards branches" may occur.
// In such cases, consider increasing the argument value for @setEvalBranchQuota().
const port_list: []const struct {
Expand All @@ -26,6 +24,7 @@ const port_list: []const struct {
.{ .name = "gd32", .dep_name = "port/gigadevice/gd32" },
.{ .name = "samd51", .dep_name = "port/microchip/samd51" },
.{ .name = "atmega", .dep_name = "port/microchip/atmega" },
.{ .name = "attiny", .dep_name = "port/microchip/attiny" },
.{ .name = "nrf5x", .dep_name = "port/nordic/nrf5x" },
.{ .name = "lpc", .dep_name = "port/nxp/lpc" },
.{ .name = "mcx", .dep_name = "port/nxp/mcx" },
Expand All @@ -36,15 +35,6 @@ const port_list: []const struct {
.{ .name = "tm4c", .dep_name = "port/texasinstruments/tm4c" },
};

const exe_targets: []const std.Target.Query = &.{
.{ .cpu_arch = .aarch64, .os_tag = .macos },
.{ .cpu_arch = .aarch64, .os_tag = .linux },
.{ .cpu_arch = .aarch64, .os_tag = .windows },
.{ .cpu_arch = .x86_64, .os_tag = .macos },
.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl },
.{ .cpu_arch = .x86_64, .os_tag = .windows },
};

pub fn build(b: *Build) void {
const optimize = b.standardOptimizeOption(.{});

Expand Down Expand Up @@ -81,6 +71,7 @@ pub const PortSelect = struct {
gd32: bool = false,
samd51: bool = false,
atmega: bool = false,
attiny: bool = false,
nrf5x: bool = false,
lpc: bool = false,
mcx: bool = false,
Expand Down Expand Up @@ -771,6 +762,11 @@ pub fn MicroBuild(port_select: PortSelect) type {
.name = "avr5",
.root_source_file = mb.core_dep.namedLazyPath("cpu_avr5"),
};
} else if (std.mem.eql(u8, target.cpu.model.name, "avr25")) {
return .{
.name = "avr25",
.root_source_file = mb.core_dep.namedLazyPath("cpu_avr25"),
};
} else if (std.mem.startsWith(u8, target.cpu.model.name, "cortex_m")) {
return .{
.name = target.cpu.model.name,
Expand Down
1 change: 1 addition & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
.@"port/espressif/esp" = .{ .path = "port/espressif/esp", .lazy = true },
.@"port/gigadevice/gd32" = .{ .path = "port/gigadevice/gd32", .lazy = true },
.@"port/microchip/atmega" = .{ .path = "port/microchip/atmega", .lazy = true },
.@"port/microchip/attiny" = .{ .path = "port/microchip/attiny", .lazy = true },
.@"port/microchip/samd51" = .{ .path = "port/microchip/samd51", .lazy = true },
.@"port/nordic/nrf5x" = .{ .path = "port/nordic/nrf5x", .lazy = true },
.@"port/nxp/lpc" = .{ .path = "port/nxp/lpc", .lazy = true },
Expand Down
1 change: 1 addition & 0 deletions core/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub fn build(b: *std.Build) !void {
b.addNamedLazyPath("cpu_cortex_m", b.path("src/cpus/cortex_m.zig"));
b.addNamedLazyPath("cpu_riscv32", b.path("src/cpus/riscv32.zig"));
b.addNamedLazyPath("cpu_avr5", b.path("src/cpus/avr5.zig"));
b.addNamedLazyPath("cpu_avr25", b.path("src/cpus/avr25.zig"));
b.addNamedLazyPath("cpu_msp430", b.path("src/cpus/msp430.zig"));
b.addNamedLazyPath("cpu_msp430x", b.path("src/cpus/msp430x.zig"));

Expand Down
165 changes: 165 additions & 0 deletions core/src/cpus/avr25.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
const std = @import("std");
const microzig = @import("microzig");

pub const interrupt = struct {
pub fn enable_interrupts() void {
asm volatile ("sei");
}

pub fn disable_interrupts() void {
asm volatile ("cli");
}
};

/// AVR interrupt handler function type.
pub const HandlerFn = *const fn () callconv(.avr_signal) void;

/// Complete list of interrupt values based on the chip's `interrupts` array.
pub const Interrupt = microzig.utilities.GenerateInterruptEnum(i32);

/// Allowable `interrupt` options for microzig.options.
pub const InterruptOptions = microzig.utilities.GenerateInterruptOptions(&.{
.{ .InterruptEnum = Interrupt, .HandlerFn = HandlerFn },
});

pub inline fn sbi(comptime reg: u5, comptime bit: u3) void {
asm volatile ("sbi %[reg], %[bit]"
:
: [reg] "I" (reg),
[bit] "I" (bit),
);
}

pub inline fn cbi(comptime reg: u5, comptime bit: u3) void {
asm volatile ("cbi %[reg], %[bit]"
:
: [reg] "I" (reg),
[bit] "I" (bit),
);
}

pub const vector_table_asm = blk: {
const fields = std.meta.fields(microzig.chip.VectorTable);
std.debug.assert(std.mem.eql(u8, "RESET", fields[0].name));
// avr25 devices use rjmp (2-byte) instead of jmp (4-byte)
var asm_str: []const u8 = "rjmp microzig_start\n";

const interrupt_options = microzig.options.interrupts;

for (fields[1..]) |field| {
const handler = @field(interrupt_options, field.name);
if (handler) |func| {
const isr = make_isr_handler(field.name, func);
asm_str = asm_str ++ "rjmp " ++ isr.exported_name ++ "\n";
} else {
asm_str = asm_str ++ "rjmp microzig_unhandled_vector\n";
}
}

break :blk asm_str;
};

fn vector_table() linksection("microzig_flash_start") callconv(.naked) noreturn {
asm volatile (vector_table_asm);
}

// @breakpoint() on AVR is calling abort, so we export simple function that is calling hang
export fn abort() noreturn {
microzig.hang();
}

pub fn export_startup_logic() void {
_ = startup_logic;
@export(&vector_table, .{
.name = "_start",
});
}

fn make_isr_handler(comptime name: []const u8, comptime func: anytype) type {
const calling_convention = switch (@typeInfo(@TypeOf(func))) {
.@"fn" => |info| info.calling_convention,
.pointer => |info| switch (@typeInfo(info.child)) {
.@"fn" => |fn_info| fn_info.calling_convention,
else => @compileError("Declarations in 'interrupts' namespace must all be functions. '" ++ name ++ "' is not a function"),
},
else => @compileError("Declarations in 'interrupts' namespace must all be functions. '" ++ name ++ "' is not a function"),
};

switch (calling_convention) {
.auto, .avr_signal, .avr_interrupt => {},
else => @compileError("Calling conventions for interrupts must be 'avr_interrupt', 'avr_signal', or unspecified. The avr_signal calling convention leaves global interrupts disabled during the ISR, where avr_interrupt enables global interrupts for nested ISRs."),
}

return struct {
pub const exported_name = "microzig_isr_" ++ name;

comptime {
@export(func, .{ .name = exported_name });
}
};
}

pub const startup_logic = struct {
export fn microzig_unhandled_vector() callconv(.c) noreturn {
@panic("Unhandled interrupt");
}

extern fn microzig_main() noreturn;

export fn microzig_start() callconv(.c) noreturn {
// At startup the stack pointer is at the end of RAM
// so, no need to set it manually!

copy_data_to_ram();
clear_bss();

microzig_main();
}

fn copy_data_to_ram() void {
asm volatile (
\\ ; load Z register with the address of the data in flash
\\ ldi r30, lo8(microzig_data_load_start)
\\ ldi r31, hi8(microzig_data_load_start)
\\ ; load X register with address of the data in ram
\\ ldi r26, lo8(microzig_data_start)
\\ ldi r27, hi8(microzig_data_start)
\\ ; load address of end of the data in ram
\\ ldi r24, lo8(microzig_data_end)
\\ ldi r25, hi8(microzig_data_end)
\\ rjmp .L2
\\
\\.L1:
\\ lpm r18, Z+ ; copy from Z into r18 and increment Z
\\ st X+, r18 ; store r18 at location X and increment X
\\
\\.L2:
\\ cp r26, r24
\\ cpc r27, r25 ; check and branch if we are at the end of data
\\ brne .L1
);
// Probably a good idea to add clobbers here, but compiler doesn't seem to care
}

fn clear_bss() void {
asm volatile (
\\ ; load X register with the beginning of bss section
\\ ldi r26, lo8(microzig_bss_start)
\\ ldi r27, hi8(microzig_bss_start)
\\ ; load end of the bss in registers
\\ ldi r24, lo8(microzig_bss_end)
\\ ldi r25, hi8(microzig_bss_end)
\\ ldi r18, 0x00
\\ rjmp .L4
\\
\\.L3:
\\ st X+, r18
\\
\\.L4:
\\ cp r26, r24
\\ cpc r27, r25 ; check and branch if we are at the end of bss
\\ brne .L3
);
// Probably a good idea to add clobbers here, but compiler doesn't seem to care
}
};
65 changes: 35 additions & 30 deletions core/src/cpus/avr5.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ pub const interrupt = struct {
}
};

/// AVR interrupt handler function type.
pub const HandlerFn = *const fn () callconv(.avr_signal) void;

/// Complete list of interrupt values based on the chip's `interrupts` array.
pub const Interrupt = microzig.utilities.GenerateInterruptEnum(i32);

/// Allowable `interrupt` options for microzig.options.
pub const InterruptOptions = microzig.utilities.GenerateInterruptOptions(&.{
.{ .InterruptEnum = Interrupt, .HandlerFn = HandlerFn },
});

pub inline fn sbi(comptime reg: u5, comptime bit: u3) void {
asm volatile ("sbi %[reg], %[bit]"
:
Expand All @@ -28,31 +39,26 @@ pub inline fn cbi(comptime reg: u5, comptime bit: u3) void {
}

pub const vector_table_asm = blk: {
std.debug.assert(std.mem.eql(u8, "RESET", std.meta.fields(microzig.chip.VectorTable)[0].name));
const asm_str: []const u8 = "jmp microzig_start\n";

//const has_interrupts = @hasDecl(root, "microzig_options");
//for (@typeInfo(root.VectorTableOptions).@"struct".fields) |field| {
// const new_insn = if (has_interrupts) overload: {
// const interrupts = root.microzig_options.interrupts;
// if (@hasDecl(interrupts, field.name)) {
// const handler = @field(interrupts, field.name);

// const isr = make_isr_handler(field.name, handler);

// break :overload "jmp " ++ isr.exported_name;
// } else {
// break :overload "jmp microzig_unhandled_vector";
// }
// } else "jmp microzig_unhandled_vector";

// asm_str = asm_str ++ new_insn ++ "\n";
//}
const fields = std.meta.fields(microzig.chip.VectorTable);
std.debug.assert(std.mem.eql(u8, "RESET", fields[0].name));
var asm_str: []const u8 = "jmp microzig_start\n";

const interrupt_options = microzig.options.interrupts;

for (fields[1..]) |field| {
const handler = @field(interrupt_options, field.name);
if (handler) |func| {
const isr = make_isr_handler(field.name, func);
asm_str = asm_str ++ "jmp " ++ isr.exported_name ++ "\n";
} else {
asm_str = asm_str ++ "jmp microzig_unhandled_vector\n";
}
}

break :blk asm_str;
};

fn vector_table() callconv(.naked) noreturn {
fn vector_table() linksection("microzig_flash_start") callconv(.naked) noreturn {
asm volatile (vector_table_asm);
}

Expand All @@ -70,25 +76,24 @@ pub fn export_startup_logic() void {

fn make_isr_handler(comptime name: []const u8, comptime func: anytype) type {
const calling_convention = switch (@typeInfo(@TypeOf(func))) {
.Fn => |info| info.calling_convention,
.@"fn" => |info| info.calling_convention,
.pointer => |info| switch (@typeInfo(info.child)) {
.@"fn" => |fn_info| fn_info.calling_convention,
else => @compileError("Declarations in 'interrupts' namespace must all be functions. '" ++ name ++ "' is not a function"),
},
else => @compileError("Declarations in 'interrupts' namespace must all be functions. '" ++ name ++ "' is not a function"),
};

switch (calling_convention) {
.Unspecified, .Signal, .Interrupt => {},
else => @compileError("Calling conventions for interrupts must be 'Interrupt', 'Signal', or unspecified. The signal calling convention leaves global interrupts disabled during the ISR, where the interrupt calling conventions enables global interrupts for nested ISRs."),
.auto, .avr_signal, .avr_interrupt => {},
else => @compileError("Calling conventions for interrupts must be 'avr_interrupt', 'avr_signal', or unspecified. The avr_signal calling convention leaves global interrupts disabled during the ISR, where avr_interrupt enables global interrupts for nested ISRs."),
}

return struct {
pub const exported_name = "microzig_isr_" ++ name;

pub fn isr_vector() callconv(.Signal) void {
@call(.always_inline, func, .{});
}

comptime {
const options = .{ .name = exported_name, .linkage = .Strong };
@export(&isr_vector, options);
@export(func, .{ .name = exported_name });
}
};
}
Expand Down
45 changes: 45 additions & 0 deletions examples/microchip/attiny/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const std = @import("std");
const microzig = @import("microzig");

const MicroBuild = microzig.MicroBuild(.{
.attiny = true,
});

pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const maybe_example = b.option([]const u8, "example", "only build matching examples");

const mz_dep = b.dependency("microzig", .{});
const mb = MicroBuild.init(b, mz_dep) orelse return;

const available_examples = [_]Example{
.{ .target = mb.ports.attiny.boards.digispark, .name = "digispark_blinky", .file = "src/blinky.zig" },
.{ .target = mb.ports.attiny.boards.adafruit.trinket, .name = "trinket_blinky", .file = "src/blinky.zig" },
.{ .target = mb.ports.attiny.boards.adafruit.gemma, .name = "gemma_blinky", .file = "src/blinky.zig" },
.{ .target = mb.ports.attiny.chips.attiny85, .name = "attiny85_blinky", .file = "src/blinky.zig" },
.{ .target = mb.ports.attiny.chips.attiny85, .name = "attiny85_blinky_interrupt", .file = "src/blinky_interrupt.zig" },
.{ .target = mb.ports.attiny.chips.attiny84, .name = "attiny84_blinky", .file = "src/blinky84.zig" },
};

for (available_examples) |example| {
if (maybe_example) |selected_example|
if (!std.mem.containsAtLeast(u8, example.name, 1, selected_example))
continue;

const fw = mb.add_firmware(.{
.name = example.name,
.target = example.target,
.optimize = optimize,
.root_source_file = b.path(example.file),
});

mb.install_firmware(fw, .{});
mb.install_firmware(fw, .{ .format = .elf });
}
}

const Example = struct {
target: *const microzig.Target,
name: []const u8,
file: []const u8,
};
14 changes: 14 additions & 0 deletions examples/microchip/attiny/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.{
.name = .examples_microchip_attiny,
.fingerprint = 0x83cbabd71b524269,
.version = "0.0.0",
.dependencies = .{
.microzig = .{ .path = "../../.." },
},

.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}
Loading
Loading