-
Notifications
You must be signed in to change notification settings - Fork 174
Add ATtiny port with avr25 CPU support and fix AVR vector tables #923
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
Open
mattnite
wants to merge
5
commits into
main
Choose a base branch
from
attiny
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
2495248
Add ATtiny port with avr25 CPU support and fix AVR vector tables
mattnite 38f9b58
Remove unused chips.zig from attiny port
mattnite 8e782e7
Remove unused imports from ATtiny HAL files
mattnite 9ab36bb
Import cleanup
mattnite d596238
Declare led pin at board layer
mattnite File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this a duplicate? |
||
| 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 | ||
| } | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,17 @@ pub const interrupt = struct { | |
| } | ||
| }; | ||
|
|
||
| /// AVR interrupt handler function type. | ||
| pub const HandlerFn = *const fn () callconv(.avr_signal) void; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure if this should actually be |
||
|
|
||
| /// 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]" | ||
| : | ||
|
|
@@ -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); | ||
| } | ||
|
|
||
|
|
@@ -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 }); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| 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.boards.adafruit.gemma, .name = "gemma_blinky_interrupt", .file = "src/blinky_interrupt.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, | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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", | ||
| }, | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's start looking at making a table instead of if elses.