diff --git a/port/raspberrypi/rp2xxx/src/hal/chip.zig b/port/raspberrypi/rp2xxx/src/hal/chip.zig new file mode 100644 index 00000000..366f42f0 --- /dev/null +++ b/port/raspberrypi/rp2xxx/src/hal/chip.zig @@ -0,0 +1,4 @@ +pub const Chip = enum { + RP2040, + RP2350, +}; diff --git a/port/raspberrypi/rp2xxx/src/hal/compatibility.zig b/port/raspberrypi/rp2xxx/src/hal/compatibility.zig index beb1b1db..1ab282a7 100644 --- a/port/raspberrypi/rp2xxx/src/hal/compatibility.zig +++ b/port/raspberrypi/rp2xxx/src/hal/compatibility.zig @@ -1,10 +1,6 @@ const std = @import("std"); const microzig = @import("microzig"); - -pub const Chip = enum { - RP2040, - RP2350, -}; +const Chip = @import("chip.zig").Chip; pub const chip: Chip = blk: { if (std.mem.eql(u8, microzig.config.chip_name, "RP2040")) { diff --git a/port/raspberrypi/rp2xxx/src/hal/pio.zig b/port/raspberrypi/rp2xxx/src/hal/pio.zig index b9382b13..b1561fbc 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio.zig @@ -12,7 +12,7 @@ const chip_specific = switch (chip) { .RP2350 => @import("pio/rp2350.zig"), }; pub const StateMachine = common.StateMachine; -pub const Instruction = common.Instruction; +pub const Instruction = common.Instruction(chip); pub const PinMapping = common.PinMapping; pub const PinMappingOptions = common.PinMappingOptions; pub const StateMachineInitOptions = chip_specific.StateMachineInitOptions; @@ -24,7 +24,9 @@ pub const assembler = @import("pio/assembler.zig"); const encoder = @import("pio/assembler/encoder.zig"); pub const Program = assembler.Program; -pub const assemble = assembler.assemble; +pub inline fn assemble(comptime source: []const u8, comptime options: assembler.AssembleOptions) assembler.Output { + return assembler.assemble(chip, source, options); +} pub fn num(n: u2) Pio { switch (chip) { diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig index 9738d37a..1f11a610 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig @@ -1,6 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; +const Chip = @import("../chip.zig").Chip; const tokenizer = @import("assembler/tokenizer.zig"); const encoder = @import("assembler/encoder.zig"); @@ -71,9 +72,9 @@ pub const Diagnostics = struct { } }; -pub fn assemble_impl(comptime source: []const u8, diags: *?Diagnostics, options: AssembleOptions) !Output { - const tokens = try tokenizer.tokenize(source, diags, options.tokenize); - const encoder_output = try encoder.encode(tokens.slice(), diags, options.encode); +pub fn assemble_impl(comptime chip: Chip, comptime source: []const u8, diags: *?Diagnostics, options: AssembleOptions) !Output { + const tokens = try tokenizer.tokenize(chip, source, diags, options.tokenize); + const encoder_output = try encoder.encode(chip, tokens.slice(), diags, options.encode); var programs = std.BoundedArray(Program, options.encode.max_programs).init(0) catch unreachable; for (encoder_output.programs.slice()) |bounded| try programs.append(bounded.to_exported_program()); @@ -121,9 +122,9 @@ fn format_compile_error(comptime message: []const u8, comptime source: []const u }); } -pub fn assemble(comptime source: []const u8, comptime options: AssembleOptions) Output { +pub fn assemble(comptime chip: Chip, comptime source: []const u8, comptime options: AssembleOptions) Output { var diags: ?Diagnostics = null; - return assemble_impl(source, &diags, options) catch |err| { + return assemble_impl(chip, source, &diags, options) catch |err| { if (diags) |d| @compileError(format_compile_error(d.message.slice(), source, d.index)); @compileError(@errorName(err)); diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig index 4bad38dc..dea3b96d 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig @@ -1,6 +1,8 @@ const std = @import("std"); const assembler = @import("../assembler.zig"); const tokenizer = @import("tokenizer.zig"); +const Chip = @import("../../chip.zig").Chip; + const c = @cImport({ @cDefine("PICO_NO_HARDWARE", "1"); @cInclude("stdint.h"); @@ -12,7 +14,9 @@ const c = @cImport({ @cInclude("comparison_tests/hello.pio.h"); @cInclude("comparison_tests/hub75.pio.h"); @cInclude("comparison_tests/i2c.pio.h"); + @cInclude("comparison_tests/irq.pio.h"); @cInclude("comparison_tests/manchester_encoding.pio.h"); + @cInclude("comparison_tests/movrx.pio.h"); @cInclude("comparison_tests/nec_carrier_burst.pio.h"); @cInclude("comparison_tests/nec_carrier_control.pio.h"); @cInclude("comparison_tests/nec_receive.pio.h"); @@ -31,7 +35,13 @@ const c = @cImport({ }); fn pio_comparison(comptime source: []const u8) !void { - const output = comptime assembler.assemble(source, .{}); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + try pio_comparison_chip(chip, source); + } +} + +fn pio_comparison_chip(comptime chip: Chip, comptime source: []const u8) !void { + const output = comptime assembler.assemble(chip, source, .{}); try std.testing.expect(output.programs.len > 0); inline for (output.programs) |program| { @@ -87,11 +97,21 @@ test "pio.comparison.i2c" { try pio_comparison(@embedFile("comparison_tests/i2c.pio")); } +test "pio.comparison.irq" { + @setEvalBranchQuota(22000); + try pio_comparison_chip(.RP2350, @embedFile("comparison_tests/irq.pio")); +} + test "pio.comparison.manchester_encoding" { @setEvalBranchQuota(11000); try pio_comparison(@embedFile("comparison_tests/manchester_encoding.pio")); } +test "pio.comparison.movrx" { + @setEvalBranchQuota(11000); + try pio_comparison_chip(.RP2350, @embedFile("comparison_tests/movrx.pio")); +} + test "pio.comparison.nec_carrier_burst" { @setEvalBranchQuota(6000); try pio_comparison(@embedFile("comparison_tests/nec_carrier_burst.pio")); diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/README.md b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/README.md index 8ca91ece..bc988afc 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/README.md +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/README.md @@ -1,4 +1,6 @@ # PIO example programs for testing -These were all taken from [the official pico examples repo](https://github.com/raspberrypi/pico-examples). +These were all taken from [the official pico examples +repo](https://github.com/raspberrypi/pico-examples). + The headers are generated using `pioasm`. diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/irq.pio b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/irq.pio new file mode 100644 index 00000000..a1192a3c --- /dev/null +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/irq.pio @@ -0,0 +1,14 @@ +.program irq +.side_set 1 + +.wrap_target + irq set 1 prev side 0 + irq set 1 rel side 0 + irq set 1 next side 0 + irq wait 1 prev side 0 + irq wait 1 rel side 0 + irq wait 1 next side 0 + irq clear 1 prev side 0 + irq clear 1 rel side 0 + irq clear 1 next side 0 +.wrap diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/irq.pio.h b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/irq.pio.h new file mode 100644 index 00000000..6434f5ce --- /dev/null +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/irq.pio.h @@ -0,0 +1,14 @@ +#pragma once + +// TODO: Exercise more? delays, optional sideset, etc? +static const uint16_t irq_program_instructions[] = { + 0xc009, // irq set 1 prev side 0 + 0xc011, // irq set 1 rel side 0 + 0xc019, // irq set 1 next side 0 + 0xc029, // irq wait 1 prev side 0 + 0xc031, // irq wait 1 rel side 0 + 0xc039, // irq wait 1 next side 0 + 0xc049, // irq clear 1 prev side 0 + 0xc051, // irq clear 1 rel side 0 + 0xc059, // irq clear 1 next side 0 +}; diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio new file mode 100644 index 00000000..5ecfed80 --- /dev/null +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio @@ -0,0 +1,15 @@ +.program movrx + +.wrap_target + mov rxfifoy, isr + mov rxfifo0, isr + mov rxfifo1, isr + mov rxfifo2, isr + mov rxfifo3, isr + + mov osr, rxfifoy + mov osr, rxfifo0 + mov osr, rxfifo1 + mov osr, rxfifo2 + mov osr, rxfifo3 +.wrap diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio.h b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio.h new file mode 100644 index 00000000..09f9ca44 --- /dev/null +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio.h @@ -0,0 +1,16 @@ +#pragma once + +static const uint16_t movrx_program_instructions[] = { + // 0b1000_ssss_0001_yiii + 0x8018, // mov rxfifoy, isr + 0x8010, // mov rxfifo0, isr + 0x8011, // mov rxfifo1, isr + 0x8012, // mov rxfifo2, isr + 0x8013, // mov rxfifo3, isr + // 0b1000_ssss_1001_yiii + 0x8098, // mov osr, rxfifoy + 0x8090, // mov osr, rxfifo0 + 0x8091, // mov osr, rxfifo1 + 0x8092, // mov osr, rxfifo2 + 0x8093, // mov osr, rxfifo3 +}; diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig index e8159f82..cbb3b6ee 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig @@ -9,6 +9,7 @@ const Token = tokenizer.Token; const Value = tokenizer.Value; const Expression = @import("Expression.zig"); +const Chip = @import("../../chip.zig").Chip; pub const Options = struct { max_defines: u32 = 16, @@ -16,11 +17,12 @@ pub const Options = struct { }; pub fn encode( - comptime tokens: []const Token, + comptime chip: Chip, + comptime tokens: []const Token(chip), diags: *?assembler.Diagnostics, comptime options: Options, -) !Encoder(options).Output { - var encoder = Encoder(options).init(tokens); +) !Encoder(chip, options).Output { + var encoder = Encoder(chip, options).init(tokens); return try encoder.encode_output(diags); } @@ -41,10 +43,10 @@ pub const DefineWithIndex = struct { index: u32, }; -pub fn Encoder(comptime options: Options) type { +pub fn Encoder(comptime chip: Chip, comptime options: Options) type { return struct { output: Self.Output, - tokens: []const Token, + tokens: []const Token(chip), index: u32, const Self = @This(); @@ -56,7 +58,7 @@ pub fn Encoder(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, 32); + const BoundedInstructions = std.BoundedArray(Instruction(chip), 32); const BoundedLabels = std.BoundedArray(Label, 32); const Label = struct { name: []const u8, @@ -105,7 +107,7 @@ pub fn Encoder(comptime options: Options) type { } }; - fn init(tokens: []const Token) Self { + fn init(tokens: []const Token(chip)) Self { return Self{ .output = Self.Output{ .global_defines = BoundedDefines.init(0) catch unreachable, @@ -117,14 +119,14 @@ pub fn Encoder(comptime options: Options) type { }; } - fn peek_token(self: Self) ?Token { + fn peek_token(self: Self) ?Token(chip) { return if (self.index < self.tokens.len) self.tokens[self.index] else null; } - fn get_token(self: *Self) ?Token { + fn get_token(self: *Self) ?Token(chip) { return if (self.peek_token()) |token| blk: { self.consume(1); break :blk token; @@ -303,12 +305,12 @@ pub fn Encoder(comptime options: Options) type { fn encode_instruction( self: *Self, program: *BoundedProgram, - token: Token.Instruction, + token: Token(chip).Instruction, token_index: u32, diags: *?Diagnostics, ) !void { // guaranteed to be an instruction variant - const payload: Instruction.Payload = switch (token.payload) { + const payload: Instruction(chip).Payload = switch (token.payload) { .nop => .{ .mov = .{ .destination = .y, @@ -360,18 +362,46 @@ pub fn Encoder(comptime options: Options) type { .source = mov.source, }, }, + // NOTE: These instructions values only exist for RP2350 + .movtorx => |mov| .{ + .movtorx = .{ + .idx = mov.idx, + .idxl = @intFromBool(mov.idxl), + }, + }, + .movfromrx => |mov| .{ + .movfromrx = .{ + .idx = mov.idx, + .idxl = @intFromBool(mov.idxl), + }, + }, .irq => |irq| blk: { - const irq_num = try self.evaluate(u5, program.*, irq.num, token_index, diags); - break :blk .{ - .irq = .{ - .clear = @intFromBool(irq.clear), - .wait = @intFromBool(irq.wait), - .index = if (irq.rel) - @as(u5, 0x10) | irq_num - else - irq_num, + switch (chip) { + .RP2040 => { + const irq_num = try self.evaluate(u5, program.*, irq.num, token_index, diags); + break :blk .{ + .irq = .{ + .clear = @intFromBool(irq.clear), + .wait = @intFromBool(irq.wait), + .index = if (irq.rel) + @as(u5, 0x10) | irq_num + else + irq_num, + }, + }; }, - }; + .RP2350 => { + const irq_num = try self.evaluate(u3, program.*, irq.num, token_index, diags); + break :blk .{ + .irq = .{ + .clear = @intFromBool(irq.clear), + .wait = @intFromBool(irq.wait), + .index = @as(u3, irq_num), + .idxmode = @intFromEnum(irq.idxmode), + }, + }; + }, + } }, .set => |set| .{ .set = .{ @@ -381,14 +411,16 @@ pub fn Encoder(comptime options: Options) type { }, }; - const tag: Instruction.Tag = switch (token.payload) { + const tag: Instruction(chip).Tag = switch (token.payload) { .nop => .mov, .jmp => .jmp, .wait => .wait, .in => .in, .out => .out, - .push => .push_pull, - .pull => .push_pull, + .push => .push_pull_mov_rx, + .pull => .push_pull_mov_rx, + .movtorx => .push_pull_mov_rx, + .movfromrx => .push_pull_mov_rx, .mov => .mov, .irq => .irq, .set => .set, @@ -422,7 +454,7 @@ pub fn Encoder(comptime options: Options) type { delay, ); - try program.instructions.append(Instruction{ + try program.instructions.append(Instruction(chip){ .tag = tag, .payload = payload, .delay_side_set = delay_side_set, @@ -485,7 +517,7 @@ pub fn Encoder(comptime options: Options) type { switch (token.data) { .instruction => |instr| try self.encode_instruction(program, instr, token.index, diags), .word => |word| try program.instructions.append( - @as(Instruction, @bitCast(try self.evaluate(u16, program.*, word, token.index, diags))), + @as(Instruction(chip), @bitCast(try self.evaluate(u16, program.*, word, token.index, diags))), ), // already processed .label, .wrap_target, .wrap => {}, @@ -534,87 +566,112 @@ pub fn Encoder(comptime options: Options) type { }; } -pub const Instruction = packed struct(u16) { - payload: Payload, - delay_side_set: u5, - tag: Tag, - - pub const Payload = packed union { - jmp: Jmp, - wait: Wait, - in: In, - out: Out, - push: Push, - pull: Pull, - mov: Mov, - irq: Irq, - set: Set, - }; +pub fn Instruction(comptime chip: Chip) type { + return packed struct(u16) { + payload: Payload, + delay_side_set: u5, + tag: Tag, + + pub const Payload = packed union { + jmp: Jmp, + wait: Wait, + in: In, + out: Out, + push: Push, + pull: Pull, + mov: Mov, + movtorx: MovToRx, + movfromrx: MovFromRx, + irq: Irq, + set: Set, + }; - pub const Tag = enum(u3) { - jmp, - wait, - in, - out, - push_pull, - mov, - irq, - set, - }; + pub const Tag = enum(u3) { + jmp, + wait, + in, + out, + push_pull_mov_rx, + mov, + irq, + set, + }; - pub const Jmp = packed struct(u8) { - address: u5, - condition: Token.Instruction.Jmp.Condition, - }; + pub const Jmp = packed struct(u8) { + address: u5, + condition: Token(chip).Instruction.Jmp.Condition, + }; - pub const Wait = packed struct(u8) { - index: u5, - source: Token.Instruction.Wait.Source, - polarity: u1, - }; + pub const Wait = packed struct(u8) { + index: u5, + source: Token(chip).Instruction.Wait.Source, + polarity: u1, + }; - pub const In = packed struct(u8) { - bit_count: u5, - source: Token.Instruction.In.Source, - }; + pub const In = packed struct(u8) { + bit_count: u5, + source: Token(chip).Instruction.In.Source, + }; - pub const Out = packed struct(u8) { - bit_count: u5, - destination: Token.Instruction.Out.Destination, - }; + pub const Out = packed struct(u8) { + bit_count: u5, + destination: Token(chip).Instruction.Out.Destination, + }; - pub const Push = packed struct(u8) { - _reserved0: u5 = 0, - block: u1, - if_full: u1, - _reserved1: u1 = 0, - }; + pub const Push = packed struct(u8) { + _reserved0: u5 = 0, + block: u1, + if_full: u1, + _reserved1: u1 = 0, + }; - pub const Pull = packed struct(u8) { - _reserved0: u5 = 0, - block: u1, - if_empty: u1, - _reserved1: u1 = 1, - }; + pub const Pull = packed struct(u8) { + _reserved0: u5 = 0, + block: u1, + if_empty: u1, + _reserved1: u1 = 1, + }; - pub const Mov = packed struct(u8) { - source: Token.Instruction.Mov.Source, - operation: Token.Instruction.Mov.Operation, - destination: Token.Instruction.Mov.Destination, - }; + pub const Mov = packed struct(u8) { + source: Token(chip).Instruction.Mov.Source, + operation: Token(chip).Instruction.Mov.Operation, + destination: Token(chip).Instruction.Mov.Destination, + }; - pub const Irq = packed struct(u8) { - index: u5, - wait: u1, - clear: u1, - reserved: u1 = 0, - }; + pub const Irq = switch (chip) { + .RP2040 => packed struct(u8) { + index: u5, + wait: u1, + clear: u1, + reserved: u1 = 0, + }, + .RP2350 => packed struct(u8) { + index: u3, + idxmode: u2, + wait: u1, + clear: u1, + reserved: u1 = 0, + }, + }; + + // RP2350 only, but we need them for the switch case + pub const MovToRx = packed struct(u8) { + idx: u3, + idxl: u1, + _reserved0: u4 = 1, + }; + pub const MovFromRx = packed struct(u8) { + idx: u3, + idxl: u1, + _reserved0: u4 = 9, + }; - pub const Set = packed struct(u8) { - data: u5, - destination: Token.Instruction.Set.Destination, + pub const Set = packed struct(u8) { + data: u5, + destination: Token(chip).Instruction.Set.Destination, + }; }; -}; +} //============================================================================== // Encoder Tests @@ -624,22 +681,22 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const expectEqualStrings = std.testing.expectEqualStrings; -fn encode_bounded_output_impl(source: []const u8, diags: *?assembler.Diagnostics) !Encoder(.{}).Output { - const tokens = try tokenizer.tokenize(source, diags, .{}); - var encoder = Encoder(.{}).init(tokens.slice()); +fn encode_bounded_output_impl(comptime chip: Chip, source: []const u8, diags: *?assembler.Diagnostics) !Encoder(chip, .{}).Output { + const tokens = try tokenizer.tokenize(chip, source, diags, .{}); + var encoder = Encoder(chip, .{}).init(tokens.slice()); return try encoder.encode_output(diags); } -fn encode_bounded_output(source: []const u8) !Encoder(.{}).Output { +fn encode_bounded_output(comptime chip: Chip, source: []const u8) !Encoder(chip, .{}).Output { var diags: ?assembler.Diagnostics = null; - return encode_bounded_output_impl(source, &diags) catch |err| if (diags) |d| blk: { + return encode_bounded_output_impl(chip, source, &diags) catch |err| if (diags) |d| blk: { std.log.err("error at index {}: {s}", .{ d.index, d.message.slice() }); break :blk err; } else err; } test "encode.define" { - const output = try encode_bounded_output(".define foo 5"); + const output = try encode_bounded_output(.RP2040, ".define foo 5"); try expectEqual(@as(usize, 0), output.global_defines.len); try expectEqual(@as(usize, 1), output.private_defines.len); @@ -650,7 +707,7 @@ test "encode.define" { } test "encode.define.public" { - const output = try encode_bounded_output(".define PUBLIC foo 5"); + const output = try encode_bounded_output(.RP2040, ".define PUBLIC foo 5"); try expectEqual(@as(usize, 1), output.global_defines.len); try expectEqual(@as(usize, 0), output.private_defines.len); @@ -658,7 +715,7 @@ test "encode.define.public" { } test "encode.program.empty" { - const output = try encode_bounded_output(".program arst"); + const output = try encode_bounded_output(.RP2040, ".program arst"); try expectEqual(@as(usize, 0), output.global_defines.len); try expectEqual(@as(usize, 0), output.private_defines.len); @@ -669,7 +726,7 @@ test "encode.program.empty" { } test "encode.program.define" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\.define bruh 7 ); @@ -688,7 +745,7 @@ test "encode.program.define" { } test "encode.program.define.public" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\.define public bruh 7 ); @@ -707,7 +764,7 @@ test "encode.program.define.public" { } test "encode.program.define.namespaced" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\.define public bruh 7 \\.program what @@ -736,7 +793,7 @@ test "encode.program.define.namespaced" { } test "encode.origin" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\.origin 0 ); @@ -753,7 +810,7 @@ test "encode.origin" { } test "encode.wrap_target" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\nop \\.wrap_target @@ -772,7 +829,7 @@ test "encode.wrap_target" { } test "encode.wrap" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\nop \\.wrap @@ -791,7 +848,7 @@ test "encode.wrap" { } test "encode.side_set" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\.side_set 1 ); @@ -805,7 +862,7 @@ test "encode.side_set" { } test "encode.side_set.opt" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\.side_set 1 opt ); @@ -820,7 +877,7 @@ test "encode.side_set.opt" { } test "encode.side_set.pindirs" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\.side_set 1 pindirs ); @@ -835,7 +892,7 @@ test "encode.side_set.pindirs" { } test "encode.label" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\nop \\my_label: @@ -856,7 +913,7 @@ test "encode.label" { } test "encode.label.public" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\nop \\nop @@ -878,7 +935,7 @@ test "encode.label.public" { } test "encode.side_set.bits" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\.side_set 1 opt \\nop side 1 @@ -903,7 +960,7 @@ test "encode.side_set.bits" { } test "encode.evaluate.global" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.define NUM 5 \\.define public FOO NUM ); @@ -915,7 +972,7 @@ test "encode.evaluate.global" { } test "encode.evaluate.addition" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.define public FOO (1+5) ); @@ -925,7 +982,7 @@ test "encode.evaluate.addition" { } test "encode.evaluate.subtraction" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.define public FOO (5-1) ); @@ -935,7 +992,7 @@ test "encode.evaluate.subtraction" { } test "encode.evaluate.multiplication" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.define public FOO (5*2) ); @@ -945,7 +1002,7 @@ test "encode.evaluate.multiplication" { } test "encode.evaluate.division" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.define public FOO (6/2) ); @@ -955,7 +1012,7 @@ test "encode.evaluate.division" { } test "encode.evaluate.bit reversal" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.define public FOO ::1 ); @@ -965,7 +1022,7 @@ test "encode.evaluate.bit reversal" { } test "encode.jmp.label" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program arst \\nop \\my_label: @@ -987,14 +1044,14 @@ test "encode.jmp.label" { try expectEqual(false, label.public); const instr = program.instructions.get(3); - try expectEqual(Instruction.Tag.jmp, instr.tag); + try expectEqual(Instruction(.RP2040).Tag.jmp, instr.tag); try expectEqual(@as(u5, 0), instr.delay_side_set); - try expectEqual(Token.Instruction.Jmp.Condition.always, instr.payload.jmp.condition); + try expectEqual(Token(.RP2040).Instruction.Jmp.Condition.always, instr.payload.jmp.condition); try expectEqual(@as(u5, 1), instr.payload.jmp.address); } test "encode.jmp.label origin" { - const output = try encode_bounded_output( + const output = try encode_bounded_output(.RP2040, \\.program program_at_4 \\.origin 4 \\nop @@ -1024,9 +1081,9 @@ test "encode.jmp.label origin" { try expectEqual(false, label.public); const instr = program.instructions.get(2); - try expectEqual(Instruction.Tag.jmp, instr.tag); + try expectEqual(Instruction(.RP2040).Tag.jmp, instr.tag); try expectEqual(@as(u5, 0), instr.delay_side_set); - try expectEqual(Token.Instruction.Jmp.Condition.always, instr.payload.jmp.condition); + try expectEqual(Token(.RP2040).Instruction.Jmp.Condition.always, instr.payload.jmp.condition); try expectEqual(@as(u5, 5), instr.payload.jmp.address); } @@ -1040,9 +1097,9 @@ test "encode.jmp.label origin" { try expectEqual(false, label.public); const instr = program.instructions.get(3); - try expectEqual(Instruction.Tag.jmp, instr.tag); + try expectEqual(Instruction(.RP2040).Tag.jmp, instr.tag); try expectEqual(@as(u5, 0), instr.delay_side_set); - try expectEqual(Token.Instruction.Jmp.Condition.always, instr.payload.jmp.condition); + try expectEqual(Token(.RP2040).Instruction.Jmp.Condition.always, instr.payload.jmp.condition); try expectEqual(@as(u5, 22), instr.payload.jmp.address); } } diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 4a3aedb8..ee7be59b 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -5,18 +5,20 @@ const assembler = @import("../assembler.zig"); const Diagnostics = assembler.Diagnostics; const Expression = @import("Expression.zig"); +const Chip = @import("../../chip.zig").Chip; pub const Options = struct { capacity: u32 = 256, }; pub fn tokenize( + comptime chip: Chip, source: []const u8, diags: *?assembler.Diagnostics, comptime options: Options, -) !std.BoundedArray(Token, options.capacity) { - var tokens = std.BoundedArray(Token, options.capacity).init(0) catch unreachable; - var tokenizer = Tokenizer.init(source); +) !std.BoundedArray(Token(chip), options.capacity) { + var tokens = std.BoundedArray(Token(chip), options.capacity).init(0) catch unreachable; + var tokenizer = Tokenizer(chip).init(source); while (try tokenizer.next(diags)) |token| try tokens.append(token); @@ -62,1082 +64,1300 @@ pub const Value = union(enum) { // '/' -> '*' -> block comment // '%' -> -> -> -> '{' -> code block // '.' -> directive -pub const Tokenizer = struct { - source: []const u8, - index: u32, - - pub fn format( - self: Tokenizer, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = fmt; - _ = options; +pub fn Tokenizer(chip: Chip) type { + return struct { + const Self = @This(); + source: []const u8, + index: u32, - try writer.print( - \\parser: - \\ index: {} - \\ - \\ - , .{self.index}); - - var printed_cursor = false; - var line_it = std.mem.tokenize(u8, self.source, "\n\r"); - while (line_it.next()) |line| { - try writer.print("{s}\n", .{line}); - if (!printed_cursor and line_it.index > self.index) { - try writer.writeByteNTimes(' ', line.len - (line_it.index - self.index)); - try writer.writeAll("\x1b[30;42;1m^\x1b[0m\n"); - printed_cursor = true; + pub fn format( + self: Self, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = fmt; + _ = options; + + try writer.print( + \\parser: + \\ index: {} + \\ + \\ + , .{self.index}); + + var printed_cursor = false; + var line_it = std.mem.tokenize(u8, self.source, "\n\r"); + while (line_it.next()) |line| { + try writer.print("{s}\n", .{line}); + if (!printed_cursor and line_it.index > self.index) { + try writer.writeByteNTimes(' ', line.len - (line_it.index - self.index)); + try writer.writeAll("\x1b[30;42;1m^\x1b[0m\n"); + printed_cursor = true; + } } } - } - fn init(source: []const u8) Tokenizer { - return Tokenizer{ - .source = source, - .index = 0, - }; - } + fn init(source: []const u8) Self { + return Self{ + .source = source, + .index = 0, + }; + } - fn consume(self: *Tokenizer, count: u32) void { - assert(self.index < self.source.len); - self.index += count; - } + fn consume(self: *Self, count: u32) void { + assert(self.index < self.source.len); + self.index += count; + } - fn peek(self: Tokenizer) ?u8 { - return if (self.index < self.source.len) - self.source[self.index] - else - null; - } + fn peek(self: Self) ?u8 { + return if (self.index < self.source.len) + self.source[self.index] + else + null; + } - fn get(self: *Tokenizer) ?u8 { - return if (self.index < self.source.len) blk: { - defer self.index += 1; - break :blk self.source[self.index]; - } else null; - } + fn get(self: *Self) ?u8 { + return if (self.index < self.source.len) blk: { + defer self.index += 1; + break :blk self.source[self.index]; + } else null; + } - fn skip_line(self: *Tokenizer) void { - while (self.get()) |c| - if (c == '\n') - return; - } + fn skip_line(self: *Self) void { + while (self.get()) |c| + if (c == '\n') + return; + } - fn skip_until_end_of_comment_block(self: *Tokenizer) void { - while (self.get()) |c| { - if (c == '*') { - if (self.peek()) |p| { - self.consume(1); - if (p == '/') { - return; + fn skip_until_end_of_comment_block(self: *Self) void { + while (self.get()) |c| { + if (c == '*') { + if (self.peek()) |p| { + self.consume(1); + if (p == '/') { + return; + } } } } } - } - fn skip_until_end_of_code_block(self: *Tokenizer) void { - // TODO: assert we have the code identifier and open curly bracket - while (self.get()) |c| { - if (c == '%') { - if (self.peek()) |p| { - self.consume(1); - if (p == '}') { - return; + fn skip_until_end_of_code_block(self: *Self) void { + // TODO: assert we have the code identifier and open curly bracket + while (self.get()) |c| { + if (c == '%') { + if (self.peek()) |p| { + self.consume(1); + if (p == '}') { + return; + } } } } } - } - fn read_until_whitespace_or_end(self: *Tokenizer) ![]const u8 { - const start = self.index; - var end: ?u32 = null; - while (self.peek()) |p| { - switch (p) { - ' ', '\n', '\r', '\t', ',' => { - end = self.index; - break; - }, - else => self.consume(1), - } - } else end = self.index; + fn read_until_whitespace_or_end(self: *Self) ![]const u8 { + const start = self.index; + var end: ?u32 = null; + while (self.peek()) |p| { + switch (p) { + ' ', '\n', '\r', '\t', ',' => { + end = self.index; + break; + }, + else => self.consume(1), + } + } else end = self.index; - return self.source[start .. end orelse return error.EndOfStream]; - } + return self.source[start .. end orelse return error.EndOfStream]; + } - fn skip_whitespace(self: *Tokenizer) void { - while (self.peek()) |p| { - switch (p) { - ' ', '\t', '\r', '\n', ',' => self.consume(1), - else => return, + fn skip_whitespace(self: *Self) void { + while (self.peek()) |p| { + switch (p) { + ' ', '\t', '\r', '\n', ',' => self.consume(1), + else => return, + } } } - } - /// returns array of args - fn get_args(self: *Tokenizer, comptime num: u32, diags: *?Diagnostics) TokenizeError![num]?[]const u8 { - var args: [num]?[]const u8 = undefined; - for (&args) |*arg| - arg.* = try self.get_arg(diags); + /// returns array of args + fn get_args(self: *Self, comptime num: u32, diags: *?Diagnostics) TokenizeError![num]?[]const u8 { + var args: [num]?[]const u8 = undefined; + for (&args) |*arg| + arg.* = try self.get_arg(diags); - return args; - } + return args; + } - const PeekResult = struct { - str: []const u8, - start: u32, - }; + const PeekResult = struct { + str: []const u8, + start: u32, + }; - fn peek_arg(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!?PeekResult { - var tmp_index = self.index; - return self.peek_arg_impl(&tmp_index, diags); - } + fn peek_arg(self: *Self, diags: *?Diagnostics) TokenizeError!?PeekResult { + var tmp_index = self.index; + return self.peek_arg_impl(&tmp_index, diags); + } - fn consume_peek(self: *Tokenizer, result: PeekResult) void { - assert(self.index <= result.start); - self.index = result.start + @as(u32, @intCast(result.str.len)); - } + fn consume_peek(self: *Self, result: PeekResult) void { + assert(self.index <= result.start); + self.index = result.start + @as(u32, @intCast(result.str.len)); + } - /// gets next arg without consuming the stream - fn peek_arg_impl( - self: *Tokenizer, - index: *u32, - diags: *?Diagnostics, - ) TokenizeError!?PeekResult { - - // skip whitespace - while (index.* < self.source.len) { - switch (self.source[index.*]) { - ' ', '\t', ',' => index.* += 1, - else => break, - } + fn unconsume(self: *Self, result: PeekResult) void { + assert(self.index > result.start); + self.index = result.start; } - if (index.* == self.source.len) - return null; + /// gets next arg without consuming the stream + fn peek_arg_impl( + self: *Self, + index: *u32, + diags: *?Diagnostics, + ) TokenizeError!?PeekResult { + + // skip whitespace + while (index.* < self.source.len) { + switch (self.source[index.*]) { + ' ', '\t', ',' => index.* += 1, + else => break, + } + } - const start = index.*; - const end = end: { - break :end switch (self.source[start]) { - '(' => blk: { - var depth: u32 = 0; - break :blk while (index.* < self.source.len) : (index.* += 1) { - switch (self.source[index.*]) { - '(' => depth += 1, - ')' => { - depth -= 1; - - if (depth == 0) { - index.* += 1; - break index.*; - } - }, - else => {}, + if (index.* == self.source.len) + return null; + + const start = index.*; + const end = end: { + break :end switch (self.source[start]) { + '(' => blk: { + var depth: u32 = 0; + break :blk while (index.* < self.source.len) : (index.* += 1) { + switch (self.source[index.*]) { + '(' => depth += 1, + ')' => { + depth -= 1; + + if (depth == 0) { + index.* += 1; + break index.*; + } + }, + else => {}, + } + } else { + diags.* = Diagnostics.init(start, "mismatched parenthesis", .{}); + return error.InvalidExpression; + }; + }, + '[' => while (index.* < self.source.len) : (index.* += 1) { + if (self.source[index.*] == ']') { + index.* += 1; + break index.*; } } else { diags.* = Diagnostics.init(start, "mismatched parenthesis", .{}); return error.InvalidExpression; - }; - }, - '[' => while (index.* < self.source.len) : (index.* += 1) { - if (self.source[index.*] == ']') { - index.* += 1; - break index.*; - } - } else { - diags.* = Diagnostics.init(start, "mismatched parenthesis", .{}); - return error.InvalidExpression; - }, - else => while (index.* < self.source.len) { - switch (self.source[index.*]) { - // ; and / are to stop at comments - ' ', '\t', '\r', '\n', ',', ';', '/' => break index.*, - else => index.* += 1, - } - } else index.*, + }, + else => while (index.* < self.source.len) { + switch (self.source[index.*]) { + // ; and / are to stop at comments + ' ', '\t', '\r', '\n', ',', ';', '/' => break index.*, + else => index.* += 1, + } + } else index.*, + }; }; - }; - return if (start != end) - PeekResult{ - .str = self.source[start..end], - .start = start, - } - else - null; - } - - fn get_arg(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!?[]const u8 { - return if (try self.peek_arg_impl(&self.index, diags)) |result| - result.str - else - null; - } + return if (start != end) + PeekResult{ + .str = self.source[start..end], + .start = start, + } + else + null; + } - const Identifier = struct { - index: u32, - str: []const u8, - }; + fn get_arg(self: *Self, diags: *?Diagnostics) TokenizeError!?[]const u8 { + return if (try self.peek_arg_impl(&self.index, diags)) |result| + result.str + else + null; + } - fn get_identifier(self: *Tokenizer) TokenizeError!Identifier { - self.skip_whitespace(); - return Identifier{ - .index = self.index, - .str = try self.read_until_whitespace_or_end(), + const Identifier = struct { + index: u32, + str: []const u8, }; - } - const TokenizeError = error{ - EndOfStream, - NoValue, - NotAnExpression, - Overflow, - InvalidCharacter, - InvalidSource, - InvalidCondition, - MissingArg, - InvalidDestination, - InvalidOperation, - InvalidExpression, - TooBig, - }; + fn get_identifier(self: *Self) TokenizeError!Identifier { + self.skip_whitespace(); + return Identifier{ + .index = self.index, + .str = try self.read_until_whitespace_or_end(), + }; + } - fn get_program(self: *Tokenizer, index: u32, diags: *?Diagnostics) TokenizeError!Token { - const name = (try self.get_arg(diags)) orelse { - diags.* = Diagnostics.init(index, "missing program name", .{}); - return error.MissingArg; - }; - return Token{ - .index = index, - .data = .{ .program = name }, + const TokenizeError = error{ + EndOfStream, + NoValue, + NotAnExpression, + Overflow, + InvalidCharacter, + InvalidSource, + InvalidCondition, + MissingArg, + InvalidDestination, + InvalidOperation, + InvalidExpression, + TooBig, }; - } - fn assert_is_lower(str: []const u8) void { - for (str) |c| - assert(std.ascii.isLower(c)); - } + fn get_program(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(chip) { + const name = (try self.get_arg(diags)) orelse { + diags.* = Diagnostics.init(index, "missing program name", .{}); + return error.MissingArg; + }; + return Token(chip){ + .index = index, + .data = .{ .program = name }, + }; + } + + fn assert_is_lower(str: []const u8) void { + for (str) |c| + assert(std.ascii.isLower(c)); + } - fn eql_lower(comptime lhs: []const u8, rhs: []const u8) bool { - assert_is_lower(lhs); - if (lhs.len != rhs.len) - return false; + fn eql_lower(comptime lhs: []const u8, rhs: []const u8) bool { + assert_is_lower(lhs); + if (lhs.len != rhs.len) + return false; - var buf: [lhs.len]u8 = undefined; - for (&buf, rhs) |*b, r| - b.* = std.ascii.toLower(r); + var buf: [lhs.len]u8 = undefined; + for (&buf, rhs) |*b, r| + b.* = std.ascii.toLower(r); - return std.mem.eql(u8, &buf, lhs); - } + return std.mem.eql(u8, &buf, lhs); + } - fn get_define(self: *Tokenizer, index: u32, diags: *?Diagnostics) TokenizeError!Token { - const maybe_public = try self.get_identifier(); - const is_public = eql_lower("public", maybe_public.str); - - const name = if (is_public) - try self.get_identifier() - else - maybe_public; - - return Token{ - .index = index, - .data = .{ - .define = .{ - .name = name.str, - .value = Value{ - .expression = (try self.get_arg(diags)) orelse { - diags.* = Diagnostics.init(index, "failed to get expression", .{}); - return error.InvalidExpression; + fn get_define(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(chip) { + const maybe_public = try self.get_identifier(); + const is_public = eql_lower("public", maybe_public.str); + + const name = if (is_public) + try self.get_identifier() + else + maybe_public; + + return Token(chip){ + .index = index, + .data = .{ + .define = .{ + .name = name.str, + .value = Value{ + .expression = (try self.get_arg(diags)) orelse { + diags.* = Diagnostics.init(index, "failed to get expression", .{}); + return error.InvalidExpression; + }, }, + .public = is_public, + .index = name.index, }, - .public = is_public, - .index = name.index, }, - }, - }; - } + }; + } - fn get_expression(self: *Tokenizer) TokenizeError!Value { - const start = self.index; - var count: u32 = 1; + fn get_expression(self: *Self) TokenizeError!Value { + const start = self.index; + var count: u32 = 1; - if (self.get()) |c| - if (c != '(') - return error.NotAnExpression; + if (self.get()) |c| + if (c != '(') + return error.NotAnExpression; - while (self.get()) |c| { - switch (c) { - '(' => count += 1, - ')' => { - count -= 1; - }, - else => {}, - } + while (self.get()) |c| { + switch (c) { + '(' => count += 1, + ')' => { + count -= 1; + }, + else => {}, + } - if (count == 0) { - return Value{ - .expression = self.source[start..self.index], - }; + if (count == 0) { + return Value{ + .expression = self.source[start..self.index], + }; + } + } else { + return error.NotAnExpression; } - } else { - return error.NotAnExpression; } - } - fn get_value(self: *Tokenizer) TokenizeError!Value { - self.skip_whitespace(); + fn get_value(self: *Self) TokenizeError!Value { + self.skip_whitespace(); - if (self.peek()) |p| - if (p == '(') - return try self.get_expression() - else { - const identifier = try self.get_identifier(); - return try Value.from_string(identifier.str); + if (self.peek()) |p| + if (p == '(') + return try self.get_expression() + else { + const identifier = try self.get_identifier(); + return try Value.from_string(identifier.str); + } + else + return error.NoValue; + } + + fn get_origin(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(chip) { + _ = diags; + return Token(chip){ + .index = index, + .data = .{ + .origin = try self.get_value(), + }, + }; + } + + fn get_side_set(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(chip) { + const args = try self.get_args(3, diags); + const count = try Value.from_string(args[0] orelse { + diags.* = Diagnostics.init(index, "missing count", .{}); + return error.MissingArg; + }); + var opt = false; + var pindirs = false; + + if (args[1]) |arg| { + if (std.mem.eql(u8, "opt", arg)) + opt = true + else if (std.mem.eql(u8, "pindirs", arg)) + pindirs = true; } - else - return error.NoValue; - } - fn get_origin(self: *Tokenizer, index: u32, diags: *?Diagnostics) TokenizeError!Token { - _ = diags; - return Token{ - .index = index, - .data = .{ - .origin = try self.get_value(), - }, - }; - } + if (args[2]) |arg| { + if (std.mem.eql(u8, "pindirs", arg)) + pindirs = true; + } - fn get_side_set(self: *Tokenizer, index: u32, diags: *?Diagnostics) TokenizeError!Token { - const args = try self.get_args(3, diags); - const count = try Value.from_string(args[0] orelse { - diags.* = Diagnostics.init(index, "missing count", .{}); - return error.MissingArg; - }); - var opt = false; - var pindirs = false; - - if (args[1]) |arg| { - if (std.mem.eql(u8, "opt", arg)) - opt = true - else if (std.mem.eql(u8, "pindirs", arg)) - pindirs = true; + return Token(chip){ + .index = index, + .data = .{ + .side_set = .{ + .count = count, + .opt = opt, + .pindir = pindirs, + }, + }, + }; } - if (args[2]) |arg| { - if (std.mem.eql(u8, "pindirs", arg)) - pindirs = true; + fn get_wrap_target(_: *Self, index: u32, _: *?Diagnostics) TokenizeError!Token(chip) { + return Token(chip){ + .index = index, + .data = .{ .wrap_target = {} }, + }; + } + + fn get_wrap(_: *Self, index: u32, _: *?Diagnostics) TokenizeError!Token(chip) { + return Token(chip){ + .index = index, + .data = .{ .wrap = {} }, + }; } - return Token{ - .index = index, - .data = .{ - .side_set = .{ - .count = count, - .opt = opt, - .pindir = pindirs, + fn get_lang_opt(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(chip) { + _ = diags; + return Token(chip){ + .index = index, + .data = .{ + .lang_opt = .{ + .lang = (try self.get_identifier()).str, + .name = (try self.get_identifier()).str, + .option = (try self.get_identifier()).str, + }, }, - }, - }; - } + }; + } - fn get_wrap_target(_: *Tokenizer, index: u32, _: *?Diagnostics) TokenizeError!Token { - return Token{ - .index = index, - .data = .{ .wrap_target = {} }, - }; - } + fn get_word(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(chip) { + _ = diags; + return Token(chip){ + .index = index, + .data = .{ .word = try self.get_value() }, + }; + } - fn get_wrap(_: *Tokenizer, index: u32, _: *?Diagnostics) TokenizeError!Token { - return Token{ - .index = index, - .data = .{ .wrap = {} }, - }; - } + const directives = std.StaticStringMap(*const fn (*Self, u32, *?Diagnostics) TokenizeError!Token(chip)).initComptime(.{ + .{ "program", get_program }, + .{ "define", get_define }, + .{ "origin", get_origin }, + .{ "side_set", get_side_set }, + .{ "wrap_target", get_wrap_target }, + .{ "wrap", get_wrap }, + .{ "lang_opt", get_lang_opt }, + .{ "word", get_word }, + }); + + fn get_directive(self: *Self, diags: *?Diagnostics) !Token(chip) { + const index = self.index; + const identifier = try self.read_until_whitespace_or_end(); + return if (directives.get(identifier)) |handler| ret: { + const ret = try handler(self, index, diags); + self.skip_line(); + break :ret ret; + } else error.InvalidDirective; + } + + fn get_nop(_: *Self, _: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { + return Token(chip).Instruction.Payload{ + .nop = {}, + }; + } - fn get_lang_opt(self: *Tokenizer, index: u32, diags: *?Diagnostics) TokenizeError!Token { - _ = diags; - return Token{ - .index = index, - .data = .{ - .lang_opt = .{ - .lang = (try self.get_identifier()).str, - .name = (try self.get_identifier()).str, - .option = (try self.get_identifier()).str, + fn target_from_string(str: []const u8) TokenizeError!Token(chip).Instruction.Jmp.Target { + const value = Value.from_string(str); + return Token(chip).Instruction.Payload{ + .jmp = .{ + .condition = .always, + .target = switch (value) { + .string => |label| Token(chip).Instruction.Jmp.Target{ + .label = label, + }, + else => Token(chip).Instruction.Jmp.Target{ + .value = value, + }, + }, }, - }, - }; - } + }; + } - fn get_word(self: *Tokenizer, index: u32, diags: *?Diagnostics) TokenizeError!Token { - _ = diags; - return Token{ - .index = index, - .data = .{ .word = try self.get_value() }, - }; - } + fn get_jmp(self: *Self, diags: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { + const Condition = Token(chip).Instruction.Jmp.Condition; + const conditions = std.StaticStringMap(Condition).initComptime(.{ + .{ "!x", .x_is_zero }, + .{ "x--", .x_dec }, + .{ "!y", .y_is_zero }, + .{ "y--", .y_dec }, + .{ "x!=y", .x_is_not_y }, + .{ "pin", .pin }, + .{ "!osre", .osre_not_empty }, + }); + + const maybe_cond = (try self.get_arg(diags)) orelse return error.MissingArg; + const maybe_cond_lower = try lowercase_bounded(256, maybe_cond); + const cond: Condition = conditions.get(maybe_cond_lower.slice()) orelse .always; + const target_str = if (cond == .always) + maybe_cond + else + (try self.get_arg(diags)) orelse return error.MissingArg; - const directives = std.StaticStringMap(*const fn (*Tokenizer, u32, *?Diagnostics) TokenizeError!Token).initComptime(.{ - .{ "program", get_program }, - .{ "define", get_define }, - .{ "origin", get_origin }, - .{ "side_set", get_side_set }, - .{ "wrap_target", get_wrap_target }, - .{ "wrap", get_wrap }, - .{ "lang_opt", get_lang_opt }, - .{ "word", get_word }, - }); + return Token(chip).Instruction.Payload{ + .jmp = .{ .condition = cond, .target = target_str }, + }; + } - fn get_directive(self: *Tokenizer, diags: *?Diagnostics) !Token { - const index = self.index; - const identifier = try self.read_until_whitespace_or_end(); - return if (directives.get(identifier)) |handler| ret: { - const ret = try handler(self, index, diags); - self.skip_line(); - break :ret ret; - } else error.InvalidDirective; - } + fn get_wait(self: *Self, diags: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { + const polarity = try std.fmt.parseInt(u1, (try self.get_arg(diags)) orelse return error.MissingArg, 0); + const source_str = (try self.get_arg(diags)) orelse return error.MissingArg; + const pin = try Value.from_string((try self.get_arg(diags)) orelse return error.MissingArg); + + var buf: [8]u8 = undefined; + for (source_str, 0..) |c, i| + buf[i] = std.ascii.toLower(c); + + const source_lower = buf[0..source_str.len]; + var source: Token(chip).Instruction.Wait.Source = undefined; + switch (comptime chip) { + .RP2040 => { + source = if (std.mem.eql(u8, "gpio", source_lower)) + .gpio + else if (std.mem.eql(u8, "pin", source_lower)) + .pin + else if (std.mem.eql(u8, "irq", source_lower)) + .irq + else + return error.InvalidSource; + }, + .RP2350 => { + source = if (std.mem.eql(u8, "gpio", source_lower)) + .gpio + else if (std.mem.eql(u8, "jmppin", source_lower)) + .jmppin + else if (std.mem.eql(u8, "pin", source_lower)) + .pin + else if (std.mem.eql(u8, "irq", source_lower)) + .irq + else + return error.InvalidSource; + }, + } - fn get_nop(_: *Tokenizer, _: *?Diagnostics) TokenizeError!Token.Instruction.Payload { - return Token.Instruction.Payload{ - .nop = {}, - }; - } + const rel: bool = if (source == .irq) + if (try self.peek_arg(diags)) |rel_result| blk: { + const is_rel = std.mem.eql(u8, "rel", rel_result.str); + if (is_rel) + self.consume_peek(rel_result); - fn target_from_string(str: []const u8) TokenizeError!Token.Instruction.Jmp.Target { - const value = Value.from_string(str); - return Token.Instruction.Payload{ - .jmp = .{ - .condition = .always, - .target = switch (value) { - .string => |label| Token.Instruction.Jmp.Target{ - .label = label, - }, - else => Token.Instruction.Jmp.Target{ - .value = value, - }, + break :blk is_rel; + } else false + else + false; + + return Token(chip).Instruction.Payload{ + .wait = .{ + .polarity = polarity, + .source = source, + .num = pin, + .rel = rel, }, - }, - }; - } + }; + } - fn get_jmp(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!Token.Instruction.Payload { - const Condition = Token.Instruction.Jmp.Condition; - const conditions = std.StaticStringMap(Condition).initComptime(.{ - .{ "!x", .x_is_zero }, - .{ "x--", .x_dec }, - .{ "!y", .y_is_zero }, - .{ "y--", .y_dec }, - .{ "x!=y", .x_is_not_y }, - .{ "pin", .pin }, - .{ "!osre", .osre_not_empty }, - }); + /// get the lowercase of a string, returns an error if it's too big + fn lowercase_bounded(comptime max_size: usize, str: []const u8) TokenizeError!std.BoundedArray(u8, max_size) { + if (str.len > max_size) + return error.TooBig; - const maybe_cond = (try self.get_arg(diags)) orelse return error.MissingArg; - const maybe_cond_lower = try lowercase_bounded(256, maybe_cond); - const cond: Condition = conditions.get(maybe_cond_lower.slice()) orelse .always; - const target_str = if (cond == .always) - maybe_cond - else - (try self.get_arg(diags)) orelse return error.MissingArg; + var ret = std.BoundedArray(u8, max_size).init(0) catch unreachable; + for (str) |c| + try ret.append(std.ascii.toLower(c)); - return Token.Instruction.Payload{ - .jmp = .{ .condition = cond, .target = target_str }, - }; - } + return ret; + } - fn get_wait(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!Token.Instruction.Payload { - const polarity = try std.fmt.parseInt(u1, (try self.get_arg(diags)) orelse return error.MissingArg, 0); - const source_str = (try self.get_arg(diags)) orelse return error.MissingArg; - const pin = try Value.from_string((try self.get_arg(diags)) orelse return error.MissingArg); - - var buf: [8]u8 = undefined; - for (source_str, 0..) |c, i| - buf[i] = std.ascii.toLower(c); - - const source_lower = buf[0..source_str.len]; - const source: Token.Instruction.Wait.Source = - if (std.mem.eql(u8, "gpio", source_lower)) - .gpio - else if (std.mem.eql(u8, "pin", source_lower)) - .pin - else if (std.mem.eql(u8, "irq", source_lower)) - .irq - else - return error.InvalidSource; - - const rel: bool = if (source == .irq) - if (try self.peek_arg(diags)) |rel_result| blk: { - const is_rel = std.mem.eql(u8, "rel", rel_result.str); - if (is_rel) - self.consume_peek(rel_result); - - break :blk is_rel; - } else false - else - false; - - return Token.Instruction.Payload{ - .wait = .{ - .polarity = polarity, - .source = source, - .num = pin, - .rel = rel, - }, - }; - } + // TODO: I need to take a break. There is no rush to finish this. The thing + // I need to keep in mind with `get_args()` is that I must only consume the + // args that are used. side set and delay may be on the same line - /// get the lowercase of a string, returns an error if it's too big - fn lowercase_bounded(comptime max_size: usize, str: []const u8) TokenizeError!std.BoundedArray(u8, max_size) { - if (str.len > max_size) - return error.TooBig; + fn get_in(self: *Self, diags: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { + const source_str = (try self.get_arg(diags)) orelse return error.MissingArg; + const bit_count_str = (try self.get_arg(diags)) orelse return error.MissingArg; - var ret = std.BoundedArray(u8, max_size).init(0) catch unreachable; - for (str) |c| - try ret.append(std.ascii.toLower(c)); + const source_lower = try lowercase_bounded(256, source_str); + const bit_count_tmp = try std.fmt.parseInt(u6, bit_count_str, 0); + const bit_count = if (bit_count_tmp == 32) + @as(u5, 0) + else + @as(u5, @intCast(bit_count_tmp)); - return ret; - } + return Token(chip).Instruction.Payload{ + .in = .{ + .source = std.meta.stringToEnum(Token(chip).Instruction.In.Source, source_lower.slice()) orelse return error.InvalidSource, + .bit_count = bit_count, + }, + }; + } - // TODO: I need to take a break. There is no rush to finish this. The thing - // I need to keep in mind with `get_args()` is that I must only consume the - // args that are used. side set and delay may be on the same line - - fn get_in(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!Token.Instruction.Payload { - const source_str = (try self.get_arg(diags)) orelse return error.MissingArg; - const bit_count_str = (try self.get_arg(diags)) orelse return error.MissingArg; - - const source_lower = try lowercase_bounded(256, source_str); - const bit_count_tmp = try std.fmt.parseInt(u6, bit_count_str, 0); - const bit_count = if (bit_count_tmp == 32) - @as(u5, 0) - else - @as(u5, @intCast(bit_count_tmp)); - - return Token.Instruction.Payload{ - .in = .{ - .source = std.meta.stringToEnum(Token.Instruction.In.Source, source_lower.slice()) orelse return error.InvalidSource, - .bit_count = bit_count, - }, - }; - } + fn get_out(self: *Self, diags: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { + const dest_src = (try self.get_arg(diags)) orelse return error.MissingArg; + const bit_count_str = (try self.get_arg(diags)) orelse return error.MissingArg; - fn get_out(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!Token.Instruction.Payload { - const dest_src = (try self.get_arg(diags)) orelse return error.MissingArg; - const bit_count_str = (try self.get_arg(diags)) orelse return error.MissingArg; - - const dest_lower = try lowercase_bounded(256, dest_src); - const bit_count_tmp = try std.fmt.parseInt(u6, bit_count_str, 0); - const bit_count = if (bit_count_tmp == 32) - @as(u5, 0) - else - @as(u5, @intCast(bit_count_tmp)); - - return Token.Instruction.Payload{ - .out = .{ - .destination = std.meta.stringToEnum(Token.Instruction.Out.Destination, dest_lower.slice()) orelse return error.InvalidDestination, - .bit_count = bit_count, - }, - }; - } + const dest_lower = try lowercase_bounded(256, dest_src); + const bit_count_tmp = try std.fmt.parseInt(u6, bit_count_str, 0); + const bit_count = if (bit_count_tmp == 32) + @as(u5, 0) + else + @as(u5, @intCast(bit_count_tmp)); - fn block_from_peek(self: *Tokenizer, result: PeekResult) TokenizeError!bool { - const block_lower = try lowercase_bounded(256, result.str); - const is_block = std.mem.eql(u8, "block", block_lower.slice()); - const is_noblock = std.mem.eql(u8, "noblock", block_lower.slice()); + return Token(chip).Instruction.Payload{ + .out = .{ + .destination = std.meta.stringToEnum(Token(chip).Instruction.Out.Destination, dest_lower.slice()) orelse return error.InvalidDestination, + .bit_count = bit_count, + }, + }; + } - if (is_block or is_noblock) - self.consume_peek(result); + fn block_from_peek(self: *Self, result: PeekResult) TokenizeError!bool { + const block_lower = try lowercase_bounded(256, result.str); + const is_block = std.mem.eql(u8, "block", block_lower.slice()); + const is_noblock = std.mem.eql(u8, "noblock", block_lower.slice()); - return if (is_block) - true - else if (is_noblock) - false - else - true; - } + if (is_block or is_noblock) + self.consume_peek(result); - fn get_push(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!Token.Instruction.Payload { - return if (try self.peek_arg(diags)) |first_result| ret: { - const lower = try lowercase_bounded(256, first_result.str); - const iffull = std.mem.eql(u8, "iffull", lower.slice()); + return if (is_block) + true + else if (is_noblock) + false + else + true; + } - const block: bool = if (iffull) blk: { - self.consume_peek(first_result); - break :blk if (try self.peek_arg(diags)) |block_result| - try self.block_from_peek(block_result) - else - true; - } else try self.block_from_peek(first_result); + fn get_push(self: *Self, diags: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { + return if (try self.peek_arg(diags)) |first_result| ret: { + const lower = try lowercase_bounded(256, first_result.str); + const iffull = std.mem.eql(u8, "iffull", lower.slice()); + + const block: bool = if (iffull) blk: { + self.consume_peek(first_result); + break :blk if (try self.peek_arg(diags)) |block_result| + try self.block_from_peek(block_result) + else + true; + } else try self.block_from_peek(first_result); - break :ret Token.Instruction.Payload{ + break :ret Token(chip).Instruction.Payload{ + .push = .{ + .iffull = iffull, + .block = block, + }, + }; + } else Token(chip).Instruction.Payload{ .push = .{ - .iffull = iffull, - .block = block, + .iffull = false, + .block = true, }, }; - } else Token.Instruction.Payload{ - .push = .{ - .iffull = false, - .block = true, - }, - }; - } + } - fn get_pull(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!Token.Instruction.Payload { - return if (try self.peek_arg(diags)) |first_result| ret: { - const lower = try lowercase_bounded(256, first_result.str); - const ifempty = std.mem.eql(u8, "ifempty", lower.slice()); + fn get_pull(self: *Self, diags: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { + return if (try self.peek_arg(diags)) |first_result| ret: { + const lower = try lowercase_bounded(256, first_result.str); + const ifempty = std.mem.eql(u8, "ifempty", lower.slice()); - const block: bool = if (ifempty) blk: { - self.consume_peek(first_result); - break :blk if (try self.peek_arg(diags)) |block_result| - try self.block_from_peek(block_result) - else - true; - } else try self.block_from_peek(first_result); + const block: bool = if (ifempty) blk: { + self.consume_peek(first_result); + break :blk if (try self.peek_arg(diags)) |block_result| + try self.block_from_peek(block_result) + else + true; + } else try self.block_from_peek(first_result); - break :ret Token.Instruction.Payload{ + break :ret Token(chip).Instruction.Payload{ + .pull = .{ + .ifempty = ifempty, + .block = block, + }, + }; + } else Token(chip).Instruction.Payload{ .pull = .{ - .ifempty = ifempty, - .block = block, + .ifempty = false, + .block = true, }, }; - } else Token.Instruction.Payload{ - .pull = .{ - .ifempty = false, - .block = true, - }, - }; - } + } - fn get_mov(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!Token.Instruction.Payload { - const dest_str = (try self.get_arg(diags)) orelse return error.MissingArg; - const dest_lower = try lowercase_bounded(256, dest_str); - const destination = std.meta.stringToEnum(Token.Instruction.Mov.Destination, dest_lower.slice()) orelse return error.InvalidDestination; - - const second = try self.get_arg(diags) orelse return error.MissingArg; - const op_prefixed: ?[]const u8 = if (std.mem.startsWith(u8, second, "!")) - "!" - else if (std.mem.startsWith(u8, second, "~")) - "~" - else if (std.mem.startsWith(u8, second, "::")) - "::" - else - null; - - const source_str = if (op_prefixed) |op_str| - if (second.len == op_str.len) - (try self.get_arg(diags)) orelse return error.MissingArg - else - second[op_str.len..] - else - second; - - const source_lower = try lowercase_bounded(256, source_str); - const source = std.meta.stringToEnum(Token.Instruction.Mov.Source, source_lower.slice()) orelse return error.InvalidSource; - const operation: Token.Instruction.Mov.Operation = if (op_prefixed) |op_str| - if (std.mem.eql(u8, "!", op_str)) - .invert - else if (std.mem.eql(u8, "~", op_str)) - .invert - else if (std.mem.eql(u8, "::", op_str)) - .bit_reverse + fn get_mov(self: *Self, diags: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { + // Peek so that we can unwind for the mov_rx case + const dest_str = try self.peek_arg(diags) orelse return error.MissingArg; + const dest_lower = try lowercase_bounded(256, dest_str.str); + // If the destination is rxfifo_ or rxfifoy, then it's a mov (to) rx instruction + if (chip == .RP2350 and std.mem.startsWith(u8, dest_lower.slice(), "rxfifo")) { + return try self.get_movrx(diags); + } + + // NOTE: Destination MUST be OSR for mov (from rx) but the normal + // mov can also have OSR as the destination, so we need to peek a + // second time to the source and look for rxfifo. + // + // Determine if it's a mov from rx based on source + self.consume_peek(dest_str); + const peek_source_str = try self.peek_arg(diags) orelse return error.MissingArg; + const peek_source_lower = try lowercase_bounded(256, peek_source_str.str); + // If the destination is osr, and the source is rxfifo_ or rxfifoy, then it's a mov (from) rx instruction + if (chip == .RP2350 and std.mem.startsWith(u8, peek_source_lower.slice(), "rxfifo")) { + // Need to unconsume first arg + self.unconsume(dest_str); + + return try self.get_movrx(diags); + } + + const destination = std.meta.stringToEnum(Token(chip).Instruction.Mov.Destination, dest_lower.slice()) orelse return error.InvalidDestination; + + const second = try self.get_arg(diags) orelse return error.MissingArg; + const op_prefixed: ?[]const u8 = if (std.mem.startsWith(u8, second, "!")) + "!" + else if (std.mem.startsWith(u8, second, "~")) + "~" + else if (std.mem.startsWith(u8, second, "::")) + "::" else - return error.InvalidOperation - else - .none; + null; - return Token.Instruction.Payload{ - .mov = .{ - .destination = destination, - .source = source, - .operation = operation, - }, - }; - } + const source_str = if (op_prefixed) |op_str| + if (second.len == op_str.len) + (try self.get_arg(diags)) orelse return error.MissingArg + else + second[op_str.len..] + else + second; + + const source_lower = try lowercase_bounded(256, source_str); + const source = std.meta.stringToEnum(Token(chip).Instruction.Mov.Source, source_lower.slice()) orelse return error.InvalidSource; + const operation: Token(chip).Instruction.Mov.Operation = if (op_prefixed) |op_str| + if (std.mem.eql(u8, "!", op_str)) + .invert + else if (std.mem.eql(u8, "~", op_str)) + .invert + else if (std.mem.eql(u8, "::", op_str)) + .bit_reverse + else + return error.InvalidOperation + else + .none; - fn get_irq(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!Token.Instruction.Payload { - const first = (try self.get_arg(diags)) orelse return error.MissingArg; - - var clear = false; - var wait = false; - var has_mode = false; - const first_lower = try lowercase_bounded(256, first); - if (std.mem.eql(u8, "set", first_lower.slice())) { - has_mode = true; - // do nothing - } else if (std.mem.eql(u8, "nowait", first_lower.slice())) { - has_mode = true; - // do nothing - } else if (std.mem.eql(u8, "wait", first_lower.slice())) { - has_mode = true; - wait = true; - } else if (std.mem.eql(u8, "clear", first_lower.slice())) { - has_mode = true; - clear = true; + return Token(chip).Instruction.Payload{ + .mov = .{ + .destination = destination, + .source = source, + .operation = operation, + }, + }; } - const num = Value{ - .expression = if (has_mode) - (try self.get_arg(diags)) orelse { - diags.* = Diagnostics.init(self.index, "irq (mode) (rel): failed to get num argument", .{}); - return error.MissingArg; + fn get_movrx(self: *Self, diags: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { + // NOTE: Assuming there's one space after opcode + const dest_idx = self.index + 1; + const dest_str = (try self.get_arg(diags)) orelse return error.MissingArg; + const dest_lower = try lowercase_bounded(256, dest_str); + // NOTE: Assuming there's a comma and one space + const source_idx = self.index + 2; + const source_str = (try self.get_arg(diags)) orelse return error.MissingArg; + const source_lower = try lowercase_bounded(256, source_str); + if (std.mem.startsWith(u8, dest_lower.slice(), "rxfifo")) { + // MOV (to RX) + // -- Source must be isr + if (!std.mem.eql(u8, source_lower.slice(), "isr")) { + diags.* = Diagnostics.init(source_idx, "mov (to rx): source must be isr", .{}); + return error.InvalidSource; } - else - first, - }; + const dest_index_char = dest_lower.slice()["rxfifo".len - 0 ..]; + if (dest_index_char[0] == 'y') { + return Token(chip).Instruction.Payload{ + .movtorx = .{ + .idxl = true, + .idx = 0, + }, + }; + } else { + // -- Parse out the index + diags.* = Diagnostics.init( + dest_idx, + "mov (to rx): destination must be rxfifoy or rxfifo[]", + .{}, + ); + const value = try std.fmt.parseInt(u8, dest_index_char, 10); + if (value > 3) { + return error.InvalidDestination; + } + diags.* = null; + return Token(chip).Instruction.Payload{ + .movtorx = .{ + .idxl = false, + .idx = @intCast(value), + }, + }; + } + } else if (std.mem.eql(u8, dest_lower.slice(), "osr")) { + // MOV (from RX) + var idxl: bool = false; + var idx: u3 = 0; + if (std.mem.startsWith(u8, source_lower.slice(), "rxfifo")) { + const src_index_char = source_lower.slice()["rxfifo".len - 0 ..]; + if (src_index_char[0] == 'y') { + idxl = true; + idx = 0; + } else { + // -- Parse out the index + idxl = false; + idx = 0; + diags.* = Diagnostics.init( + @intCast(source_idx + "rxfifo".len), + "mov (from rx): source must be rxfifoy or rxfifo[]", + .{}, + ); + const value = try std.fmt.parseInt(u8, src_index_char, 10); + if (value > 3) { + return error.InvalidSource; + } + idx = @intCast(value); + } + } else { + diags.* = Diagnostics.init( + @intCast(self.index - source_str.len), + "mov (from rx): source must be rxfifoy or rxfifo[]", + .{}, + ); + return error.InvalidSource; + } + diags.* = null; + return Token(chip).Instruction.Payload{ + .movfromrx = .{ + .idxl = idxl, + .idx = idx, + }, + }; + } else { + diags.* = Diagnostics.init( + dest_idx, + "unknown destination", + .{}, + ); + return error.InvalidDestination; + } + } - const rel: bool = if (try self.peek_arg(diags)) |result| blk: { - const rel_lower = try lowercase_bounded(256, result.str); - const is_rel = std.mem.eql(u8, "rel", rel_lower.slice()); - if (is_rel) - self.consume_peek(result); + fn get_irq(self: *Self, diags: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { + const first = (try self.get_arg(diags)) orelse return error.MissingArg; + + var clear = false; + var wait = false; + var has_mode = false; + const first_lower = try lowercase_bounded(256, first); + if (std.mem.eql(u8, "set", first_lower.slice())) { + has_mode = true; + // do nothing + } else if (std.mem.eql(u8, "nowait", first_lower.slice())) { + has_mode = true; + // do nothing + } else if (std.mem.eql(u8, "wait", first_lower.slice())) { + has_mode = true; + wait = true; + } else if (std.mem.eql(u8, "clear", first_lower.slice())) { + has_mode = true; + clear = true; + } - break :blk is_rel; - } else false; + const num = Value{ + .expression = if (has_mode) + (try self.get_arg(diags)) orelse { + diags.* = Diagnostics.init(self.index, "irq (mode) (prev,rel,next): failed to get num argument", .{}); + return error.MissingArg; + } + else + first, + }; - return Token.Instruction.Payload{ - .irq = .{ - .clear = clear, - .wait = wait, - .num = num, - .rel = rel, - }, - }; - } + switch (comptime chip) { + .RP2040 => { + const rel: bool = if (try self.peek_arg(diags)) |result| blk: { + const rel_lower = try lowercase_bounded(256, result.str); + const is_rel = std.mem.eql(u8, "rel", rel_lower.slice()); + if (is_rel) + self.consume_peek(result); + + break :blk is_rel; + } else false; + + return Token(chip).Instruction.Payload{ + .irq = .{ + .clear = clear, + .wait = wait, + .num = num, + .rel = rel, + }, + }; + }, + .RP2350 => { + const IdxMode = Token(chip).Instruction.Irq.IdxMode; + const idx_mode: IdxMode = if (try self.peek_arg(diags)) |result| blk: { + const idxmode_lower = try lowercase_bounded(256, result.str); + if (std.mem.eql(u8, "rel", idxmode_lower.slice())) { + self.consume_peek(result); + break :blk .rel; + } else if (std.mem.eql(u8, "prev", idxmode_lower.slice())) { + self.consume_peek(result); + break :blk .prev; + } else if (std.mem.eql(u8, "next", idxmode_lower.slice())) { + self.consume_peek(result); + break :blk .next; + } else { + break :blk .direct; + } + } else .direct; + + return Token(chip).Instruction.Payload{ + .irq = .{ + .clear = clear, + .wait = wait, + .num = num, + .idxmode = idx_mode, + }, + }; + }, + } + } - fn get_set(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!Token.Instruction.Payload { - const dest_str = (try self.get_arg(diags)) orelse { - diags.* = Diagnostics.init(0, "missing destination", .{}); - return error.MissingArg; - }; - const value = try self.get_value(); + fn get_set(self: *Self, diags: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { + const dest_str = (try self.get_arg(diags)) orelse { + diags.* = Diagnostics.init(0, "missing destination", .{}); + return error.MissingArg; + }; + const value = try self.get_value(); - const dest_lower = try lowercase_bounded(256, dest_str); + const dest_lower = try lowercase_bounded(256, dest_str); - return Token.Instruction.Payload{ - .set = .{ - .destination = std.meta.stringToEnum(Token.Instruction.Set.Destination, dest_lower.slice()) orelse return error.InvalidDestination, - .value = value, - }, - }; - } + return Token(chip).Instruction.Payload{ + .set = .{ + .destination = std.meta.stringToEnum(Token(chip).Instruction.Set.Destination, dest_lower.slice()) orelse return error.InvalidDestination, + .value = value, + }, + }; + } - const instructions = std.StaticStringMap(*const fn (*Tokenizer, *?Diagnostics) TokenizeError!Token.Instruction.Payload).initComptime(.{ - .{ "nop", get_nop }, - .{ "jmp", get_jmp }, - .{ "wait", get_wait }, - .{ "in", get_in }, - .{ "out", get_out }, - .{ "push", get_push }, - .{ "pull", get_pull }, - .{ "mov", get_mov }, - .{ "irq", get_irq }, - .{ "set", get_set }, - }); + const instructions = std.StaticStringMap(*const fn (*Self, *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload).initComptime(.{ + .{ "nop", get_nop }, + .{ "jmp", get_jmp }, + .{ "wait", get_wait }, + .{ "in", get_in }, + .{ "out", get_out }, + .{ "push", get_push }, + .{ "pull", get_pull }, + .{ "mov", get_mov }, + .{ "irq", get_irq }, + .{ "set", get_set }, + }); - fn get_instruction(self: *Tokenizer, name: Identifier, diags: *?Diagnostics) !Token { - const name_lower = try lowercase_bounded(256, name.str); - const payload = if (instructions.get(name_lower.slice())) |handler| - try handler(self, diags) - else { - diags.* = Diagnostics.init(name.index, "invalid instruction", .{}); - return error.InvalidInstruction; - }; + fn get_instruction(self: *Self, name: Identifier, diags: *?Diagnostics) !Token(chip) { + const name_lower = try lowercase_bounded(256, name.str); + const payload = if (instructions.get(name_lower.slice())) |handler| + try handler(self, diags) + else { + diags.* = Diagnostics.init(name.index, "invalid instruction", .{}); + return error.InvalidInstruction; + }; - var side_set: ?Value = null; - var delay: ?Value = null; + var side_set: ?Value = null; + var delay: ?Value = null; - if (try self.peek_arg(diags)) |result| { - if (eql_lower("side", result.str)) { - self.consume_peek(result); + if (try self.peek_arg(diags)) |result| { + if (eql_lower("side", result.str)) { + self.consume_peek(result); - const side_set_str = (try self.get_arg(diags)) orelse return error.MissingArg; - side_set = Value{ .expression = side_set_str }; - } else if (std.mem.startsWith(u8, result.str, "[") and std.mem.endsWith(u8, result.str, "]")) { - self.consume_peek(result); - delay = Value{ .expression = result.str[1 .. result.str.len - 1] }; + const side_set_str = (try self.get_arg(diags)) orelse return error.MissingArg; + side_set = Value{ .expression = side_set_str }; + } else if (std.mem.startsWith(u8, result.str, "[") and std.mem.endsWith(u8, result.str, "]")) { + self.consume_peek(result); + delay = Value{ .expression = result.str[1 .. result.str.len - 1] }; + } } - } - if (try self.peek_arg(diags)) |result| { - if (eql_lower("side", result.str)) { - self.consume_peek(result); - - const side_set_str = (try self.get_arg(diags)) orelse return error.MissingArg; - assert(side_set == null); - side_set = Value{ .expression = side_set_str }; - } else if (std.mem.startsWith(u8, result.str, "[") and std.mem.endsWith(u8, result.str, "]")) { - self.consume_peek(result); - assert(delay == null); - delay = Value{ - .expression = result.str[1 .. result.str.len - 1], - }; + if (try self.peek_arg(diags)) |result| { + if (eql_lower("side", result.str)) { + self.consume_peek(result); + + const side_set_str = (try self.get_arg(diags)) orelse return error.MissingArg; + assert(side_set == null); + side_set = Value{ .expression = side_set_str }; + } else if (std.mem.startsWith(u8, result.str, "[") and std.mem.endsWith(u8, result.str, "]")) { + self.consume_peek(result); + assert(delay == null); + delay = Value{ + .expression = result.str[1 .. result.str.len - 1], + }; + } } - } - self.skip_line(); - return Token{ - .index = name.index, - .data = .{ - .instruction = .{ - .payload = payload, - .side_set = side_set, - .delay = delay, + self.skip_line(); + return Token(chip){ + .index = name.index, + .data = .{ + .instruction = .{ + .payload = payload, + .side_set = side_set, + .delay = delay, + }, }, - }, - }; - } + }; + } - fn next(self: *Tokenizer, diags: *?assembler.Diagnostics) !?Token { - while (self.peek()) |p| { - switch (p) { - ' ', '\t', '\n', '\r', ',' => self.consume(1), - ';' => self.skip_line(), - '/' => { - self.consume(1); - if (self.peek()) |p2| { + fn next(self: *Self, diags: *?assembler.Diagnostics) !?Token(chip) { + while (self.peek()) |p| { + switch (p) { + ' ', '\t', '\n', '\r', ',' => self.consume(1), + ';' => self.skip_line(), + '/' => { self.consume(1); - switch (p2) { - '/' => self.skip_line(), - '*' => self.skip_until_end_of_comment_block(), - else => unreachable, - } - } else return null; - }, - '%' => { - self.consume(1); - self.skip_until_end_of_code_block(); - }, - '.' => { - self.consume(1); - return try self.get_directive(diags); - }, - 'a'...'z', 'A'...'Z', '0'...'9', '_' => { - const first = try self.get_identifier(); - - // definitely a label - return if (eql_lower("public", first.str)) - Token{ - .index = first.index, - .data = .{ - .label = .{ - .public = true, - .name = blk: { - const tmp = (try self.get_identifier()).str; - break :blk tmp[0 .. tmp.len - 1]; + if (self.peek()) |p2| { + self.consume(1); + switch (p2) { + '/' => self.skip_line(), + '*' => self.skip_until_end_of_comment_block(), + else => unreachable, + } + } else return null; + }, + '%' => { + self.consume(1); + self.skip_until_end_of_code_block(); + }, + '.' => { + self.consume(1); + return try self.get_directive(diags); + }, + 'a'...'z', 'A'...'Z', '0'...'9', '_' => { + const first = try self.get_identifier(); + + // definitely a label + return if (eql_lower("public", first.str)) + Token(chip){ + .index = first.index, + .data = .{ + .label = .{ + .public = true, + .name = blk: { + const tmp = (try self.get_identifier()).str; + break :blk tmp[0 .. tmp.len - 1]; + }, }, }, - }, - } - else if (std.mem.endsWith(u8, first.str, ":")) - Token{ - .index = first.index, - .data = .{ - .label = .{ - .name = first.str[0 .. first.str.len - 1], + } + else if (std.mem.endsWith(u8, first.str, ":")) + Token(chip){ + .index = first.index, + .data = .{ + .label = .{ + .name = first.str[0 .. first.str.len - 1], + }, }, - }, - } - else - try self.get_instruction(first, diags); - }, - else => return error.Unhandled, + } + else + try self.get_instruction(first, diags); + }, + else => return error.Unhandled, + } } - } - - return null; - } -}; -pub const Token = struct { - index: u32, - data: union(enum) { - program: []const u8, - define: Token.Define, - origin: Value, - side_set: SideSet, - wrap_target: void, - wrap: void, - lang_opt: LangOpt, - word: Value, - label: Label, - instruction: Instruction, - }, - - pub const Tag = std.meta.Tag(std.meta.FieldType(Token, .data)); - - pub const Label = struct { - name: []const u8, - public: bool = false, + return null; + } }; +} - // TODO: use Value instead of numbers - pub const Instruction = struct { - payload: Payload, - side_set: ?Value = null, - // TODO: delay can look like [T1-1], so we could consider the square - // brackets to be an expression - delay: ?Value = null, - - pub const Payload = union(enum) { - nop: void, - jmp: Jmp, - wait: Wait, - in: In, - out: Out, - push: Push, - pull: Pull, - mov: Mov, - irq: Irq, - set: Set, +pub fn Token(comptime chip: Chip) type { + return struct { + const Self = @This(); + index: u32, + data: union(enum) { + program: []const u8, + define: Self.Define, + origin: Value, + side_set: SideSet, + wrap_target: void, + wrap: void, + lang_opt: LangOpt, + word: Value, + label: Label, + instruction: Instruction, + }, + + pub const Tag = std.meta.Tag(std.meta.FieldType(Token(chip), .data)); + + pub const Label = struct { + name: []const u8, + public: bool = false, }; - pub const Jmp = struct { - condition: Condition, - target: []const u8, - - pub const Condition = enum(u3) { - always = 0b000, - x_is_zero = 0b001, // !X - x_dec = 0b010, // X-- - y_is_zero = 0b011, // !Y - y_dec = 0b100, // Y-- - x_is_not_y = 0b101, //X!=Y - pin = 0b110, // PIN - osre_not_empty = 0b111, // !OSRE + // TODO: use Value instead of numbers + pub const Instruction = struct { + payload: Payload, + side_set: ?Value = null, + // TODO: delay can look like [T1-1], so we could consider the square + // brackets to be an expression + delay: ?Value = null, + + pub const Payload = union(enum) { + nop: void, + jmp: Jmp, + wait: Wait, + in: In, + out: Out, + push: Push, + pull: Pull, + mov: Mov, + movtorx: if (chip == .RP2350) MovToRx else noreturn, + movfromrx: if (chip == .RP2350) MovFromRx else noreturn, + irq: Irq, + set: Set, }; - }; - pub const Wait = struct { - polarity: u1, - source: Source, - num: Value, - rel: bool, - - pub const Source = enum(u2) { - gpio = 0b00, - pin = 0b01, - irq = 0b10, + pub const Jmp = struct { + condition: Condition, + target: []const u8, + + pub const Condition = enum(u3) { + always = 0b000, + x_is_zero = 0b001, // !X + x_dec = 0b010, // X-- + y_is_zero = 0b011, // !Y + y_dec = 0b100, // Y-- + x_is_not_y = 0b101, //X!=Y + pin = 0b110, // PIN + osre_not_empty = 0b111, // !OSRE + }; }; - }; - pub const In = struct { - source: Source, - bit_count: u5, - - pub const Source = enum(u3) { - pins = 0b00, - x = 0b001, - y = 0b010, - null = 0b011, - isr = 0b110, - osr = 0b111, + pub const Wait = struct { + polarity: u1, + source: Source, + num: Value, + rel: bool, + + pub const Source = switch (chip) { + .RP2040 => enum(u2) { + gpio = 0b00, + pin = 0b01, + irq = 0b10, + }, + .RP2350 => enum(u2) { + gpio = 0b00, + pin = 0b01, + irq = 0b10, + jmppin = 0b11, + }, + }; }; - }; - pub const Out = struct { - destination: Destination, - bit_count: u5, - - pub const Destination = enum(u3) { - pins = 0b000, - x = 0b001, - y = 0b010, - null = 0b011, - pindirs = 0b100, - pc = 0b101, - isr = 0b110, - exec = 0b111, + pub const In = struct { + source: Source, + bit_count: u5, + + pub const Source = enum(u3) { + pins = 0b00, + x = 0b001, + y = 0b010, + null = 0b011, + isr = 0b110, + osr = 0b111, + }; }; - }; - pub const Push = struct { - block: bool, - iffull: bool, - }; + pub const Out = struct { + destination: Destination, + bit_count: u5, + + pub const Destination = enum(u3) { + pins = 0b000, + x = 0b001, + y = 0b010, + null = 0b011, + pindirs = 0b100, + pc = 0b101, + isr = 0b110, + exec = 0b111, + }; + }; - pub const Pull = struct { - block: bool, - ifempty: bool, - }; + pub const Push = struct { + block: bool, + iffull: bool, + }; - pub const Mov = struct { - destination: Destination, - operation: Operation, - source: Source, - - pub const Destination = enum(u3) { - pins = 0b000, - x = 0b001, - y = 0b010, - exec = 0b100, - pc = 0b101, - isr = 0b110, - osr = 0b111, + pub const Pull = struct { + block: bool, + ifempty: bool, }; - pub const Operation = enum(u2) { - none = 0b00, - invert = 0b01, - bit_reverse = 0b10, + pub const Mov = struct { + destination: Destination, + operation: Operation, + source: Source, + + pub const Destination = switch (chip) { + .RP2040 => enum(u3) { + pins = 0b000, + x = 0b001, + y = 0b010, + exec = 0b100, + pc = 0b101, + isr = 0b110, + osr = 0b111, + }, + .RP2350 => enum(u3) { + pins = 0b000, + x = 0b001, + y = 0b010, + pindirs = 0b011, + exec = 0b100, + pc = 0b101, + isr = 0b110, + osr = 0b111, + }, + }; + + pub const Operation = enum(u2) { + none = 0b00, + invert = 0b01, + bit_reverse = 0b10, + }; + + pub const Source = enum(u3) { + pins = 0b000, + x = 0b001, + y = 0b010, + null = 0b011, + status = 0b101, + isr = 0b110, + osr = 0b111, + }; }; - pub const Source = enum(u3) { - pins = 0b00, - x = 0b001, - y = 0b010, - null = 0b011, - status = 0b101, - isr = 0b110, - osr = 0b111, + pub const MovToRx = struct { + idxl: bool, + idx: u3, + }; + pub const MovFromRx = struct { + idxl: bool, + idx: u3, }; - }; - pub const Irq = struct { - clear: bool, - wait: bool, - num: Value, - rel: bool, - }; + pub const Irq = switch (chip) { + .RP2040 => struct { + clear: bool, + wait: bool, + num: Value, + rel: bool, + }, + .RP2350 => struct { + clear: bool, + wait: bool, + num: Value, + idxmode: IdxMode, + + pub const IdxMode = enum(u2) { + direct = 0b00, + prev = 0b01, + rel = 0b10, + next = 0b11, + }; + }, + }; - pub const Set = struct { - destination: Destination, - value: Value, + pub const Set = struct { + destination: Destination, + value: Value, - pub const Destination = enum(u3) { - pins = 0b000, - x = 0b001, - y = 0b010, - pindirs = 0b100, + pub const Destination = enum(u3) { + pins = 0b000, + x = 0b001, + y = 0b010, + pindirs = 0b100, + }; }; }; - }; - pub const Define = struct { - name: []const u8, - value: Value, - public: bool = false, - index: u32, - }; + pub const Define = struct { + name: []const u8, + value: Value, + public: bool = false, + index: u32, + }; - pub const SideSet = struct { - count: Value, - opt: bool = false, - pindir: bool = false, - }; + pub const SideSet = struct { + count: Value, + opt: bool = false, + pindir: bool = false, + }; - pub const LangOpt = struct { - lang: []const u8, - name: []const u8, - option: []const u8, + pub const LangOpt = struct { + lang: []const u8, + name: []const u8, + option: []const u8, + }; }; -}; +} //============================================================================== // Tokenization Tests @@ -1147,11 +1367,15 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const expectEqualStrings = std.testing.expectEqualStrings; -const DirectiveTag = @typeInfo(Token.Directive).Union.tag_type.?; -const PayloadTag = @typeInfo(Token.Instruction.Payload).Union.tag_type.?; +fn DirectiveTag(comptime chip: Chip) type { + return @typeInfo(Token(chip).Directive).Union.tag_type.?; +} +fn PayloadTag(comptime chip: Chip) type { + return @typeInfo(Token(chip).Instruction.Payload).Union.tag_type.?; +} -fn expect_program(expected: []const u8, actual: Token) !void { - try expectEqual(Token.Tag.program, @as(Token.Tag, actual.data)); +fn expect_program(comptime chip: Chip, expected: []const u8, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.program, @as(Token(chip).Tag, actual.data)); try expectEqualStrings(expected, actual.data.program); } @@ -1172,21 +1396,22 @@ fn expect_opt_value(expected: ?Value, actual: ?Value) !void { }; } -fn expect_define(expected: Token.Define, actual: Token) !void { - try expectEqual(Token.Tag.define, @as(Token.Tag, actual.data)); +fn expect_define(comptime chip: Chip, expected: Token(chip).Define, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.define, @as(Token(chip).Tag, actual.data)); const define = actual.data.define; try expectEqualStrings(expected.name, define.name); try expect_value(expected.value, define.value); + try expectEqual(expected.index, define.index); } -fn expect_origin(expected: Value, actual: Token) !void { - try expectEqual(Token.Tag.origin, @as(Token.Tag, actual.data)); +fn expect_origin(comptime chip: Chip, expected: Value, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.origin, @as(Token(chip).Tag, actual.data)); try expect_value(expected, actual.data.origin); } -fn expect_side_set(expected: Token.SideSet, actual: Token) !void { - try expectEqual(Token.Tag.side_set, @as(Token.Tag, actual.data)); +fn expect_side_set(comptime chip: Chip, expected: Token(chip).SideSet, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.side_set, @as(Token(chip).Tag, actual.data)); const side_set = actual.data.side_set; try expect_value(expected.count, side_set.count); @@ -1194,16 +1419,16 @@ fn expect_side_set(expected: Token.SideSet, actual: Token) !void { try expectEqual(expected.pindir, side_set.pindir); } -fn expect_wrap_target(actual: Token) !void { - try expectEqual(Token.Tag.wrap_target, @as(Token.Tag, actual.data)); +fn expect_wrap_target(comptime chip: Chip, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.wrap_target, @as(Token(chip).Tag, actual.data)); } -fn expect_wrap(actual: Token) !void { - try expectEqual(Token.Tag.wrap, @as(Token.Tag, actual.data)); +fn expect_wrap(comptime chip: Chip, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.wrap, @as(Token(chip).Tag, actual.data)); } -fn expect_lang_opt(expected: Token.LangOpt, actual: Token) !void { - try expectEqual(Token.Tag.lang_opt, @as(Token.Tag, actual.data)); +fn expect_lang_opt(comptime chip: Chip, expected: Token(chip).LangOpt, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.lang_opt, @as(Token(chip).Tag, actual.data)); const lang_opt = actual.data.lang_opt; try expectEqualStrings(expected.lang, lang_opt.lang); @@ -1211,13 +1436,13 @@ fn expect_lang_opt(expected: Token.LangOpt, actual: Token) !void { try expectEqualStrings(expected.option, lang_opt.option); } -fn expect_word(expected: Value, actual: Token) !void { - try expectEqual(Token.Tag.word, @as(Token.Tag, actual.data)); +fn expect_word(comptime chip: Chip, expected: Value, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.word, @as(Token(chip).Tag, actual.data)); try expect_value(expected, actual.data.word); } -fn expect_label(expected: Token.Label, actual: Token) !void { - try expectEqual(Token.Tag.label, @as(Token.Tag, actual.data)); +fn expect_label(comptime chip: Chip, expected: Token(chip).Label, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.label, @as(Token(chip).Tag, actual.data)); const label = actual.data.label; try expectEqual(expected.public, label.public); @@ -1229,25 +1454,27 @@ const ExpectedNopInstr = struct { side_set: ?Value = null, }; -fn expect_instr_nop(expected: ExpectedNopInstr, actual: Token) !void { - try expectEqual(Token.Tag.instruction, @as(Token.Tag, actual.data)); - try expectEqual(PayloadTag.nop, @as(PayloadTag, actual.data.instruction.payload)); +fn expect_instr_nop(comptime chip: Chip, expected: ExpectedNopInstr, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.instruction, @as(Token(chip).Tag, actual.data)); + try expectEqual(PayloadTag(chip).nop, @as(PayloadTag(chip), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); try expect_opt_value(expected.side_set, instr.side_set); } -const ExpectedSetInstr = struct { - dest: Token.Instruction.Set.Destination, - value: Value, - delay: ?Value = null, - side_set: ?Value = null, -}; +fn ExpectedSetInstr(comptime chip: Chip) type { + return struct { + dest: Token(chip).Instruction.Set.Destination, + value: Value, + delay: ?Value = null, + side_set: ?Value = null, + }; +} -fn expect_instr_set(expected: ExpectedSetInstr, actual: Token) !void { - try expectEqual(Token.Tag.instruction, @as(Token.Tag, actual.data)); - try expectEqual(PayloadTag.set, @as(PayloadTag, actual.data.instruction.payload)); +fn expect_instr_set(comptime chip: Chip, expected: ExpectedSetInstr(chip), actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.instruction, @as(Token(chip).Tag, actual.data)); + try expectEqual(PayloadTag(chip).set, @as(PayloadTag(chip), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1258,16 +1485,18 @@ fn expect_instr_set(expected: ExpectedSetInstr, actual: Token) !void { try expect_value(expected.value, set.value); } -const ExpectedJmpInstr = struct { - cond: Token.Instruction.Jmp.Condition = .always, - target: []const u8, - delay: ?Value = null, - side_set: ?Value = null, -}; +fn ExpectedJmpInstr(comptime chip: Chip) type { + return struct { + cond: Token(chip).Instruction.Jmp.Condition = .always, + target: []const u8, + delay: ?Value = null, + side_set: ?Value = null, + }; +} -fn expect_instr_jmp(expected: ExpectedJmpInstr, actual: Token) !void { - try expectEqual(Token.Tag.instruction, @as(Token.Tag, actual.data)); - try expectEqual(PayloadTag.jmp, @as(PayloadTag, actual.data.instruction.payload)); +fn expect_instr_jmp(comptime chip: Chip, expected: ExpectedJmpInstr(chip), actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.instruction, @as(Token(chip).Tag, actual.data)); + try expectEqual(PayloadTag(chip).jmp, @as(PayloadTag(chip), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1278,19 +1507,21 @@ fn expect_instr_jmp(expected: ExpectedJmpInstr, actual: Token) !void { try expectEqualStrings(expected.target, jmp.target); } -const ExpectedWaitInstr = struct { - polarity: u1, - source: Token.Instruction.Wait.Source, - num: Value, - // only valid for irq source - rel: bool = false, - delay: ?Value = null, - side_set: ?Value = null, -}; +fn ExpectedWaitInstr(comptime chip: Chip) type { + return struct { + polarity: u1, + source: Token(chip).Instruction.Wait.Source, + num: Value, + // only valid for irq source + rel: bool = false, + delay: ?Value = null, + side_set: ?Value = null, + }; +} -fn expect_instr_wait(expected: ExpectedWaitInstr, actual: Token) !void { - try expectEqual(Token.Tag.instruction, @as(Token.Tag, actual.data)); - try expectEqual(PayloadTag.wait, @as(PayloadTag, actual.data.instruction.payload)); +fn expect_instr_wait(comptime chip: Chip, expected: ExpectedWaitInstr(chip), actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.instruction, @as(Token(chip).Tag, actual.data)); + try expectEqual(PayloadTag(chip).wait, @as(PayloadTag(chip), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1302,16 +1533,18 @@ fn expect_instr_wait(expected: ExpectedWaitInstr, actual: Token) !void { try expect_value(expected.num, wait.num); } -const ExpectedInInstr = struct { - source: Token.Instruction.In.Source, - bit_count: u5, - delay: ?Value = null, - side_set: ?Value = null, -}; +fn ExpectedInInstr(comptime chip: Chip) type { + return struct { + source: Token(chip).Instruction.In.Source, + bit_count: u5, + delay: ?Value = null, + side_set: ?Value = null, + }; +} -fn expect_instr_in(expected: ExpectedInInstr, actual: Token) !void { - try expectEqual(Token.Tag.instruction, @as(Token.Tag, actual.data)); - try expectEqual(PayloadTag.in, @as(PayloadTag, actual.data.instruction.payload)); +fn expect_instr_in(comptime chip: Chip, expected: ExpectedInInstr(chip), actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.instruction, @as(Token(chip).Tag, actual.data)); + try expectEqual(PayloadTag(chip).in, @as(PayloadTag(chip), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1322,16 +1555,18 @@ fn expect_instr_in(expected: ExpectedInInstr, actual: Token) !void { try expectEqual(expected.bit_count, in.bit_count); } -const ExpectedOutInstr = struct { - destination: Token.Instruction.Out.Destination, - bit_count: u5, - delay: ?Value = null, - side_set: ?Value = null, -}; +fn ExpectedOutInstr(comptime chip: Chip) type { + return struct { + destination: Token(chip).Instruction.Out.Destination, + bit_count: u5, + delay: ?Value = null, + side_set: ?Value = null, + }; +} -fn expect_instr_out(expected: ExpectedOutInstr, actual: Token) !void { - try expectEqual(Token.Tag.instruction, @as(Token.Tag, actual.data)); - try expectEqual(PayloadTag.out, @as(PayloadTag, actual.data.instruction.payload)); +fn expect_instr_out(comptime chip: Chip, expected: ExpectedOutInstr(chip), actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.instruction, @as(Token(chip).Tag, actual.data)); + try expectEqual(PayloadTag(chip).out, @as(PayloadTag(chip), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1349,9 +1584,9 @@ const ExpectedPushInstr = struct { side_set: ?Value = null, }; -fn expect_instr_push(expected: ExpectedPushInstr, actual: Token) !void { - try expectEqual(Token.Tag.instruction, @as(Token.Tag, actual.data)); - try expectEqual(PayloadTag.push, @as(PayloadTag, actual.data.instruction.payload)); +fn expect_instr_push(comptime chip: Chip, expected: ExpectedPushInstr, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.instruction, @as(Token(chip).Tag, actual.data)); + try expectEqual(PayloadTag(chip).push, @as(PayloadTag(chip), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1369,9 +1604,9 @@ const ExpectedPullInstr = struct { side_set: ?Value = null, }; -fn expect_instr_pull(expected: ExpectedPullInstr, actual: Token) !void { - try expectEqual(Token.Tag.instruction, @as(Token.Tag, actual.data)); - try expectEqual(PayloadTag.pull, @as(PayloadTag, actual.data.instruction.payload)); +fn expect_instr_pull(comptime chip: Chip, expected: ExpectedPullInstr, actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.instruction, @as(Token(chip).Tag, actual.data)); + try expectEqual(PayloadTag(chip).pull, @as(PayloadTag(chip), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1382,17 +1617,19 @@ fn expect_instr_pull(expected: ExpectedPullInstr, actual: Token) !void { try expectEqual(expected.ifempty, pull.ifempty); } -const ExpectedMovInstr = struct { - source: Token.Instruction.Mov.Source, - destination: Token.Instruction.Mov.Destination, - operation: Token.Instruction.Mov.Operation = .none, - delay: ?Value = null, - side_set: ?Value = null, -}; +fn ExpectedMovInstr(comptime chip: Chip) type { + return struct { + source: Token(chip).Instruction.Mov.Source, + destination: Token(chip).Instruction.Mov.Destination, + operation: Token(chip).Instruction.Mov.Operation = .none, + delay: ?Value = null, + side_set: ?Value = null, + }; +} -fn expect_instr_mov(expected: ExpectedMovInstr, actual: Token) !void { - try expectEqual(Token.Tag.instruction, @as(Token.Tag, actual.data)); - try expectEqual(PayloadTag.mov, @as(PayloadTag, actual.data.instruction.payload)); +fn expect_instr_mov(comptime chip: Chip, expected: ExpectedMovInstr(chip), actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.instruction, @as(Token(chip).Tag, actual.data)); + try expectEqual(PayloadTag(chip).mov, @as(PayloadTag(chip), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1404,18 +1641,30 @@ fn expect_instr_mov(expected: ExpectedMovInstr, actual: Token) !void { try expectEqual(expected.destination, mov.destination); } -const ExpectedIrqInstr = struct { - clear: bool, - wait: bool, - num: u5, - rel: bool = false, - delay: ?Value = null, - side_set: ?Value = null, -}; +fn ExpectedIrqInstr(comptime chip: Chip) type { + return switch (chip) { + .RP2040 => struct { + clear: bool, + wait: bool, + num: u5, + rel: bool = false, + delay: ?Value = null, + side_set: ?Value = null, + }, + .RP2350 => struct { + clear: bool, + wait: bool, + num: u5, + idxmode: u2 = 0, + delay: ?Value = null, + side_set: ?Value = null, + }, + }; +} -fn expect_instr_irq(expected: ExpectedIrqInstr, actual: Token) !void { - try expectEqual(Token.Tag.instruction, @as(Token.Tag, actual.data)); - try expectEqual(PayloadTag.irq, @as(PayloadTag, actual.data.instruction.payload)); +fn expect_instr_irq(comptime chip: Chip, expected: ExpectedIrqInstr(chip), actual: Token(chip)) !void { + try expectEqual(Token(chip).Tag.instruction, @as(Token(chip).Tag, actual.data)); + try expectEqual(PayloadTag(chip).irq, @as(PayloadTag(chip), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1424,201 +1673,255 @@ fn expect_instr_irq(expected: ExpectedIrqInstr, actual: Token) !void { const irq = instr.payload.irq; try expectEqual(expected.clear, irq.clear); try expectEqual(expected.wait, irq.wait); - try expectEqual(expected.rel, irq.rel); + switch (chip) { + .RP2040 => { + try expectEqual(expected.rel, irq.rel); + }, + .RP2350 => { + // try expectEqual(expected.idxmode, irq.idxmode); + }, + } } -fn bounded_tokenize(source: []const u8) !std.BoundedArray(Token, 256) { +fn bounded_tokenize(comptime chip: Chip, source: []const u8) !std.BoundedArray(Token(chip), 256) { var diags: ?assembler.Diagnostics = null; - return tokenize(source, &diags, .{}) catch |err| if (diags) |d| blk: { - std.log.err("error at index {}: {s}", .{ d.index, d.message.slice() }); + return tokenize(chip, source, &diags, .{}) catch |err| if (diags) |d| blk: { + std.log.err("error with chip {s} at index {}: {s}", .{ @tagName(chip), d.index, d.message.slice() }); break :blk err; } else err; } test "tokenize.empty string" { - const tokens = try bounded_tokenize(""); - try expectEqual(@as(usize, 0), tokens.len); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ""); + try expectEqual(@as(usize, 0), tokens.len); + } } test "tokenize.whitespace" { - const tokens = try bounded_tokenize(" \t\r\n"); - try expectEqual(@as(usize, 0), tokens.len); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, " \t\r\n"); + try expectEqual(@as(usize, 0), tokens.len); + } } test "tokenize.comma line comment" { - const tokens = try bounded_tokenize("; this is a line comment"); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, "; this is a line comment"); - try expectEqual(@as(usize, 0), tokens.len); + try expectEqual(@as(usize, 0), tokens.len); + } } test "tokenize.slash line comment" { - const tokens = try bounded_tokenize("// this is a line comment"); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, "// this is a line comment"); - try expectEqual(@as(usize, 0), tokens.len); + try expectEqual(@as(usize, 0), tokens.len); + } } test "tokenize.block comment" { - const tokens = try bounded_tokenize( - \\/* this is - \\ a block comment */ - ); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, + \\/* this is + \\ a block comment */ + ); - try expectEqual(@as(usize, 0), tokens.len); + try expectEqual(@as(usize, 0), tokens.len); + } } test "tokenize.code block" { - const tokens = try bounded_tokenize( - \\% c-sdk { - \\ int foo; - \\%} - ); - - try expectEqual(@as(usize, 0), tokens.len); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, + \\% c-sdk { + \\ int foo; + \\%} + ); + + try expectEqual(@as(usize, 0), tokens.len); + } } test "tokenize.directive.program" { - const tokens = try bounded_tokenize(".program arst"); - try expect_program("arst", tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ".program arst"); + try expect_program(chip, "arst", tokens.get(0)); + } } test "tokenize.directive.define" { - const tokens = try bounded_tokenize(".define symbol_name 1"); - - try expect_define(.{ - .name = "symbol_name", - .value = .{ .expression = "1" }, - .index = 8, - }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ".define symbol_name 1"); + try expect_define(chip, .{ + .name = "symbol_name", + .value = .{ .expression = "1" }, + .index = 8, + }, tokens.get(0)); + } } test "tokenize.directive.define.public" { - const tokens = try bounded_tokenize(".define public symbol_name 0x1"); - - try expect_define(.{ - .name = "symbol_name", - .value = .{ .expression = "0x1" }, - .public = true, - .index = 15, - }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ".define public symbol_name 0x1"); + try expect_define(chip, .{ + .name = "symbol_name", + .value = .{ .expression = "0x1" }, + .index = 15, + }, tokens.get(0)); + } } test "tokenize.directive.define.with expression" { - const tokens = try bounded_tokenize( - \\.define symbol_name 0x1 - \\.define something (symbol_name * 2) - ); - - try expect_define(.{ - .name = "symbol_name", - .value = .{ .expression = "0x1" }, - .index = 8, - }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, + \\.define symbol_name 0x1 + \\.define something (symbol_name * 2) + ); + + try expect_define(chip, .{ + .name = "symbol_name", + .value = .{ .expression = "0x1" }, + .index = 8, + }, tokens.get(0)); - try expect_define(.{ - .name = "something", - .value = .{ .expression = "(symbol_name * 2)" }, - .index = 32, - }, tokens.get(1)); + try expect_define(chip, .{ + .name = "something", + .value = .{ .expression = "(symbol_name * 2)" }, + .index = 32, + }, tokens.get(1)); + } } test "tokenize.directive.origin" { - const tokens = try bounded_tokenize(".origin 0x10"); - try expect_origin(.{ .integer = 0x10 }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ".origin 0x10"); + try expect_origin(chip, .{ .integer = 0x10 }, tokens.get(0)); + } } test "tokenize.directive.side_set" { - const tokens = try bounded_tokenize(".side_set 1"); - try expect_side_set(.{ .count = .{ .integer = 1 } }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ".side_set 1"); + try expect_side_set(chip, .{ .count = .{ .integer = 1 } }, tokens.get(0)); + } } test "tokenize.directive.side_set.opt" { - const tokens = try bounded_tokenize(".side_set 1 opt"); - try expect_side_set(.{ .count = .{ .integer = 1 }, .opt = true }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ".side_set 1 opt"); + try expect_side_set(chip, .{ .count = .{ .integer = 1 }, .opt = true }, tokens.get(0)); + } } test "tokenize.directive.side_set.pindirs" { - const tokens = try bounded_tokenize(".side_set 1 pindirs"); - try expect_side_set(.{ .count = .{ .integer = 1 }, .pindir = true }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ".side_set 1 pindirs"); + try expect_side_set(chip, .{ .count = .{ .integer = 1 }, .pindir = true }, tokens.get(0)); + } } test "tokenize.directive.wrap_target" { - const tokens = try bounded_tokenize(".wrap_target"); - try expect_wrap_target(tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ".wrap_target"); + try expect_wrap_target(chip, tokens.get(0)); + } } test "tokenize.directive.wrap" { - const tokens = try bounded_tokenize(".wrap"); - try expect_wrap(tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ".wrap"); + try expect_wrap(chip, tokens.get(0)); + } } test "tokenize.directive.lang_opt" { - const tokens = try bounded_tokenize(".lang_opt c flag foo"); - try expect_lang_opt(.{ .lang = "c", .name = "flag", .option = "foo" }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ".lang_opt c flag foo"); + try expect_lang_opt(chip, .{ .lang = "c", .name = "flag", .option = "foo" }, tokens.get(0)); + } } test "tokenize.directive.word" { - const tokens = try bounded_tokenize(".word 0xaaaa"); - try expect_word(.{ .integer = 0xaaaa }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, ".word 0xaaaa"); + try expect_word(chip, .{ .integer = 0xaaaa }, tokens.get(0)); + } } test "tokenize.label" { - const tokens = try bounded_tokenize("my_label:"); - try expect_label(.{ .name = "my_label" }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, "my_label:"); + try expect_label(chip, .{ .name = "my_label" }, tokens.get(0)); + } } test "tokenize.label.public" { - const tokens = try bounded_tokenize("public my_label:"); - try expect_label(.{ .name = "my_label", .public = true }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, "public my_label:"); + try expect_label(chip, .{ .name = "my_label", .public = true }, tokens.get(0)); + } } test "tokenize.instr.nop" { - const tokens = try bounded_tokenize("nop"); - try expect_instr_nop(.{}, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, "nop"); + try expect_instr_nop(chip, .{}, tokens.get(0)); + } } test "tokenize.instr.jmp.label" { - const tokens = try bounded_tokenize("jmp my_label"); - try expect_instr_jmp(.{ .target = "my_label" }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, "jmp my_label"); + try expect_instr_jmp(chip, .{ .target = "my_label" }, tokens.get(0)); + } } test "tokenize.instr.jmp.value" { - const tokens = try bounded_tokenize("jmp 0x2"); - try expect_instr_jmp(.{ .target = "0x2" }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, "jmp 0x2"); + try expect_instr_jmp(chip, .{ .target = "0x2" }, tokens.get(0)); + } } test "tokenize.instr.jmp.conditions" { - const Condition = Token.Instruction.Jmp.Condition; - const cases = std.StaticStringMap(Condition).initComptime(.{ - .{ "!x", .x_is_zero }, - .{ "x--", .x_dec }, - .{ "!y", .y_is_zero }, - .{ "y--", .y_dec }, - .{ "x!=y", .x_is_not_y }, - .{ "pin", .pin }, - .{ "!osre", .osre_not_empty }, - }); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const Condition = Token(chip).Instruction.Jmp.Condition; + const cases = std.StaticStringMap(Condition).initComptime(.{ + .{ "!x", .x_is_zero }, + .{ "x--", .x_dec }, + .{ "!y", .y_is_zero }, + .{ "y--", .y_dec }, + .{ "x!=y", .x_is_not_y }, + .{ "pin", .pin }, + .{ "!osre", .osre_not_empty }, + }); - inline for (comptime cases.keys(), comptime cases.values()) |op, cond| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("jmp {s} my_label", .{op})); + inline for (comptime cases.keys(), comptime cases.values()) |op, cond| { + const tokens = try bounded_tokenize(chip, comptime std.fmt.comptimePrint("jmp {s} my_label", .{op})); - try expect_instr_jmp(.{ .cond = cond, .target = "my_label" }, tokens.get(0)); + try expect_instr_jmp(chip, .{ .cond = cond, .target = "my_label" }, tokens.get(0)); + } } } test "tokenize.instr.wait" { - inline for (.{ "gpio", "pin", "irq" }) |source| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("wait 0 {s} 1", .{source})); - try expect_instr_wait(.{ - .polarity = 0, - .source = @field(Token.Instruction.Wait.Source, source), - .num = .{ .integer = 1 }, - }, tokens.get(0)); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + inline for (.{ "gpio", "pin", "irq" }) |source| { + const tokens = try bounded_tokenize(chip, comptime std.fmt.comptimePrint("wait 0 {s} 1", .{source})); + try expect_instr_wait(chip, .{ + .polarity = 0, + .source = @field(Token(chip).Instruction.Wait.Source, source), + .num = .{ .integer = 1 }, + }, tokens.get(0)); + } } } test "tokenize.instr.wait.irq.rel" { - const tokens = try bounded_tokenize("wait 1 irq 1 rel"); - try expect_instr_wait(.{ + const tokens = try bounded_tokenize(.RP2040, "wait 1 irq 1 rel"); + try expect_instr_wait(.RP2040, .{ .polarity = 1, .source = .irq, .num = .{ .integer = 1 }, @@ -1626,6 +1929,17 @@ test "tokenize.instr.wait.irq.rel" { }, tokens.get(0)); } +test "tokenize.instr.wait.jmppin" { + // JMPPIN is a valid source on RP2350 + const tokens = try bounded_tokenize(.RP2350, "wait 1 jmppin 1"); + try expect_instr_wait(.RP2350, .{ + .polarity = 1, + .source = .jmppin, + .num = .{ .integer = 1 }, + .rel = true, + }, tokens.get(0)); +} + test "tokenize.instr.in" { inline for (.{ "pins", @@ -1635,13 +1949,13 @@ test "tokenize.instr.in" { "isr", "osr", }, 1..) |source, bit_count| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("in {s}, {}", .{ + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("in {s}, {}", .{ source, bit_count, })); - try expect_instr_in(.{ - .source = @field(Token.Instruction.In.Source, source), + try expect_instr_in(.RP2040, .{ + .source = @field(Token(.RP2040).Instruction.In.Source, source), .bit_count = @as(u5, @intCast(bit_count)), }, tokens.get(0)); } @@ -1658,126 +1972,137 @@ test "tokenize.instr.out" { "isr", "exec", }, 1..) |destination, bit_count| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("out {s}, {}", .{ + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("out {s}, {}", .{ destination, bit_count, })); - try expect_instr_out(.{ - .destination = @field(Token.Instruction.Out.Destination, destination), + try expect_instr_out(.RP2040, .{ + .destination = @field(Token(.RP2040).Instruction.Out.Destination, destination), .bit_count = @as(u5, @intCast(bit_count)), }, tokens.get(0)); } } test "tokenize.instr.push" { - const tokens = try bounded_tokenize("push"); - try expect_instr_push(.{}, tokens.get(0)); + const tokens = try bounded_tokenize(.RP2040, "push"); + try expect_instr_push(.RP2040, .{}, tokens.get(0)); } test "tokenize.instr.push.block" { - const tokens = try bounded_tokenize("push block"); - try expect_instr_push(.{ + const tokens = try bounded_tokenize(.RP2040, "push block"); + try expect_instr_push(.RP2040, .{ .block = true, }, tokens.get(0)); } test "tokenize.instr.push.noblock" { - const tokens = try bounded_tokenize("push noblock"); - try expect_instr_push(.{ + const tokens = try bounded_tokenize(.RP2040, "push noblock"); + try expect_instr_push(.RP2040, .{ .block = false, }, tokens.get(0)); } test "tokenize.instr.push.iffull" { - const tokens = try bounded_tokenize("push iffull noblock"); - try expect_instr_push(.{ + const tokens = try bounded_tokenize(.RP2040, "push iffull noblock"); + try expect_instr_push(.RP2040, .{ .block = false, .iffull = true, }, tokens.get(0)); } test "tokenize.instr.pull" { - const tokens = try bounded_tokenize("pull"); - try expect_instr_pull(.{}, tokens.get(0)); + const tokens = try bounded_tokenize(.RP2040, "pull"); + try expect_instr_pull(.RP2040, .{}, tokens.get(0)); } test "tokenize.instr.pull.block" { - const tokens = try bounded_tokenize("pull block"); - try expect_instr_pull(.{ + const tokens = try bounded_tokenize(.RP2040, "pull block"); + try expect_instr_pull(.RP2040, .{ .block = true, }, tokens.get(0)); } test "tokenize.instr.pull.noblock" { - const tokens = try bounded_tokenize("pull noblock"); - try expect_instr_pull(.{ + const tokens = try bounded_tokenize(.RP2040, "pull noblock"); + try expect_instr_pull(.RP2040, .{ .block = false, }, tokens.get(0)); } test "tokenize.instr.pull.ifempty" { - const tokens = try bounded_tokenize("pull ifempty noblock"); - try expect_instr_pull(.{ + const tokens = try bounded_tokenize(.RP2040, "pull ifempty noblock"); + try expect_instr_pull(.RP2040, .{ .block = false, .ifempty = true, }, tokens.get(0)); } test "tokenize.instr.mov" { - inline for (.{ - "pins", - "x", - "y", - "null", - "status", - "isr", - "osr", - }) |source| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("mov x {s}", .{source})); - - try expect_instr_mov(.{ - .source = @field(Token.Instruction.Mov.Source, source), - .destination = .x, - }, tokens.get(0)); - } - - inline for (.{ - "pins", - "x", - "y", - "exec", - "pc", - "isr", - "osr", - }) |dest| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("mov {s} x", .{dest})); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + inline for (.{ + "pins", + "x", + "y", + "null", + "status", + "isr", + "osr", + }) |source| { + const tokens = try bounded_tokenize(chip, comptime std.fmt.comptimePrint("mov x {s}", .{source})); + + try expect_instr_mov(chip, .{ + .source = @field(Token(chip).Instruction.Mov.Source, source), + .destination = .x, + }, tokens.get(0)); + } - try expect_instr_mov(.{ - .source = .x, - .destination = @field(Token.Instruction.Mov.Destination, dest), - }, tokens.get(0)); - } + inline for (.{ + "pins", + "x", + "y", + "exec", + "pc", + "isr", + "osr", + }) |dest| { + const tokens = try bounded_tokenize(chip, comptime std.fmt.comptimePrint("mov {s} x", .{dest})); + + try expect_instr_mov(chip, .{ + .source = .x, + .destination = @field(Token(chip).Instruction.Mov.Destination, dest), + }, tokens.get(0)); + } + // RP2350 also supports pindirs as dest + { + const tokens = try bounded_tokenize(.RP2350, "mov pindirs x"); - const Operation = Token.Instruction.Mov.Operation; - const operations = std.StaticStringMap(Operation).initComptime(.{ - .{ "!", .invert }, - .{ "~", .invert }, - .{ "::", .bit_reverse }, - }); + try expect_instr_mov(.RP2350, .{ + .source = .x, + .destination = @field(Token(.RP2350).Instruction.Mov.Destination, "pindirs"), + }, tokens.get(0)); + } - inline for (.{ "", " " }) |space| { - inline for (comptime operations.keys(), comptime operations.values()) |str, operation| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("mov x {s}{s}y", .{ - str, - space, - })); + const Operation = Token(chip).Instruction.Mov.Operation; + const operations = std.StaticStringMap(Operation).initComptime(.{ + .{ "!", .invert }, + .{ "~", .invert }, + .{ "::", .bit_reverse }, + }); - try expect_instr_mov(.{ - .destination = .x, - .operation = operation, - .source = .y, - }, tokens.get(0)); + inline for (.{ "", " " }) |space| { + inline for (comptime operations.keys(), comptime operations.values()) |str, operation| { + const tokens = try bounded_tokenize(chip, comptime std.fmt.comptimePrint("mov x {s}{s}y", .{ + str, + space, + })); + + try expect_instr_mov(chip, .{ + .destination = .x, + .operation = operation, + .source = .y, + }, tokens.get(0)); + } } } } @@ -1797,22 +2122,24 @@ test "tokenize.instr.irq" { }); inline for (comptime modes.keys(), comptime modes.values(), 0..) |key, value, num| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("irq {s} {}", .{ - key, - num, - })); + inline for (comptime .{ Chip.RP2040, Chip.RP2350 }) |chip| { + const tokens = try bounded_tokenize(chip, comptime std.fmt.comptimePrint("irq {s} {}", .{ + key, + num, + })); - try expect_instr_irq(.{ - .clear = value.clear, - .wait = value.wait, - .num = num, - }, tokens.get(0)); + try expect_instr_irq(chip, .{ + .clear = value.clear, + .wait = value.wait, + .num = num, + }, tokens.get(0)); + } } } test "tokenize.instr.irq.rel" { - const tokens = try bounded_tokenize("irq set 2 rel"); - try expect_instr_irq(.{ + const tokens = try bounded_tokenize(.RP2040, "irq set 2 rel"); + try expect_instr_irq(.RP2040, .{ .clear = false, .wait = false, .num = 2, @@ -1820,6 +2147,16 @@ test "tokenize.instr.irq.rel" { }, tokens.get(0)); } +test "tokenize.instr.irq.relnext" { + const tokens = try bounded_tokenize(.RP2350, "irq set 2 next"); + try expect_instr_irq(.RP2350, .{ + .clear = false, + .wait = false, + .num = 2, + .idxmode = @intFromEnum(Token(.RP2350).Instruction.Irq.IdxMode.next), + }, tokens.get(0)); +} + test "tokenize.instr.set" { inline for (.{ "pins", @@ -1827,17 +2164,17 @@ test "tokenize.instr.set" { "y", "pindirs", }) |dest| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("set {s}, 2", .{dest})); - try expect_instr_set(.{ - .dest = @field(Token.Instruction.Set.Destination, dest), + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("set {s}, 2", .{dest})); + try expect_instr_set(.RP2040, .{ + .dest = @field(Token(.RP2040).Instruction.Set.Destination, dest), .value = .{ .integer = 2 }, }, tokens.get(0)); } } test "tokenize.instr.set.with expression including define" { - const tokens = try bounded_tokenize("set X, (NUM_CYCLES - 1) ; initialise the loop counter"); - try expect_instr_set(.{ + const tokens = try bounded_tokenize(.RP2040, "set X, (NUM_CYCLES - 1) ; initialise the loop counter"); + try expect_instr_set(.RP2040, .{ .dest = .x, .value = .{ .expression = "(NUM_CYCLES - 1)" }, }, tokens.get(0)); @@ -1858,15 +2195,15 @@ const instruction_examples = .{ test "tokenize.instr.label prefixed" { inline for (instruction_examples) |instr| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("my_label: {s}", .{instr})); + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("my_label: {s}", .{instr})); try expectEqual(@as(usize, 2), tokens.len); - try expect_label(.{ .name = "my_label" }, tokens.get(0)); + try expect_label(.RP2040, .{ .name = "my_label" }, tokens.get(0)); } } test "tokenize.instr.side_set" { inline for (instruction_examples) |instr| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("{s} side 0", .{instr})); + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("{s} side 0", .{instr})); const token = tokens.get(0); try expect_value(.{ .expression = "0", @@ -1877,7 +2214,7 @@ test "tokenize.instr.side_set" { test "tokenize.instr.delay" { inline for (instruction_examples) |instr| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("{s} [1]", .{instr})); + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("{s} [1]", .{instr})); const token = tokens.get(0); try expectEqual(@as(?Value, null), token.data.instruction.side_set); try expect_value(.{ @@ -1888,7 +2225,7 @@ test "tokenize.instr.delay" { test "tokenize.instr.delay.expression" { inline for (instruction_examples) |instr| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("{s} [T-1]", .{instr})); + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("{s} [T-1]", .{instr})); const token = tokens.get(0); try expectEqual(@as(?Value, null), token.data.instruction.side_set); try expect_value(.{ @@ -1899,7 +2236,7 @@ test "tokenize.instr.delay.expression" { test "tokenize.instr.side_set.expression" { inline for (instruction_examples) |instr| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("{s} side (N-1)", .{instr})); + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("{s} side (N-1)", .{instr})); const token = tokens.get(0); try expect_value(.{ .expression = "(N-1)", @@ -1910,7 +2247,7 @@ test "tokenize.instr.side_set.expression" { test "tokenize.instr.side_set and delay" { inline for (instruction_examples) |instr| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("{s} side 1 [2]", .{instr})); + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("{s} side 1 [2]", .{instr})); const token = tokens.get(0); try expect_value(.{ .expression = "1", @@ -1923,7 +2260,7 @@ test "tokenize.instr.side_set and delay" { test "tokenize.instr.side_set and delay reversed" { inline for (instruction_examples) |instr| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("{s} [2] side 1", .{instr})); + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("{s} [2] side 1", .{instr})); const token = tokens.get(0); try expect_value(.{ .expression = "1", @@ -1935,8 +2272,8 @@ test "tokenize.instr.side_set and delay reversed" { } test "tokenize.instr.comment with no whitespace" { - const tokens = try bounded_tokenize("nop side 0x0 [1]; CSn front porch"); - try expect_instr_nop(.{ + const tokens = try bounded_tokenize(.RP2040, "nop side 0x0 [1]; CSn front porch"); + try expect_instr_nop(.RP2040, .{ .side_set = .{ .expression = "0x0" }, .delay = .{ .expression = "1" }, }, tokens.get(0)); diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/common.zig b/port/raspberrypi/rp2xxx/src/hal/pio/common.zig index c688ec81..1def10ee 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/common.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/common.zig @@ -2,7 +2,7 @@ const std = @import("std"); const assert = std.debug.assert; const microzig = @import("microzig"); -const chip = @import("../compatibility.zig").chip; +const Chip = @import("../chip.zig").Chip; pub const PIO = microzig.chip.types.peripherals.PIO0; pub const PIO0 = microzig.chip.peripherals.PIO0; @@ -17,14 +17,26 @@ pub const Instruction = encoder.Instruction; pub const Program = assembler.Program; // global state for keeping track of used things -var used_instruction_space = switch (chip) { - .RP2040 => [_]u32{ 0, 0 }, - .RP2350 => [_]u32{ 0, 0, 0 }, -}; -var claimed_state_machines = switch (chip) { - .RP2040 => [_]u4{ 0, 0 }, - .RP2350 => [_]u4{ 0, 0, 0 }, -}; +fn UsedInstructionSpace(chip: Chip) type { + switch (chip) { + .RP2040 => return struct { + pub var val = [_]u32{ 0, 0 }; + }, + .RP2350 => return struct { + pub var val = [_]u32{ 0, 0, 0 }; + }, + } +} +fn ClaimedStateMachines(chip: Chip) type { + switch (chip) { + .RP2040 => return struct { + pub var val = [_]u4{ 0, 0 }; + }, + .RP2350 => return struct { + pub var val = [_]u4{ 0, 0, 0 }; + }, + } +} pub const Fifo = enum { tx, @@ -109,7 +121,7 @@ pub const PinMappingOptions = struct { in_base: u5 = 0, }; -pub fn PioImpl(EnumType: type) type { +pub fn PioImpl(EnumType: type, chip: Chip) type { return struct { pub fn get_instruction_memory(self: EnumType) *volatile [32]u32 { const regs = self.get_regs(); @@ -121,7 +133,7 @@ pub fn PioImpl(EnumType: type) type { if (origin != offset) return false; - const used_mask = used_instruction_space[@intFromEnum(self)]; + const used_mask = UsedInstructionSpace(chip).val[@intFromEnum(self)]; const program_mask = program.get_mask(); // We can add the program if the masks don't overlap, if there is @@ -151,7 +163,7 @@ pub fn PioImpl(EnumType: type) type { instruction_memory[i] = insn; const program_mask = program.get_mask(); - used_instruction_space[@intFromEnum(self)] |= program_mask << offset; + UsedInstructionSpace(chip).val[@intFromEnum(self)] |= program_mask << offset; } /// Public functions will need to lock independently, so only exposing this function for now @@ -168,11 +180,11 @@ pub fn PioImpl(EnumType: type) type { // TODO: const lock = hw.Lock.claim() // defer lock.unlock(); - const claimed_mask = claimed_state_machines[@intFromEnum(self)]; + const claimed_mask = ClaimedStateMachines(chip).val[@intFromEnum(self)]; return for (0..4) |i| { const sm_mask = (@as(u4, 1) << @as(u2, @intCast(i))); if (0 == (claimed_mask & sm_mask)) { - claimed_state_machines[@intFromEnum(self)] |= sm_mask; + ClaimedStateMachines(chip).val[@intFromEnum(self)] |= sm_mask; break @as(StateMachine, @enumFromInt(i)); } } else error.NoSpace; @@ -469,7 +481,7 @@ pub fn PioImpl(EnumType: type) type { self: EnumType, sm: StateMachine, initial_pc: u5, - options: StateMachineInitOptions, + options: StateMachineInitOptions(chip), ) void { // Halt the machine, set some sensible defaults self.sm_set_enabled(sm, false); @@ -484,7 +496,7 @@ pub fn PioImpl(EnumType: type) type { // Finally, clear some internal SM state self.sm_restart(sm); self.sm_clkdiv_restart(sm); - self.sm_exec(sm, Instruction{ + self.sm_exec(sm, Instruction(chip){ .tag = .jmp, .delay_side_set = 0, @@ -497,7 +509,7 @@ pub fn PioImpl(EnumType: type) type { }); } - pub fn sm_exec(self: EnumType, sm: StateMachine, instruction: Instruction) void { + pub fn sm_exec(self: EnumType, sm: StateMachine, instruction: Instruction(chip)) void { const sm_regs = self.get_sm_regs(sm); sm_regs.instr.raw = @as(u16, @bitCast(instruction)); } @@ -506,7 +518,7 @@ pub fn PioImpl(EnumType: type) type { self: EnumType, sm: StateMachine, program: Program, - options: LoadAndStartProgramOptions, + options: LoadAndStartProgramOptions(chip), ) !void { const expected_side_set_pins = if (program.side_set) |side_set| if (side_set.optional) @@ -552,57 +564,63 @@ pub fn PioImpl(EnumType: type) type { }; } -pub const ShiftOptions = switch (chip) { - .RP2040 => struct { - autopush: bool = false, - autopull: bool = false, - in_shiftdir: Direction = .right, - out_shiftdir: Direction = .right, - /// 0 means full 32-bits - push_threshold: u5 = 0, - /// 0 means full 32-bits - pull_threshold: u5 = 0, - join_tx: bool = false, - join_rx: bool = false, - - pub const Direction = enum(u1) { - left, - right, - }; - }, - .RP2350 => struct { - autopush: bool = false, - autopull: bool = false, - in_shiftdir: Direction = .right, - out_shiftdir: Direction = .right, - /// 0 means full 32-bits - push_threshold: u5 = 0, - /// 0 means full 32-bits - pull_threshold: u5 = 0, - join_tx: bool = false, - join_rx: bool = false, - - fjoin_rx_get: bool = false, - fjoin_rx_put: bool = false, - in_count: u5 = 0, - - pub const Direction = enum(u1) { - left, - right, - }; - }, -}; +pub fn ShiftOptions(chip: Chip) type { + return switch (chip) { + .RP2040 => struct { + autopush: bool = false, + autopull: bool = false, + in_shiftdir: Direction = .right, + out_shiftdir: Direction = .right, + /// 0 means full 32-bits + push_threshold: u5 = 0, + /// 0 means full 32-bits + pull_threshold: u5 = 0, + join_tx: bool = false, + join_rx: bool = false, + + pub const Direction = enum(u1) { + left, + right, + }; + }, + .RP2350 => struct { + autopush: bool = false, + autopull: bool = false, + in_shiftdir: Direction = .right, + out_shiftdir: Direction = .right, + /// 0 means full 32-bits + push_threshold: u5 = 0, + /// 0 means full 32-bits + pull_threshold: u5 = 0, + join_tx: bool = false, + join_rx: bool = false, + + fjoin_rx_get: bool = false, + fjoin_rx_put: bool = false, + in_count: u5 = 0, + + pub const Direction = enum(u1) { + left, + right, + }; + }, + }; +} -pub const StateMachineInitOptions = struct { - clkdiv: ClkDivOptions = .{}, - pin_mappings: PinMappingOptions = .{}, - exec: ExecOptions = .{}, - shift: ShiftOptions = .{}, -}; +pub fn StateMachineInitOptions(chip: Chip) type { + return struct { + clkdiv: ClkDivOptions = .{}, + pin_mappings: PinMappingOptions = .{}, + exec: ExecOptions = .{}, + shift: ShiftOptions(chip) = .{}, + }; +} -pub const LoadAndStartProgramOptions = struct { - clkdiv: ClkDivOptions, - shift: ShiftOptions = .{}, - pin_mappings: PinMappingOptions = .{}, - exec: ExecOptions = .{}, -}; +pub fn LoadAndStartProgramOptions(chip: Chip) type { + return struct { + clkdiv: ClkDivOptions, + shift: ShiftOptions(chip) = .{}, + pin_mappings: PinMappingOptions = .{}, + exec: ExecOptions = .{}, + }; +} diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/rp2040.zig b/port/raspberrypi/rp2xxx/src/hal/pio/rp2040.zig index 2b8144d8..61187890 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/rp2040.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/rp2040.zig @@ -13,6 +13,7 @@ pub const Pio = enum(u1) { pio0 = 0, pio1 = 1, + const PioImpl = common.PioImpl(Pio, .RP2040); pub fn get_regs(self: Pio) *volatile common.PIO { return switch (self) { .pio0 => common.PIO0, @@ -20,7 +21,7 @@ pub const Pio = enum(u1) { }; } - pub const get_instruction_memory = common.PioImpl(Pio).get_instruction_memory; + pub const get_instruction_memory = PioImpl.get_instruction_memory; pub fn gpio_init(self: Pio, pin: gpio.Pin) void { pin.set_function(switch (self) { @@ -29,17 +30,17 @@ pub const Pio = enum(u1) { }); } - pub const can_add_program_at_offset = common.PioImpl(Pio).can_add_program_at_offset; - pub const find_offset_for_program = common.PioImpl(Pio).find_offset_for_program; - pub const add_program_at_offset_unlocked = common.PioImpl(Pio).add_program_at_offset_unlocked; - pub const add_program = common.PioImpl(Pio).add_program; - pub const claim_unused_state_machine = common.PioImpl(Pio).claim_unused_state_machine; - pub const get_sm_regs = common.PioImpl(Pio).get_sm_regs; - pub const get_irq_regs = common.PioImpl(Pio).get_irq_regs; - pub const sm_set_clkdiv = common.PioImpl(Pio).sm_set_clkdiv; - pub const sm_set_exec_options = common.PioImpl(Pio).sm_set_exec_options; - - pub fn sm_set_shift_options(self: Pio, sm: common.StateMachine, options: common.ShiftOptions) void { + pub const can_add_program_at_offset = PioImpl.can_add_program_at_offset; + pub const find_offset_for_program = PioImpl.find_offset_for_program; + pub const add_program_at_offset_unlocked = PioImpl.add_program_at_offset_unlocked; + pub const add_program = PioImpl.add_program; + pub const claim_unused_state_machine = PioImpl.claim_unused_state_machine; + pub const get_sm_regs = PioImpl.get_sm_regs; + pub const get_irq_regs = PioImpl.get_irq_regs; + pub const sm_set_clkdiv = PioImpl.sm_set_clkdiv; + pub const sm_set_exec_options = PioImpl.sm_set_exec_options; + + pub fn sm_set_shift_options(self: Pio, sm: common.StateMachine, options: common.ShiftOptions(.RP2040)) void { const sm_regs = self.get_sm_regs(sm); sm_regs.shiftctrl.write(.{ .AUTOPUSH = @intFromBool(options.autopush), @@ -57,19 +58,19 @@ pub const Pio = enum(u1) { }); } - pub const sm_set_pin_mappings = common.PioImpl(Pio).sm_set_pin_mappings; - pub const sm_set_pindir = common.PioImpl(Pio).sm_set_pindir; - pub const sm_set_pin = common.PioImpl(Pio).sm_set_pin; - pub const sm_get_rx_fifo = common.PioImpl(Pio).sm_get_rx_fifo; - pub const sm_is_rx_fifo_empty = common.PioImpl(Pio).sm_is_rx_fifo_empty; - pub const sm_blocking_read = common.PioImpl(Pio).sm_blocking_read; - pub const sm_read = common.PioImpl(Pio).sm_read; - pub const sm_is_tx_fifo_full = common.PioImpl(Pio).sm_is_tx_fifo_full; - pub const sm_get_tx_fifo = common.PioImpl(Pio).sm_get_tx_fifo; - pub const sm_write = common.PioImpl(Pio).sm_write; - pub const sm_blocking_write = common.PioImpl(Pio).sm_blocking_write; - pub const sm_set_enabled = common.PioImpl(Pio).sm_set_enabled; - pub const sm_clear_debug = common.PioImpl(Pio).sm_clear_debug; + pub const sm_set_pin_mappings = PioImpl.sm_set_pin_mappings; + pub const sm_set_pindir = PioImpl.sm_set_pindir; + pub const sm_set_pin = PioImpl.sm_set_pin; + pub const sm_get_rx_fifo = PioImpl.sm_get_rx_fifo; + pub const sm_is_rx_fifo_empty = PioImpl.sm_is_rx_fifo_empty; + pub const sm_blocking_read = PioImpl.sm_blocking_read; + pub const sm_read = PioImpl.sm_read; + pub const sm_is_tx_fifo_full = PioImpl.sm_is_tx_fifo_full; + pub const sm_get_tx_fifo = PioImpl.sm_get_tx_fifo; + pub const sm_write = PioImpl.sm_write; + pub const sm_blocking_write = PioImpl.sm_blocking_write; + pub const sm_set_enabled = PioImpl.sm_set_enabled; + pub const sm_clear_debug = PioImpl.sm_clear_debug; /// changing the state of fifos will clear them pub fn sm_clear_fifos(self: Pio, sm: common.StateMachine) void { @@ -93,12 +94,12 @@ pub const Pio = enum(u1) { xor_shiftctrl.write(mask); } - pub const sm_fifo_level = common.PioImpl(Pio).sm_fifo_level; - pub const sm_clear_interrupt = common.PioImpl(Pio).sm_clear_interrupt; - pub const sm_enable_interrupt = common.PioImpl(Pio).sm_enable_interrupt; - pub const sm_restart = common.PioImpl(Pio).sm_restart; - pub const sm_clkdiv_restart = common.PioImpl(Pio).sm_clkdiv_restart; - pub const sm_init = common.PioImpl(Pio).sm_init; - pub const sm_exec = common.PioImpl(Pio).sm_exec; - pub const sm_load_and_start_program = common.PioImpl(Pio).sm_load_and_start_program; + pub const sm_fifo_level = PioImpl.sm_fifo_level; + pub const sm_clear_interrupt = PioImpl.sm_clear_interrupt; + pub const sm_enable_interrupt = PioImpl.sm_enable_interrupt; + pub const sm_restart = PioImpl.sm_restart; + pub const sm_clkdiv_restart = PioImpl.sm_clkdiv_restart; + pub const sm_init = PioImpl.sm_init; + pub const sm_exec = PioImpl.sm_exec; + pub const sm_load_and_start_program = PioImpl.sm_load_and_start_program; }; diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/rp2350.zig b/port/raspberrypi/rp2xxx/src/hal/pio/rp2350.zig index ceb3e6ba..21ff4957 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/rp2350.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/rp2350.zig @@ -16,6 +16,8 @@ pub const Pio = enum(u2) { pio1 = 1, pio2 = 2, + const PioImpl = common.PioImpl(Pio, .RP2350); + pub fn get_regs(self: Pio) *volatile common.PIO { return switch (self) { .pio0 => common.PIO0, @@ -24,7 +26,7 @@ pub const Pio = enum(u2) { }; } - pub const get_instruction_memory = common.PioImpl(Pio).get_instruction_memory; + pub const get_instruction_memory = PioImpl.get_instruction_memory; pub fn gpio_init(self: Pio, pin: gpio.Pin) void { pin.set_function(switch (self) { @@ -34,17 +36,17 @@ pub const Pio = enum(u2) { }); } - pub const can_add_program_at_offset = common.PioImpl(Pio).can_add_program_at_offset; - pub const find_offset_for_program = common.PioImpl(Pio).find_offset_for_program; - pub const add_program_at_offset_unlocked = common.PioImpl(Pio).add_program_at_offset_unlocked; - pub const add_program = common.PioImpl(Pio).add_program; - pub const claim_unused_state_machine = common.PioImpl(Pio).claim_unused_state_machine; - pub const get_sm_regs = common.PioImpl(Pio).get_sm_regs; - pub const get_irq_regs = common.PioImpl(Pio).get_irq_regs; - pub const sm_set_clkdiv = common.PioImpl(Pio).sm_set_clkdiv; - pub const sm_set_exec_options = common.PioImpl(Pio).sm_set_exec_options; - - pub fn sm_set_shift_options(self: Pio, sm: common.StateMachine, options: common.ShiftOptions) void { + pub const can_add_program_at_offset = PioImpl.can_add_program_at_offset; + pub const find_offset_for_program = PioImpl.find_offset_for_program; + pub const add_program_at_offset_unlocked = PioImpl.add_program_at_offset_unlocked; + pub const add_program = PioImpl.add_program; + pub const claim_unused_state_machine = PioImpl.claim_unused_state_machine; + pub const get_sm_regs = PioImpl.get_sm_regs; + pub const get_irq_regs = PioImpl.get_irq_regs; + pub const sm_set_clkdiv = PioImpl.sm_set_clkdiv; + pub const sm_set_exec_options = PioImpl.sm_set_exec_options; + + pub fn sm_set_shift_options(self: Pio, sm: common.StateMachine, options: common.ShiftOptions(.RP2350)) void { const sm_regs = self.get_sm_regs(sm); sm_regs.shiftctrl.write(.{ .AUTOPUSH = @intFromBool(options.autopush), @@ -66,19 +68,19 @@ pub const Pio = enum(u2) { }); } - pub const sm_set_pin_mappings = common.PioImpl(Pio).sm_set_pin_mappings; - pub const sm_set_pindir = common.PioImpl(Pio).sm_set_pindir; - pub const sm_set_pin = common.PioImpl(Pio).sm_set_pin; - pub const sm_get_rx_fifo = common.PioImpl(Pio).sm_get_rx_fifo; - pub const sm_is_rx_fifo_empty = common.PioImpl(Pio).sm_is_rx_fifo_empty; - pub const sm_blocking_read = common.PioImpl(Pio).sm_blocking_read; - pub const sm_read = common.PioImpl(Pio).sm_read; - pub const sm_is_tx_fifo_full = common.PioImpl(Pio).sm_is_tx_fifo_full; - pub const sm_get_tx_fifo = common.PioImpl(Pio).sm_get_tx_fifo; - pub const sm_write = common.PioImpl(Pio).sm_write; - pub const sm_blocking_write = common.PioImpl(Pio).sm_blocking_write; - pub const sm_set_enabled = common.PioImpl(Pio).sm_set_enabled; - pub const sm_clear_debug = common.PioImpl(Pio).sm_clear_debug; + pub const sm_set_pin_mappings = PioImpl.sm_set_pin_mappings; + pub const sm_set_pindir = PioImpl.sm_set_pindir; + pub const sm_set_pin = PioImpl.sm_set_pin; + pub const sm_get_rx_fifo = PioImpl.sm_get_rx_fifo; + pub const sm_is_rx_fifo_empty = PioImpl.sm_is_rx_fifo_empty; + pub const sm_blocking_read = PioImpl.sm_blocking_read; + pub const sm_read = PioImpl.sm_read; + pub const sm_is_tx_fifo_full = PioImpl.sm_is_tx_fifo_full; + pub const sm_get_tx_fifo = PioImpl.sm_get_tx_fifo; + pub const sm_write = PioImpl.sm_write; + pub const sm_blocking_write = PioImpl.sm_blocking_write; + pub const sm_set_enabled = PioImpl.sm_set_enabled; + pub const sm_clear_debug = PioImpl.sm_clear_debug; /// changing the state of fifos will clear them pub fn sm_clear_fifos(self: Pio, sm: common.StateMachine) void { @@ -105,12 +107,12 @@ pub const Pio = enum(u2) { xor_shiftctrl.write(mask); } - pub const sm_fifo_level = common.PioImpl(Pio).sm_fifo_level; - pub const sm_clear_interrupt = common.PioImpl(Pio).sm_clear_interrupt; - pub const sm_enable_interrupt = common.PioImpl(Pio).sm_enable_interrupt; - pub const sm_restart = common.PioImpl(Pio).sm_restart; - pub const sm_clkdiv_restart = common.PioImpl(Pio).sm_clkdiv_restart; - pub const sm_init = common.PioImpl(Pio).sm_init; - pub const sm_exec = common.PioImpl(Pio).sm_exec; - pub const sm_load_and_start_program = common.PioImpl(Pio).sm_load_and_start_program; + pub const sm_fifo_level = PioImpl.sm_fifo_level; + pub const sm_clear_interrupt = PioImpl.sm_clear_interrupt; + pub const sm_enable_interrupt = PioImpl.sm_enable_interrupt; + pub const sm_restart = PioImpl.sm_restart; + pub const sm_clkdiv_restart = PioImpl.sm_clkdiv_restart; + pub const sm_init = PioImpl.sm_init; + pub const sm_exec = PioImpl.sm_exec; + pub const sm_load_and_start_program = PioImpl.sm_load_and_start_program; };